mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Arthur Crepin Leblond <arthur@marmottus.net>
To: netdev-bot+sashiko@kernel.org
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org, arnd@arndb.de,
	netdev@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH net-next v10 3/3] w5100: detect carrier state using link status bit and optional interrupt
Date: Thu, 24 Sep 2026 10:26:27 +0200	[thread overview]
Message-ID: <20260924082627.GA51120@marmottus.net> (raw)
In-Reply-To: <179021495594.2160803.16936851129108019714@kernel.org>

On Thu, Sep 24, 2026 at 01:55:55AM +0000, netdev-bot+sashiko@kernel.org wrote:
>[Severity: Medium]
>What happens to the carrier state when the PHYCFGR read fails here?
>
>w5500_get_phycfgr_lnk() returns a negative errno on SPI failure, but
>w5500_report_carrier_state() only acts on state > 0 and state == 0, and it
>returns void, so the error is neither acted on nor propagated.
>
>The carrier is forced off beforehand (netif_carrier_off() in w5100_probe()
>for the link_irq case, and in w5100_stop()/w5100_suspend()), and
>w5100_open() samples the state exactly once:
>
>	if (priv->link_irq > 0) {
>		w5500_report_carrier_state(ndev);
>		enable_irq(priv->link_irq);
>	}
>
>	return 0;
>
>Since w5100_open() still returns 0 and the link IRQ is edge triggered
>(IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING), no further event arrives while
>the link stays up.  Can a single failed read at open (or resume, or restart)
>therefore leave the netdev permanently without carrier, with dev_activate()
>refusing to leave noop_qdisc and dev_watchdog() skipping the device, until
>the cable is toggled or the interface is cycled?  Meanwhile w5500_get_link()
>re-reads the register and keeps reporting "Link detected: yes".

I'll add a retry loop.


>[Severity: Low]
>This isn't a bug as such, since the commit message documents the intent, but
>for a w5500 board whose DT has no second (LINKLED) interrupt the two link
>uAPIs end up permanently disagreeing.
>
>w5100_probe() installs the new ops for every w5500:
>
>	ndev->ethtool_ops = ops->chip_id == W5500 ? &w5500_ethtool_ops :
>						    &w5100_ethtool_ops;
>
>so ethtool reports the real PHYCFGR bit, while every netif_carrier_on|off()
>call in this patch (including the netif_carrier_off() in probe) sits inside
>an if (priv->link_irq > 0) block.
>
>With the carrier bit never touched, netif_carrier_ok() stays true (nothing
>in net/core sets __LINK_STATE_NOCARRIER at registration).  Would it be
>better to only publish .get_link when the driver can also maintain the
>carrier, so that ip link and ETHTOOL_GLINK cannot report IFF_LOWER_UP and
>"Link detected: no" at the same time?
>

No but that's a fair point, .get_link() should probably be
implemented only on w5500 with a link irq.

