Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Christian Marangi" <ansuelsmth@gmail.com>, devicetree@vger.kernel.org
Cc: robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH net-next v20 09/15] net: phylink: support late PCS provider attach
Date: Sun, 15 Nov 2026 10:45:17 +0000	[thread overview]
Message-ID: <sashiko-outbox-215400@kernel.org> (raw)
In-Reply-To: <20261114104426.235620-10-ansuelsmth@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] net: phylink: broken MLO_AN_FIXED and MLO_AN_INBAND recovery in
  phylink_resolve()
- [Medium] net: phylink: capability mask corruption in
  phylink_calculate_capabilities()
- [Medium] net: phylink: data race on linkmode bitmaps in
  phylink_create()
- [Low] net: pcs: inaccurate Returns kernel-doc for
  fwnode_pcs_get_from_provider()
--

commit 483dcebdc3997073778f57fa52e0f5ffb3f2eaf9
Author: Christian Marangi <ansuelsmth@gmail.com>

net: phylink: support late PCS provider attach

This commit adds the FWNODE_PCS_PROVIDER_ADD notifier event and
fwnode_pcs_get_from_provider() helper so phylink instances can attach
PCS providers that register after phylink creation and re-trigger link
resolution.

> diff --git a/drivers/net/pcs/pcs.c b/drivers/net/pcs/pcs.c
> index 25b3248e8b93b..fef560eddf2a9 100644
> --- a/drivers/net/pcs/pcs.c
> +++ b/drivers/net/pcs/pcs.c
> @@ -69,6 +69,10 @@ fwnode_pcs_add_provider(struct fwnode_handle *fwnode,
>  
>  	mutex_unlock(&fwnode_pcs_mutex);
>  
> +	blocking_notifier_call_chain(&fwnode_pcs_notify_list,
> +				     FWNODE_PCS_PROVIDER_ADD,
> +				     pp);
> +
>  	pr_debug("Added pcs provider from %pfwf\n", fwnode);
>  
>  	return pp;

[Severity: Medium]
Can firing FWNODE_PCS_PROVIDER_ADD here cause a data race on
pl->supported, pl->link_config.advertising, and pl->supported_interfaces
if a PCS provider registers while phylink_create() or
phylink_validate_phy() is running on another CPU?

phylink_create() registers pcs_provider_notify() before setup is finished
and releases pl->state_mutex before calling phylink_parse_mode() and
phylink_parse_fixedlink() (and phylink_create() is called without
rtnl_lock):

CPU0                                  CPU1
phylink_create()
  register_fwnode_pcs_notifier()
  ...
  mutex_unlock(&pl->state_mutex)
                                      fwnode_pcs_add_provider()
                                        ...
                                        pcs_provider_notify()
                                          mutex_lock(&pl->state_mutex)
                                          phylink_calculate_capabilities()
                                            linkmode_fill(pl->supported)
  phylink_parse_mode()
    phylink_set(pl->supported, ...)  <-- data race

phylink_validate_phy() similarly reads pl->supported_interfaces outside
pl->state_mutex before phylink_bringup_phy() acquires the lock, racing
with pcs_provider_notify() updating pl->supported_interfaces.

[ ... ]

> diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> index e807fcccb5de9..b2d5ced432091 100644
> --- a/drivers/net/phy/phylink.c
> +++ b/drivers/net/phy/phylink.c

[ ... ]

> @@ -2127,20 +2151,42 @@ static int pcs_provider_notify(struct notifier_block *self,
>  	struct fwnode_pcs_provider *pp = data;
>  	struct phylink_pcs *pcs, *tmp;
>  	bool resolve = false;
> +	int count, i;
>  
>  	rtnl_lock();
>  	mutex_lock(&pl->state_mutex);
>  
> -	/*
> -	 * Loop all the PCS for phylink instance and check if
> -	 * this notification is relevant for some of them.
> -	 */
> -	list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list) {
> -		if (!fwnode_pcs_matches_provider(pp, pl->fwnode, pcs))
> -			continue;
> +	switch (val) {
> +	case FWNODE_PCS_PROVIDER_ADD:
> +		count = fwnode_phylink_pcs_count(pl->fwnode);
> +		for (i = 0; i < count; i++) {
> +			pcs = fwnode_pcs_get_from_provider(pp, pl->fwnode, i);
> +			if (IS_ERR_OR_NULL(pcs))
> +				continue;
> +
> +			/* Trigger a resolve only if a PCS is actually added */
> +			if (phylink_add_pcs(pl, pcs))
> +				resolve = true;
> +		}
> +
> +		/* Force an interface reconfig if major config fail */
> +		if (resolve && pl->major_config_failed)
> +			pl->force_major_config = true;

[Severity: High]
Does setting pl->force_major_config here actually recover MLO_AN_FIXED and
MLO_AN_INBAND links when phylink_major_config() previously failed during
phylink_start()?

When phylink_start() runs before the PCS provider is registered,
phylink_major_config() fails and returns before phylink_pcs_neg_mode()
can set pl->act_link_an_mode, leaving pl->act_link_an_mode at 0
(MLO_AN_PHY):

drivers/net/phy/phylink.c:phylink_major_config() {
	...
		if (!pcs) {
			phylink_err(pl,
				    "couldn't find a PCS for %s\n",
				    phy_modes(state->interface));

			pl->major_config_failed = true;
			return;
		}
	}

	pcs_changed = pl->pcs != pcs;

	phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising);
	...
}

When the PCS provider later attaches and phylink_resolve() runs,
phylink_resolve() selects the link_state source using pl->act_link_an_mode
before calling phylink_major_config():

drivers/net/phy/phylink.c:phylink_resolve() {
	...
	} else if (pl->act_link_an_mode == MLO_AN_FIXED) {
		phylink_get_fixed_state(pl, &link_state);
		mac_config = link_state.link;
	} else if (pl->act_link_an_mode == MLO_AN_PHY) {
		link_state = pl->phy_state;
		mac_config = link_state.link;
	} else {
		phylink_mac_pcs_get_state(pl, &link_state);
	...
	if ((mac_config && link_state.interface != pl->link_config.interface) ||
	    pl->force_major_config) {
		...
		phylink_major_config(pl, false, &link_state);
		pl->link_config.interface = link_state.interface;
		pl->force_major_config = false;
	}
}

Because pl->act_link_an_mode is still MLO_AN_PHY, phylink_resolve() copies
the unpopulated pl->phy_state (where link is false and advertising is
zero) into link_state.  For MLO_AN_INBAND 802.3z links without a PHY,
phylink_pcs_neg_mode() sees ETHTOOL_LINK_MODE_Autoneg_BIT cleared in
state->advertising, selects PHYLINK_PCS_NEG_INBAND_DISABLED, and programs
an empty advertisement into the PCS.  For MLO_AN_FIXED links without
polling or a link GPIO interrupt, link_state.link is false,
pl->force_major_config is cleared, and the fixed link stays down forever.

Even when pl->act_link_an_mode is already MLO_AN_INBAND (such as after
FWNODE_PCS_PROVIDER_DEL followed by FWNODE_PCS_PROVIDER_ADD),
phylink_resolve() calls phylink_mac_pcs_get_state() while pl->pcs is still
NULL, before phylink_major_config() attaches and enables the new PCS, so a
non-polled PCS is never queried for its link state after attachment.

> -		phylink_del_pcs(pl, pcs);
> -		resolve = true;
> +		break;
> +	case FWNODE_PCS_PROVIDER_DEL:
> +		/*
> +		 * Loop all the PCS for phylink instance and check if
> +		 * this notification is relevant for some of them.
> +		 */
> +		list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list) {
> +			if (!fwnode_pcs_matches_provider(pp, pl->fwnode, pcs))
> +				continue;
> +
> +			phylink_del_pcs(pl, pcs);
> +			resolve = true;
> +		}
> +		break;
>  	}
>  
>  	/* Exit early if nothing has changed */

