mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Lee Jones <lee@kernel.org>
To: Salah Ahmed <haddad2inc@gmail.com>
Cc: Pavel Machek <pavel@kernel.org>,
	linux-leds@vger.kernel.org,
	Griffin Kroah-Hartman <griffin@kroah.com>,
	Peng Fan <peng.fan@nxp.com>, Rosen Penev <rosenp@gmail.com>,
	Rong Zhang <i@rong.moe>,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH] leds: trigger-netdev: add byte-rate threshold mode
Date: Fri, 13 Nov 2026 12:31:52 +0000	[thread overview]
Message-ID: <179370046939.793788.505185499377646992@kernel.org> (raw)
In-Reply-To: <20261030215924.18060-1-haddad2inc@gmail.com>

On Sat, 31 Oct 2026, Salah Ahmed wrote:

Please review, explain or fix the Sashiko review(s).

> The tx/rx modes blink once per observed change of the packet counters,
> which misses traffic forwarded in hardware (DSA bridge offload) and
> flickers on background broadcast noise. Add a rate_threshold attribute
> (KiB/s): when non-zero the trigger drives the LED from the interface byte
> rate instead, blinking on a 50..150 ms ladder above the threshold and
> resting in the baseline state (solid on link, dark without carrier) below
> it.
>
> The rate is measured over the interval between two observed counter
> changes, not over a fixed window. Drivers that refresh their counters in
> bursts - a cached switch MIB polled every few seconds, for instance -
> would otherwise dump a whole accumulation period into the first fixed
> window that follows the refresh: the reported rate is then several times
> the real one, followed by zero until the next refresh, which turns the LED
> into a stutter instead of a steady blink. Measuring between changes makes
> the trigger independent of the driver's refresh interval, and the last
> measured rate is kept until the counter has been silent for twice the
> interval it was measured over (capped at 10 s) so that the blink lasts as
> long as the traffic does.
>
> The blink is re-armed only when the ladder step changes: led_blink_set()
> restarts the software blink timer and truncates the phase in progress, so
> re-arming it on every evaluation shows up as an irregular cadence.
>
> Rate mode is independent of the link/tx/rx mode bits on purpose, so that
> ports whose packet counters are blind to offloaded flows still show
> activity. It yields to hw_control, and writing 0 restores the link state
> even for an LED with no tx/rx bit set, because the packet path only touches
> the LED when the activity counter changes.
>
> Byte counters can legitimately go backwards when a driver resets them, and
> an unsigned subtraction would then report an enormous rate, so a decreasing
> counter re-anchors the measurement instead. div_u64() keeps 32-bit
> architectures linking without __udivdi3/__aeabi_uldivmod.
>
> Bursty transfers can straddle a ladder boundary on every evaluation,
> and cutting the blink off the instant the counter goes quiet makes a
> pausing transfer look like the LED died. The measured rate is therefore
> smoothed the way field-accepted userspace activity daemons do it -
> immediate attack at or above the threshold, a slow rise and a quick
> release below it - and when the counter goes silent the blink fades out
> quadratically from the last peak over 20 s, with the ladder step only
> ever slowing during the fade.
>
> set_baseline_state() runs after every netdev notifier event and after
> every mode or device_name write, and both callers cancel the work first.
> It sets a solid brightness and cancels the work first, so with a rate
> threshold set it also owns the rate-mode bookkeeping: clear the armed
> ladder step, stop a software blink that is still armed (while it runs, a
> solid brightness set here only lands in blink_brightness), and reschedule
> the work. Rescheduling is what keeps rate mode alive on a link-only LED
> across carrier changes and honest on tx/rx LEDs under load: without it the
> work computes the same ladder step as before, skips the re-arm while the
> brightness has just been reset to solid, and a below-threshold sample
> after a cleared rate_last_ms leaves an old blink running.
>
> The state a rate-mode blink rests in is the trigger's baseline state, not
> a second policy: netdev_baseline_on() answers "is this LED on at rest?"
> from the same link, speed and duplex mode bits the baseline uses, and
> netdev_set_resting() applies the same level, blink_brightness. Both
> set_baseline_state() and the post-blink path call it, so the two cannot
> drift apart: a mode without a link bit rests dark, and a multi-level LED
> rests at its blinking level rather than at LED_ON (1), which would read
> nearly dark. On a single-level LED (max_brightness = 1) the two values
> coincide, so nothing differs there.
>
> Signed-off-by: Salah Ahmed <haddad2inc@gmail.com>
> ---
>  drivers/leds/trigger/ledtrig-netdev.c | 417 +++++++++++++++++++++-----
>  1 file changed, 349 insertions(+), 68 deletions(-)
>
> diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
> index 5b01324845..941cb13cdf 100644
> --- a/drivers/leds/trigger/ledtrig-netdev.c
> +++ b/drivers/leds/trigger/ledtrig-netdev.c
> @@ -20,6 +20,7 @@
>  #include <linux/leds.h>
>  #include <linux/linkmode.h>
>  #include <linux/list.h>
> +#include <linux/math64.h>
>  #include <linux/module.h>
>  #include <linux/netdevice.h>
>  #include <linux/mutex.h>
> @@ -31,6 +32,21 @@
>
>  #define NETDEV_LED_DEFAULT_INTERVAL	50
>
> +/* Rate mode: a measured rate is kept for two counter refresh intervals before
> + * it counts as stale, capped so that a dead link cannot blink for long.
> + * NETDEV_RATE_MAX_ELAPSED_MS keeps the u32 divisor of div_u64() in range.
> + */

