All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RESUBMIT net-next] net: phy: factor out legacy PHY fixup support and make it always built-in
@ 2026-09-13 20:00 Heiner Kallweit
  2026-09-15 15:13 ` Andrew Lunn
  2026-09-16  1:19 ` Jakub Kicinski
  0 siblings, 2 replies; 3+ messages in thread
From: Heiner Kallweit @ 2026-09-13 20:00 UTC (permalink / raw)
  To: Andrew Lunn, Russell King - ARM Linux, Paolo Abeni,
	Jakub Kicinski, David Miller, Eric Dumazet, Andrew Lunn
  Cc: netdev@vger.kernel.org

PHY fixup registration is used from platform code in init phase only.
Let's move the PHY fixup code from the modular part of phylib to the
always built-in part of phylib. This allows to annotate the fixup
registration as __init. No caller uses the return code of PHY fixup
registration, therefore change related functions to return void.

phy_needs_fixup() and phy_scan_fixups() wouldn't have to be moved to
the built-in part of phylib. But doing so allows to fully factor out
legacy fixup support into its own source code file, and make struct
phy_fixup and phy_fixup_list strictly private to phy_fixup.c.

phy_scan_fixups() is used after init phase only, then phy_fixup_list
is read-only. So we don't need the mutex when accessing the list.
Also when registering PHY fixups the mutex isn't needed, because
fixup registration is done sequentially from platform init code.
Actually there is only one platform with more than one fixup.

In addition this change lays the foundation for enabling modular
phylib on platforms where the fixup just sets a flag.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
This is a resubmit from Jan 2026. The change depends on removal of the
dnet driver, and at that time the dnet driver had just been removed,
with a certain risk that this removal has to be reverted.
---
 drivers/net/phy/Makefile          |  2 +-
 drivers/net/phy/phy_device.c      | 90 ----------------------------
 drivers/net/phy/phy_fixup.c       | 99 +++++++++++++++++++++++++++++++
 drivers/net/phy/phylib-internal.h |  1 +
 include/linux/phy.h               |  8 +--
 5 files changed, 105 insertions(+), 95 deletions(-)
 create mode 100644 drivers/net/phy/phy_fixup.c

diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index e23df5e836e..4674eaf243e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -8,7 +8,7 @@ libphy-y			:= phy.o phy-c45.o phy-core.o phy_device.o \
 
 ifdef CONFIG_PHYLIB
 # built-in whenever PHYLIB is built-in or module
-obj-y				+= stubs.o
+obj-y				+= stubs.o phy_fixup.o
 endif
 
 libphy-$(CONFIG_SWPHY)		+= swphy.o
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e00a..27c0cb13860 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -49,14 +49,6 @@ MODULE_DESCRIPTION("PHY library");
 MODULE_AUTHOR("Andy Fleming");
 MODULE_LICENSE("GPL");
 
-struct phy_fixup {
-	struct list_head list;
-	char bus_id[MII_BUS_ID_SIZE + 3];
-	u32 phy_uid;
-	u32 phy_uid_mask;
-	int (*run)(struct phy_device *phydev);
-};
-
 static struct phy_driver genphy_c45_driver = {
 	.phy_id         = 0xffffffff,
 	.phy_id_mask    = 0xffffffff,
@@ -237,9 +229,6 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev)
 
 static struct phy_driver genphy_driver;
 
