On Fri, Feb 02, 2018 at 09:43:20PM +0200, Andy Shevchenko wrote: > +Cc: Dmitry > > On Fri, Feb 2, 2018 at 6:05 PM, 张波 <zbs...@126.com> wrote: > > > > in matrix_keypad.c, the function disable_row_irqs() may be called by > > matrix_keypad_interrupt() or matrix_keypad_stop(), there is race condition > > to disble irqs. > > > > > > If while matrix_keypad_stop() is calling, and the keypad interrupt is > > triggered, disable_row_irqs() is called by matrix_keypad_interrupt() and > > matrix_keypad_stop() at the same time. then disable_row_irqs() is called > > twice, and the device enter suspend state before keypad->work is executed. > > At this condition the device will start keypad and enable irq once after > > resume. and irqs are disabled yet because irqs are disabled twice and only > > enable once. > > then the device can't trigger keypad interrupts. > > this problem could reproduce easily by change code to add time delay in > > matrix_keypad_interrupt() just before calling schedule_delayed_work. > > > > Thanks for the patch. > > First of all, fix your email handlers. The patch is mangled quite badly. > > Second, follow the process [1] and don't forget to put your > Signed-off-by tag (hint `git commit -a -s`). > > [1]: > http://elixir.free-electrons.com/linux/latest/source/Documentation/process/submit-checklist.rst
Also, I do not think we need a new flag, simply taking the lock around "keypad->stopped = true" in matrix_keypad_stop() instead of trying to rely on mb() should fix the issue, as this will ensure that disabling interrupts and scheduling the scan works as an atomic operation. > > > > > diff --git a/drivers/input/keyboard/matrix_keypad.c > > b/drivers/input/keyboard/matrix_keypad.c > > index 1f316d66e6f7..03224ae9eedb 100644 > > --- a/drivers/input/keyboard/matrix_keypad.c > > +++ b/drivers/input/keyboard/matrix_keypad.c > > @@ -36,6 +36,7 @@ struct matrix_keypad { > > uint32_t last_key_state[MATRIX_MAX_COLS]; > > struct delayed_work work; > > spinlock_t lock; > > + int irq_enabled; > > bool scan_pending; > > bool stopped; > > bool gpio_all_disabled; > > @@ -90,6 +91,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad) > > { > > const struct matrix_keypad_platform_data *pdata = keypad->pdata; > > int i; > > + unsigned long flags; > > + > > + spin_lock_irqsave(&keypad->lock, flags); > > + > > + if (keypad->irq_enabled == 1) > > + goto out; > > > > if (pdata->clustered_irq > 0) > > enable_irq(pdata->clustered_irq); > > @@ -97,19 +104,31 @@ static void enable_row_irqs(struct matrix_keypad > > *keypad) > > for (i = 0; i < pdata->num_row_gpios; i++) > > enable_irq(gpio_to_irq(pdata->row_gpios[i])); > > } > > + keypad->irq_enabled = 1; > > + > > +out: > > + spin_unlock_irqrestore(&keypad->lock, flags); > > } > > > > static void disable_row_irqs(struct matrix_keypad *keypad) > > { > > const struct matrix_keypad_platform_data *pdata = keypad->pdata; > > int i; > > + unsigned long flags; > > > > + spin_lock_irqsave(&keypad->lock, flags); > > + if (keypad->irq_enabled == 0) > > + goto out; > > if (pdata->clustered_irq > 0) > > disable_irq_nosync(pdata->clustered_irq); > > else { > > for (i = 0; i < pdata->num_row_gpios; i++) > > disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); > > } > > + keypad->irq_enabled = 0; > > + > > +out: > > + spin_unlock_irqrestore(&keypad->lock, flags); > > } > > > > /* > > @@ -167,18 +186,13 @@ static void matrix_keypad_scan(struct work_struct > > *work) > > activate_all_cols(pdata, true); > > > > /* Enable IRQs again */ > > - spin_lock_irq(&keypad->lock); > > keypad->scan_pending = false; > > enable_row_irqs(keypad); > > - spin_unlock_irq(&keypad->lock); > > } > > > > static irqreturn_t matrix_keypad_interrupt(int irq, void *id) > > { > > struct matrix_keypad *keypad = id; > > - unsigned long flags; > > - > > - spin_lock_irqsave(&keypad->lock, flags); You really need to take the lock here, since if you are not using "clustered" interrupt, your interrupt routines may run simultaneously, which you do not want. > > > > /* > > * See if another IRQ beaten us to it and scheduled the > > @@ -194,7 +208,6 @@ static irqreturn_t matrix_keypad_interrupt(int irq, > > void *id) > > msecs_to_jiffies(keypad->pdata->debounce_ms)); > > > > out: > > - spin_unlock_irqrestore(&keypad->lock, flags); > > return IRQ_HANDLED; > > } > > > > -- > With Best Regards, > Andy Shevchenko Thanks. -- Dmitry