diff mbox series

leds: trigger: netdev: skip setting baseline state in activate if hw-controlled

Message ID 49f1b91e-a637-4062-83c6-f851f7c80628@gmail.com
State Superseded
Headers show
Series leds: trigger: netdev: skip setting baseline state in activate if hw-controlled | expand

Commit Message

Heiner Kallweit Nov. 29, 2023, 10:41 a.m. UTC
The current codes uses the sw_control path in set_baseline_state() when
called from netdev_trig_activate() even if we're hw-controlled. This
may result in errors when led_set_brightness() is called because we may
not have set_brightness led ops (if hw doesn't support setting a LED
to ON).

Therefore set trigger_data->hw_control = true before calling
set_device_name() from netdev_trig_activate(). In this call chain we
have to prevent set_baseline_state() from being called, because this
would call hw_control_set(). Use led_cdev->trigger_data == NULL as
indicator for being called from netdev_trig_activate().

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/leds/trigger/ledtrig-netdev.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

Comments

Andrew Lunn Nov. 29, 2023, 4:36 p.m. UTC | #1
On Wed, Nov 29, 2023 at 11:41:51AM +0100, Heiner Kallweit wrote:
> The current codes uses the sw_control path in set_baseline_state() when
> called from netdev_trig_activate() even if we're hw-controlled. This
> may result in errors when led_set_brightness() is called because we may
> not have set_brightness led ops (if hw doesn't support setting a LED
> to ON).

Not having software on/off control of the LED is a problem. It breaks
the whole concept of offloading/accelerating. If we cannot control the
LED, there is nothing to accelerate. What do we do when the user
selects a configuration which is not supported by the hardware? The
API is not atomic, you cannot set multiple things at once. So the user
might be trying to get from one offloadable configuration to another
offloadable configuration, but needs to go via an configuration which
is not offloadable. Do we return -EOPNOTSUPP?

Before we accept patches like this, we need to discuss the concept of
how we support LEDs which cannot be controlled in software.

    Andrew
Andrew Lunn Nov. 29, 2023, 10:02 p.m. UTC | #2
> RTL8168 LED control allows to switch between different hw triggers. I
> was under the
> assumption that this is not uncommon.

I did take a look around various datasheets, and i did find a couple
like this, but the majority have the ability to do software control.

> In order to deal with the non-atomic issue we have to set
> trigger_data->mode to the
> resulting new mode, based on what the user set. Question is what we show to the
> user. If we show nothing, then he will expect the new mode to be active.
> If we show an error, then he may assume that his input had no effect.
> So we may have to show technically an OK, plus a message that his input has been
> stored, but is not supported by hw.

It is hard to show anything to the user. We are just doing

echo 1 > file.

There is no channel to the user other than an error code.

There is also the question about what the LED should show. Ideally it
should indicate some sort of state to indicate there is an
error. Either constantly blink, turn off, etc. Maybe that is the
solution. You implement a set_brightness function which just indicates
an error on the LED, but otherwise return O.K?

   Andrew
Heiner Kallweit Dec. 1, 2023, 10:32 p.m. UTC | #3
On 29.11.2023 23:02, Andrew Lunn wrote:
>> RTL8168 LED control allows to switch between different hw triggers. I
>> was under the
>> assumption that this is not uncommon.
> 
> I did take a look around various datasheets, and i did find a couple
> like this, but the majority have the ability to do software control.
> 
>> In order to deal with the non-atomic issue we have to set
>> trigger_data->mode to the
>> resulting new mode, based on what the user set. Question is what we show to the
>> user. If we show nothing, then he will expect the new mode to be active.
>> If we show an error, then he may assume that his input had no effect.
>> So we may have to show technically an OK, plus a message that his input has been
>> stored, but is not supported by hw.
> 
> It is hard to show anything to the user. We are just doing
> 
> echo 1 > file.
> 
> There is no channel to the user other than an error code.
> 
> There is also the question about what the LED should show. Ideally it
> should indicate some sort of state to indicate there is an
> error. Either constantly blink, turn off, etc. Maybe that is the
> solution. You implement a set_brightness function which just indicates
> an error on the LED, but otherwise return O.K?
> 
Let's take a very simple use case: We have a one bit configuration to
switch a LED between link_100 and link_1000 hw trigger mode.

Then we have the atomicity issue you described: We can't go directly
from one hw-controlled mode to the other, we have to go via both
modes active or no mode active.

And unfortunately we don't have the option to indicate this by some
optical LED activity like blinking, especially if the link is down
at the moment.

Would be a pity if our nice framework can't support such a simple
use case. So, what I could imagine, we react based on the return code
from hw_control_is_supported():

- 0: use hw control
- -EOPNOTSUPP: fall back to LED software control, no error returned to use
- -ENOTSUPP (another idea: ENOEXEC): store new mode in trigger_data->mode and return error to the user
- other errors: don't store new mode and return error to user