-static LIST_HEAD(phy_fixup_list);
-static DEFINE_MUTEX(phy_fixup_lock);
-
 static bool phy_drv_wol_enabled(struct phy_device *phydev)
 {
 	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
@@ -427,85 +416,6 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(mdio_bus_phy_pm_ops, mdio_bus_phy_suspend,
 			 mdio_bus_phy_resume);
 
-/**
- * phy_register_fixup - creates a new phy_fixup and adds it to the list
- * @bus_id: A string which matches phydev->mdio.dev.bus_id (or NULL)
- * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
- * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
- *	comparison (or 0 to disable id-based matching)
- * @run: The actual code to be run when a matching PHY is found
- */
-static int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
-			      int (*run)(struct phy_device *))
-{
-	struct phy_fixup *fixup = kzalloc_obj(*fixup);
-
-	if (!fixup)
-		return -ENOMEM;
-
-	if (bus_id)
-		strscpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id));
-	fixup->phy_uid = phy_uid;
-	fixup->phy_uid_mask = phy_uid_mask;
-	fixup->run = run;
-
-	mutex_lock(&phy_fixup_lock);
-	list_add_tail(&fixup->list, &phy_fixup_list);
-	mutex_unlock(&phy_fixup_lock);
-
-	return 0;
-}
-
-/* Registers a fixup to be run on any PHY with the UID in phy_uid */
-int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
-			       int (*run)(struct phy_device *))
-{
-	return phy_register_fixup(NULL, phy_uid, phy_uid_mask, run);
-}
-EXPORT_SYMBOL(phy_register_fixup_for_uid);
-
-/* Registers a fixup to be run on the PHY with id string bus_id */
-int phy_register_fixup_for_id(const char *bus_id,
-			      int (*run)(struct phy_device *))
-{
-	return phy_register_fixup(bus_id, 0, 0, run);
-}
-EXPORT_SYMBOL(phy_register_fixup_for_id);
-
-static bool phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
-{
-	if (!strcmp(fixup->bus_id, phydev_name(phydev)))
-		return true;
-
-	if (fixup->phy_uid_mask &&
-	    phy_id_compare(phydev->phy_id, fixup->phy_uid, fixup->phy_uid_mask))
-		return true;
-
-	return false;
-}
-
-/* Runs any matching fixups for this phydev */
-static int phy_scan_fixups(struct phy_device *phydev)
-{
-	struct phy_fixup *fixup;
-
-	mutex_lock(&phy_fixup_lock);
-	list_for_each_entry(fixup, &phy_fixup_list, list) {
-		if (phy_needs_fixup(phydev, fixup)) {
-			int err = fixup->run(phydev);
-
-			if (err < 0) {
-				mutex_unlock(&phy_fixup_lock);
-				return err;
-			}
-			phydev->has_fixups = true;
-		}
-	}
-	mutex_unlock(&phy_fixup_lock);
-
-	return 0;
-}
-
 /**
  * genphy_match_phy_device - match a PHY device with a PHY driver
  * @phydev: target phy_device struct
diff --git a/drivers/net/phy/phy_fixup.c b/drivers/net/phy/phy_fixup.c
new file mode 100644
index 00000000000..f49fc4cefe8
--- /dev/null
+++ b/drivers/net/phy/phy_fixup.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PHY fixup support
+ */
+
+#include <linux/list.h>
+#include <linux/phy.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "phylib-internal.h"
+
+static struct list_head phy_fixup_list __ro_after_init =
+	LIST_HEAD_INIT(phy_fixup_list);
+
+struct phy_fixup {
+	struct list_head list;
+	char bus_id[MII_BUS_ID_SIZE + 3];
+	u32 phy_uid;
+	u32 phy_uid_mask;
+	int (*run)(struct phy_device *phydev);
+};
+
+/**
+ * phy_register_fixup - creates a new phy_fixup and adds it to the list
+ * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID)
+ * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
+ *	It can also be PHY_ANY_UID
+ * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
+ *	comparison
+ * @run: The actual code to be run when a matching PHY is found
+ */
+static void __init phy_register_fixup(const char *bus_id, u32 phy_uid,
+				      u32 phy_uid_mask,
+				      int (*run)(struct phy_device *))
+{
+	struct phy_fixup *fixup = kzalloc_obj(*fixup);
+
+	if (!fixup)
+		return;
+
+	if (bus_id)
+		strscpy(fixup->bus_id, bus_id);
+	fixup->phy_uid = phy_uid;
+	fixup->phy_uid_mask = phy_uid_mask;
+	fixup->run = run;
+
+	list_add_tail(&fixup->list, &phy_fixup_list);
+}
+
+/* Registers a fixup to be run on any PHY with the UID in phy_uid */
+void __init phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
+				       int (*run)(struct phy_device *))
+{
+	phy_register_fixup(NULL, phy_uid, phy_uid_mask, run);
+}
+
+/* Registers a fixup to be run on the PHY with id string bus_id */
+void __init phy_register_fixup_for_id(const char *bus_id,
+				      int (*run)(struct phy_device *))
+{
+	phy_register_fixup(bus_id, 0, 0, run);
+}
+
+static bool phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
+{
+	if (!strcmp(fixup->bus_id, phydev_name(phydev)))
+		return true;
+
+	if (fixup->phy_uid_mask &&
+	    phy_id_compare(phydev->phy_id, fixup->phy_uid, fixup->phy_uid_mask))
+		return true;
+
+	return false;
+}
+
+/**
+ * phy_scan_fixups - runs any matching fixups for this phydev
+ * @phydev: the phydev to search and run fixups for
+ * Returns: 0 or an errno
+ */
+int phy_scan_fixups(struct phy_device *phydev)
+{
+	struct phy_fixup *fixup;
+
+	list_for_each_entry(fixup, &phy_fixup_list, list) {
+		if (phy_needs_fixup(phydev, fixup)) {
+			int err = fixup->run(phydev);
+
+			if (err < 0)
+				return err;
+
+			phydev->has_fixups = true;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(phy_scan_fixups);
diff --git a/drivers/net/phy/phylib-internal.h b/drivers/net/phy/phylib-internal.h
index 664ed7faa51..38ee294f3c9 100644
--- a/drivers/net/phy/phylib-internal.h
+++ b/drivers/net/phy/phylib-internal.h
@@ -23,6 +23,7 @@ void of_set_phy_eee_broken(struct phy_device *phydev);
 void of_set_phy_timing_role(struct phy_device *phydev);
 int phy_speed_down_core(struct phy_device *phydev);
 void phy_check_downshift(struct phy_device *phydev);
+int phy_scan_fixups(struct phy_device *phydev);
 
 int mdiobus_register_device(struct mdio_device *mdiodev);
 int mdiobus_unregister_device(struct mdio_device *mdiodev);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5f8d65868e0..f799b3684cd 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2422,10 +2422,10 @@ int phy_get_mac_termination(struct phy_device *phydev, struct device *dev,
 void phy_resolve_pause(unsigned long *local_adv, unsigned long *partner_adv,
 		       bool *tx_pause, bool *rx_pause);
 
-int phy_register_fixup_for_id(const char *bus_id,
-			      int (*run)(struct phy_device *));
-int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
-			       int (*run)(struct phy_device *));
+void __init phy_register_fixup_for_id(const char *bus_id,
+				      int (*run)(struct phy_device *));
+void __init phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
+				       int (*run)(struct phy_device *));
 
 int phy_eee_tx_clock_stop_capable(struct phy_device *phydev);
 int phy_eee_rx_clock_stop(struct phy_device *phydev, bool clk_stop_enable);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH RESUBMIT net-next] net: phy: factor out legacy PHY fixup support and make it always built-in
  2026-09-13 20:00 [PATCH RESUBMIT net-next] net: phy: factor out legacy PHY fixup support and make it always built-in Heiner Kallweit
@ 2026-09-15 15:13 ` Andrew Lunn
  2026-09-16  1:19 ` Jakub Kicinski
  1 sibling, 0 replies; 3+ messages in thread
From: Andrew Lunn @ 2026-09-15 15:13 UTC (permalink / raw)
  To: Heiner Kallweit
  Cc: Russell King - ARM Linux, Paolo Abeni, Jakub Kicinski,
	David Miller, Eric Dumazet, Andrew Lunn, netdev@vger.kernel.org

On Sun, Sep 13, 2026 at 10:00:03PM +0200, Heiner Kallweit wrote:
> PHY fixup registration is used from platform code in init phase only.
> Let's move the PHY fixup code from the modular part of phylib to the
> always built-in part of phylib. This allows to annotate the fixup
> registration as __init. No caller uses the return code of PHY fixup
> registration, therefore change related functions to return void.
> 
> phy_needs_fixup() and phy_scan_fixups() wouldn't have to be moved to
> the built-in part of phylib. But doing so allows to fully factor out
> legacy fixup support into its own source code file, and make struct
> phy_fixup and phy_fixup_list strictly private to phy_fixup.c.
> 
> phy_scan_fixups() is used after init phase only, then phy_fixup_list
> is read-only. So we don't need the mutex when accessing the list.
> Also when registering PHY fixups the mutex isn't needed, because
> fixup registration is done sequentially from platform init code.
> Actually there is only one platform with more than one fixup.
> 
> In addition this change lays the foundation for enabling modular
> phylib on platforms where the fixup just sets a flag.
> 
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH RESUBMIT net-next] net: phy: factor out legacy PHY fixup support and make it always built-in
  2026-09-13 20:00 [PATCH RESUBMIT net-next] net: phy: factor out legacy PHY fixup support and make it always built-in Heiner Kallweit
  2026-09-15 15:13 ` Andrew Lunn
@ 2026-09-16  1:19 ` Jakub Kicinski
  1 sibling, 0 replies; 3+ messages in thread
