diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-01-19 14:13:37 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-01-22 03:23:19 -0500 |
commit | e9728f0dd7dc06fb0f0d18552ab9599005cd2ab7 (patch) | |
tree | fbbf52c9d3f453c52124a01ff93167d589f5f7a9 /drivers/input/misc/pwm-beeper.c | |
parent | 48a55d7de79f95176f3ab372be66165a60be222f (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.c | 21 |
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 | } |