>[ ... ]
>
>> @@ -656,9 +744,16 @@ static int w5100_open(struct net_device *ndev)
>>  	struct w5100_priv *priv = netdev_priv(ndev);
>>
>>  	netif_info(priv, ifup, ndev, "enabling\n");
>> -	w5100_hw_start(priv);
>>  	napi_enable(&priv->napi);
>> +	enable_irq(priv->irq);
>> +	w5100_hw_start(priv);
>>  	netif_start_queue(ndev);
>> +
>> +	if (priv->link_irq > 0) {
>> +		w5500_report_carrier_state(ndev);
>> +		enable_irq(priv->link_irq);
>> +	}
>> +
>>  	return 0;
>>  }
>
>[Severity: Medium]
>Can a link transition be missed here because PHYCFGR is sampled before the
>edge triggered IRQ is enabled?
>
>The same read-then-enable order is used in w5100_restart() and
>w5100_resume().  The IRQ is requested with
>
>	IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
>	IRQF_NO_AUTOEN
>
>so at open the enable_irq() is the initial irq_startup(): the flow handler
>has never run, no IRQS_PENDING state exists in the irq_desc for the core to
>resend, and replay of an edge that happened while the line was masked
>depends on the irqchip latching it (GPIO controllers such as bcm2835 clear
>the per-line edge enable bits on mask, so nothing is recorded).
>
>A transition inside the window between the read and enable_irq() would then
>be seen by neither, leaving the carrier stale until the next physical
>change.  Would enabling the IRQ first and reading PHYCFGR afterwards be
>safe, given that w5500_report_carrier_state() is idempotent and serialized
>by priv->link_lock?  The v7 changelog entry ("Enable the link IRQ before
>reading the PHYCFGR bit") suggests this order was intentional at some
>point.

I'll enable the irq before reporting the state.

>
>>
>> @@ -667,13 +762,38 @@ static int w5100_stop(struct net_device *ndev)
>>  	struct w5100_priv *priv = netdev_priv(ndev);
>>
>>  	netif_info(priv, ifdown, ndev, "shutting down\n");
>> +
>> +	disable_irq(priv->irq);
>> +	if (priv->link_irq > 0)
>> +		disable_irq(priv->link_irq);
>> +
>> +	cancel_work_sync(&priv->restart_work);
>> +	cancel_work_sync(&priv->setrx_work);
>> +	flush_work(&priv->rx_work);
>> +	flush_work(&priv->tx_work);
>> +
>>  	w5100_hw_close(priv);
>
>[Severity: High]
>Can flush_work(&priv->rx_work) block here indefinitely, since it now runs
>before w5100_hw_close() stops the producer of receive traffic?
>
>disable_irq() stops interrupt handling, but socket 0 is still open and the
>chip keeps filling its 16 KB RX buffer.  The worker drains without a budget
>or a shutdown check:
>
>drivers/net/ethernet/wiznet/w5100.c:w5100_rx_work() {
>	while ((skb = w5100_rx_skb(priv->ndev)))
>		netif_rx(skb);
>
>	w5100_enable_intr(priv);
>}
>
>and w5100_rx_skb() only returns NULL once the hardware buffer reads empty:
>
>	u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR(priv));
>
>	if (rx_buf_len == 0)
>		return NULL;
>
>The SPI drain rate is well below the wire rate, so an adjacent sender
>flooding the link can keep RSR non-zero for as long as it likes.
>
>ndo_stop runs under RTNL, so would "ip link set dev down" (and
>unregister_netdev() from w5100_remove(), which now runs first) stall with
>RTNL held, blocking all other network configuration?  Closing the hardware
>before flushing rx_work, as the previous code did, would avoid this.

Yeah, I've had this suggestion when running Sashiko locally too. This
patch is a Pandora box or race conditions! But hopefully I figured it
out and it's fixed in v11.
I am really learning a lot along the way regarding the synchronization
of ndos and how the calls are made.