From: Jakub Kicinski @ 2026-09-16  1:19 UTC (permalink / raw)
  To: hkallweit1
  Cc: Jakub Kicinski, andrew, linux, pabeni, davem, edumazet,
	andrew+netdev, netdev

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: phy: factor out legacy PHY fixup support and make it always built-in

This moves the legacy PHY fixup code out of the modular part of phylib
into a new always-built-in file, drivers/net/phy/phy_fixup.c, marks the
registration helpers __init and changes them to return void.  struct
phy_fixup and phy_fixup_list become private to the new file and the
fixup mutex is dropped.

> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 41d1b20cde2c0..5b13a74e2fa92 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c

[ ... ]

> @@ -427,85 +416,6 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
>  static SIMPLE_DEV_PM_OPS(mdio_bus_phy_pm_ops, mdio_bus_phy_suspend,
>  			 mdio_bus_phy_resume);
>  
> -/**
> - * phy_register_fixup - creates a new phy_fixup and adds it to the list
> - * @bus_id: A string which matches phydev->mdio.dev.bus_id (or NULL)
> - * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
> - * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
> - *	comparison (or 0 to disable id-based matching)
> - * @run: The actual code to be run when a matching PHY is found
> - */

[ ... ]

> diff --git a/drivers/net/phy/phy_fixup.c b/drivers/net/phy/phy_fixup.c
> new file mode 100644
> index 0000000000000..f49fc4cefe817
> --- /dev/null
> +++ b/drivers/net/phy/phy_fixup.c
> @@ -0,0 +1,99 @@