Not fully intuitive and the subtle difference between EOPNOTSUPP and
ENOTSUPP may confuse driver authors adding device LED support.
However for the user it should be ok, he always has the option to read
back the attribute in order to check whether new mode has been stored.
That's the best I can see so far.

>    Andrew

Heiner
Andrew Lunn Dec. 5, 2023, 3 a.m. UTC | #4
> Let's take a very simple use case: We have a one bit configuration to
> switch a LED between link_100 and link_1000 hw trigger mode.
> 
> Then we have the atomicity issue you described: We can't go directly
> from one hw-controlled mode to the other, we have to go via both
> modes active or no mode active.
> 
> And unfortunately we don't have the option to indicate this by some
> optical LED activity like blinking, especially if the link is down
> at the moment.
> 
> Would be a pity if our nice framework can't support such a simple
> use case. So, what I could imagine, we react based on the return code
> from hw_control_is_supported():
> 
> - 0: use hw control
> - -EOPNOTSUPP: fall back to LED software control, no error returned to use
> - -ENOTSUPP (another idea: ENOEXEC): store new mode in trigger_data->mode and return error to the user
> - other errors: don't store new mode and return error to user
> 
> Not fully intuitive and the subtle difference between EOPNOTSUPP and
> ENOTSUPP may confuse driver authors adding device LED support.

Using an NFS error code for LEDs will definitely confuse
developers. This is not a network file system, where it is valid to
use ENOTSUPP.

I actually think we need to define some best practices, ordered on
what the hardware can do.

1) With software control, set_brightness should do what you expect,
not return an error.

2) Without full software control, but there is a mechanism to report a
problem, like constant blinking, or off, do that, and return
-EOPNOTSUPP.

3) Really dumb hardware like this, set_brightness should be a NULL
pointer. The core returns -EOPNOTSUPP.

The core should return this -EOPNOTSUPP to user space, but it should
accept the configuration change. So the user can put it into an
invalid state, in order to get to a valid state with further
configuration.

I don't see an easy way to let the user know what the valid states
are. We currently have a 10bit state. I don't think we can put all the
valid ones in a /sysfs file, especially when QCA8K pretty much
supports everything.

	 Andrew
Heiner Kallweit Dec. 5, 2023, 12:40 p.m. UTC | #5
On 05.12.2023 04:00, Andrew Lunn wrote:
>> Let's take a very simple use case: We have a one bit configuration to
>> switch a LED between link_100 and link_1000 hw trigger mode.
>>
>> Then we have the atomicity issue you described: We can't go directly
>> from one hw-controlled mode to the other, we have to go via both
>> modes active or no mode active.
>>
>> And unfortunately we don't have the option to indicate this by some
>> optical LED activity like blinking, especially if the link is down
>> at the moment.
>>
>> Would be a pity if our nice framework can't support such a simple
>> use case. So, what I could imagine, we react based on the return code
>> from hw_control_is_supported():
>>
>> - 0: use hw control
>> - -EOPNOTSUPP: fall back to LED software control, no error returned to use
>> - -ENOTSUPP (another idea: ENOEXEC): store new mode in trigger_data->mode and return error to the user
>> - other errors: don't store new mode and return error to user
>>
>> Not fully intuitive and the subtle difference between EOPNOTSUPP and
>> ENOTSUPP may confuse driver authors adding device LED support.
> 
> Using an NFS error code for LEDs will definitely confuse
> developers. This is not a network file system, where it is valid to
> use ENOTSUPP.
> 
> I actually think we need to define some best practices, ordered on
> what the hardware can do.
> 
> 1) With software control, set_brightness should do what you expect,
> not return an error.
> 
> 2) Without full software control, but there is a mechanism to report a
> problem, like constant blinking, or off, do that, and return
> -EOPNOTSUPP.
> 
> 3) Really dumb hardware like this, set_brightness should be a NULL
> pointer. The core returns -EOPNOTSUPP.
> 
> The core should return this -EOPNOTSUPP to user space, but it should
> accept the configuration change. So the user can put it into an
> invalid state, in order to get to a valid state with further
> configuration.
> 
Sounds good to me. Let me come up with a RFC patch.

> I don't see an easy way to let the user know what the valid states
> are. We currently have a 10bit state. I don't think we can put all the
> valid ones in a /sysfs file, especially when QCA8K pretty much
> supports everything.
> 
> 	 Andrew

