All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.