[ ... ]

> +/**
> + * phy_register_fixup - creates a new phy_fixup and adds it to the list
> + * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID)
> + * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
> + *	It can also be PHY_ANY_UID
> + * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
> + *	comparison
> + * @run: The actual code to be run when a matching PHY is found
> + */

[Severity: Low]
Was the wording change in this kernel-doc block intentional?  The version
removed from phy_device.c said "(or NULL)" for @bus_id and "(or 0 to
disable id-based matching)" for @phy_uid_mask, which matches what the code
does.  The new text mentions PHY_ANY_ID and PHY_ANY_UID instead.

Grepping the tree at this commit, PHY_ANY_ID and PHY_ANY_UID have no
definition anywhere; the only hits are these two new comment lines in
phy_fixup.c plus one sentence in Documentation/networking/phy.rst.

The matching code moved by this patch, phy_needs_fixup() in phy_fixup.c,
has no wildcard handling:

	if (!strcmp(fixup->bus_id, phydev_name(phydev)))
		return true;

	if (fixup->phy_uid_mask &&
	    phy_id_compare(phydev->phy_id, fixup->phy_uid, fixup->phy_uid_mask))
		return true;

and phy_register_fixup() only special-cases a NULL bus_id:

	if (bus_id)
		strscpy(fixup->bus_id, bus_id);
	fixup->phy_uid = phy_uid;
	fixup->phy_uid_mask = phy_uid_mask;

So platform code written against this comment would either fail to build
on the undefined identifier, or register a fixup that never matches.
Would it be better to keep the "(or NULL)" and "(or 0 to disable id-based
matching)" wording?

> +static void __init phy_register_fixup(const char *bus_id, u32 phy_uid,
> +				      u32 phy_uid_mask,
> +				      int (*run)(struct phy_device *))

[ ... ]

> +/* Registers a fixup to be run on any PHY with the UID in phy_uid */
> +void __init phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
> +				       int (*run)(struct phy_device *))
> +{
> +	phy_register_fixup(NULL, phy_uid, phy_uid_mask, run);
> +}
> +
> +/* Registers a fixup to be run on the PHY with id string bus_id */
> +void __init phy_register_fixup_for_id(const char *bus_id,
> +				      int (*run)(struct phy_device *))
> +{
> +	phy_register_fixup(bus_id, 0, 0, run);
> +}

[ ... ]

> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 3d8afe6b7f1c5..257634a543a14 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -2440,10 +2440,10 @@ int phy_get_mac_termination(struct phy_device *phydev, struct device *dev,
>  void phy_resolve_pause(unsigned long *local_adv, unsigned long *partner_adv,
>  		       bool *tx_pause, bool *rx_pause);
>  
> -int phy_register_fixup_for_id(const char *bus_id,
> -			      int (*run)(struct phy_device *));
> -int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
> -			       int (*run)(struct phy_device *));
> +void __init phy_register_fixup_for_id(const char *bus_id,
> +				      int (*run)(struct phy_device *));
> +void __init phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
> +				       int (*run)(struct phy_device *));

[Severity: Low]
Should Documentation/networking/phy.rst be updated in the same patch?  The
"Board Fixups" section still documents the old prototypes:

 int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
		int (*run)(struct phy_device *));
 int phy_register_fixup_for_id(const char *phy_id,
		int (*run)(struct phy_device *));

Two things there are now stale.  The return type is void, so code that
assigns or tests the return value as the document suggests no longer
compiles.

The document also just says platform code registers fixups, with no
mention that registration is now init-only: the helpers are __init and are
no longer exported, and phy_register_fixup() adds to phy_fixup_list, which
is declared

	static struct list_head phy_fixup_list __ro_after_init =
		LIST_HEAD_INIT(phy_fixup_list);

so a caller outside init context gets a section mismatch from modpost and,
if it ever runs after init, writes to read-only memory.  Modules can no
longer call these at all.

Same section of phy.rst also describes PHY_ANY_ID and PHY_ANY_UID as
wildcards, which ties in with the kernel-doc comment above.
-- 
pw-bot: cr

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-16  1:19 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-13 20:00 [PATCH RESUBMIT net-next] net: phy: factor out legacy PHY fixup support and make it always built-in Heiner Kallweit
2026-09-15 15:13 ` Andrew Lunn
2026-09-16  1:19 ` Jakub Kicinski

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.