>
>> -	netif_carrier_off(ndev);
>> +
>> +	if (priv->link_irq > 0)  {
>> +		mutex_lock(&priv->link_lock);
>> +		netif_carrier_off(ndev);
>> +		mutex_unlock(&priv->link_lock);
>> +	}
>> +
>
>[Severity: Medium]
>Making this netif_carrier_off() conditional, together with the new carrier
>sampling in w5100_open()/w5100_resume(), looks like it also repairs an
>existing user visible breakage.  Should that be split out with a Fixes: tag?
>
>In the baseline tree, dacf281771a9 ("w5100: remove unused gpio link
>detection") removed w5100_get_link()/w5100_detect_link() and both
>netif_carrier_on() call sites, but kept the unconditional
>netif_carrier_off() in w5100_stop() and w5100_suspend().  git grep
>netif_carrier_on on the baseline returns no match in this driver.
>
>A freshly registered netdev has __LINK_STATE_NOCARRIER clear, so the first
>ifup works, but after the first ifdown (or suspend cycle) the bit is set and
>nothing ever clears it again, and dev_activate() takes the "Delay activation
>until next carrier-on event" path, so the interface cannot transmit until
>the module is reloaded.
>
>As written, the fix is buried in a feature patch that also rewrites probe
>ordering, IRQ enable/disable and work cancellation, which makes it hard to
>pick up for stable.

It still don't think it is a fix. The patch is bringing back the
interrupt and improves it but it is too much of a change to be called
a fix.

>[Severity: Medium]
>The commit message says:
>
>    w5100_remove(), w5100_stop() and w5100_suspend() call
>    cancel_work_sync()/flush_work() to make sure there is no pending
>    work
>
>but w5100_suspend() only drains restart_work.  w5100_stop() and
>w5100_remove() drain all four (restart_work, setrx_work, rx_work, tx_work).
>Could the changelog be adjusted, or the suspend path made to match?
>
>[Severity: High]
>Does leaving setrx_work, rx_work and tx_work pending here allow the chip to
>be re-armed after w5100_hw_close()?
>
>disable_irq()/synchronize_irq() waits for the threaded handler only, not for
>work already queued on priv->xfer_wq:
>
>w5100_interrupt()      -> queue_work(priv->xfer_wq, &priv->rx_work)
>w5100_start_tx()       -> queue_work(priv->xfer_wq, &priv->tx_work)
>w5100_set_rx_mode()    -> schedule_work(&priv->setrx_work)
>
>A pending w5100_rx_work() ends with w5100_enable_intr(), re-arming
>W5500_SIMR / W5100_IMR; w5100_setrx_work() calls w5100_hw_start(), which
>issues S0_CR_OPEN and enables interrupts again; w5100_tx_work() can program
>S0_TX_WR and issue S0_CR_SEND.
>
>Any of these can run concurrently with, or after, the w5100_hw_close() a few
>lines below, so the controller can stay open with socket interrupts armed
>for the whole suspend while the CPU IRQ is disabled, and the SPI transfers
>are issued after the device was marked absent.  All in-tree ops set
>may_sleep = true, so this is the live path.  Would replicating the
>w5100_stop() sequence here be appropriate?
>
>[Severity: Medium]
>Can restart_work be re-queued after this cancel_work_sync(), because the
>cancel happens before netif_device_detach()?
>
>In that window the device is still running, still present, and (for the
>link IRQ case) still carrier-ok, and the tx watchdog timer is not lowered on
>the suspend path.  On the dev_close() path, dev_deactivate_many() calls
>netdev_watchdog_down() before ndo_stop, which is what makes the same
>ordering safe in w5100_stop().
>
>dev_watchdog() only requires:
>
>net/sched/sch_generic.c:dev_watchdog() {
>		if (netif_device_present(dev) &&
>		    netif_running(dev) &&
>		    netif_carrier_ok(dev)) {
>	...
>}
>
>and w5100_tx_timeout() is now unconditional:
>
>	schedule_work(&priv->restart_work);
>
>The re-queued w5100_restart() would then pass its
>
>	if (!netif_running(ndev) || !netif_device_present(ndev))
>		return;
>
>test, since nothing serializes that check with netif_device_detach(), and
>proceed to w5100_hw_reset(), enable_irq(priv->irq), w5100_hw_start() and the
>PHYCFGR read, concurrently with the w5100_hw_close() below.  Would detaching
>first and cancelling afterwards close this?

I reworked all those in the upcoming v11. I really tried to figure out
all the edge cases.

Arthur

      reply	other threads:[~2026-09-24  8:26 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-21 10:52 [PATCH net-next v10 0/3] w5100: restore GPIO-based link detection Arthur Crepin Leblond
2026-09-21 10:52 ` [PATCH net-next v10 1/3] dt-bindings: net: wiznet,w5100: convert to DT schema Arthur Crepin Leblond
2026-09-24  1:55   ` netdev-bot+sashiko
2026-09-24  7:12     ` Arthur Crepin Leblond
2026-09-21 10:52 ` [PATCH net-next v10 2/3] dt-bindings: net: wiznet,w5100: add link status interrupt Arthur Crepin Leblond
2026-09-24  1:55   ` netdev-bot+sashiko
2026-09-24  7:14     ` Arthur Crepin Leblond
2026-09-21 10:52 ` [PATCH net-next v10 3/3] w5100: detect carrier state using link status bit and optional interrupt Arthur Crepin Leblond
2026-09-21 15:31   ` Arthur Crepin Leblond
2026-09-24  1:55   ` netdev-bot+sashiko
2026-09-24  8:26     ` Arthur Crepin Leblond [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=20260924082627.GA51120@marmottus.net \
    --to=arthur@marmottus.net \
    --cc=andrew+netdev@lunn.ch \
    --cc=arnd@arndb.de \
    --cc=conor+dt@kernel.org \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev-bot+sashiko@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=robh@kernel.org \
    /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®