From: Jiri Pirko <jpirko@redhat.com> Date: Thu, 25 Sep 2008 15:42:45 +0200 Subject: [input] atkbd: delay executing of LED switching request Message-id: 20080925154245.27c95fd6@psychotron.englab.brq.redhat.com O-Subject: [RHEL5.3 patch] BZ461233 atkbd: Delay executing of LED switching request Bugzilla: 461233 RH-Acked-by: Rik van Riel <riel@redhat.com> RH-Acked-by: Markus Armbruster <armbru@redhat.com> RH-Acked-by: Brian Maly <bmaly@redhat.com> BZ461233 https://bugzilla.redhat.com/show_bug.cgi?id=461233 Description: On some boxes keyboard controllers are too slow to withstand continuous flow of requests to turn keyboard LEDs on and off and start losing some keypresses or even all of them. This causes a local denial of service situation. Upstream status: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=da4249c99fd59c4e224e4f9acaf07669d205bb1d Brew build: https://brewweb.devel.redhat.com/taskinfo?taskID=1493452 Test status: Booted on x86_64, i686. Tested on i686. With -92 kernel I did reproduce this issue. All keypresses were lost. I had to "kill -9" appropriate bash via ssh to turn it back to work. With -116 patched by this patch I was not able to reproduce issue. Not a single keypress lost. What's interesting is that with -92 led blinks regularly and quite fast. With -116 and -115 kernels (without the patch) I experienced about 3 times slower blinking and irregular. Jirka diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index dd99588..46c96f0 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -223,6 +223,7 @@ struct atkbd { unsigned long time; struct work_struct event_work; + unsigned long event_jiffies; struct mutex event_mutex; unsigned long event_mask; }; @@ -557,12 +558,30 @@ static void atkbd_event_work(void *data) } /* + * Schedule switch for execution. We need to throttle requests, + * otherwise keyboard may become unresponsive. + */ +static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) +{ + unsigned long delay = msecs_to_jiffies(50); + + if (time_after(jiffies, atkbd->event_jiffies + delay)) + delay = 0; + + atkbd->event_jiffies = jiffies; + set_bit(event_bit, &atkbd->event_mask); + wmb(); + schedule_delayed_work(&atkbd->event_work, delay); +} + +/* * Event callback from the input module. Events that change the state of * the hardware are processed here. If action can not be performed in * interrupt context it is offloaded to atkbd_event_work. */ -static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static int atkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) { struct atkbd *atkbd = dev->private; @@ -572,19 +591,12 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co switch (type) { case EV_LED: - set_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask); - wmb(); - schedule_work(&atkbd->event_work); + atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); return 0; case EV_REP: - - if (!atkbd->softrepeat) { - set_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask); - wmb(); - schedule_work(&atkbd->event_work); - } - + if (!atkbd->softrepeat) + atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT); return 0; }