Incorrect multi-line comment format for LEDs.

> +#define NETDEV_RATE_STALE_MULT		2
> +#define NETDEV_RATE_STALE_MAX		(10 * HZ)
> +#define NETDEV_RATE_MAX_ELAPSED_MS	1000000
> +
> +/* Afterglow: when the counter goes quiet the blink does not cut off at once;
> + * the last measured peak fades out quadratically over this window, mirroring
> + * the decay shape of the userspace activity daemons this mode replaces in the
> + * field, so a transfer that pauses briefly does not look like the LED died.
> + */
> +#define NETDEV_RATE_AFTERGLOW		(20 * HZ)
> +
>  /*
>   * Configurable sysfs attributes:
>   *
> @@ -42,6 +58,14 @@
>   * rx -  LED blinks on receive data
>   * tx_err -  LED blinks on transmit error
>   * rx_err -  LED blinks on receive error
> + * rate_threshold - KiB/s; when non-zero the trigger blinks according to
> + *         the interface byte rate (ladder 50..150 ms) instead of per-packet,
> + *         resting in the baseline (link) state below the threshold.
> + *         Byte-rate mode sees traffic that packet counters miss once the
> + *         driver reports wire bytes in its stats (e.g. cached switch MIB on
> + *         DSA ports). The rate is measured between two counter updates
> + *         rather than over a fixed window, so counters that are refreshed
> + *         in bursts report their true average rate.
>   *
>   * Note: If the user selects a mode that is not supported by hw, default
>   * behavior is to fall back to software control of the LED. However not every
> @@ -68,6 +92,23 @@ struct led_netdev_data {
>  	atomic_t interval;
>  	unsigned int last_activity;
>
> +	/* Rate mode state: rate_bytes/rate_jiffies anchor the last observed
> +	 * counter change, rate_interval is the time that produced it,
> +	 * rate_kbps the rate measured over that interval, and rate_last_ms the
> +	 * blink period currently armed (0 = solid). rate_threshold_kbps is the
> +	 * threshold in KiB/s, 0 = rate mode disabled.
> +	 */
> +	u64 rate_bytes;
> +	unsigned long rate_jiffies;
> +	unsigned long rate_interval;
> +	unsigned long rate_kbps;
> +	unsigned long rate_last_ms;
> +	unsigned long rate_threshold_kbps;
> +	unsigned long rate_smooth;
> +	unsigned long rate_peak;
> +	unsigned long rate_ag;
> +	bool rate_valid;
> +
>  	unsigned long mode;
>  	int link_speed;
>  	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes);
> @@ -79,6 +120,80 @@ struct led_netdev_data {
>
>  static const struct attribute_group netdev_trig_link_speed_attrs_group;
>
> +/*
> + * The resting (non-blinking) state of a netdev LED. The link and duplex
> + * mode bits decide whether it is lit at all, and the level it is lit at is
> + * blink_brightness rather than LED_ON, so that a multi-level LED is not
> + * dimmed to its lowest step and a tx/rx-only LED is not left solid once the
> + * traffic stops. Both the baseline path and the rate-mode path that returns
> + * the LED to rest after a blink use this, so they cannot drift apart.
> + */
> +static bool netdev_baseline_on(struct led_netdev_data *trigger_data)
> +{
> +	if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_10)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_100)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_1000)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_2500)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_5000)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_10000)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_25000)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_40000)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_50000)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) &&
> +	    trigger_data->link_speed == SPEED_100000)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
> +	    trigger_data->duplex == DUPLEX_HALF)
> +		return true;
> +
> +	if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) &&
> +	    trigger_data->duplex == DUPLEX_FULL)
> +		return true;
> +
> +	return false;
> +}
> +
> +static void netdev_set_resting(struct led_netdev_data *trigger_data)
> +{
> +	struct led_classdev *led_cdev = trigger_data->led_cdev;
> +
> +	if (trigger_data->carrier_link_up && netdev_baseline_on(trigger_data))
> +		led_set_brightness(led_cdev, led_cdev->blink_brightness);
> +	else
> +		led_set_brightness(led_cdev, LED_OFF);
> +}
> +
>  static void set_baseline_state(struct led_netdev_data *trigger_data)
>  {
>  	int current_brightness;
> @@ -91,85 +206,54 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
>  		return;
>  	}
>
> +	/* While a rate-mode software blink is still armed, the solid
> +	 * brightness below only lands in blink_brightness (LED_BLINK_SW),
> +	 * and the work re-arms the blink only on a ladder step change: with
> +	 * rate_last_ms cleared, a below-threshold sample computes the same
> +	 * zero step and the old blink keeps running. Stop it here; the work
> +	 * re-arms it from the measured rate, as rate_threshold_store() does.
> +	 */
> +	if (trigger_data->rate_threshold_kbps)
> +		led_stop_software_blink(led_cdev);
> +
>  	current_brightness = led_cdev->brightness;
>  	if (current_brightness)
>  		led_cdev->blink_brightness = current_brightness;
>  	if (!led_cdev->blink_brightness)
>  		led_cdev->blink_brightness = led_cdev->max_brightness;
>
> -	if (!trigger_data->carrier_link_up) {
> -		led_set_brightness(led_cdev, LED_OFF);
> -	} else {
> -		bool blink_on = false;
> +	netdev_set_resting(trigger_data);
>
> -		if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_10)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_100)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_1000)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_2500)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_5000)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_10000)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_25000)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_40000)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_50000)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) &&
> -		    trigger_data->link_speed == SPEED_100000)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
> -		    trigger_data->duplex == DUPLEX_HALF)
> -			blink_on = true;
> -
> -		if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) &&
> -		    trigger_data->duplex == DUPLEX_FULL)
> -			blink_on = true;
> -
> -		if (blink_on)
> -			led_set_brightness(led_cdev,
> -					   led_cdev->blink_brightness);
> -		else
> -			led_set_brightness(led_cdev, LED_OFF);
> -
> -		/* If we are looking for RX/TX start periodically
> -		 * checking stats
> +	/* If we are looking for RX/TX (or a rate threshold is set) start
> +	 * periodically checking stats
> +	 */
> +	if (trigger_data->carrier_link_up &&
> +	    (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
> +	     test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ||
> +	     test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ||
> +	     test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode) ||
> +	     trigger_data->rate_threshold_kbps)) {
> +		/* Rate mode re-arms the blink only when the ladder step
> +		 * changes, and the brightness has just been set to a solid
> +		 * state, so forget the step that was armed before this.
>  		 */
> -		if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
> -		    test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ||
> -		    test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ||
> -		    test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
> -			schedule_delayed_work(&trigger_data->work, 0);
> +		trigger_data->rate_last_ms = 0;
> +		schedule_delayed_work(&trigger_data->work, 0);
>  	}
>  }
>
> +/* Drop the measured rate whenever the counter source changes, so that the
> + * next sample re-anchors instead of subtracting counters of two interfaces.
> + */
> +static void netdev_trig_rate_reset(struct led_netdev_data *trigger_data)
> +{
> +	trigger_data->rate_valid = false;
> +	trigger_data->rate_kbps = 0;
> +	trigger_data->rate_smooth = 0;
> +	trigger_data->rate_peak = 0;
> +	trigger_data->rate_ag = 0;
> +}
> +
>  static bool supports_hw_control(struct led_classdev *led_cdev)
>  {
>  	if (!led_cdev->hw_control_get || !led_cdev->hw_control_set ||
> @@ -311,6 +395,7 @@ static int set_device_name(struct led_netdev_data *trigger_data,
>  		get_device_state(trigger_data);
>
>  	trigger_data->last_activity = 0;
> +	netdev_trig_rate_reset(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))
> @@ -509,7 +594,51 @@ static ssize_t interval_store(struct device *dev,
>  	return size;
>  }
>
> +static ssize_t rate_threshold_show(struct device *dev,
> +				   struct device_attribute *attr, char *buf)
> +{
> +	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
> +
> +	return sprintf(buf, "%lu\n", trigger_data->rate_threshold_kbps);

sysfs_emit()

> +}
> +
> +static ssize_t rate_threshold_store(struct device *dev,
> +				    struct device_attribute *attr, const char *buf,
> +				    size_t size)
> +{
> +	struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
> +	unsigned long value;
> +	int ret;
> +
> +	if (trigger_data->hw_control)
> +		return -EINVAL;
> +
> +	ret = kstrtoul(buf, 0, &value);
> +	if (ret)
> +		return ret;
> +
> +	cancel_delayed_work_sync(&trigger_data->work);
> +
> +	trigger_data->rate_threshold_kbps = value;
> +	trigger_data->rate_last_ms = 0;
> +	netdev_trig_rate_reset(trigger_data);
> +
> +	/* Leaving rate mode has to undo the blink it armed: while a software
> +	 * blink is running led_set_brightness() only changes the blink
> +	 * brightness, and the packet mode below touches the LED only when the
> +	 * activity counter changes, so a link-only LED would keep blinking.
> +	 */
> +	led_stop_software_blink(trigger_data->led_cdev);
> +	set_baseline_state(trigger_data);	/* resets timer */
> +
> +	if (value)
> +		schedule_delayed_work(&trigger_data->work, 0);
> +
> +	return size;
> +}
> +
>  static DEVICE_ATTR_RW(interval);
> +static DEVICE_ATTR_RW(rate_threshold);
>
>  static ssize_t offloaded_show(struct device *dev,
>  			      struct device_attribute *attr, char *buf)
> @@ -593,6 +722,7 @@ static struct attribute *netdev_trig_attrs[] = {
>  	&dev_attr_rx_err.attr,
>  	&dev_attr_tx_err.attr,
>  	&dev_attr_interval.attr,
> +	&dev_attr_rate_threshold.attr,

Is this documented?

Thinking about it, do we even want a whole new rate-smoothing,
ladder-stepping and quadratic-afterglow engine living in ledtrig-netdev?
If you already have a userspace daemon doing this, why can't it stay in
userspace?

>  	&dev_attr_offloaded.attr,
>  	NULL
>  };
> @@ -639,12 +769,14 @@ static int netdev_trig_notify(struct notifier_block *nb,
>  		dev_put(trigger_data->net_dev);
>  		dev_hold(dev);
>  		trigger_data->net_dev = dev;
> +		netdev_trig_rate_reset(trigger_data);
>  		if (evt == NETDEV_CHANGENAME)
>  			get_device_state(trigger_data);
>  		break;
>  	case NETDEV_UNREGISTER:
>  		dev_put(trigger_data->net_dev);
>  		trigger_data->net_dev = NULL;
> +		netdev_trig_rate_reset(trigger_data);
>  		break;
>  	case NETDEV_UP:
>  		trigger_data->hw_control = can_hw_control(trigger_data);
> @@ -674,6 +806,8 @@ static void netdev_trig_work(struct work_struct *work)
>  	unsigned int new_activity;
>  	struct rtnl_link_stats64 temp;
>  	unsigned long interval;
> +	unsigned long now, ms, elapsed, stale;
> +	u64 bytes;
>  	int invert;
>
>  	/* If we dont have a device, insure we are off */
> @@ -682,6 +816,153 @@ static void netdev_trig_work(struct work_struct *work)
>  		return;
>  	}
>
> +	/* Rate mode: blink on a ladder driven by the interface byte rate instead
> +	 * of per packet. Independent of the packet-mode bits so that interfaces
> +	 * whose packet counters are blind to hardware-forwarded traffic still
> +	 * show activity, and so that a sub-threshold link rests in its baseline
> +	 * state (solid with a link bit, dark without one).
> +	 *
> +	 * The rate is measured over the interval between two observed counter
> +	 * changes, not over a fixed window. Counters that are refreshed in
> +	 * bursts - a cached switch MIB polled every few seconds, for instance -
> +	 * dump their whole accumulation into the first fixed window that follows
> +	 * the refresh, which reports a rate several times too high and then
> +	 * zero until the next refresh. Measuring between changes makes the
> +	 * trigger independent of how often the driver refreshes its counters,
> +	 * and keeps the blink running for as long as the traffic does.
> +	 */
> +	if (trigger_data->rate_threshold_kbps && !trigger_data->hw_control) {
> +		dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
> +		bytes = dev_stats->rx_bytes + dev_stats->tx_bytes;
> +		now = jiffies;
> +
> +		if (!trigger_data->rate_valid) {
> +			trigger_data->rate_bytes = bytes;
> +			trigger_data->rate_jiffies = now;
> +			trigger_data->rate_interval = HZ;
> +			trigger_data->rate_valid = true;
> +		} else if (bytes != trigger_data->rate_bytes) {
> +			if (bytes > trigger_data->rate_bytes) {
> +				trigger_data->rate_interval =
> +					now - trigger_data->rate_jiffies;
> +				elapsed = jiffies_to_msecs(trigger_data->rate_interval);
> +				if (elapsed > NETDEV_RATE_MAX_ELAPSED_MS)
> +					elapsed = NETDEV_RATE_MAX_ELAPSED_MS;
> +				trigger_data->rate_kbps =
> +					div_u64((bytes - trigger_data->rate_bytes) * 1000,
> +						elapsed * 1024);
> +			} else {
> +				/* Counters went backwards (reset or wrapped):
> +				 * re-anchor instead of reporting a huge rate.
> +				 */
> +				trigger_data->rate_kbps = 0;
> +			}
> +			trigger_data->rate_bytes = bytes;
> +			trigger_data->rate_jiffies = now;
> +		} else {
> +			/* No change: keep the last measured rate for as long as
> +			 * the counter keeps being refreshed, then fall back to
> +			 * the link state.
> +			 */
> +			stale = trigger_data->rate_interval * NETDEV_RATE_STALE_MULT;
> +			if (stale > NETDEV_RATE_STALE_MAX)
> +				stale = NETDEV_RATE_STALE_MAX;
> +			if (time_after(now, trigger_data->rate_jiffies + stale))
> +				trigger_data->rate_kbps = 0;
> +		}
> +
> +		/* Smoothing ported from the userspace daemon this replaces: attack
> +		 * immediately at or above the threshold so the first evaluation of
> +		 * a transfer already blinks, rise slowly and release quickly below
> +		 * it so the ladder step does not chatter when a bursty rate
> +		 * straddles a boundary.
> +		 */
> +		if (trigger_data->rate_kbps >= trigger_data->rate_threshold_kbps)
> +			trigger_data->rate_smooth = trigger_data->rate_kbps;
> +		else if (trigger_data->rate_kbps < trigger_data->rate_smooth)
> +			trigger_data->rate_smooth = (trigger_data->rate_smooth +
> +				3 * trigger_data->rate_kbps) / 4;
> +		else
> +			trigger_data->rate_smooth = (3 * trigger_data->rate_smooth +
> +				trigger_data->rate_kbps) / 4;
> +
> +		if (trigger_data->rate_smooth >= trigger_data->rate_threshold_kbps) {
> +			if (trigger_data->rate_smooth > trigger_data->rate_peak)
> +				trigger_data->rate_peak = trigger_data->rate_smooth;
> +			trigger_data->rate_ag = 0;
> +		}
> +
> +		ms = 0;
> +		if (trigger_data->carrier_link_up) {
> +			unsigned long rate = trigger_data->rate_smooth;
> +
> +			/* Quadratic afterglow from the last peak: the synthetic rate
> +			 * decays as peak * remaining^2 / window^2 over
> +			 * NETDEV_RATE_AFTERGLOW, then the LED returns to the link
> +			 * state. div_u64() keeps 32-bit builds linking.
> +			 */
> +			if (rate < trigger_data->rate_threshold_kbps &&
> +			    trigger_data->rate_peak >= trigger_data->rate_threshold_kbps &&
> +			    trigger_data->rate_ag < NETDEV_RATE_AFTERGLOW) {
> +				unsigned long rem;
> +
> +				trigger_data->rate_ag += HZ / 2;
> +				rem = NETDEV_RATE_AFTERGLOW - trigger_data->rate_ag;
> +				rate = div_u64((u64)trigger_data->rate_peak * rem * rem,
> +					       NETDEV_RATE_AFTERGLOW *
> +							NETDEV_RATE_AFTERGLOW);
> +			}
> +
> +			if (rate >= trigger_data->rate_threshold_kbps) {
> +				if (rate >= 4096)
> +					ms = 50;
> +				else if (rate >= 1024)
> +					ms = 60;
> +				else if (rate >= 256)
> +					ms = 80;
> +				else if (rate >= 32)
> +					ms = 100;
> +				else
> +					ms = 150;
> +			}

Where do all these magic numbers come from?  Can they be defined, or,
again is this policy better kept out of the kernel entirely?

> +
> +			/* During the fade the step only ever gets longer: re-arming
> +			 * a faster blink mid-decay looks like stutter.
> +			 */
> +			if (ms && trigger_data->rate_ag &&
> +			    ms < trigger_data->rate_last_ms)
> +				ms = trigger_data->rate_last_ms;
> +		}
> +
> +		if (!ms) {
> +			trigger_data->rate_peak = 0;
> +			trigger_data->rate_ag = 0;
> +		}
> +
> +		/* Re-arm only when the blink period actually changes: every
> +		 * led_blink_set() restarts the software blink and truncates the
> +		 * phase in progress, which shows up as an irregular cadence.
> +		 */
> +		if (ms != trigger_data->rate_last_ms) {
> +			led_stop_software_blink(trigger_data->led_cdev);
> +			if (ms) {
> +				unsigned long delay_on = ms, delay_off = ms;
> +
> +				led_blink_set(trigger_data->led_cdev, &delay_on,
> +					      &delay_off);
> +			} else {
> +				/* Resting state == the baseline state: same mode bits,
> +				 * same brightness level (see netdev_set_resting()).
> +				 */
> +				netdev_set_resting(trigger_data);
> +			}
> +			trigger_data->rate_last_ms = ms;
> +		}
> +
> +		schedule_delayed_work(&trigger_data->work, HZ / 2);
> +		return;
> +	}
> +
>  	/* If we are not looking for RX/TX then return  */
>  	if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
>  	    !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) &&
> -- 
> 2.53.0
>
>

-- 
Lee Jones

      reply	other threads:[~2026-11-13 12:31 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 21:59 Salah Ahmed
2026-11-13 12:31 ` Lee Jones [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=179370046939.793788.505185499377646992@kernel.org \
    --to=lee@kernel.org \
    --cc=griffin@kroah.com \
    --cc=haddad2inc@gmail.com \
    --cc=i@rong.moe \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=pavel@kernel.org \
    --cc=peng.fan@nxp.com \
    --cc=rosenp@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®