Heiner
Heiner Kallweit Dec. 5, 2023, 12:57 p.m. UTC | #6
On 05.12.2023 13:40, Heiner Kallweit wrote:
> On 05.12.2023 04:00, Andrew Lunn wrote:
>>> Let's take a very simple use case: We have a one bit configuration to
>>> switch a LED between link_100 and link_1000 hw trigger mode.
>>>
>>> Then we have the atomicity issue you described: We can't go directly
>>> from one hw-controlled mode to the other, we have to go via both
>>> modes active or no mode active.
>>>
>>> And unfortunately we don't have the option to indicate this by some
>>> optical LED activity like blinking, especially if the link is down
>>> at the moment.
>>>
>>> Would be a pity if our nice framework can't support such a simple
>>> use case. So, what I could imagine, we react based on the return code
>>> from hw_control_is_supported():
>>>
>>> - 0: use hw control
>>> - -EOPNOTSUPP: fall back to LED software control, no error returned to use
>>> - -ENOTSUPP (another idea: ENOEXEC): store new mode in trigger_data->mode and return error to the user
>>> - other errors: don't store new mode and return error to user
>>>
>>> Not fully intuitive and the subtle difference between EOPNOTSUPP and
>>> ENOTSUPP may confuse driver authors adding device LED support.
>>
>> Using an NFS error code for LEDs will definitely confuse
>> developers. This is not a network file system, where it is valid to
>> use ENOTSUPP.
>>
>> I actually think we need to define some best practices, ordered on
>> what the hardware can do.
>>
>> 1) With software control, set_brightness should do what you expect,
>> not return an error.
>>
>> 2) Without full software control, but there is a mechanism to report a
>> problem, like constant blinking, or off, do that, and return
>> -EOPNOTSUPP.
>>
>> 3) Really dumb hardware like this, set_brightness should be a NULL
>> pointer. The core returns -EOPNOTSUPP.
>>
>> The core should return this -EOPNOTSUPP to user space, but it should
>> accept the configuration change. So the user can put it into an
>> invalid state, in order to get to a valid state with further
>> configuration.
>>
> Sounds good to me. Let me come up with a RFC patch.
> 
>> I don't see an easy way to let the user know what the valid states
>> are. We currently have a 10bit state. I don't think we can put all the
>> valid ones in a /sysfs file, especially when QCA8K pretty much
>> supports everything.
>>
>> 	 Andrew
> 
> Heiner

Patch is so simple that I send it this way. What do you think?

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index ec0395a6b..a24f3aade 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -310,6 +310,7 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 				     size_t size, enum led_trigger_netdev_modes attr)
 {
 	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
+	struct led_classdev *led_cdev = trigger_data->led_cdev;
 	unsigned long state, mode = trigger_data->mode;
 	int ret;
 	int bit;
@@ -349,6 +350,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 	trigger_data->mode = mode;
 	trigger_data->hw_control = can_hw_control(trigger_data);
 
+	if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking &&
+	    !trigger_data->hw_control)
+		return -EOPNOTSUPP;
+
 	set_baseline_state(trigger_data);
 
 	return size;
Andrew Lunn Dec. 6, 2023, 5:45 p.m. UTC | #7
> >> I actually think we need to define some best practices, ordered on
> >> what the hardware can do.
> >>
> >> 1) With software control, set_brightness should do what you expect,
> >> not return an error.
> >>
> >> 2) Without full software control, but there is a mechanism to report a
> >> problem, like constant blinking, or off, do that, and return
> >> -EOPNOTSUPP.
> >>
> >> 3) Really dumb hardware like this, set_brightness should be a NULL
> >> pointer. The core returns -EOPNOTSUPP.
> >>
> >> The core should return this -EOPNOTSUPP to user space, but it should
> >> accept the configuration change. So the user can put it into an
> >> invalid state, in order to get to a valid state with further
> >> configuration.
> >>
> > Sounds good to me. Let me come up with a RFC patch.
> > 
> >> I don't see an easy way to let the user know what the valid states
> >> are. We currently have a 10bit state. I don't think we can put all the
> >> valid ones in a /sysfs file, especially when QCA8K pretty much
> >> supports everything.
> >>
> >> 	 Andrew
> > 
> > Heiner
> 
> Patch is so simple that I send it this way. What do you think?

That is simpler than i expected.

But i think we need to document our expectations. What do we expect an
LED driver to do when it cannot support software blinking. So please
could you add a comment somewhere. Maybe extend the

/*
 *Configurable sysfs attributes:
 *

section?

Thanks
	Andrew
diff mbox series

Patch

diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 7ed2d0b64..b58396600 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -251,7 +251,11 @@  static int set_device_name(struct led_netdev_data *trigger_data,
 
 	trigger_data->last_activity = 0;
 
-	set_baseline_state(trigger_data);
+	/* skip if we're called from netdev_trig_activate() and hw_control is true */
+	if (!trigger_data->hw_control ||
+	    led_get_trigger_data(trigger_data->led_cdev))
+		set_baseline_state(trigger_data);
+
 	mutex_unlock(&trigger_data->lock);
 	rtnl_unlock();
 
@@ -568,8 +572,8 @@  static int netdev_trig_activate(struct led_classdev *led_cdev)
 		if (dev) {
 			const char *name = dev_name(dev);
 
-			set_device_name(trigger_data, name, strlen(name));
 			trigger_data->hw_control = true;
+			set_device_name(trigger_data, name, strlen(name));
 
 			rc = led_cdev->hw_control_get(led_cdev, &mode);
 			if (!rc)