summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2017-01-19 11:13:37 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2017-01-22 00:23:19 -0800
commite9728f0dd7dc06fb0f0d18552ab9599005cd2ab7 (patch)
treefbbf52c9d3f453c52124a01ff93167d589f5f7a9 /drivers/input
parent48a55d7de79f95176f3ab372be66165a60be222f (diff)
Input: pwm-beeper - fix race when suspending
Usually userspace sends SND_BELL and SND_TONE events, and by the time pwm_beeper_suspend() runs userpsace is already frozen, but theoretically in-kernel users may send these events too, and that may cause pwm_beeper_event() scheduling another work after we canceled it. Let's introduce a "suspended" flag and check it in pwm_beeper_event() to avoid this race. Reviewed-by: Thierry Reding <thierry.reding@gmail.com> Tested-by: David Lechner <david@lechnology.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/misc/pwm-beeper.c21
1 files changed, 18 insertions, 3 deletions
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index 58a60b42c836..9d7987d9ba1b 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -27,6 +27,7 @@ struct pwm_beeper {
struct pwm_device *pwm;
struct work_struct work;
unsigned long period;
+ bool suspended;
};
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
@@ -73,7 +74,8 @@ static int pwm_beeper_event(struct input_dev *input,
else
beeper->period = HZ_TO_NANOSECONDS(value);
- schedule_work(&beeper->work);
+ if (!beeper->suspended)
+ schedule_work(&beeper->work);
return 0;
}
@@ -154,6 +156,15 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev)
{
struct pwm_beeper *beeper = dev_get_drvdata(dev);
+ /*
+ * Spinlock is taken here is not to protect write to
+ * beeper->suspended, but to ensure that pwm_beeper_event
+ * does not re-submit work once flag is set.
+ */
+ spin_lock_irq(&beeper->input->event_lock);
+ beeper->suspended = true;
+ spin_unlock_irq(&beeper->input->event_lock);
+
pwm_beeper_stop(beeper);
return 0;
@@ -163,8 +174,12 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
{
struct pwm_beeper *beeper = dev_get_drvdata(dev);
- if (beeper->period)
- __pwm_beeper_set(beeper);
+ spin_lock_irq(&beeper->input->event_lock);
+ beeper->suspended = false;
+ spin_unlock_irq(&beeper->input->event_lock);
+
+ /* Let worker figure out if we should resume beeping */
+ schedule_work(&beeper->work);
return 0;
}