Skip to contentSkip to conversation

led: ledtrig-transient: replace timer_list with hrtimer

PATCH In review David Lin 3 participants 10 messages Apr 24, 2017 – Apr 27, 2017

Subsystems (3)
lore
Share & tools
lore

David Lin

author
#0 lore raw

PATCH 1/1 led: ledtrig-transient: replace timer_list with hrtimer

This patch replaces the kernel timer used by led transient trigger as an one-shot timer with an hrtimer. As Android is moving away from the obsoleted timed_output to ledtrig-transient for the vibrator HAL, ledtrig-transient needs to be able to handle the "duration" property to millisecond precision as modern haptic actuators can be driven in precisely one cycle (~1 ms) in order to provide a crisp and subtle feedback.

Cc Richard Purdie <[email protected]>
Cc Jacek Anaszewski <[email protected]>
Cc Pavel Machek <[email protected]>
Cc Rob Herring <[email protected]>
Cc Rom Lemarchand <[email protected]>
Cc Joel Fernandes <[email protected]>
---
 drivers/leds/trigger/ledtrig-transient.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)
drivers/leds/trigger/ledtrig-transient.c +17 -12
index 7e6011bd3646..94bb3bfc46e9 100644
@@ -23,25 +23,28 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/slab.h>
-#include <linux/timer.h>
+#include <linux/hrtimer.h>
 #include <linux/leds.h>
 #include "../leds.h"
 
 struct transient_trig_data {
+	struct led_classdev *led_cdev;
 	int activate;
 	int state;
 	int restore_state;
 	unsigned long duration;
-	struct timer_list timer;
+	struct hrtimer timer;
 };
 