[Severity: Medium]
Does calling phylink_calculate_capabilities(pl) below this check in
pcs_provider_notify() overwrite the mode-, PHY-, and SFP-restricted
capability masks in pl->supported and pl->link_config.advertising?

After phylink_calculate_capabilities() runs in phylink_create(),
phylink_parse_mode(), phylink_parse_fixedlink(), phylink_bringup_phy(),
and phylink_sfp_set_config() narrow pl->supported and
pl->link_config.advertising to the active link mode, attached PHY, or SFP
module without updating pl->advertising.  When pcs_provider_notify() later
calls phylink_calculate_capabilities(), it refills pl->supported with all
ones and validates only against the raw MAC and PCS capabilities, wiping
out those restrictions:

drivers/net/phy/phylink.c:phylink_calculate_capabilities() {
	...
	/* Get user-disabled advertising modes */
	linkmode_xor(disabled_advertising, pl->advertising,
		     pl->link_config.advertising);

	/* Calculate the full supported advertisement */
	linkmode_fill(pl->supported);
	linkmode_copy(pl->link_config.advertising, pl->supported);
	__phylink_validate(pl, pl->supported, &pl->link_config);
	...
}

In addition, if pl->link_config.interface is only supported by a PCS that
has not attached yet (or was just removed), __phylink_validate() returns
-EINVAL early when test_bit(state->interface, interfaces) fails, leaving
pl->supported and pl->advertising filled with all ones and causing
phylink_parse_mode() or phylink_validate_phy() to fail with -EINVAL before
the PCS provider can attach.

