aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc/pwm-beeper.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2017-01-19 14:13:37 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2017-01-22 03:23:19 -0500
commite9728f0dd7dc06fb0f0d18552ab9599005cd2ab7 (patch)
treefbbf52c9d3f453c52124a01ff93167d589f5f7a9 /drivers/input/misc/pwm-beeper.c
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/misc/pwm-beeper.c')
-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 {
27 struct pwm_device *pwm; 27 struct pwm_device *pwm;
28 struct work_struct work; 28 struct work_struct work;
29 unsigned long period; 29 unsigned long period;
30 bool suspended;
30}; 31};
31 32
32#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) 33#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
@@ -73,7 +74,8 @@ static int pwm_beeper_event(struct input_dev *input,
73 else 74 else
74 beeper->period = HZ_TO_NANOSECONDS(value); 75 beeper->period = HZ_TO_NANOSECONDS(value);
75 76
76 schedule_work(&beeper->work); 77 if (!beeper->suspended)
78 schedule_work(&beeper->work);
77 79
78 return 0; 80 return 0;
79} 81}
@@ -154,6 +156,15 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev)
154{ 156{
155 struct pwm_beeper *beeper = dev_get_drvdata(dev); 157 struct pwm_beeper *beeper = dev_get_drvdata(dev);
156 158
159 /*
160 * Spinlock is taken here is not to protect write to
161 * beeper->suspended, but to ensure that pwm_beeper_event
162 * does not re-submit work once flag is set.
163 */
164 spin_lock_irq(&beeper->input->event_lock);
165 beeper->suspended = true;
166 spin_unlock_irq(&beeper->input->event_lock);
167
157 pwm_beeper_stop(beeper); 168 pwm_beeper_stop(beeper);
158 169
159 return 0; 170 return 0;
@@ -163,8 +174,12 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
163{ 174{
164 struct pwm_beeper *beeper = dev_get_drvdata(dev); 175 struct pwm_beeper *beeper = dev_get_drvdata(dev);
165 176
166 if (beeper->period) 177 spin_lock_irq(&beeper->input->event_lock);
167 __pwm_beeper_set(beeper); 178 beeper->suspended = false;
179 spin_unlock_irq(&beeper->input->event_lock);
180
181 /* Let worker figure out if we should resume beeping */
182 schedule_work(&beeper->work);
168 183
169 return 0; 184 return 0;
170} 185}