diff mbox series

[net-next,v2,1/6] net: dsa: add support for phylink mac_select_pcs()

Message ID E1nKlY3-009aKs-Oo@rmk-PC.armlinux.org.uk (mailing list archive)
State Accepted
Commit bde018222c6b084ac32933a9f933581dd83da18e
Delegated to: Netdev Maintainers
Headers show
Series net: dsa: qca8k: convert to phylink_pcs and mark as non-legacy | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 22 this patch: 22
netdev/cc_maintainers warning 1 maintainers not CCed: linux@armlinux.org.uk
netdev/build_clang success Errors and warnings before: 24 this patch: 24
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 27 this patch: 27
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 36 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Russell King (Oracle) Feb. 17, 2022, 6:30 p.m. UTC
Add DSA support for the phylink mac_select_pcs() method so DSA drivers
can return provide phylink with the appropriate PCS for the PHY
interface mode.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
 include/net/dsa.h |  3 +++
 net/dsa/port.c    | 15 +++++++++++++++
 2 files changed, 18 insertions(+)

Comments

Vladimir Oltean Feb. 19, 2022, 9:12 p.m. UTC | #1
Hello,

On Thu, Feb 17, 2022 at 06:30:35PM +0000, Russell King (Oracle) wrote:
> Add DSA support for the phylink mac_select_pcs() method so DSA drivers
> can return provide phylink with the appropriate PCS for the PHY
> interface mode.
> 
> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
> ---
>  include/net/dsa.h |  3 +++
>  net/dsa/port.c    | 15 +++++++++++++++
>  2 files changed, 18 insertions(+)
> 
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index 85cb9aed4c51..87aef3ed88a7 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -788,6 +788,9 @@ struct dsa_switch_ops {
>  	void	(*phylink_validate)(struct dsa_switch *ds, int port,
>  				    unsigned long *supported,
>  				    struct phylink_link_state *state);
> +	struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds,
> +						      int port,
> +						      phy_interface_t iface);
>  	int	(*phylink_mac_link_state)(struct dsa_switch *ds, int port,
>  					  struct phylink_link_state *state);
>  	void	(*phylink_mac_config)(struct dsa_switch *ds, int port,
> diff --git a/net/dsa/port.c b/net/dsa/port.c
> index cca5cf686f74..d8534fd9fab9 100644
> --- a/net/dsa/port.c
> +++ b/net/dsa/port.c
> @@ -1053,6 +1053,20 @@ static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
>  	}
>  }
>  
> +static struct phylink_pcs *
> +dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
> +				phy_interface_t interface)
> +{
> +	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
> +	struct dsa_switch *ds = dp->ds;
> +	struct phylink_pcs *pcs = NULL;
> +
> +	if (ds->ops->phylink_mac_select_pcs)
> +		pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
> +
> +	return pcs;
> +}
> +
>  static void dsa_port_phylink_mac_config(struct phylink_config *config,
>  					unsigned int mode,
>  					const struct phylink_link_state *state)
> @@ -1119,6 +1133,7 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
>  
>  static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
>  	.validate = dsa_port_phylink_validate,
> +	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,

This patch breaks probing on DSA switch drivers that weren't converted
to supported_interfaces, due to this check in phylink_create():

	/* Validate the supplied configuration */
	if (mac_ops->mac_select_pcs &&
	    phy_interface_empty(config->supported_interfaces)) {
		dev_err(config->dev,
			"phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
		return ERR_PTR(-EINVAL);
	}

>  	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
>  	.mac_config = dsa_port_phylink_mac_config,
>  	.mac_an_restart = dsa_port_phylink_mac_an_restart,
> -- 
> 2.30.2
>
Vladimir Oltean Feb. 19, 2022, 9:22 p.m. UTC | #2
On Sat, Feb 19, 2022 at 11:12:41PM +0200, Vladimir Oltean wrote:
> >  static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
> >  	.validate = dsa_port_phylink_validate,
> > +	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
> 
> This patch breaks probing on DSA switch drivers that weren't converted
> to supported_interfaces, due to this check in phylink_create():

And this is only the most superficial layer of breakage. Everywhere in
phylink.c where pl->mac_ops->mac_select_pcs() is used, its presence is
checked and non-zero return codes from it are treated as hard errors,
even -EOPNOTSUPP, even if this particular error code is probably
intended to behave identically as the absence of the function pointer,
for compatibility.
Russell King (Oracle) Feb. 21, 2022, 1:30 p.m. UTC | #3
On Sat, Feb 19, 2022 at 11:22:24PM +0200, Vladimir Oltean wrote:
> On Sat, Feb 19, 2022 at 11:12:41PM +0200, Vladimir Oltean wrote:
> > >  static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
> > >  	.validate = dsa_port_phylink_validate,
> > > +	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
> > 
> > This patch breaks probing on DSA switch drivers that weren't converted
> > to supported_interfaces, due to this check in phylink_create():
> 
> And this is only the most superficial layer of breakage. Everywhere in
> phylink.c where pl->mac_ops->mac_select_pcs() is used, its presence is
> checked and non-zero return codes from it are treated as hard errors,
> even -EOPNOTSUPP, even if this particular error code is probably
> intended to behave identically as the absence of the function pointer,
> for compatibility.

I don't understand what problem you're getting at here - and I don't
think there is a problem.

While I know it's conventional in DSA to use EOPNOTSUPP to indicate
that a called method is not implemented, this is not something that
is common across the board - and is not necessary here.

The implementation of dsa_port_phylink_mac_select_pcs() returns a
NULL PCS when the DSA operation for it is not implemented. This
means that:

1) phylink_validate_mac_and_pcs() won't fail due to mac_select_pcs()
   being present but DSA drivers not implementing it.

2) phylink_major_config() will not attempt to call phylink_set_pcs()
   to change the PCS.

So, that much is perfectly safe.

As for your previous email reporting the problem with phylink_create(),
thanks for the report and sorry for the breakage - the breakage was
obviously not intended, and came about because of all the patch
shuffling I've done over the last six months trying to get these
changes in, and having forgotten about this dependency.

I imagine the reason you've raised EOPNOTSUPP is because you wanted to
change dsa_port_phylink_mac_select_pcs() to return an error-pointer
encoded with that error code rather than NULL, but you then (no
surprises to me) caused phylink to fail.

Considering the idea of using EOPNOTSUPP, at the two places we call
mac_select_pcs(), we would need to treat this the same way we currently
treat NULL. We would also need phylink_create() to call
mac_select_pcs() if the method is non-NULL to discover if the DSA
sub-driver implements the method - but we would need to choose an
interface at this point.

I think at this point, I'd rather:

1) add a bool in struct phylink to indicate whether we should be calling
   mac_select_pcs, and replace the

	if (pl->mac_ops->mac_select_pcs)

   with

        if (pl->using_mac_select_pcs)

2) have phylink_create() do:

	bool using_mac_select_pcs = false;

	if (mac_ops->mac_select_pcs &&
	    mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != 
	      ERR_PTR(-EOPNOTSUPP))
		using_mac_select_pcs = true;

	if (using_mac_select_pcs &&
	    phy_interface_empty(config->supported_interfaces)) {
		...

	...

	pl->using_mac_select_pcs = using_mac_select_pcs;

which should give what was intended until DSA drivers are all updated
to fill in config->supported_interfaces.
Russell King (Oracle) Feb. 21, 2022, 2:15 p.m. UTC | #4
On Mon, Feb 21, 2022 at 01:30:09PM +0000, Russell King (Oracle) wrote:
> On Sat, Feb 19, 2022 at 11:22:24PM +0200, Vladimir Oltean wrote:
> > On Sat, Feb 19, 2022 at 11:12:41PM +0200, Vladimir Oltean wrote:
> > > >  static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
> > > >  	.validate = dsa_port_phylink_validate,
> > > > +	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
> > > 
> > > This patch breaks probing on DSA switch drivers that weren't converted
> > > to supported_interfaces, due to this check in phylink_create():
> > 
> > And this is only the most superficial layer of breakage. Everywhere in
> > phylink.c where pl->mac_ops->mac_select_pcs() is used, its presence is
> > checked and non-zero return codes from it are treated as hard errors,
> > even -EOPNOTSUPP, even if this particular error code is probably
> > intended to behave identically as the absence of the function pointer,
> > for compatibility.
> 
> I don't understand what problem you're getting at here - and I don't
> think there is a problem.
> 
> While I know it's conventional in DSA to use EOPNOTSUPP to indicate
> that a called method is not implemented, this is not something that
> is common across the board - and is not necessary here.
> 
> The implementation of dsa_port_phylink_mac_select_pcs() returns a
> NULL PCS when the DSA operation for it is not implemented. This
> means that:
> 
> 1) phylink_validate_mac_and_pcs() won't fail due to mac_select_pcs()
>    being present but DSA drivers not implementing it.
> 
> 2) phylink_major_config() will not attempt to call phylink_set_pcs()
>    to change the PCS.
> 
> So, that much is perfectly safe.
> 
> As for your previous email reporting the problem with phylink_create(),
> thanks for the report and sorry for the breakage - the breakage was
> obviously not intended, and came about because of all the patch
> shuffling I've done over the last six months trying to get these
> changes in, and having forgotten about this dependency.
> 
> I imagine the reason you've raised EOPNOTSUPP is because you wanted to
> change dsa_port_phylink_mac_select_pcs() to return an error-pointer
> encoded with that error code rather than NULL, but you then (no
> surprises to me) caused phylink to fail.
> 
> Considering the idea of using EOPNOTSUPP, at the two places we call
> mac_select_pcs(), we would need to treat this the same way we currently
> treat NULL. We would also need phylink_create() to call
> mac_select_pcs() if the method is non-NULL to discover if the DSA
> sub-driver implements the method - but we would need to choose an
> interface at this point.
> 
> I think at this point, I'd rather:
> 
> 1) add a bool in struct phylink to indicate whether we should be calling
>    mac_select_pcs, and replace the
> 
> 	if (pl->mac_ops->mac_select_pcs)
> 
>    with
> 
>         if (pl->using_mac_select_pcs)
> 
> 2) have phylink_create() do:
> 
> 	bool using_mac_select_pcs = false;
> 
> 	if (mac_ops->mac_select_pcs &&
> 	    mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != 
> 	      ERR_PTR(-EOPNOTSUPP))
> 		using_mac_select_pcs = true;
> 
> 	if (using_mac_select_pcs &&
> 	    phy_interface_empty(config->supported_interfaces)) {
> 		...
> 
> 	...
> 
> 	pl->using_mac_select_pcs = using_mac_select_pcs;
> 
> which should give what was intended until DSA drivers are all updated
> to fill in config->supported_interfaces.

Please try this patch, thanks:

diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 6c7ab4a7a3be..de0557bbd4a7 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -74,6 +74,7 @@ struct phylink {
 	struct work_struct resolve;
 
 	bool mac_link_dropped;
+	bool using_mac_select_pcs;
 
 	struct sfp_bus *sfp_bus;
 	bool sfp_may_have_phy;
@@ -416,7 +417,7 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
 	int ret;
 
 	/* Get the PCS for this interface mode */
-	if (pl->mac_ops->mac_select_pcs) {
+	if (pl->using_mac_select_pcs) {
 		pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
 		if (IS_ERR(pcs))
 			return PTR_ERR(pcs);
@@ -791,7 +792,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
 
 	phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
 
-	if (pl->mac_ops->mac_select_pcs) {
+	if (pl->using_mac_select_pcs) {
 		pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
 		if (IS_ERR(pcs)) {
 			phylink_err(pl,
@@ -1204,11 +1205,17 @@ struct phylink *phylink_create(struct phylink_config *config,
 			       phy_interface_t iface,
 			       const struct phylink_mac_ops *mac_ops)
 {
+	bool using_mac_select_pcs = false;
 	struct phylink *pl;
 	int ret;
 
-	/* Validate the supplied configuration */
 	if (mac_ops->mac_select_pcs &&
+	    mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
+	      ERR_PTR(-EOPNOTSUPP))
+		using_mac_select_pcs = true;
+
+	/* Validate the supplied configuration */
+	if (using_mac_select_pcs &&
 	    phy_interface_empty(config->supported_interfaces)) {
 		dev_err(config->dev,
 			"phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
@@ -1232,6 +1239,7 @@ struct phylink *phylink_create(struct phylink_config *config,
 		return ERR_PTR(-EINVAL);
 	}
 
+	pl->using_mac_select_pcs = using_mac_select_pcs;
 	pl->phy_state.interface = iface;
 	pl->link_interface = iface;
 	if (iface == PHY_INTERFACE_MODE_MOCA)
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 258782bf4271..367d141c6971 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1058,8 +1058,8 @@ dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
 				phy_interface_t interface)
 {
 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+	struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
 	struct dsa_switch *ds = dp->ds;
-	struct phylink_pcs *pcs = NULL;
 
 	if (ds->ops->phylink_mac_select_pcs)
 		pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
Vladimir Oltean Feb. 21, 2022, 2:32 p.m. UTC | #5
Hello,

On Mon, Feb 21, 2022 at 01:30:09PM +0000, Russell King (Oracle) wrote:
> On Sat, Feb 19, 2022 at 11:22:24PM +0200, Vladimir Oltean wrote:
> > On Sat, Feb 19, 2022 at 11:12:41PM +0200, Vladimir Oltean wrote:
> > > >  static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
> > > >  	.validate = dsa_port_phylink_validate,
> > > > +	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
> > > 
> > > This patch breaks probing on DSA switch drivers that weren't converted
> > > to supported_interfaces, due to this check in phylink_create():
> > 
> > And this is only the most superficial layer of breakage. Everywhere in
> > phylink.c where pl->mac_ops->mac_select_pcs() is used, its presence is
> > checked and non-zero return codes from it are treated as hard errors,
> > even -EOPNOTSUPP, even if this particular error code is probably
> > intended to behave identically as the absence of the function pointer,
> > for compatibility.
> 
> I don't understand what problem you're getting at here - and I don't
> think there is a problem.
> 
> While I know it's conventional in DSA to use EOPNOTSUPP to indicate
> that a called method is not implemented, this is not something that
> is common across the board - and is not necessary here.
> 
> The implementation of dsa_port_phylink_mac_select_pcs() returns a
> NULL PCS when the DSA operation for it is not implemented. This
> means that:
> 
> 1) phylink_validate_mac_and_pcs() won't fail due to mac_select_pcs()
>    being present but DSA drivers not implementing it.
> 
> 2) phylink_major_config() will not attempt to call phylink_set_pcs()
>    to change the PCS.
> 
> So, that much is perfectly safe.
> 
> As for your previous email reporting the problem with phylink_create(),
> thanks for the report and sorry for the breakage - the breakage was
> obviously not intended, and came about because of all the patch
> shuffling I've done over the last six months trying to get these
> changes in, and having forgotten about this dependency.
> 
> I imagine the reason you've raised EOPNOTSUPP is because you wanted to
> change dsa_port_phylink_mac_select_pcs() to return an error-pointer
> encoded with that error code rather than NULL, but you then (no
> surprises to me) caused phylink to fail.
> 
> Considering the idea of using EOPNOTSUPP, at the two places we call
> mac_select_pcs(), we would need to treat this the same way we currently
> treat NULL. We would also need phylink_create() to call
> mac_select_pcs() if the method is non-NULL to discover if the DSA
> sub-driver implements the method - but we would need to choose an
> interface at this point.
> 
> I think at this point, I'd rather:
> 
> 1) add a bool in struct phylink to indicate whether we should be calling
>    mac_select_pcs, and replace the
> 
> 	if (pl->mac_ops->mac_select_pcs)
> 
>    with
> 
>         if (pl->using_mac_select_pcs)
> 
> 2) have phylink_create() do:
> 
> 	bool using_mac_select_pcs = false;
> 
> 	if (mac_ops->mac_select_pcs &&
> 	    mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != 
> 	      ERR_PTR(-EOPNOTSUPP))
> 		using_mac_select_pcs = true;
> 
> 	if (using_mac_select_pcs &&
> 	    phy_interface_empty(config->supported_interfaces)) {
> 		...
> 
> 	...
> 
> 	pl->using_mac_select_pcs = using_mac_select_pcs;
> 
> which should give what was intended until DSA drivers are all updated
> to fill in config->supported_interfaces.

I didn't study the problem enough to be in the position to suggest the
best solution.

As you've explained*, phylink works properly when mac_select_pcs()
returns NULL but not special error codes, so extra handling would be
required for those - and you've shown an approach that seems reasonable
if we use -EOPNOTSUPP.

Alternatively, phylink_create() gets the initial PHY interface mode
passed to it, I wonder, couldn't we call mac_select_pcs() with that in
order to determine whether the function is a stub or not? I see that
only axienet_mac_select_pcs returns NULL based on the interface mode,
and I assume it will never get passed an invalid-to-it PHY interface
mode. For this reason we can't pass PHY_INTERFACE_MODE_NA as long as we
check for NULL instead of -EOPNOTSUPP - because drivers may not expect
this PHY interface type. So this second approach may also work and may
require less phylink rework, although it may be slightly more prone to
subtle breakage, if for whatever reason I don't see right now, the
phylink instance gets created with an undetermined PHY mode.

Either of these solutions is fine by me, with -EOPNOTSUPP probably being
preferable for the extra safety at the expense of extra rework.

*and as I haven't considered, to be honest. When phylink_major_config()
gets called after a SGMII to 10GBaseR switchover, and mac_select_pcs is
called and returns NULL, the current behavior is to keep working with
the PCS for SGMII. Is that intended?
Vladimir Oltean Feb. 21, 2022, 2:41 p.m. UTC | #6
On Mon, Feb 21, 2022 at 02:15:26PM +0000, Russell King (Oracle) wrote:
> Please try this patch, thanks:

Probing, bring the interface up, down, and testing with traffic over a
QSGMII port with a pre-March 2020 phylink_pcs works as before, thanks.
Russell King (Oracle) Feb. 21, 2022, 2:44 p.m. UTC | #7
On Mon, Feb 21, 2022 at 04:32:54PM +0200, Vladimir Oltean wrote:
> Alternatively, phylink_create() gets the initial PHY interface mode
> passed to it, I wonder, couldn't we call mac_select_pcs() with that in
> order to determine whether the function is a stub or not?

That would be rather prone to odd behaviour depending on how
phylink_create() is called, depending on the initial interface mode.
If the initial interface mode causes mac_select_pcs() to return NULL
but it actually needed to return a PCS for a different interface mode,
then we fail.

> *and as I haven't considered, to be honest. When phylink_major_config()
> gets called after a SGMII to 10GBaseR switchover, and mac_select_pcs is
> called and returns NULL, the current behavior is to keep working with
> the PCS for SGMII. Is that intended?

It was not originally intended, but as a result of the discussion
around this patch which didn't go anywhere useful, I dropped it as
a means to a path of least resistance.

https://patchwork.kernel.org/project/linux-arm-kernel/patch/E1mpSba-00BXp6-9e@rmk-PC.armlinux.org.uk/
Vladimir Oltean Feb. 21, 2022, 2:55 p.m. UTC | #8
On Mon, Feb 21, 2022 at 02:44:34PM +0000, Russell King (Oracle) wrote:
> On Mon, Feb 21, 2022 at 04:32:54PM +0200, Vladimir Oltean wrote:
> > Alternatively, phylink_create() gets the initial PHY interface mode
> > passed to it, I wonder, couldn't we call mac_select_pcs() with that in
> > order to determine whether the function is a stub or not?
> 
> That would be rather prone to odd behaviour depending on how
> phylink_create() is called, depending on the initial interface mode.
> If the initial interface mode causes mac_select_pcs() to return NULL
> but it actually needed to return a PCS for a different interface mode,
> then we fail.

I agree. I just wanted to make it clear that if you have a better idea
than a pointer-encoded -EOPNOTSUPP, I'm not bent on going with -EOPNOTSUPP.

> > *and as I haven't considered, to be honest. When phylink_major_config()
> > gets called after a SGMII to 10GBaseR switchover, and mac_select_pcs is
> > called and returns NULL, the current behavior is to keep working with
> > the PCS for SGMII. Is that intended?
> 
> It was not originally intended, but as a result of the discussion
> around this patch which didn't go anywhere useful, I dropped it as
> a means to a path of least resistance.
> 
> https://patchwork.kernel.org/project/linux-arm-kernel/patch/E1mpSba-00BXp6-9e@rmk-PC.armlinux.org.uk/

Oh, but that patch didn't close exactly this condition that we're
talking about here, did it? It allows phylink_set_pcs() to be called
with NULL, but phylink_major_config() still has the non-NULL check,
which prevents it from having any effect in this scenario:

	/* If we have a new PCS, switch to the new PCS after preparing the MAC
	 * for the change.
	 */
	if (pcs)
		phylink_set_pcs(pl, pcs);

I re-read the conversation and I still don't see this argument being
given, otherwise I wouldn't have opposed...
Russell King (Oracle) Feb. 21, 2022, 4:38 p.m. UTC | #9
On Mon, Feb 21, 2022 at 04:55:40PM +0200, Vladimir Oltean wrote:
> On Mon, Feb 21, 2022 at 02:44:34PM +0000, Russell King (Oracle) wrote:
> > > *and as I haven't considered, to be honest. When phylink_major_config()
> > > gets called after a SGMII to 10GBaseR switchover, and mac_select_pcs is
> > > called and returns NULL, the current behavior is to keep working with
> > > the PCS for SGMII. Is that intended?
> > 
> > It was not originally intended, but as a result of the discussion
> > around this patch which didn't go anywhere useful, I dropped it as
> > a means to a path of least resistance.
> > 
> > https://patchwork.kernel.org/project/linux-arm-kernel/patch/E1mpSba-00BXp6-9e@rmk-PC.armlinux.org.uk/
> 
> Oh, but that patch didn't close exactly this condition that we're
> talking about here, did it? It allows phylink_set_pcs() to be called
> with NULL, but phylink_major_config() still has the non-NULL check,
> which prevents it from having any effect in this scenario:
> 
> 	/* If we have a new PCS, switch to the new PCS after preparing the MAC
> 	 * for the change.
> 	 */
> 	if (pcs)
> 		phylink_set_pcs(pl, pcs);
> 
> I re-read the conversation and I still don't see this argument being
> given, otherwise I wouldn't have opposed...

The reason for that condition above is to avoid disrupting the case
where drivers which do not (yet) provide a mac_select_pcs()
implementation (therefore  pcs will be NULL here) but instead register
their PCS by calling phylink_set_pcs() immediately after a call to
phylink_create(). If the above call to phylink_set_pcs() was
unconditional, then:

1) it would break these existing drivers,
2) we would definitely need to make phylink_set_pcs() safe to call
   with a NULL pcs argument to avoid crashing when in e.g. pcs-less
   modes.

If that usage was eliminated, then the problem goes away... but that
means changing drivers, and changing drivers is always a long hard
slog that takes months and several kernel cycles to do.
diff mbox series

Patch

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 85cb9aed4c51..87aef3ed88a7 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -788,6 +788,9 @@  struct dsa_switch_ops {
 	void	(*phylink_validate)(struct dsa_switch *ds, int port,
 				    unsigned long *supported,
 				    struct phylink_link_state *state);
+	struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds,
+						      int port,
+						      phy_interface_t iface);
 	int	(*phylink_mac_link_state)(struct dsa_switch *ds, int port,
 					  struct phylink_link_state *state);
 	void	(*phylink_mac_config)(struct dsa_switch *ds, int port,
diff --git a/net/dsa/port.c b/net/dsa/port.c
index cca5cf686f74..d8534fd9fab9 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1053,6 +1053,20 @@  static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
 	}
 }
 
+static struct phylink_pcs *
+dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
+				phy_interface_t interface)
+{
+	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+	struct dsa_switch *ds = dp->ds;
+	struct phylink_pcs *pcs = NULL;
+
+	if (ds->ops->phylink_mac_select_pcs)
+		pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
+
+	return pcs;
+}
+
 static void dsa_port_phylink_mac_config(struct phylink_config *config,
 					unsigned int mode,
 					const struct phylink_link_state *state)
@@ -1119,6 +1133,7 @@  static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
 
 static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
 	.validate = dsa_port_phylink_validate,
+	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
 	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
 	.mac_config = dsa_port_phylink_mac_config,
 	.mac_an_restart = dsa_port_phylink_mac_an_restart,