[ ... ]

> diff --git a/include/linux/pcs/pcs.h b/include/linux/pcs/pcs.h
> index 83e0a2ad4b099..00a636c82c65f 100644
> --- a/include/linux/pcs/pcs.h
> +++ b/include/linux/pcs/pcs.h

[ ... ]

> +/**
> + * fwnode_pcs_get_from_provider() - Retrieve a PCS from a specific provider
> + * @provider: PCS provider to use
> + * @fwnode: firmware node
> + * @index: index fwnode PCS handle in firmware node
> + *
> + * Get a PCS from the firmware node at index specifically provided by
> + * passed PCS provider.
> + *
> + * Unlike fwnode_pcs_get(), this function does not search the global list of
> + * PCS providers. The caller must provide the provider responsible for the
> + * referenced PCS.
> + *
> + * Returns: a pointer to the phylink_pcs or a negative error pointer. Can
> + * return -ENODEV if the PCS is not present in global providers list (either
> + * due to driver still needs to be probed or it failed to probe/removed) or
> + * can return -EINVAL if the PCS provider doesn't expose any PCS for the fwnode.
> + */

[Severity: Low]
This isn't a bug, but the Returns description says
fwnode_pcs_get_from_provider() can return -ENODEV if the PCS is not
present in the global providers list, which contradicts the preceding
paragraph and the implementation in drivers/net/pcs/pcs.c (which only
checks the passed provider and never searches fwnode_pcs_providers).

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114104426.235620-1-ansuelsmth@gmail.com?part=9

  reply	other threads:[~2026-11-15 10:45 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 10:43 [PATCH net-next v20 00/15] net: pcs: Introduce support for fwnode PCS Christian Marangi
2026-11-14 10:43 ` [PATCH net-next v20 01/15] net: phylink: keep and use MAC supported_interfaces in phylink struct Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 02/15] net: phylink: make PCS phylink instance access RCU protected Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 03/15] net: phylink: introduce internal phylink PCS handling Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 04/15] net: pcs: implement Firmware node support for PCS driver Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 05/15] net: phylink: save phylink instance fwnode on phylink_create Christian Marangi
2026-11-14 10:43 ` [PATCH net-next v20 06/15] net: phylink: make phylink_validate state locked Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 07/15] net: phylink: add capabilities helper and track modified advertising Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 08/15] net: phylink: support PCS provider release Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 09/15] net: phylink: support late PCS provider attach Christian Marangi
2026-11-15 10:45   ` sashiko-bot [this message]
2026-11-14 10:43 ` [PATCH net-next v20 10/15] net: Document PCS subsystem Christian Marangi
2026-11-14 10:43 ` [PATCH net-next v20 11/15] MAINTAINERS: add myself as PCS subsystem maintainer Christian Marangi
2026-11-14 10:43 ` [PATCH net-next v20 12/15] net: phylink: add .pcs_link_down PCS OP Christian Marangi
2026-11-14 10:43 ` [PATCH net-next v20 13/15] dt-bindings: net: pcs: Document support for Airoha Ethernet PCS Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 14/15] net: pcs: airoha: add PCS driver for Airoha AN7581 SoC Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 10:43 ` [PATCH net-next v20 15/15] net: airoha: add phylink support Christian Marangi
2026-11-15 10:45   ` sashiko-bot
2026-11-14 13:27 ` [PATCH net-next v20 00/15] net: pcs: Introduce support for fwnode PCS Frank Wunderlich

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=sashiko-outbox-215400@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=ansuelsmth@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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