-static void transient_timer_function(unsigned long data)
+static enum hrtimer_restart transient_timer_function(struct hrtimer *timer)
 {
-	struct led_classdev *led_cdev = (struct led_classdev *) data;
-	struct transient_trig_data *transient_data = led_cdev->trigger_data;
+	struct transient_trig_data *transient_data =
+		container_of(timer, struct transient_trig_data, timer);
 
 	transient_data->activate = 0;
-	led_set_brightness_nosleep(led_cdev, transient_data->restore_state);
+	led_set_brightness_nosleep(transient_data->led_cdev,
+				   transient_data->restore_state);
+	return HRTIMER_NORESTART;
 }
 
 static ssize_t transient_activate_show(struct device *dev,
@@ -70,7 +73,7 @@ static ssize_t transient_activate_store(struct device *dev,
 
 	/* cancel the running timer */
 	if (state == 0 && transient_data->activate == 1) {
-		del_timer(&transient_data->timer);
+		hrtimer_cancel(&transient_data->timer);
 		transient_data->activate = state;
 		led_set_brightness_nosleep(led_cdev,
 					transient_data->restore_state);
@@ -84,8 +87,9 @@ static ssize_t transient_activate_store(struct device *dev,
 		led_set_brightness_nosleep(led_cdev, transient_data->state);
 		transient_data->restore_state =
 		    (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
-		mod_timer(&transient_data->timer,
-			  jiffies + msecs_to_jiffies(transient_data->duration));
+		hrtimer_start(&transient_data->timer,
+			      ms_to_ktime(transient_data->duration),
+			      HRTIMER_MODE_REL);
 	}
 
 	/* state == 0 && transient_data->activate == 0
@@ -168,6 +172,7 @@ static void transient_trig_activate(struct led_classdev *led_cdev)
 			"unable to allocate transient trigger\n");
 		return;
 	}
+	tdata->led_cdev = led_cdev;
 	led_cdev->trigger_data = tdata;
 
 	rc = device_create_file(led_cdev->dev, &dev_attr_activate);
@@ -182,8 +187,8 @@ static void transient_trig_activate(struct led_classdev *led_cdev)
 	if (rc)
 		goto err_out_state;
 
-	setup_timer(&tdata->timer, transient_timer_function,
-		    (unsigned long) led_cdev);
+	hrtimer_init(&tdata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	tdata->timer.function = transient_timer_function;
 	led_cdev->activated = true;
 
 	return;
@@ -203,7 +208,7 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev)
 	struct transient_trig_data *transient_data = led_cdev->trigger_data;
 
 	if (led_cdev->activated) {
-		del_timer_sync(&transient_data->timer);
+		hrtimer_cancel(&transient_data->timer);
 		led_set_brightness_nosleep(led_cdev,
 					transient_data->restore_state);
 		device_remove_file(led_cdev->dev, &dev_attr_activate);
Signature · 1 line
2.12.2.816.g2cccc81164-goog
Acked-by on v1 1/1
#1 lore raw
Quoted 7 lines · David Lin

This patch replaces the kernel timer used by led transient trigger as an one-shot timer with an hrtimer. As Android is moving away from the obsoleted timed_output to ledtrig-transient for the vibrator HAL, ledtrig-transient needs to be able to handle the "duration" property to millisecond precision as modern haptic actuators can be driven in precisely one cycle (~1 ms) in order to provide a crisp and subtle feedback.

(Insert rant about using LED subsystem for something that is not a LED; if we are going to these subtleties, LEDs _do_ have different properties than vibration motor...).

Otherwise it looks good.

Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#2 lore raw
In reply to David Lin

Hi David,

Thanks for the patch.

Unfortunately we cannot switch to using hr timers just like that without introducing side effects for many devices. We had similar attempt of increasing timer tirgger accuracy two years ago [0].

In short words, for drivers that can sleep while setting brightness and/or are using a bus like I2C you will not be able to enforce 1ms delay period.

I recommend you to go through the thread [0] so that we had a well defined ground for the discussion on how to address this issue properly.

Alternatively, in order to avoid all quirks related to LED subsystem, I'd propose to implement this feature in the GPIO subsystem, which seems to be more suitable place for it.

[0] https://lkml.org/lkml/2015/4/28/260

Best regards,
Jacek Anaszewski

Quoted 109 lines · David Lin

This patch replaces the kernel timer used by led transient trigger as an one-shot timer with an hrtimer. As Android is moving away from the obsoleted timed_output to ledtrig-transient for the vibrator HAL, ledtrig-transient needs to be able to handle the "duration" property to millisecond precision as modern haptic actuators can be driven in precisely one cycle (~1 ms) in order to provide a crisp and subtle feedback.

Cc Richard Purdie <[email protected]>
Cc Jacek Anaszewski <[email protected]>
Cc Pavel Machek <[email protected]>
Cc Rob Herring <[email protected]>
Cc Rom Lemarchand <[email protected]>
Cc Joel Fernandes <[email protected]>
---
 drivers/leds/trigger/ledtrig-transient.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)
drivers/leds/trigger/ledtrig-transient.c +17 -12
index 7e6011bd3646..94bb3bfc46e9 100644
@@ -23,25 +23,28 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/slab.h>
-#include <linux/timer.h>
+#include <linux/hrtimer.h>
 #include <linux/leds.h>
 #include "../leds.h"
 
 struct transient_trig_data {
+	struct led_classdev *led_cdev;
 	int activate;
 	int state;
 	int restore_state;
 	unsigned long duration;
-	struct timer_list timer;
+	struct hrtimer timer;
 };
 
-static void transient_timer_function(unsigned long data)
+static enum hrtimer_restart transient_timer_function(struct hrtimer *timer)
 {
-	struct led_classdev *led_cdev = (struct led_classdev *) data;
-	struct transient_trig_data *transient_data = led_cdev->trigger_data;
+	struct transient_trig_data *transient_data =
+		container_of(timer, struct transient_trig_data, timer);
 
 	transient_data->activate = 0;
-	led_set_brightness_nosleep(led_cdev, transient_data->restore_state);
+	led_set_brightness_nosleep(transient_data->led_cdev,
+				   transient_data->restore_state);
+	return HRTIMER_NORESTART;
 }
 
 static ssize_t transient_activate_show(struct device *dev,
@@ -70,7 +73,7 @@ static ssize_t transient_activate_store(struct device *dev,
 
 	/* cancel the running timer */
 	if (state == 0 && transient_data->activate == 1) {
-		del_timer(&transient_data->timer);
+		hrtimer_cancel(&transient_data->timer);
 		transient_data->activate = state;
 		led_set_brightness_nosleep(led_cdev,
 					transient_data->restore_state);
@@ -84,8 +87,9 @@ static ssize_t transient_activate_store(struct device *dev,
 		led_set_brightness_nosleep(led_cdev, transient_data->state);
 		transient_data->restore_state =
 		    (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
-		mod_timer(&transient_data->timer,
-			  jiffies + msecs_to_jiffies(transient_data->duration));
+		hrtimer_start(&transient_data->timer,
+			      ms_to_ktime(transient_data->duration),
+			      HRTIMER_MODE_REL);
 	}
 
 	/* state == 0 && transient_data->activate == 0
@@ -168,6 +172,7 @@ static void transient_trig_activate(struct led_classdev *led_cdev)
 			"unable to allocate transient trigger\n");
 		return;
 	}
+	tdata->led_cdev = led_cdev;
 	led_cdev->trigger_data = tdata;
 
 	rc = device_create_file(led_cdev->dev, &dev_attr_activate);
@@ -182,8 +187,8 @@ static void transient_trig_activate(struct led_classdev *led_cdev)
 	if (rc)
 		goto err_out_state;
 
-	setup_timer(&tdata->timer, transient_timer_function,
-		    (unsigned long) led_cdev);
+	hrtimer_init(&tdata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	tdata->timer.function = transient_timer_function;
 	led_cdev->activated = true;
 
 	return;
@@ -203,7 +208,7 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev)
 	struct transient_trig_data *transient_data = led_cdev->trigger_data;
 
 	if (led_cdev->activated) {
-		del_timer_sync(&transient_data->timer);
+		hrtimer_cancel(&transient_data->timer);
 		led_set_brightness_nosleep(led_cdev,
 					transient_data->restore_state);
 		device_remove_file(led_cdev->dev, &dev_attr_activate);
#3 lore raw

Hi!

Quoted 12 lines

Hi David,

Thanks for the patch.

Unfortunately we cannot switch to using hr timers just like that without introducing side effects for many devices. We had similar attempt of increasing timer tirgger accuracy two years ago [0].

In short words, for drivers that can sleep while setting brightness and/or are using a bus like I2C you will not be able to enforce 1ms delay period.

Quoted 7 lines

I recommend you to go through the thread [0] so that we had a well defined ground for the discussion on how to address this issue properly.

Alternatively, in order to avoid all quirks related to LED subsystem, I'd propose to implement this feature in the GPIO subsystem, which seems to be more suitable place for it.

Actually.. make that "implement it in force feedback subsystem where it belongs". And we actually have force feedback subsystem, already, see drivers/input/ff-core.c .

(Nokia N900 actually uses that subsystem for the vibration motor, so there's existing code...)

Thanks,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
author
#4 lore raw
In reply to Jacek Anaszewski

Hi Jacek,

Quoted 9 lines · Jacek Anaszewski

Hi David,

Thanks for the patch.

Unfortunately we cannot switch to using hr timers just like that without introducing side effects for many devices. We had similar attempt of increasing timer tirgger accuracy two years ago [0].

Quoted 8 lines

In short words, for drivers that can sleep while setting brightness and/or are using a bus like I2C you will not be able to enforce 1ms delay period.

I recommend you to go through the thread [0] so that we had a well defined ground for the discussion on how to address this issue properly.

I think I understand the background now, and would agree that not all the LED driver require hrtimer as human eye can't probably tell there's a 10ms variation in a blink. However, there's a need to support hrtimer if the LED subsystem claims support the use case of vibrator (please see Documentation/leds/ledtrig-transient.txt) as even a 5ms of variation is perceivable to the user. I'm thinking if a better interim solution is to introduce a LEDS_TRIGGER_TRANSIENT_HRTIMER config to work with both timers in compile time. Would you agree?

David

#5 lore raw

Hi David,

Quoted 17 lines · David Lin

Hi Jacek,

Quoted 13 lines · Jacek Anaszewski

Hi David,

Thanks for the patch.

Unfortunately we cannot switch to using hr timers just like that without introducing side effects for many devices. We had similar attempt of increasing timer tirgger accuracy two years ago [0].

In short words, for drivers that can sleep while setting brightness and/or are using a bus like I2C you will not be able to enforce 1ms delay period.

Quoted 8 lines
Quoted 4 lines

I recommend you to go through the thread [0] so that we had a well defined ground for the discussion on how to address this issue properly.

I think I understand the background now, and would agree that not all the LED driver require hrtimer as human eye can't probably tell there's a 10ms variation in a blink.

The main problem are side effects occurring when an event scheduled by hrtimer can't finish before the next one begins. We get warnings like in the example below (copied from [0]) then, and they have probably negative impact on the whole system performance.

echo "timer" > trigger
echo 1 > delay_on
echo 1 > delay_off
echo usec > delay_unit

[  178.584433] hrtimer: interrupt took 300747 ns
Quoted 7 lines

However, there's a need to
support hrtimer if the LED subsystem claims support the use case of vibrator (please see Documentation/leds/ledtrig-transient.txt) as even a 5ms of variation is perceivable to the user. I'm thinking if a better interim solution is to introduce a LEDS_TRIGGER_TRANSIENT_HRTIMER config to work with both timers in compile time. Would you agree?

I think that it would be better if LED class driver set a flag marking itself as capable of setting brightness with high rate. I'd limit that only to leds-gpio and devices driven through memory mapped registers.

Having the flag e.g. LED_BRIGHTNESS_FAST, we could add support for hr timers also to ledtrig-timer.

You can try also the other option mentioned by Pavel in [1].

[1] https://lkml.org/lkml/2017/4/24/881

Signature · 2 lines
Best regards,
Jacek Anaszewski
author
#6 lore raw
Quoted 8 lines · Jacek Anaszewski
Quoted 7 lines

However, there's a need to
support hrtimer if the LED subsystem claims support the use case of vibrator (please see Documentation/leds/ledtrig-transient.txt) as even a 5ms of variation is perceivable to the user. I'm thinking if a better interim solution is to introduce a LEDS_TRIGGER_TRANSIENT_HRTIMER config to work with both timers in compile time. Would you agree?

Quoted 7 lines

I think that it would be better if LED class driver set a flag marking itself as capable of setting brightness with high rate. I'd limit that only to leds-gpio and devices driven through memory mapped registers.

Having the flag e.g. LED_BRIGHTNESS_FAST, we could add support for hr timers also to ledtrig-timer.

Can I resubmit the patch implementing LED_BRIGHTNESS_FAST using hrtimer?

Quoted 2 lines

You can try also the other option mentioned by Pavel in [1].

Thanks, Pavel. It does look like that input-ff is a more appropriate subsystem for implementing a vibrator/haptics driver. It also seems that it's relying on the userspace to control the timing of the play/stop events which is likely to be less accurate than a hrtimer in the kernel. But it provides more effect control than the LED subsystem.

#7 lore raw
Quoted 15 lines · David Lin
Quoted 13 lines · Jacek Anaszewski
Quoted 7 lines

However, there's a need to
support hrtimer if the LED subsystem claims support the use case of vibrator (please see Documentation/leds/ledtrig-transient.txt) as even a 5ms of variation is perceivable to the user. I'm thinking if a better interim solution is to introduce a LEDS_TRIGGER_TRANSIENT_HRTIMER config to work with both timers in compile time. Would you agree?

I think that it would be better if LED class driver set a flag marking itself as capable of setting brightness with high rate. I'd limit that only to leds-gpio and devices driven through memory mapped registers.

Quoted 4 lines
Quoted 2 lines

Having the flag e.g. LED_BRIGHTNESS_FAST, we could add support for hr timers also to ledtrig-timer.

Can I resubmit the patch implementing LED_BRIGHTNESS_FAST using hrtimer?

Yeah, but please split the changes into two patches:

1/2 - addition of a flag to linux/leds.h and corresponding update of
Documentation/leds/leds-class.txt
2/2 - addition of hr timer support to ledtrig-transient.c

Signature · 2 lines
Best regards,
Jacek Anaszewski
#8 lore raw
In reply to David Lin
Quoted 22 lines · David Lin

Hi Jacek,

Quoted 9 lines · Jacek Anaszewski

Hi David,

Thanks for the patch.

Unfortunately we cannot switch to using hr timers just like that without introducing side effects for many devices. We had similar attempt of increasing timer tirgger accuracy two years ago [0].

Quoted 8 lines

In short words, for drivers that can sleep while setting brightness and/or are using a bus like I2C you will not be able to enforce 1ms delay period.

I recommend you to go through the thread [0] so that we had a well defined ground for the discussion on how to address this issue properly.

Quoted 6 lines

I think I understand the background now, and would agree that not all the LED driver require hrtimer as human eye can't probably tell there's a 10ms variation in a blink. However, there's a need to support hrtimer if the LED subsystem claims support the use case of vibrator (please see Documentation/leds/ledtrig-transient.txt) as even a 5ms of variation is perceivable to the user. I'm thinking if a

I believe we should fix the documentation. It is LED subsystem, requirements are different, and we _already_ have haptic feedback subsystem.
Pavel

IOW, I suggest this: (hmm, and more led->LED is needed, and more english fixes. Oh well.)

Documentation/leds/ledtrig-transient.txt +1 -8
index 3bd38b4..c5cf475 100644
@@ -16,17 +16,11 @@ set a timer to hold a state, however when user space application crashes or
 goes away without deactivating the timer, the hardware will be left in that
 state permanently.
 
-As a specific example of this use-case, let's look at vibrate feature on
-phones. Vibrate function on phones is implemented using PWM pins on SoC or
-PMIC. There is a need to activate one shot timer to control the vibrate
-feature, to prevent user space crashes leaving the phone in vibrate mode
-permanently causing the battery to drain.
-
 Transient trigger addresses the need for one shot timer activation. The
 transient trigger can be enabled and disabled just like the other leds
 triggers.
 
-When an led class device driver registers itself, it can specify all leds
+When an LED class device driver registers itself, it can specify all leds
 triggers it supports and a default trigger. During registration, activation
 routine for the default trigger gets called. During registration of an led
 class device, the LED state does not change.
@@ -144,7 +138,6 @@ repeat the following step as needed:
 	echo none > trigger
 
 This trigger is intended to be used for for the following example use cases:
- - Control of vibrate (phones, tablets etc.) hardware by user space app.
  - Use of LED by user space app as activity indicator.
  - Use of LED by user space app as a kind of watchdog indicator -- as
        long as the app is alive, it can keep the LED illuminated, if it dies
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#9 lore raw
Quoted 50 lines · Pavel Machek
Quoted 22 lines · David Lin

Hi Jacek,

Quoted 9 lines · Jacek Anaszewski

Hi David,

Thanks for the patch.

Unfortunately we cannot switch to using hr timers just like that without introducing side effects for many devices. We had similar attempt of increasing timer tirgger accuracy two years ago [0].

Quoted 8 lines

In short words, for drivers that can sleep while setting brightness and/or are using a bus like I2C you will not be able to enforce 1ms delay period.

I recommend you to go through the thread [0] so that we had a well defined ground for the discussion on how to address this issue properly.

Quoted 6 lines

I think I understand the background now, and would agree that not all the LED driver require hrtimer as human eye can't probably tell there's a 10ms variation in a blink. However, there's a need to support hrtimer if the LED subsystem claims support the use case of vibrator (please see Documentation/leds/ledtrig-transient.txt) as even a 5ms of variation is perceivable to the user. I'm thinking if a

I believe we should fix the documentation. It is LED subsystem, requirements are different, and we _already_ have haptic feedback subsystem.
Pavel

IOW, I suggest this: (hmm, and more led->LED is needed, and more english fixes. Oh well.)

Documentation/leds/ledtrig-transient.txt +0 -2
index 3bd38b4..c5cf475 100644
@@ -16,17 +16,11 @@ set a timer to hold a state, however when user space application crashes or
 goes away without deactivating the timer, the hardware will be left in that
 state permanently.
 
-As a specific example of this use-case, let's look at vibrate feature on
-phones. Vibrate function on phones is implemented using PWM pins on SoC or

Documentation/leds/ledtrig-transient.txt set a timer to hold a state, however when user space application crashes or · line 23

Quoted 10 lines
Documentation/leds/ledtrig-transient.txt +1 -5
-PMIC. There is a need to activate one shot timer to control the vibrate
-feature, to prevent user space crashes leaving the phone in vibrate mode
-permanently causing the battery to drain.
-
 Transient trigger addresses the need for one shot timer activation. The
 transient trigger can be enabled and disabled just like the other leds
 triggers.
 
-When an led class device driver registers itself, it can specify all leds
+When an LED class device driver registers itself, it can specify all leds

Also: s/leds/LEDs/ :-)

Documentation/leds/ledtrig-transient.txt repeat the following step as needed: · line 143

Quoted 13 lines
Documentation/leds/ledtrig-transient.txt +0 -1
 triggers it supports and a default trigger. During registration, activation
 routine for the default trigger gets called. During registration of an led
 class device, the LED state does not change.
@@ -144,7 +138,6 @@ repeat the following step as needed:
 	echo none > trigger
 
 This trigger is intended to be used for for the following example use cases:
- - Control of vibrate (phones, tablets etc.) hardware by user space app.
  - Use of LED by user space app as activity indicator.
  - Use of LED by user space app as a kind of watchdog indicator -- as
        long as the app is alive, it can keep the LED illuminated, if it dies

Ack. Will you submit an official patch?

Signature · 2 lines
Best regards,
Jacek Anaszewski