Commit e9728f0d authored by Dmitry Torokhov's avatar Dmitry Torokhov

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: default avatarThierry Reding <thierry.reding@gmail.com>
Tested-by: default avatarDavid Lechner <david@lechnology.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 48a55d7d
...@@ -27,6 +27,7 @@ struct pwm_beeper { ...@@ -27,6 +27,7 @@ struct pwm_beeper {
struct pwm_device *pwm; struct pwm_device *pwm;
struct work_struct work; struct work_struct work;
unsigned long period; unsigned long period;
bool suspended;
}; };
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
...@@ -73,6 +74,7 @@ static int pwm_beeper_event(struct input_dev *input, ...@@ -73,6 +74,7 @@ static int pwm_beeper_event(struct input_dev *input,
else else
beeper->period = HZ_TO_NANOSECONDS(value); beeper->period = HZ_TO_NANOSECONDS(value);
if (!beeper->suspended)
schedule_work(&beeper->work); schedule_work(&beeper->work);
return 0; return 0;
...@@ -154,6 +156,15 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev) ...@@ -154,6 +156,15 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev)
{ {
struct pwm_beeper *beeper = dev_get_drvdata(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); pwm_beeper_stop(beeper);
return 0; return 0;
...@@ -163,8 +174,12 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) ...@@ -163,8 +174,12 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
{ {
struct pwm_beeper *beeper = dev_get_drvdata(dev); struct pwm_beeper *beeper = dev_get_drvdata(dev);
if (beeper->period) spin_lock_irq(&beeper->input->event_lock);
__pwm_beeper_set(beeper); 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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment