diff mbox

Input: pwm-beeper - fix: scheduling while atomic

Message ID 573C8761.9070601@gmx.at (mailing list archive)
State Superseded
Headers show

Commit Message

Manfred Schlaegl May 18, 2016, 3:16 p.m. UTC
Pwm config may sleep so defer it using a worker.

Trigger:
On a Freescale i.MX53 based board we ran into "BUG: scheduling while
atomic" because input_inject_event locks interrupts, but
imx_pwm_config_v2 sleeps.

Tested on Freescale i.MX53 SoC with 4.6.0 and 4.1.24.

Unmodified applicable to
 * 4.6    (stable)
 * 4.5.4  (stable)
 * 4.4.10 (longterm)
 * 4.1.24 (longterm)

Modified applicable to
 * 3.18.33 (longterm)

Signed-off-by: Manfred Schlaegl <manfred.schlaegl@gmx.at>
---
 drivers/input/misc/pwm-beeper.c | 54 +++++++++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 18 deletions(-)

Comments

Greg Kroah-Hartman May 18, 2016, 4:06 p.m. UTC | #1
On Wed, May 18, 2016 at 05:16:49PM +0200, Manfred Schlaegl wrote:
> Pwm config may sleep so defer it using a worker.
> 
> Trigger:
> On a Freescale i.MX53 based board we ran into "BUG: scheduling while
> atomic" because input_inject_event locks interrupts, but
> imx_pwm_config_v2 sleeps.
> 
> Tested on Freescale i.MX53 SoC with 4.6.0 and 4.1.24.
> 
> Unmodified applicable to
>  * 4.6    (stable)
>  * 4.5.4  (stable)
>  * 4.4.10 (longterm)
>  * 4.1.24 (longterm)
> 
> Modified applicable to
>  * 3.18.33 (longterm)

What does this all mean?  Have you read
Documentation/stable_kernel_rules.txt for how to mark things for stable
inclusion?

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Manfred Schlaegl May 19, 2016, 7:52 a.m. UTC | #2
On 2016-05-18 18:06, Greg Kroah-Hartman wrote:
> On Wed, May 18, 2016 at 05:16:49PM +0200, Manfred Schlaegl wrote:
>> Pwm config may sleep so defer it using a worker.
>>
>> Trigger:
>> On a Freescale i.MX53 based board we ran into "BUG: scheduling while
>> atomic" because input_inject_event locks interrupts, but
>> imx_pwm_config_v2 sleeps.
>>
>> Tested on Freescale i.MX53 SoC with 4.6.0 and 4.1.24.
>>
>> Unmodified applicable to
>>  * 4.6    (stable)
>>  * 4.5.4  (stable)
>>  * 4.4.10 (longterm)
>>  * 4.1.24 (longterm)
>>
>> Modified applicable to
>>  * 3.18.33 (longterm)
> 
> What does this all mean?  Have you read
> Documentation/stable_kernel_rules.txt for how to mark things for stable
> inclusion?
> 
> thanks,
> 
> greg k-h
> 

Sorry, I'm afraid I missed that. Thanks for the clarification. I will respect that in the future.

Should I resend the patch with a cleaned up message (without "Unmodified applicable to" and "Modified applicable to" stuff)?
Is the rest of message (formally) ok?

thanks,
manfred

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index f2261ab..7d783c8 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -20,21 +20,41 @@ 
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 
 struct pwm_beeper {
 	struct input_dev *input;
 	struct pwm_device *pwm;
+	struct work_struct work;
 	unsigned long period;
 };
 
 #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
 
+static void __pwm_beeper_set(struct pwm_beeper *beeper)
+{
+	unsigned long period = beeper->period;
+
+	pwm_config(beeper->pwm, period / 2, period);
+
+	if (period == 0)
+		pwm_disable(beeper->pwm);
+	else
+		pwm_enable(beeper->pwm);
+}
+
+static void pwm_beeper_work(struct work_struct *work)
+{
+	struct pwm_beeper *beeper =
+		container_of(work, struct pwm_beeper, work);
+
+	__pwm_beeper_set(beeper);
+}
+
 static int pwm_beeper_event(struct input_dev *input,
 			    unsigned int type, unsigned int code, int value)
 {
-	int ret = 0;
 	struct pwm_beeper *beeper = input_get_drvdata(input);
-	unsigned long period;
 
 	if (type != EV_SND || value < 0)
 		return -EINVAL;
@@ -49,18 +69,12 @@  static int pwm_beeper_event(struct input_dev *input,
 		return -EINVAL;
 	}
 
-	if (value == 0) {
-		pwm_disable(beeper->pwm);
-	} else {
-		period = HZ_TO_NANOSECONDS(value);
-		ret = pwm_config(beeper->pwm, period / 2, period);
-		if (ret)
-			return ret;
-		ret = pwm_enable(beeper->pwm);
-		if (ret)
-			return ret;
-		beeper->period = period;
-	}
+	if (value == 0)
+		beeper->period = 0;
+	else
+		beeper->period = HZ_TO_NANOSECONDS(value);
+
+	schedule_work(&beeper->work);
 
 	return 0;
 }
@@ -87,6 +101,8 @@  static int pwm_beeper_probe(struct platform_device *pdev)
 		goto err_free;
 	}
 
+	INIT_WORK(&beeper->work, pwm_beeper_work);
+
 	beeper->input = input_allocate_device();
 	if (!beeper->input) {
 		dev_err(&pdev->dev, "Failed to allocate input device\n");
@@ -133,6 +149,8 @@  static int pwm_beeper_remove(struct platform_device *pdev)
 {
 	struct pwm_beeper *beeper = platform_get_drvdata(pdev);
 
+	cancel_work_sync(&beeper->work);
+
 	input_unregister_device(beeper->input);
 
 	pwm_disable(beeper->pwm);
@@ -147,6 +165,8 @@  static int __maybe_unused pwm_beeper_suspend(struct device *dev)
 {
 	struct pwm_beeper *beeper = dev_get_drvdata(dev);
 
+	cancel_work_sync(&beeper->work);
+
 	if (beeper->period)
 		pwm_disable(beeper->pwm);
 
@@ -157,10 +177,8 @@  static int __maybe_unused pwm_beeper_resume(struct device *dev)
 {
 	struct pwm_beeper *beeper = dev_get_drvdata(dev);
 
-	if (beeper->period) {
-		pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
-		pwm_enable(beeper->pwm);
-	}
+	if (beeper->period)
+		__pwm_beeper_set(beeper);
 
 	return 0;
 }