mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
48a55d7de7
commit
e9728f0dd7
|
@ -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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue