diff mbox series

[RFC,net-next] net: pcs: add helper module for standalone drivers

Message ID ba4e359584a6b3bc4b3470822c42186d5b0856f9.1721910728.git.daniel@makrotopia.org (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series [RFC,net-next] net: pcs: add helper module for standalone drivers | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 273 this patch: 273
netdev/build_tools success Errors and warnings before: 10 this patch: 10
netdev/cc_maintainers warning 2 maintainers not CCed: linux-mediatek@lists.infradead.org linux-arm-kernel@lists.infradead.org
netdev/build_clang success Errors and warnings before: 282 this patch: 282
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 287 this patch: 287
netdev/checkpatch warning CHECK: Please use a blank line after function/struct/union/enum declarations WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 81 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Daniel Golle July 25, 2024, 12:44 p.m. UTC
Implement helper module for standalone PCS drivers which allows
standaline PCS drivers to register and users to get instances of
'struct phylink_pcs' using device tree nodes.

At this point only a single instance for each device tree node is
supported, once we got devices providing more than one PCS we can
extend it and introduce an xlate function as well as '#pcs-cells',
similar to how this is done by the PHY framework.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
This is meant to provide the infrastructure suggested by
Russell King in an earlier review. It just took me a long while to
find the time to implement this.
Users are going to be the standalone PCS drivers for 8/10 LynxI as
well as 64/66 USXGMII PCS found on MediaTek MT7988 SoC.
See also https://patchwork.kernel.org/comment/25636726/

The full tree where this is being used can be found at

https://github.com/dangowrt/linux/commits/mt7988-for-next/

 drivers/net/pcs/Kconfig            |  4 ++
 drivers/net/pcs/Makefile           |  1 +
 drivers/net/pcs/pcs-standalone.c   | 95 +++++++++++++++++++++++++++++
 include/linux/pcs/pcs-standalone.h | 25 ++++++++
 4 files changed, 129 insertions(+)
 create mode 100644 drivers/net/pcs/pcs-standalone.c
 create mode 100644 include/linux/pcs/pcs-standalone.h

Comments

Simon Horman July 25, 2024, 4:50 p.m. UTC | #1
On Thu, Jul 25, 2024 at 01:44:49PM +0100, Daniel Golle wrote:
> Implement helper module for standalone PCS drivers which allows
> standaline PCS drivers to register and users to get instances of
> 'struct phylink_pcs' using device tree nodes.
> 
> At this point only a single instance for each device tree node is
> supported, once we got devices providing more than one PCS we can
> extend it and introduce an xlate function as well as '#pcs-cells',
> similar to how this is done by the PHY framework.
> 
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> This is meant to provide the infrastructure suggested by
> Russell King in an earlier review. It just took me a long while to
> find the time to implement this.
> Users are going to be the standalone PCS drivers for 8/10 LynxI as
> well as 64/66 USXGMII PCS found on MediaTek MT7988 SoC.
> See also https://patchwork.kernel.org/comment/25636726/
> 
> The full tree where this is being used can be found at
> 
> https://github.com/dangowrt/linux/commits/mt7988-for-next/

Hi Daniel,

I realise this is an RFC, but I'm guessing a user will need to be submitted
for this to progress into net-next.

...

> +++ b/drivers/net/pcs/pcs-standalone.c

...

> +static struct pcs_standalone *of_pcs_locate(const struct device_node *_np, u32 index)

nit: This could trivially be line wrapped to 80 columns wide

> +{
> +	struct device_node *np;
> +	struct pcs_standalone *iter, *pcssa = NULL;

nit: Reverse xmas tree

...

> +struct phylink_pcs *devm_of_pcs_get(struct device *dev,
> +				    const struct device_node *np,
> +				    unsigned int index)
> +{
> +	struct pcs_standalone *pcssa;
> +
> +	pcssa = of_pcs_locate(np ?: dev->of_node, index);
> +	if (IS_ERR_OR_NULL(pcssa))
> +		return ERR_PTR(PTR_ERR(pcssa));

nit: Perhaps ERR_CAST() ?

> +
> +	device_link_add(dev, pcssa->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
> +
> +	return pcssa->pcs;
> +}
> +EXPORT_SYMBOL_GPL(devm_of_pcs_get);
> +
> +MODULE_DESCRIPTION("Helper for standalone PCS drivers");
> +MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/pcs/pcs-standalone.h b/include/linux/pcs/pcs-standalone.h

Please consider adding this file to MAINTAINERS.

...

> +static inline int devm_pcs_register(struct device *dev, struct phylink_pcs *pcs);
> +	return -EOPNOTSUPP;
> +}

The above does not compile.

...
Russell King (Oracle) July 29, 2024, 11:28 a.m. UTC | #2
On Thu, Jul 25, 2024 at 01:44:49PM +0100, Daniel Golle wrote:
> Implement helper module for standalone PCS drivers which allows
> standaline PCS drivers to register and users to get instances of
> 'struct phylink_pcs' using device tree nodes.
> 
> At this point only a single instance for each device tree node is
> supported, once we got devices providing more than one PCS we can
> extend it and introduce an xlate function as well as '#pcs-cells',
> similar to how this is done by the PHY framework.
> 
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> This is meant to provide the infrastructure suggested by
> Russell King in an earlier review. It just took me a long while to
> find the time to implement this.
> Users are going to be the standalone PCS drivers for 8/10 LynxI as
> well as 64/66 USXGMII PCS found on MediaTek MT7988 SoC.
> See also https://patchwork.kernel.org/comment/25636726/
> 
> The full tree where this is being used can be found at
> 
> https://github.com/dangowrt/linux/commits/mt7988-for-next/
> 
>  drivers/net/pcs/Kconfig            |  4 ++
>  drivers/net/pcs/Makefile           |  1 +
>  drivers/net/pcs/pcs-standalone.c   | 95 +++++++++++++++++++++++++++++
>  include/linux/pcs/pcs-standalone.h | 25 ++++++++
>  4 files changed, 129 insertions(+)
>  create mode 100644 drivers/net/pcs/pcs-standalone.c
>  create mode 100644 include/linux/pcs/pcs-standalone.h
> 
> diff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig
> index f6aa437473de..2b02b9351fa4 100644
> --- a/drivers/net/pcs/Kconfig
> +++ b/drivers/net/pcs/Kconfig
> @@ -5,6 +5,10 @@
>  
>  menu "PCS device drivers"
>  
> +config PCS_STANDALONE
> +	tristate
> +	select PHYLINK
> +
>  config PCS_XPCS
>  	tristate "Synopsys DesignWare Ethernet XPCS"
>  	select PHYLINK
> diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile
> index 4f7920618b90..0cb0057f2b8e 100644
> --- a/drivers/net/pcs/Makefile
> +++ b/drivers/net/pcs/Makefile
> @@ -4,6 +4,7 @@
>  pcs_xpcs-$(CONFIG_PCS_XPCS)	:= pcs-xpcs.o pcs-xpcs-plat.o \
>  				   pcs-xpcs-nxp.o pcs-xpcs-wx.o
>  
> +obj-$(CONFIG_PCS_STANDALONE)	+= pcs-standalone.o
>  obj-$(CONFIG_PCS_XPCS)		+= pcs_xpcs.o
>  obj-$(CONFIG_PCS_LYNX)		+= pcs-lynx.o
>  obj-$(CONFIG_PCS_MTK_LYNXI)	+= pcs-mtk-lynxi.o
> diff --git a/drivers/net/pcs/pcs-standalone.c b/drivers/net/pcs/pcs-standalone.c
> new file mode 100644
> index 000000000000..1569793328a1
> --- /dev/null
> +++ b/drivers/net/pcs/pcs-standalone.c
> @@ -0,0 +1,95 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Helpers for standalone PCS drivers
> + *
> + * Copyright (C) 2024 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#include <linux/pcs/pcs-standalone.h>
> +#include <linux/phylink.h>
> +
> +static LIST_HEAD(pcs_list);
> +static DEFINE_MUTEX(pcs_mutex);
> +
> +struct pcs_standalone {
> +	struct device *dev;
> +	struct phylink_pcs *pcs;
> +	struct list_head list;
> +};
> +
> +static void devm_pcs_provider_release(struct device *dev, void *res)
> +{
> +	struct pcs_standalone *pcssa = (struct pcs_standalone *)res;
> +
> +	mutex_lock(&pcs_mutex);
> +	list_del(&pcssa->list);
> +	mutex_unlock(&pcs_mutex);

This needs to do notify phylink if the PCS has gone away, but the
locking for this would be somewhat difficult (because pcs->phylink
could change if the PCS changes.) That would need to be solved
somehow.

> +}
> +
> +int devm_pcs_register(struct device *dev, struct phylink_pcs *pcs)
> +{
> +	struct pcs_standalone *pcssa;
> +
> +	pcssa = devres_alloc(devm_pcs_provider_release, sizeof(*pcssa),
> +			     GFP_KERNEL);
> +	if (!pcssa)
> +		return -ENOMEM;
> +
> +	devres_add(dev, pcssa);
> +	pcssa->pcs = pcs;
> +	pcssa->dev = dev;
> +
> +	mutex_lock(&pcs_mutex);
> +	list_add_tail(&pcssa->list, &pcs_list);
> +	mutex_unlock(&pcs_mutex);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(devm_pcs_register);
> +
> +static struct pcs_standalone *of_pcs_locate(const struct device_node *_np, u32 index)
> +{
> +	struct device_node *np;
> +	struct pcs_standalone *iter, *pcssa = NULL;
> +
> +	if (!_np)
> +		return NULL;
> +
> +	np = of_parse_phandle(_np, "pcs-handle", index);
> +	if (!np)
> +		return NULL;
> +
> +	mutex_lock(&pcs_mutex);
> +	list_for_each_entry(iter, &pcs_list, list) {
> +		if (iter->dev->of_node != np)
> +			continue;
> +
> +		pcssa = iter;
> +		break;
> +	}
> +	mutex_unlock(&pcs_mutex);
> +
> +	of_node_put(np);
> +
> +	return pcssa ?: ERR_PTR(-ENODEV);
> +}
> +
> +struct phylink_pcs *devm_of_pcs_get(struct device *dev,
> +				    const struct device_node *np,
> +				    unsigned int index)
> +{
> +	struct pcs_standalone *pcssa;
> +
> +	pcssa = of_pcs_locate(np ?: dev->of_node, index);
> +	if (IS_ERR_OR_NULL(pcssa))
> +		return ERR_PTR(PTR_ERR(pcssa));
> +
> +	device_link_add(dev, pcssa->dev, DL_FLAG_AUTOREMOVE_CONSUMER);

This is really not a nice solution when one has a network device that
has multiple interfaces. This will cause all interfaces on that device
to be purged from the system when a PCS for one of the interfaces
goes away. If the system is using NFS-root, that could result in the
rootfs being lost. We should handle this more gracefully.
Daniel Golle July 29, 2024, 7:42 p.m. UTC | #3
Hi Russell,

thank you for commenting on this patch. Please help me understand which
direction I should work towards to see support for the MT7988 SoC
Ethernet in future kernels, see my questions below.

On Mon, Jul 29, 2024 at 12:28:15PM +0100, Russell King (Oracle) wrote:
> On Thu, Jul 25, 2024 at 01:44:49PM +0100, Daniel Golle wrote:
> > +static void devm_pcs_provider_release(struct device *dev, void *res)
> > +{
> > +	struct pcs_standalone *pcssa = (struct pcs_standalone *)res;
> > +
> > +	mutex_lock(&pcs_mutex);
> > +	list_del(&pcssa->list);
> > +	mutex_unlock(&pcs_mutex);
> 
> This needs to do notify phylink if the PCS has gone away, but the
> locking for this would be somewhat difficult (because pcs->phylink
> could change if the PCS changes.) That would need to be solved
> somehow.

From my understanding the only way the PCS would "go away" is by
rmmod, which is prevented by the usage counter if still in use by the
Ethernet driver (removal of instances by using unbind in sysfs is
prevented by .suppress_bind_attrs).

I understand that Ethernet MAC and PCS both being built-into the SoC may
not be the only case we may want to support in the long run, but it is
the case for the MT7988 SoC which I'd like to see supported in upstream
Linux.

So imho this is something quite hypothetical which can be prevented by
setting .suppress_bind_attrs and bumping the module usage counter, as
those are not really dedicated devices on some kind of hotplug-able bus
what-so-ever, but all just components built-into the SoC itself. They
won't just go away. At least in case of the SoC I'm looking at.

If you have other use-cases in mind which this infrastructure should be
suitable for, it'd be helpful if you would spell them out.

If your criticism was meant to be directed towards the whole idea of
using standlone drivers for the PCS units of the SoC then the easiest
would of course be to just not do that and instead keep handling the
PCS as part of the Ethernet driver.

The main reason why I like the idea of the PCS driver being separate is
because it is not even needed on older platforms, and those are quite
resource constraint so it would be a waste to carry all the USXGMII
logic, let's say, on devices with MT7621 or even MT7628.

However, there are of course other ways to achieve nearly the same, such
as Kconfig symbols which select parts of the driver to be included or
not.

Hence my question: Do you think it is worth going down this road and
introducing standalone PCS drivers, given that the infrastructure
requirements include graceful removal of any PCS instance?

Also note that the same situation (things which may "go away") applies
to PHYs (as in: drivers/phy, not drivers/net/phy) as well, and I don't
see this being addressed for any of the in-SoC Ethernet controllers
supported by the kernel.

I was hoping for clarification regarding this but never received a
reply, see https://lkml.org/lkml/2024/2/7/101

And what about used instances of drivers/pinctrl, drivers/reset,
drivers/clk, ...? Should a SoC Ethernet driver be built in a way that
all those may gracefully "go away" as well?

I'm totally up to work on improving the overall situation there, but
it'd be good to know which direction I should be aiming for.
(as in: pre-removal call-back functions? just setting
.suppress_bind_attrs for all drivers/phy/ and such by default? extending
phylink itself to handle drivers/phy instances and their disappearance,
as well as potentially more than one PCS instance per net_device? ...)

> > [...]
> > +	device_link_add(dev, pcssa->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
> 
> This is really not a nice solution when one has a network device that
> has multiple interfaces. This will cause all interfaces on that device
> to be purged from the system when a PCS for one of the interfaces
> goes away. If the system is using NFS-root, that could result in the
> rootfs being lost. We should handle this more gracefully.

"DL_FLAG_AUTOREMOVE_CONSUMER causes the device link to be automatically
purged when the consumer fails to probe or later unbinds."
(from Documentation/driver-api/device_link.rst)

The consumer is the Ethernet driver in this case. Hence the automatic
purge is only applied in case the Ethernet device goes away, and meanwhile
it would prevent the PCS driver from being rmmod'ed (which in my case is
the only way for the PCS to "disappear").

Also note that the same flag is used in pcs-rzn1-miic.c as well.


Thank you for your patiente and helping me to understand how to proceed.


Cheers


Daniel
diff mbox series

Patch

diff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig
index f6aa437473de..2b02b9351fa4 100644
--- a/drivers/net/pcs/Kconfig
+++ b/drivers/net/pcs/Kconfig
@@ -5,6 +5,10 @@ 
 
 menu "PCS device drivers"
 
+config PCS_STANDALONE
+	tristate
+	select PHYLINK
+
 config PCS_XPCS
 	tristate "Synopsys DesignWare Ethernet XPCS"
 	select PHYLINK
diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile
index 4f7920618b90..0cb0057f2b8e 100644
--- a/drivers/net/pcs/Makefile
+++ b/drivers/net/pcs/Makefile
@@ -4,6 +4,7 @@ 
 pcs_xpcs-$(CONFIG_PCS_XPCS)	:= pcs-xpcs.o pcs-xpcs-plat.o \
 				   pcs-xpcs-nxp.o pcs-xpcs-wx.o
 
+obj-$(CONFIG_PCS_STANDALONE)	+= pcs-standalone.o
 obj-$(CONFIG_PCS_XPCS)		+= pcs_xpcs.o
 obj-$(CONFIG_PCS_LYNX)		+= pcs-lynx.o
 obj-$(CONFIG_PCS_MTK_LYNXI)	+= pcs-mtk-lynxi.o
diff --git a/drivers/net/pcs/pcs-standalone.c b/drivers/net/pcs/pcs-standalone.c
new file mode 100644
index 000000000000..1569793328a1
--- /dev/null
+++ b/drivers/net/pcs/pcs-standalone.c
@@ -0,0 +1,95 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Helpers for standalone PCS drivers
+ *
+ * Copyright (C) 2024 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <linux/pcs/pcs-standalone.h>
+#include <linux/phylink.h>
+
+static LIST_HEAD(pcs_list);
+static DEFINE_MUTEX(pcs_mutex);
+
+struct pcs_standalone {
+	struct device *dev;
+	struct phylink_pcs *pcs;
+	struct list_head list;
+};
+
+static void devm_pcs_provider_release(struct device *dev, void *res)
+{
+	struct pcs_standalone *pcssa = (struct pcs_standalone *)res;
+
+	mutex_lock(&pcs_mutex);
+	list_del(&pcssa->list);
+	mutex_unlock(&pcs_mutex);
+}
+
+int devm_pcs_register(struct device *dev, struct phylink_pcs *pcs)
+{
+	struct pcs_standalone *pcssa;
+
+	pcssa = devres_alloc(devm_pcs_provider_release, sizeof(*pcssa),
+			     GFP_KERNEL);
+	if (!pcssa)
+		return -ENOMEM;
+
+	devres_add(dev, pcssa);
+	pcssa->pcs = pcs;
+	pcssa->dev = dev;
+
+	mutex_lock(&pcs_mutex);
+	list_add_tail(&pcssa->list, &pcs_list);
+	mutex_unlock(&pcs_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(devm_pcs_register);
+
+static struct pcs_standalone *of_pcs_locate(const struct device_node *_np, u32 index)
+{
+	struct device_node *np;
+	struct pcs_standalone *iter, *pcssa = NULL;
+
+	if (!_np)
+		return NULL;
+
+	np = of_parse_phandle(_np, "pcs-handle", index);
+	if (!np)
+		return NULL;
+
+	mutex_lock(&pcs_mutex);
+	list_for_each_entry(iter, &pcs_list, list) {
+		if (iter->dev->of_node != np)
+			continue;
+
+		pcssa = iter;
+		break;
+	}
+	mutex_unlock(&pcs_mutex);
+
+	of_node_put(np);
+
+	return pcssa ?: ERR_PTR(-ENODEV);
+}
+
+struct phylink_pcs *devm_of_pcs_get(struct device *dev,
+				    const struct device_node *np,
+				    unsigned int index)
+{
+	struct pcs_standalone *pcssa;
+
+	pcssa = of_pcs_locate(np ?: dev->of_node, index);
+	if (IS_ERR_OR_NULL(pcssa))
+		return ERR_PTR(PTR_ERR(pcssa));
+
+	device_link_add(dev, pcssa->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
+
+	return pcssa->pcs;
+}
+EXPORT_SYMBOL_GPL(devm_of_pcs_get);
+
+MODULE_DESCRIPTION("Helper for standalone PCS drivers");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/pcs/pcs-standalone.h b/include/linux/pcs/pcs-standalone.h
new file mode 100644
index 000000000000..ad7819f4a2eb
--- /dev/null
+++ b/include/linux/pcs/pcs-standalone.h
@@ -0,0 +1,25 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_PCS_STANDALONE_H
+#define __LINUX_PCS_STANDALONE_H
+
+#include <linux/device.h>
+#include <linux/phylink.h>
+#include <linux/phy/phy.h>
+#include <linux/of.h>
+
+#if IS_ENABLED(CONFIG_PCS_STANDALONE)
+int devm_pcs_register(struct device *dev, struct phylink_pcs *pcs);
+struct phylink_pcs *devm_of_pcs_get(struct device *dev,
+				    const struct device_node *np, unsigned int index);
+#else
+static inline int devm_pcs_register(struct device *dev, struct phylink_pcs *pcs);
+	return -EOPNOTSUPP;
+}
+static inline struct phylink_pcs *devm_of_pcs_get(struct device *dev,
+						  const struct device_node *np,
+						  unsigned int index)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+#endif /* CONFIG_PCS_STANDALONE */
+#endif /* __LINUX_PCS_STANDALONE_H */