Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'phy-listing-and-topology-tracking'

Maxime Chevallier says:

====================
Introduce PHY listing and link_topology tracking

This is V18 of the phy_link_topology series, aiming at improving support
for multiple PHYs being attached to the same MAC.

V18 is a simple rebase of the V17 on top of net-next, gathering the
tested-by and reviewed-by tags from Christophe (thanks !).

This iteration is also one patch shorter than V17 (patch 12/14 in V17 is gone),
as one of the patches used to fix an issue that has now been resolved by
Simon Horman in

743ff02152bc ethtool: Don't check for NULL info in prepare_data callbacks

As a remainder, here's what the PHY listings would look like :
- eth0 has a 88x3310 acting as media converter, and an SFP module with
an embedded 88e1111 PHY
- eth2 has a 88e1510 PHY

PHY for eth0:
PHY index: 1
Driver name: mv88x3310
PHY device name: f212a600.mdio-mii:00
Downstream SFP bus name: sfp-eth0
Upstream type: MAC

PHY for eth0:
PHY index: 2
Driver name: Marvell 88E1111
PHY device name: i2c:sfp-eth0:16
Upstream type: PHY
Upstream PHY index: 1
Upstream SFP name: sfp-eth0

PHY for eth2:
PHY index: 1
Driver name: Marvell 88E1510
PHY device name: f212a200.mdio-mii:00
Upstream type: MAC

Ethtool patches : https://github.com/minimaxwell/ethtool/tree/mc/topo-v16
(this branch is compatible with this V18 series)

Link to V17: https://lore.kernel.org/netdev/20240709063039.2909536-1-maxime.chevallier@bootlin.com/
Link to V16: https://lore.kernel.org/netdev/20240705132706.13588-1-maxime.chevallier@bootlin.com/
Link to V15: https://lore.kernel.org/netdev/20240703140806.271938-1-maxime.chevallier@bootlin.com/
Link to V14: https://lore.kernel.org/netdev/20240701131801.1227740-1-maxime.chevallier@bootlin.com/
Link to V13: https://lore.kernel.org/netdev/20240607071836.911403-1-maxime.chevallier@bootlin.com/
Link to v12: https://lore.kernel.org/netdev/20240605124920.720690-1-maxime.chevallier@bootlin.com/
Link to v11: https://lore.kernel.org/netdev/20240404093004.2552221-1-maxime.chevallier@bootlin.com/
Link to V10: https://lore.kernel.org/netdev/20240304151011.1610175-1-maxime.chevallier@bootlin.com/
Link to V9: https://lore.kernel.org/netdev/20240228114728.51861-1-maxime.chevallier@bootlin.com/
Link to V8: https://lore.kernel.org/netdev/20240220184217.3689988-1-maxime.chevallier@bootlin.com/
Link to V7: https://lore.kernel.org/netdev/20240213150431.1796171-1-maxime.chevallier@bootlin.com/
Link to V6: https://lore.kernel.org/netdev/20240126183851.2081418-1-maxime.chevallier@bootlin.com/
Link to V5: https://lore.kernel.org/netdev/20231221180047.1924733-1-maxime.chevallier@bootlin.com/
Link to V4: https://lore.kernel.org/netdev/20231215171237.1152563-1-maxime.chevallier@bootlin.com/
Link to V3: https://lore.kernel.org/netdev/20231201163704.1306431-1-maxime.chevallier@bootlin.com/
Link to V2: https://lore.kernel.org/netdev/20231117162323.626979-1-maxime.chevallier@bootlin.com/
Link to V1: https://lore.kernel.org/netdev/20230907092407.647139-1-maxime.chevallier@bootlin.com/

More discussions on specific issues that happened in 6.9-rc:

https://lore.kernel.org/netdev/20240412104615.3779632-1-maxime.chevallier@bootlin.com/
https://lore.kernel.org/netdev/20240429131008.439231-1-maxime.chevallier@bootlin.com/
https://lore.kernel.org/netdev/20240507102822.2023826-1-maxime.chevallier@bootlin.com/
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+1056 -51
+58
Documentation/netlink/specs/ethtool.yaml
··· 39 39 - ovld-detected 40 40 - power-not-available 41 41 - short-detected 42 + - 43 + name: phy-upstream-type 44 + enum-name: 45 + type: enum 46 + entries: [ mac, phy ] 42 47 43 48 attribute-sets: 44 49 - ··· 59 54 name: flags 60 55 type: u32 61 56 enum: header-flags 57 + - 58 + name: phy-index 59 + type: u32 62 60 63 61 - 64 62 name: bitset-bit ··· 1097 1089 - 1098 1090 name: total 1099 1091 type: uint 1092 + - 1093 + name: phy 1094 + attributes: 1095 + - 1096 + name: header 1097 + type: nest 1098 + nested-attributes: header 1099 + - 1100 + name: index 1101 + type: u32 1102 + - 1103 + name: drvname 1104 + type: string 1105 + - 1106 + name: name 1107 + type: string 1108 + - 1109 + name: upstream-type 1110 + type: u32 1111 + enum: phy-upstream-type 1112 + - 1113 + name: upstream-index 1114 + type: u32 1115 + - 1116 + name: upstream-sfp-name 1117 + type: string 1118 + - 1119 + name: downstream-sfp-name 1120 + type: string 1100 1121 1101 1122 operations: 1102 1123 enum-model: directional ··· 1924 1887 - status-msg 1925 1888 - done 1926 1889 - total 1890 + - 1891 + name: phy-get 1892 + doc: Get PHY devices attached to an interface 1893 + 1894 + attribute-set: phy 1895 + 1896 + do: &phy-get-op 1897 + request: 1898 + attributes: 1899 + - header 1900 + reply: 1901 + attributes: 1902 + - header 1903 + - index 1904 + - drvname 1905 + - name 1906 + - upstream-type 1907 + - upstream-index 1908 + - upstream-sfp-name 1909 + - downstream-sfp-name 1910 + dump: *phy-get-op
+51
Documentation/networking/ethtool-netlink.rst
··· 57 57 ``ETHTOOL_A_HEADER_DEV_INDEX`` u32 device ifindex 58 58 ``ETHTOOL_A_HEADER_DEV_NAME`` string device name 59 59 ``ETHTOOL_A_HEADER_FLAGS`` u32 flags common for all requests 60 + ``ETHTOOL_A_HEADER_PHY_INDEX`` u32 phy device index 60 61 ============================== ====== ============================= 61 62 62 63 ``ETHTOOL_A_HEADER_DEV_INDEX`` and ``ETHTOOL_A_HEADER_DEV_NAME`` identify the ··· 82 81 of the flag should be interpreted the way the client expects. A client must 83 82 not set flags it does not understand. 84 83 84 + ``ETHTOOL_A_HEADER_PHY_INDEX`` identifies the Ethernet PHY the message relates to. 85 + As there are numerous commands that are related to PHY configuration, and because 86 + there may be more than one PHY on the link, the PHY index can be passed in the 87 + request for the commands that needs it. It is, however, not mandatory, and if it 88 + is not passed for commands that target a PHY, the net_device.phydev pointer 89 + is used. 85 90 86 91 Bit sets 87 92 ======== ··· 2191 2184 The ``ETHTOOL_A_MODULE_FW_FLASH_DONE`` and ``ETHTOOL_A_MODULE_FW_FLASH_TOTAL`` 2192 2185 attributes encode the completed and total amount of work, respectively. 2193 2186 2187 + PHY_GET 2188 + ======= 2189 + 2190 + Retrieve information about a given Ethernet PHY sitting on the link. The DO 2191 + operation returns all available information about dev->phydev. User can also 2192 + specify a PHY_INDEX, in which case the DO request returns information about that 2193 + specific PHY. 2194 + 2195 + As there can be more than one PHY, the DUMP operation can be used to list the PHYs 2196 + present on a given interface, by passing an interface index or name in 2197 + the dump request. 2198 + 2199 + For more information, refer to :ref:`phy_link_topology` 2200 + 2201 + Request contents: 2202 + 2203 + ==================================== ====== ========================== 2204 + ``ETHTOOL_A_PHY_HEADER`` nested request header 2205 + ==================================== ====== ========================== 2206 + 2207 + Kernel response contents: 2208 + 2209 + ===================================== ====== =============================== 2210 + ``ETHTOOL_A_PHY_HEADER`` nested request header 2211 + ``ETHTOOL_A_PHY_INDEX`` u32 the phy's unique index, that can 2212 + be used for phy-specific 2213 + requests 2214 + ``ETHTOOL_A_PHY_DRVNAME`` string the phy driver name 2215 + ``ETHTOOL_A_PHY_NAME`` string the phy device name 2216 + ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` u32 the type of device this phy is 2217 + connected to 2218 + ``ETHTOOL_A_PHY_UPSTREAM_INDEX`` u32 the PHY index of the upstream 2219 + PHY 2220 + ``ETHTOOL_A_PHY_UPSTREAM_SFP_NAME`` string if this PHY is connected to 2221 + its parent PHY through an SFP 2222 + bus, the name of this sfp bus 2223 + ``ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME`` string if the phy controls an sfp bus, 2224 + the name of the sfp bus 2225 + ===================================== ====== =============================== 2226 + 2227 + When ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` is PHY_UPSTREAM_PHY, the PHY's parent is 2228 + another PHY. 2229 + 2194 2230 Request translation 2195 2231 =================== 2196 2232 ··· 2341 2291 n/a ``ETHTOOL_MSG_MM_GET`` 2342 2292 n/a ``ETHTOOL_MSG_MM_SET`` 2343 2293 n/a ``ETHTOOL_MSG_MODULE_FW_FLASH_ACT`` 2294 + n/a ``ETHTOOL_MSG_PHY_GET`` 2344 2295 =================================== =====================================
+1
Documentation/networking/index.rst
··· 91 91 operstates 92 92 packet_mmap 93 93 phonet 94 + phy-link-topology 94 95 pktgen 95 96 plip 96 97 ppp_generic
+1
MAINTAINERS
··· 8341 8341 F: include/linux/of_net.h 8342 8342 F: include/linux/phy.h 8343 8343 F: include/linux/phy_fixed.h 8344 + F: include/linux/phy_link_topology.h 8344 8345 F: include/linux/phylib_stubs.h 8345 8346 F: include/linux/platform_data/mdio-bcm-unimac.h 8346 8347 F: include/linux/platform_data/mdio-gpio.h
+1 -1
drivers/net/phy/Makefile
··· 2 2 # Makefile for Linux PHY drivers 3 3 4 4 libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \ 5 - linkmode.o 5 + linkmode.o phy_link_topology.o 6 6 mdio-bus-y += mdio_bus.o mdio_device.o 7 7 8 8 ifdef CONFIG_MDIO_DEVICE
+2
drivers/net/phy/marvell-88x2222.c
··· 553 553 .link_down = mv2222_sfp_link_down, 554 554 .attach = phy_sfp_attach, 555 555 .detach = phy_sfp_detach, 556 + .connect_phy = phy_sfp_connect_phy, 557 + .disconnect_phy = phy_sfp_disconnect_phy, 556 558 }; 557 559 558 560 static int mv2222_probe(struct phy_device *phydev)
+2
drivers/net/phy/marvell.c
··· 3613 3613 .module_remove = m88e1510_sfp_remove, 3614 3614 .attach = phy_sfp_attach, 3615 3615 .detach = phy_sfp_detach, 3616 + .connect_phy = phy_sfp_connect_phy, 3617 + .disconnect_phy = phy_sfp_disconnect_phy, 3616 3618 }; 3617 3619 3618 3620 static int m88e1510_probe(struct phy_device *phydev)
+2
drivers/net/phy/marvell10g.c
··· 503 503 static const struct sfp_upstream_ops mv3310_sfp_ops = { 504 504 .attach = phy_sfp_attach, 505 505 .detach = phy_sfp_detach, 506 + .connect_phy = phy_sfp_connect_phy, 507 + .disconnect_phy = phy_sfp_disconnect_phy, 506 508 .module_insert = mv3310_sfp_insert, 507 509 }; 508 510
+48
drivers/net/phy/phy_device.c
··· 29 29 #include <linux/phy.h> 30 30 #include <linux/phylib_stubs.h> 31 31 #include <linux/phy_led_triggers.h> 32 + #include <linux/phy_link_topology.h> 32 33 #include <linux/pse-pd/pse.h> 33 34 #include <linux/property.h> 34 35 #include <linux/rtnetlink.h> ··· 1386 1385 static DEVICE_ATTR_RO(phy_standalone); 1387 1386 1388 1387 /** 1388 + * phy_sfp_connect_phy - Connect the SFP module's PHY to the upstream PHY 1389 + * @upstream: pointer to the upstream phy device 1390 + * @phy: pointer to the SFP module's phy device 1391 + * 1392 + * This helper allows keeping track of PHY devices on the link. It adds the 1393 + * SFP module's phy to the phy namespace of the upstream phy 1394 + * 1395 + * Return: 0 on success, otherwise a negative error code. 1396 + */ 1397 + int phy_sfp_connect_phy(void *upstream, struct phy_device *phy) 1398 + { 1399 + struct phy_device *phydev = upstream; 1400 + struct net_device *dev = phydev->attached_dev; 1401 + 1402 + if (dev) 1403 + return phy_link_topo_add_phy(dev, phy, PHY_UPSTREAM_PHY, phydev); 1404 + 1405 + return 0; 1406 + } 1407 + EXPORT_SYMBOL(phy_sfp_connect_phy); 1408 + 1409 + /** 1410 + * phy_sfp_disconnect_phy - Disconnect the SFP module's PHY from the upstream PHY 1411 + * @upstream: pointer to the upstream phy device 1412 + * @phy: pointer to the SFP module's phy device 1413 + * 1414 + * This helper allows keeping track of PHY devices on the link. It removes the 1415 + * SFP module's phy to the phy namespace of the upstream phy. As the module phy 1416 + * will be destroyed, re-inserting the same module will add a new phy with a 1417 + * new index. 1418 + */ 1419 + void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy) 1420 + { 1421 + struct phy_device *phydev = upstream; 1422 + struct net_device *dev = phydev->attached_dev; 1423 + 1424 + if (dev) 1425 + phy_link_topo_del_phy(dev, phy); 1426 + } 1427 + EXPORT_SYMBOL(phy_sfp_disconnect_phy); 1428 + 1429 + /** 1389 1430 * phy_sfp_attach - attach the SFP bus to the PHY upstream network device 1390 1431 * @upstream: pointer to the phy device 1391 1432 * @bus: sfp bus representing cage being attached ··· 1569 1526 1570 1527 if (phydev->sfp_bus_attached) 1571 1528 dev->sfp_bus = phydev->sfp_bus; 1529 + 1530 + err = phy_link_topo_add_phy(dev, phydev, PHY_UPSTREAM_MAC, dev); 1531 + if (err) 1532 + goto error; 1572 1533 } 1573 1534 1574 1535 /* Some Ethernet drivers try to connect to a PHY device before ··· 2000 1953 if (dev) { 2001 1954 phydev->attached_dev->phydev = NULL; 2002 1955 phydev->attached_dev = NULL; 1956 + phy_link_topo_del_phy(dev, phydev); 2003 1957 } 2004 1958 phydev->phylink = NULL; 2005 1959
+2 -1
drivers/net/phy/phylink.c
··· 3423 3423 return ret; 3424 3424 } 3425 3425 3426 - static void phylink_sfp_disconnect_phy(void *upstream) 3426 + static void phylink_sfp_disconnect_phy(void *upstream, 3427 + struct phy_device *phydev) 3427 3428 { 3428 3429 phylink_disconnect_phy(upstream); 3429 3430 }
+2
drivers/net/phy/qcom/at803x.c
··· 770 770 .attach = phy_sfp_attach, 771 771 .detach = phy_sfp_detach, 772 772 .module_insert = at8031_sfp_insert, 773 + .connect_phy = phy_sfp_connect_phy, 774 + .disconnect_phy = phy_sfp_disconnect_phy, 773 775 }; 774 776 775 777 static int at8031_parse_dt(struct phy_device *phydev)
+2
drivers/net/phy/qcom/qca807x.c
··· 699 699 .detach = phy_sfp_detach, 700 700 .module_insert = qca807x_sfp_insert, 701 701 .module_remove = qca807x_sfp_remove, 702 + .connect_phy = phy_sfp_connect_phy, 703 + .disconnect_phy = phy_sfp_disconnect_phy, 702 704 }; 703 705 704 706 static int qca807x_probe(struct phy_device *phydev)
+24 -2
drivers/net/phy/sfp-bus.c
··· 487 487 bus->socket_ops->stop(bus->sfp); 488 488 bus->socket_ops->detach(bus->sfp); 489 489 if (bus->phydev && ops && ops->disconnect_phy) 490 - ops->disconnect_phy(bus->upstream); 490 + ops->disconnect_phy(bus->upstream, bus->phydev); 491 491 } 492 492 bus->registered = false; 493 493 } ··· 722 722 } 723 723 EXPORT_SYMBOL_GPL(sfp_bus_del_upstream); 724 724 725 + /** 726 + * sfp_get_name() - Get the SFP device name 727 + * @bus: a pointer to the &struct sfp_bus structure for the sfp module 728 + * 729 + * Gets the SFP device's name, if @bus has a registered socket. Callers must 730 + * hold RTNL, and the returned name is only valid until RTNL is released. 731 + * 732 + * Returns: 733 + * - The name of the SFP device registered with sfp_register_socket() 734 + * - %NULL if no device was registered on @bus 735 + */ 736 + const char *sfp_get_name(struct sfp_bus *bus) 737 + { 738 + ASSERT_RTNL(); 739 + 740 + if (bus->sfp_dev) 741 + return dev_name(bus->sfp_dev); 742 + 743 + return NULL; 744 + } 745 + EXPORT_SYMBOL_GPL(sfp_get_name); 746 + 725 747 /* Socket driver entry points */ 726 748 int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) 727 749 { ··· 765 743 const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); 766 744 767 745 if (ops && ops->disconnect_phy) 768 - ops->disconnect_phy(bus->upstream); 746 + ops->disconnect_phy(bus->upstream, bus->phydev); 769 747 bus->phydev = NULL; 770 748 } 771 749 EXPORT_SYMBOL_GPL(sfp_remove_phy);
+3 -1
include/linux/netdevice.h
··· 40 40 #include <net/dcbnl.h> 41 41 #endif 42 42 #include <net/netprio_cgroup.h> 43 - 44 43 #include <linux/netdev_features.h> 45 44 #include <linux/neighbour.h> 46 45 #include <linux/netdevice_xmit.h> ··· 80 81 struct xdp_metadata_ops; 81 82 struct xdp_md; 82 83 struct ethtool_netdev_state; 84 + struct phy_link_topology; 83 85 84 86 typedef u32 xdp_features_t; 85 87 ··· 1951 1951 * @fcoe_ddp_xid: Max exchange id for FCoE LRO by ddp 1952 1952 * 1953 1953 * @priomap: XXX: need comments on this one 1954 + * @link_topo: Physical link topology tracking attached PHYs 1954 1955 * @phydev: Physical device may attach itself 1955 1956 * for hardware timestamping 1956 1957 * @sfp_bus: attached &struct sfp_bus structure. ··· 2343 2342 #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) 2344 2343 struct netprio_map __rcu *priomap; 2345 2344 #endif 2345 + struct phy_link_topology *link_topo; 2346 2346 struct phy_device *phydev; 2347 2347 struct sfp_bus *sfp_bus; 2348 2348 struct lock_class_key *qdisc_tx_busylock;
+6
include/linux/phy.h
··· 554 554 * @drv: Pointer to the driver for this PHY instance 555 555 * @devlink: Create a link between phy dev and mac dev, if the external phy 556 556 * used by current mac interface is managed by another mac interface. 557 + * @phyindex: Unique id across the phy's parent tree of phys to address the PHY 558 + * from userspace, similar to ifindex. A zero index means the PHY 559 + * wasn't assigned an id yet. 557 560 * @phy_id: UID for this device found during discovery 558 561 * @c45_ids: 802.3-c45 Device Identifiers if is_c45. 559 562 * @is_c45: Set to true if this PHY uses clause 45 addressing. ··· 659 656 660 657 struct device_link *devlink; 661 658 659 + u32 phyindex; 662 660 u32 phy_id; 663 661 664 662 struct phy_c45_device_ids c45_ids; ··· 1781 1777 int phy_resume(struct phy_device *phydev); 1782 1778 int __phy_resume(struct phy_device *phydev); 1783 1779 int phy_loopback(struct phy_device *phydev, bool enable); 1780 + int phy_sfp_connect_phy(void *upstream, struct phy_device *phy); 1781 + void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy); 1784 1782 void phy_sfp_attach(void *upstream, struct sfp_bus *bus); 1785 1783 void phy_sfp_detach(void *upstream, struct sfp_bus *bus); 1786 1784 int phy_sfp_probe(struct phy_device *phydev,
+7 -1
include/linux/sfp.h
··· 550 550 void (*link_down)(void *priv); 551 551 void (*link_up)(void *priv); 552 552 int (*connect_phy)(void *priv, struct phy_device *); 553 - void (*disconnect_phy)(void *priv); 553 + void (*disconnect_phy)(void *priv, struct phy_device *); 554 554 }; 555 555 556 556 #if IS_ENABLED(CONFIG_SFP) ··· 576 576 int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, 577 577 const struct sfp_upstream_ops *ops); 578 578 void sfp_bus_del_upstream(struct sfp_bus *bus); 579 + const char *sfp_get_name(struct sfp_bus *bus); 579 580 #else 580 581 static inline int sfp_parse_port(struct sfp_bus *bus, 581 582 const struct sfp_eeprom_id *id, ··· 654 653 655 654 static inline void sfp_bus_del_upstream(struct sfp_bus *bus) 656 655 { 656 + } 657 + 658 + static inline const char *sfp_get_name(struct sfp_bus *bus) 659 + { 660 + return NULL; 657 661 } 658 662 #endif 659 663
+16
include/uapi/linux/ethtool.h
··· 2533 2533 * __u32 map_lp_advertising[link_mode_masks_nwords]; 2534 2534 */ 2535 2535 }; 2536 + 2537 + /** 2538 + * enum phy_upstream - Represents the upstream component a given PHY device 2539 + * is connected to, as in what is on the other end of the MII bus. Most PHYs 2540 + * will be attached to an Ethernet MAC controller, but in some cases, there's 2541 + * an intermediate PHY used as a media-converter, which will driver another 2542 + * MII interface as its output. 2543 + * @PHY_UPSTREAM_MAC: Upstream component is a MAC (a switch port, 2544 + * or ethernet controller) 2545 + * @PHY_UPSTREAM_PHY: Upstream component is a PHY (likely a media converter) 2546 + */ 2547 + enum phy_upstream { 2548 + PHY_UPSTREAM_MAC, 2549 + PHY_UPSTREAM_PHY, 2550 + }; 2551 + 2536 2552 #endif /* _UAPI_LINUX_ETHTOOL_H */
+20
include/uapi/linux/ethtool_netlink.h
··· 58 58 ETHTOOL_MSG_MM_GET, 59 59 ETHTOOL_MSG_MM_SET, 60 60 ETHTOOL_MSG_MODULE_FW_FLASH_ACT, 61 + ETHTOOL_MSG_PHY_GET, 61 62 62 63 /* add new constants above here */ 63 64 __ETHTOOL_MSG_USER_CNT, ··· 112 111 ETHTOOL_MSG_MM_GET_REPLY, 113 112 ETHTOOL_MSG_MM_NTF, 114 113 ETHTOOL_MSG_MODULE_FW_FLASH_NTF, 114 + ETHTOOL_MSG_PHY_GET_REPLY, 115 + ETHTOOL_MSG_PHY_NTF, 115 116 116 117 /* add new constants above here */ 117 118 __ETHTOOL_MSG_KERNEL_CNT, ··· 137 134 ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */ 138 135 ETHTOOL_A_HEADER_DEV_NAME, /* string */ 139 136 ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */ 137 + ETHTOOL_A_HEADER_PHY_INDEX, /* u32 */ 140 138 141 139 /* add new constants above here */ 142 140 __ETHTOOL_A_HEADER_CNT, ··· 1056 1052 /* add new constants above here */ 1057 1053 __ETHTOOL_A_MODULE_FW_FLASH_CNT, 1058 1054 ETHTOOL_A_MODULE_FW_FLASH_MAX = (__ETHTOOL_A_MODULE_FW_FLASH_CNT - 1) 1055 + }; 1056 + 1057 + enum { 1058 + ETHTOOL_A_PHY_UNSPEC, 1059 + ETHTOOL_A_PHY_HEADER, /* nest - _A_HEADER_* */ 1060 + ETHTOOL_A_PHY_INDEX, /* u32 */ 1061 + ETHTOOL_A_PHY_DRVNAME, /* string */ 1062 + ETHTOOL_A_PHY_NAME, /* string */ 1063 + ETHTOOL_A_PHY_UPSTREAM_TYPE, /* u32 */ 1064 + ETHTOOL_A_PHY_UPSTREAM_INDEX, /* u32 */ 1065 + ETHTOOL_A_PHY_UPSTREAM_SFP_NAME, /* string */ 1066 + ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME, /* string */ 1067 + 1068 + /* add new constants above here */ 1069 + __ETHTOOL_A_PHY_CNT, 1070 + ETHTOOL_A_PHY_MAX = (__ETHTOOL_A_PHY_CNT - 1) 1059 1071 }; 1060 1072 1061 1073 /* generic netlink info */
+15
net/core/dev.c
··· 158 158 #include <net/page_pool/types.h> 159 159 #include <net/page_pool/helpers.h> 160 160 #include <net/rps.h> 161 + #include <linux/phy_link_topology.h> 161 162 162 163 #include "dev.h" 163 164 #include "net-sysfs.h" ··· 10322 10321 } 10323 10322 } 10324 10323 10324 + static void netdev_free_phy_link_topology(struct net_device *dev) 10325 + { 10326 + struct phy_link_topology *topo = dev->link_topo; 10327 + 10328 + if (IS_ENABLED(CONFIG_PHYLIB) && topo) { 10329 + xa_destroy(&topo->phys); 10330 + kfree(topo); 10331 + dev->link_topo = NULL; 10332 + } 10333 + } 10334 + 10325 10335 /** 10326 10336 * register_netdevice() - register a network device 10327 10337 * @dev: device to register ··· 11111 11099 #ifdef CONFIG_NET_SCHED 11112 11100 hash_init(dev->qdisc_hash); 11113 11101 #endif 11102 + 11114 11103 dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; 11115 11104 setup(dev); 11116 11105 ··· 11203 11190 dev->core_stats = NULL; 11204 11191 free_percpu(dev->xdp_bulkq); 11205 11192 dev->xdp_bulkq = NULL; 11193 + 11194 + netdev_free_phy_link_topology(dev); 11206 11195 11207 11196 /* Compatibility with error handling in drivers */ 11208 11197 if (dev->reg_state == NETREG_UNINITIALIZED ||
+2 -1
net/ethtool/Makefile
··· 8 8 linkstate.o debug.o wol.o features.o privflags.o rings.o \ 9 9 channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ 10 10 tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \ 11 - module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o mm.o 11 + module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o mm.o \ 12 + phy.o
+22 -13
net/ethtool/cabletest.c
··· 13 13 14 14 const struct nla_policy ethnl_cable_test_act_policy[] = { 15 15 [ETHTOOL_A_CABLE_TEST_HEADER] = 16 - NLA_POLICY_NESTED(ethnl_header_policy), 16 + NLA_POLICY_NESTED(ethnl_header_policy_phy), 17 17 }; 18 18 19 19 static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd) ··· 58 58 struct ethnl_req_info req_info = {}; 59 59 const struct ethtool_phy_ops *ops; 60 60 struct nlattr **tb = info->attrs; 61 + struct phy_device *phydev; 61 62 struct net_device *dev; 62 63 int ret; 63 64 ··· 70 69 return ret; 71 70 72 71 dev = req_info.dev; 73 - if (!dev->phydev) { 72 + 73 + rtnl_lock(); 74 + phydev = ethnl_req_get_phydev(&req_info, 75 + tb[ETHTOOL_A_CABLE_TEST_HEADER], 76 + info->extack); 77 + if (IS_ERR_OR_NULL(phydev)) { 74 78 ret = -EOPNOTSUPP; 75 79 goto out_dev_put; 76 80 } 77 81 78 - rtnl_lock(); 79 82 ops = ethtool_phy_ops; 80 83 if (!ops || !ops->start_cable_test) { 81 84 ret = -EOPNOTSUPP; ··· 90 85 if (ret < 0) 91 86 goto out_rtnl; 92 87 93 - ret = ops->start_cable_test(dev->phydev, info->extack); 88 + ret = ops->start_cable_test(phydev, info->extack); 94 89 95 90 ethnl_ops_complete(dev); 96 91 97 92 if (!ret) 98 - ethnl_cable_test_started(dev->phydev, 99 - ETHTOOL_MSG_CABLE_TEST_NTF); 93 + ethnl_cable_test_started(phydev, ETHTOOL_MSG_CABLE_TEST_NTF); 100 94 101 95 out_rtnl: 102 96 rtnl_unlock(); ··· 220 216 221 217 const struct nla_policy ethnl_cable_test_tdr_act_policy[] = { 222 218 [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = 223 - NLA_POLICY_NESTED(ethnl_header_policy), 219 + NLA_POLICY_NESTED(ethnl_header_policy_phy), 224 220 [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED }, 225 221 }; 226 222 ··· 309 305 struct ethnl_req_info req_info = {}; 310 306 const struct ethtool_phy_ops *ops; 311 307 struct nlattr **tb = info->attrs; 308 + struct phy_device *phydev; 312 309 struct phy_tdr_config cfg; 313 310 struct net_device *dev; 314 311 int ret; ··· 322 317 return ret; 323 318 324 319 dev = req_info.dev; 325 - if (!dev->phydev) { 326 - ret = -EOPNOTSUPP; 327 - goto out_dev_put; 328 - } 329 320 330 321 ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 331 322 info, &cfg); ··· 329 328 goto out_dev_put; 330 329 331 330 rtnl_lock(); 331 + phydev = ethnl_req_get_phydev(&req_info, 332 + tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 333 + info->extack); 334 + if (!IS_ERR_OR_NULL(phydev)) { 335 + ret = -EOPNOTSUPP; 336 + goto out_dev_put; 337 + } 338 + 332 339 ops = ethtool_phy_ops; 333 340 if (!ops || !ops->start_cable_test_tdr) { 334 341 ret = -EOPNOTSUPP; ··· 347 338 if (ret < 0) 348 339 goto out_rtnl; 349 340 350 - ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg); 341 + ret = ops->start_cable_test_tdr(phydev, info->extack, &cfg); 351 342 352 343 ethnl_ops_complete(dev); 353 344 354 345 if (!ret) 355 - ethnl_cable_test_started(dev->phydev, 346 + ethnl_cable_test_started(phydev, 356 347 ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 357 348 358 349 out_rtnl:
+64 -2
net/ethtool/netlink.c
··· 2 2 3 3 #include <net/sock.h> 4 4 #include <linux/ethtool_netlink.h> 5 + #include <linux/phy_link_topology.h> 5 6 #include <linux/pm_runtime.h> 6 7 #include "netlink.h" 7 8 #include "module_fw.h" ··· 30 29 .len = ALTIFNAMSIZ - 1 }, 31 30 [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32, 32 31 ETHTOOL_FLAGS_STATS), 32 + }; 33 + 34 + const struct nla_policy ethnl_header_policy_phy[] = { 35 + [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 }, 36 + [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING, 37 + .len = ALTIFNAMSIZ - 1 }, 38 + [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32, 39 + ETHTOOL_FLAGS_BASIC), 40 + [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1), 41 + }; 42 + 43 + const struct nla_policy ethnl_header_policy_phy_stats[] = { 44 + [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 }, 45 + [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING, 46 + .len = ALTIFNAMSIZ - 1 }, 47 + [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32, 48 + ETHTOOL_FLAGS_STATS), 49 + [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1), 33 50 }; 34 51 35 52 int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, ··· 138 119 const struct nlattr *header, struct net *net, 139 120 struct netlink_ext_ack *extack, bool require_dev) 140 121 { 141 - struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy)]; 122 + struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy_phy)]; 142 123 const struct nlattr *devname_attr; 143 124 struct net_device *dev = NULL; 144 125 u32 flags = 0; ··· 153 134 /* No validation here, command policy should have a nested policy set 154 135 * for the header, therefore validation should have already been done. 155 136 */ 156 - ret = nla_parse_nested(tb, ARRAY_SIZE(ethnl_header_policy) - 1, header, 137 + ret = nla_parse_nested(tb, ARRAY_SIZE(ethnl_header_policy_phy) - 1, header, 157 138 NULL, extack); 158 139 if (ret < 0) 159 140 return ret; ··· 194 175 return -EINVAL; 195 176 } 196 177 178 + if (tb[ETHTOOL_A_HEADER_PHY_INDEX]) { 179 + if (dev) { 180 + req_info->phy_index = nla_get_u32(tb[ETHTOOL_A_HEADER_PHY_INDEX]); 181 + } else { 182 + NL_SET_ERR_MSG_ATTR(extack, header, 183 + "phy_index set without a netdev"); 184 + return -EINVAL; 185 + } 186 + } 187 + 197 188 req_info->dev = dev; 198 189 req_info->flags = flags; 199 190 return 0; 191 + } 192 + 193 + struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info, 194 + const struct nlattr *header, 195 + struct netlink_ext_ack *extack) 196 + { 197 + struct phy_device *phydev; 198 + 199 + ASSERT_RTNL(); 200 + 201 + if (!req_info->dev) 202 + return NULL; 203 + 204 + if (!req_info->phy_index) 205 + return req_info->dev->phydev; 206 + 207 + phydev = phy_link_topo_get_phy(req_info->dev, req_info->phy_index); 208 + if (!phydev) { 209 + NL_SET_ERR_MSG_ATTR(extack, header, 210 + "no phy matching phyindex"); 211 + return ERR_PTR(-ENODEV); 212 + } 213 + 214 + return phydev; 200 215 } 201 216 202 217 /** ··· 1233 1180 .doit = ethnl_act_module_fw_flash, 1234 1181 .policy = ethnl_module_fw_flash_act_policy, 1235 1182 .maxattr = ARRAY_SIZE(ethnl_module_fw_flash_act_policy) - 1, 1183 + }, 1184 + { 1185 + .cmd = ETHTOOL_MSG_PHY_GET, 1186 + .doit = ethnl_phy_doit, 1187 + .start = ethnl_phy_start, 1188 + .dumpit = ethnl_phy_dumpit, 1189 + .done = ethnl_phy_done, 1190 + .policy = ethnl_phy_get_policy, 1191 + .maxattr = ARRAY_SIZE(ethnl_phy_get_policy) - 1, 1236 1192 }, 1237 1193 }; 1238 1194
+33
net/ethtool/netlink.h
··· 251 251 * @dev: network device the request is for (may be null) 252 252 * @dev_tracker: refcount tracker for @dev reference 253 253 * @flags: request flags common for all request types 254 + * @phy_index: phy_device index connected to @dev this request is for. Can be 255 + * 0 if the request doesn't target a phy, or if the @dev's attached 256 + * phy is targeted. 254 257 * 255 258 * This is a common base for request specific structures holding data from 256 259 * parsed userspace request. These always embed struct ethnl_req_info at ··· 263 260 struct net_device *dev; 264 261 netdevice_tracker dev_tracker; 265 262 u32 flags; 263 + u32 phy_index; 266 264 }; 267 265 268 266 static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info) 269 267 { 270 268 netdev_put(req_info->dev, &req_info->dev_tracker); 271 269 } 270 + 271 + /** 272 + * ethnl_req_get_phydev() - Gets the phy_device targeted by this request, 273 + * if any. Must be called under rntl_lock(). 274 + * @req_info: The ethnl request to get the phy from. 275 + * @header: The netlink header, used for error reporting. 276 + * @extack: The netlink extended ACK, for error reporting. 277 + * 278 + * The caller must hold RTNL, until it's done interacting with the returned 279 + * phy_device. 280 + * 281 + * Return: A phy_device pointer corresponding either to the passed phy_index 282 + * if one is provided. If not, the phy_device attached to the 283 + * net_device targeted by this request is returned. If there's no 284 + * targeted net_device, or no phy_device is attached, NULL is 285 + * returned. If the provided phy_index is invalid, an error pointer 286 + * is returned. 287 + */ 288 + struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info, 289 + const struct nlattr *header, 290 + struct netlink_ext_ack *extack); 272 291 273 292 /** 274 293 * struct ethnl_reply_data - base type of reply data for GET requests ··· 434 409 extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops; 435 410 extern const struct ethnl_request_ops ethnl_plca_status_request_ops; 436 411 extern const struct ethnl_request_ops ethnl_mm_request_ops; 412 + extern const struct ethnl_request_ops ethnl_phy_request_ops; 437 413 438 414 extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; 439 415 extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; 416 + extern const struct nla_policy ethnl_header_policy_phy[ETHTOOL_A_HEADER_PHY_INDEX + 1]; 417 + extern const struct nla_policy ethnl_header_policy_phy_stats[ETHTOOL_A_HEADER_PHY_INDEX + 1]; 440 418 extern const struct nla_policy ethnl_strset_get_policy[ETHTOOL_A_STRSET_COUNTS_ONLY + 1]; 441 419 extern const struct nla_policy ethnl_linkinfo_get_policy[ETHTOOL_A_LINKINFO_HEADER + 1]; 442 420 extern const struct nla_policy ethnl_linkinfo_set_policy[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL + 1]; ··· 484 456 extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1]; 485 457 extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1]; 486 458 extern const struct nla_policy ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD + 1]; 459 + extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1]; 487 460 488 461 int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); 489 462 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info); ··· 495 466 int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info); 496 467 int ethnl_rss_dump_start(struct netlink_callback *cb); 497 468 int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 469 + int ethnl_phy_start(struct netlink_callback *cb); 470 + int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info); 471 + int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 472 + int ethnl_phy_done(struct netlink_callback *cb); 498 473 499 474 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; 500 475 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
+308
net/ethtool/phy.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2023 Bootlin 4 + * 5 + */ 6 + #include "common.h" 7 + #include "netlink.h" 8 + 9 + #include <linux/phy.h> 10 + #include <linux/phy_link_topology.h> 11 + #include <linux/sfp.h> 12 + 13 + struct phy_req_info { 14 + struct ethnl_req_info base; 15 + struct phy_device_node *pdn; 16 + }; 17 + 18 + #define PHY_REQINFO(__req_base) \ 19 + container_of(__req_base, struct phy_req_info, base) 20 + 21 + const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1] = { 22 + [ETHTOOL_A_PHY_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 23 + }; 24 + 25 + /* Caller holds rtnl */ 26 + static ssize_t 27 + ethnl_phy_reply_size(const struct ethnl_req_info *req_base, 28 + struct netlink_ext_ack *extack) 29 + { 30 + struct phy_req_info *req_info = PHY_REQINFO(req_base); 31 + struct phy_device_node *pdn = req_info->pdn; 32 + struct phy_device *phydev = pdn->phy; 33 + size_t size = 0; 34 + 35 + ASSERT_RTNL(); 36 + 37 + /* ETHTOOL_A_PHY_INDEX */ 38 + size += nla_total_size(sizeof(u32)); 39 + 40 + /* ETHTOOL_A_DRVNAME */ 41 + if (phydev->drv) 42 + size += nla_total_size(strlen(phydev->drv->name) + 1); 43 + 44 + /* ETHTOOL_A_NAME */ 45 + size += nla_total_size(strlen(dev_name(&phydev->mdio.dev)) + 1); 46 + 47 + /* ETHTOOL_A_PHY_UPSTREAM_TYPE */ 48 + size += nla_total_size(sizeof(u32)); 49 + 50 + if (phy_on_sfp(phydev)) { 51 + const char *upstream_sfp_name = sfp_get_name(pdn->parent_sfp_bus); 52 + 53 + /* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */ 54 + if (upstream_sfp_name) 55 + size += nla_total_size(strlen(upstream_sfp_name) + 1); 56 + 57 + /* ETHTOOL_A_PHY_UPSTREAM_INDEX */ 58 + size += nla_total_size(sizeof(u32)); 59 + } 60 + 61 + /* ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME */ 62 + if (phydev->sfp_bus) { 63 + const char *sfp_name = sfp_get_name(phydev->sfp_bus); 64 + 65 + if (sfp_name) 66 + size += nla_total_size(strlen(sfp_name) + 1); 67 + } 68 + 69 + return size; 70 + } 71 + 72 + static int 73 + ethnl_phy_fill_reply(const struct ethnl_req_info *req_base, struct sk_buff *skb) 74 + { 75 + struct phy_req_info *req_info = PHY_REQINFO(req_base); 76 + struct phy_device_node *pdn = req_info->pdn; 77 + struct phy_device *phydev = pdn->phy; 78 + enum phy_upstream ptype; 79 + 80 + ptype = pdn->upstream_type; 81 + 82 + if (nla_put_u32(skb, ETHTOOL_A_PHY_INDEX, phydev->phyindex) || 83 + nla_put_string(skb, ETHTOOL_A_PHY_NAME, dev_name(&phydev->mdio.dev)) || 84 + nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_TYPE, ptype)) 85 + return -EMSGSIZE; 86 + 87 + if (phydev->drv && 88 + nla_put_string(skb, ETHTOOL_A_PHY_DRVNAME, phydev->drv->name)) 89 + return -EMSGSIZE; 90 + 91 + if (ptype == PHY_UPSTREAM_PHY) { 92 + struct phy_device *upstream = pdn->upstream.phydev; 93 + const char *sfp_upstream_name; 94 + 95 + /* Parent index */ 96 + if (nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_INDEX, upstream->phyindex)) 97 + return -EMSGSIZE; 98 + 99 + if (pdn->parent_sfp_bus) { 100 + sfp_upstream_name = sfp_get_name(pdn->parent_sfp_bus); 101 + if (sfp_upstream_name && 102 + nla_put_string(skb, ETHTOOL_A_PHY_UPSTREAM_SFP_NAME, 103 + sfp_upstream_name)) 104 + return -EMSGSIZE; 105 + } 106 + } 107 + 108 + if (phydev->sfp_bus) { 109 + const char *sfp_name = sfp_get_name(phydev->sfp_bus); 110 + 111 + if (sfp_name && 112 + nla_put_string(skb, ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME, 113 + sfp_name)) 114 + return -EMSGSIZE; 115 + } 116 + 117 + return 0; 118 + } 119 + 120 + static int ethnl_phy_parse_request(struct ethnl_req_info *req_base, 121 + struct nlattr **tb, 122 + struct netlink_ext_ack *extack) 123 + { 124 + struct phy_link_topology *topo = req_base->dev->link_topo; 125 + struct phy_req_info *req_info = PHY_REQINFO(req_base); 126 + struct phy_device *phydev; 127 + 128 + phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PHY_HEADER], 129 + extack); 130 + if (!phydev) 131 + return 0; 132 + 133 + if (IS_ERR(phydev)) 134 + return PTR_ERR(phydev); 135 + 136 + if (!topo) 137 + return 0; 138 + 139 + req_info->pdn = xa_load(&topo->phys, phydev->phyindex); 140 + 141 + return 0; 142 + } 143 + 144 + int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info) 145 + { 146 + struct phy_req_info req_info = {}; 147 + struct nlattr **tb = info->attrs; 148 + struct sk_buff *rskb; 149 + void *reply_payload; 150 + int reply_len; 151 + int ret; 152 + 153 + ret = ethnl_parse_header_dev_get(&req_info.base, 154 + tb[ETHTOOL_A_PHY_HEADER], 155 + genl_info_net(info), info->extack, 156 + true); 157 + if (ret < 0) 158 + return ret; 159 + 160 + rtnl_lock(); 161 + 162 + ret = ethnl_phy_parse_request(&req_info.base, tb, info->extack); 163 + if (ret < 0) 164 + goto err_unlock_rtnl; 165 + 166 + /* No PHY, return early */ 167 + if (!req_info.pdn->phy) 168 + goto err_unlock_rtnl; 169 + 170 + ret = ethnl_phy_reply_size(&req_info.base, info->extack); 171 + if (ret < 0) 172 + goto err_unlock_rtnl; 173 + reply_len = ret + ethnl_reply_header_size(); 174 + 175 + rskb = ethnl_reply_init(reply_len, req_info.base.dev, 176 + ETHTOOL_MSG_PHY_GET_REPLY, 177 + ETHTOOL_A_PHY_HEADER, 178 + info, &reply_payload); 179 + if (!rskb) { 180 + ret = -ENOMEM; 181 + goto err_unlock_rtnl; 182 + } 183 + 184 + ret = ethnl_phy_fill_reply(&req_info.base, rskb); 185 + if (ret) 186 + goto err_free_msg; 187 + 188 + rtnl_unlock(); 189 + ethnl_parse_header_dev_put(&req_info.base); 190 + genlmsg_end(rskb, reply_payload); 191 + 192 + return genlmsg_reply(rskb, info); 193 + 194 + err_free_msg: 195 + nlmsg_free(rskb); 196 + err_unlock_rtnl: 197 + rtnl_unlock(); 198 + ethnl_parse_header_dev_put(&req_info.base); 199 + return ret; 200 + } 201 + 202 + struct ethnl_phy_dump_ctx { 203 + struct phy_req_info *phy_req_info; 204 + unsigned long ifindex; 205 + unsigned long phy_index; 206 + }; 207 + 208 + int ethnl_phy_start(struct netlink_callback *cb) 209 + { 210 + const struct genl_info *info = genl_info_dump(cb); 211 + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 212 + int ret; 213 + 214 + BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); 215 + 216 + ctx->phy_req_info = kzalloc(sizeof(*ctx->phy_req_info), GFP_KERNEL); 217 + if (!ctx->phy_req_info) 218 + return -ENOMEM; 219 + 220 + ret = ethnl_parse_header_dev_get(&ctx->phy_req_info->base, 221 + info->attrs[ETHTOOL_A_PHY_HEADER], 222 + sock_net(cb->skb->sk), cb->extack, 223 + false); 224 + ctx->ifindex = 0; 225 + ctx->phy_index = 0; 226 + 227 + if (ret) 228 + kfree(ctx->phy_req_info); 229 + 230 + return ret; 231 + } 232 + 233 + int ethnl_phy_done(struct netlink_callback *cb) 234 + { 235 + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 236 + 237 + if (ctx->phy_req_info->base.dev) 238 + ethnl_parse_header_dev_put(&ctx->phy_req_info->base); 239 + 240 + kfree(ctx->phy_req_info); 241 + 242 + return 0; 243 + } 244 + 245 + static int ethnl_phy_dump_one_dev(struct sk_buff *skb, struct net_device *dev, 246 + struct netlink_callback *cb) 247 + { 248 + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 249 + struct phy_req_info *pri = ctx->phy_req_info; 250 + struct phy_device_node *pdn; 251 + int ret = 0; 252 + void *ehdr; 253 + 254 + pri->base.dev = dev; 255 + 256 + if (!dev->link_topo) 257 + return 0; 258 + 259 + xa_for_each_start(&dev->link_topo->phys, ctx->phy_index, pdn, ctx->phy_index) { 260 + ehdr = ethnl_dump_put(skb, cb, ETHTOOL_MSG_PHY_GET_REPLY); 261 + if (!ehdr) { 262 + ret = -EMSGSIZE; 263 + break; 264 + } 265 + 266 + ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_PHY_HEADER); 267 + if (ret < 0) { 268 + genlmsg_cancel(skb, ehdr); 269 + break; 270 + } 271 + 272 + pri->pdn = pdn; 273 + ret = ethnl_phy_fill_reply(&pri->base, skb); 274 + if (ret < 0) { 275 + genlmsg_cancel(skb, ehdr); 276 + break; 277 + } 278 + 279 + genlmsg_end(skb, ehdr); 280 + } 281 + 282 + return ret; 283 + } 284 + 285 + int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 286 + { 287 + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 288 + struct net *net = sock_net(skb->sk); 289 + struct net_device *dev; 290 + int ret = 0; 291 + 292 + rtnl_lock(); 293 + 294 + if (ctx->phy_req_info->base.dev) { 295 + ret = ethnl_phy_dump_one_dev(skb, ctx->phy_req_info->base.dev, cb); 296 + } else { 297 + for_each_netdev_dump(net, dev, ctx->ifindex) { 298 + ret = ethnl_phy_dump_one_dev(skb, dev, cb); 299 + if (ret) 300 + break; 301 + 302 + ctx->phy_index = 0; 303 + } 304 + } 305 + rtnl_unlock(); 306 + 307 + return ret; 308 + }
+20 -10
net/ethtool/plca.c
··· 25 25 26 26 const struct nla_policy ethnl_plca_get_cfg_policy[] = { 27 27 [ETHTOOL_A_PLCA_HEADER] = 28 - NLA_POLICY_NESTED(ethnl_header_policy), 28 + NLA_POLICY_NESTED(ethnl_header_policy_phy), 29 29 }; 30 30 31 31 static void plca_update_sint(int *dst, struct nlattr **tb, u32 attrid, ··· 58 58 struct plca_reply_data *data = PLCA_REPDATA(reply_base); 59 59 struct net_device *dev = reply_base->dev; 60 60 const struct ethtool_phy_ops *ops; 61 + struct nlattr **tb = info->attrs; 62 + struct phy_device *phydev; 61 63 int ret; 62 64 65 + phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PLCA_HEADER], 66 + info->extack); 63 67 // check that the PHY device is available and connected 64 - if (!dev->phydev) { 68 + if (IS_ERR_OR_NULL(phydev)) { 65 69 ret = -EOPNOTSUPP; 66 70 goto out; 67 71 } ··· 84 80 memset(&data->plca_cfg, 0xff, 85 81 sizeof_field(struct plca_reply_data, plca_cfg)); 86 82 87 - ret = ops->get_plca_cfg(dev->phydev, &data->plca_cfg); 83 + ret = ops->get_plca_cfg(phydev, &data->plca_cfg); 88 84 ethnl_ops_complete(dev); 89 85 90 86 out: ··· 133 129 134 130 const struct nla_policy ethnl_plca_set_cfg_policy[] = { 135 131 [ETHTOOL_A_PLCA_HEADER] = 136 - NLA_POLICY_NESTED(ethnl_header_policy), 132 + NLA_POLICY_NESTED(ethnl_header_policy_phy), 137 133 [ETHTOOL_A_PLCA_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1), 138 134 [ETHTOOL_A_PLCA_NODE_ID] = NLA_POLICY_MAX(NLA_U32, 255), 139 135 [ETHTOOL_A_PLCA_NODE_CNT] = NLA_POLICY_RANGE(NLA_U32, 1, 255), ··· 145 141 static int 146 142 ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info) 147 143 { 148 - struct net_device *dev = req_info->dev; 149 144 const struct ethtool_phy_ops *ops; 150 145 struct nlattr **tb = info->attrs; 151 146 struct phy_plca_cfg plca_cfg; 147 + struct phy_device *phydev; 152 148 bool mod = false; 153 149 int ret; 154 150 151 + phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PLCA_HEADER], 152 + info->extack); 155 153 // check that the PHY device is available and connected 156 - if (!dev->phydev) 154 + if (IS_ERR_OR_NULL(phydev)) 157 155 return -EOPNOTSUPP; 158 156 159 157 ops = ethtool_phy_ops; ··· 174 168 if (!mod) 175 169 return 0; 176 170 177 - ret = ops->set_plca_cfg(dev->phydev, &plca_cfg, info->extack); 171 + ret = ops->set_plca_cfg(phydev, &plca_cfg, info->extack); 178 172 return ret < 0 ? ret : 1; 179 173 } 180 174 ··· 197 191 198 192 const struct nla_policy ethnl_plca_get_status_policy[] = { 199 193 [ETHTOOL_A_PLCA_HEADER] = 200 - NLA_POLICY_NESTED(ethnl_header_policy), 194 + NLA_POLICY_NESTED(ethnl_header_policy_phy), 201 195 }; 202 196 203 197 static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base, ··· 207 201 struct plca_reply_data *data = PLCA_REPDATA(reply_base); 208 202 struct net_device *dev = reply_base->dev; 209 203 const struct ethtool_phy_ops *ops; 204 + struct nlattr **tb = info->attrs; 205 + struct phy_device *phydev; 210 206 int ret; 211 207 208 + phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PLCA_HEADER], 209 + info->extack); 212 210 // check that the PHY device is available and connected 213 - if (!dev->phydev) { 211 + if (IS_ERR_OR_NULL(phydev)) { 214 212 ret = -EOPNOTSUPP; 215 213 goto out; 216 214 } ··· 233 223 memset(&data->plca_st, 0xff, 234 224 sizeof_field(struct plca_reply_data, plca_st)); 235 225 236 - ret = ops->get_plca_status(dev->phydev, &data->plca_st); 226 + ret = ops->get_plca_status(phydev, &data->plca_st); 237 227 ethnl_ops_complete(dev); 238 228 out: 239 229 return ret;
+19 -12
net/ethtool/pse-pd.c
··· 28 28 /* PSE_GET */ 29 29 30 30 const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1] = { 31 - [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 31 + [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy), 32 32 }; 33 33 34 - static int pse_get_pse_attributes(struct net_device *dev, 34 + static int pse_get_pse_attributes(struct phy_device *phydev, 35 35 struct netlink_ext_ack *extack, 36 36 struct pse_reply_data *data) 37 37 { 38 - struct phy_device *phydev = dev->phydev; 39 - 40 38 if (!phydev) { 41 - NL_SET_ERR_MSG(extack, "No PHY is attached"); 39 + NL_SET_ERR_MSG(extack, "No PHY found"); 42 40 return -EOPNOTSUPP; 43 41 } 44 42 ··· 56 58 { 57 59 struct pse_reply_data *data = PSE_REPDATA(reply_base); 58 60 struct net_device *dev = reply_base->dev; 61 + struct nlattr **tb = info->attrs; 62 + struct phy_device *phydev; 59 63 int ret; 60 64 61 65 ret = ethnl_ops_begin(dev); 62 66 if (ret < 0) 63 67 return ret; 64 68 65 - ret = pse_get_pse_attributes(dev, info->extack, data); 69 + phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PSE_HEADER], 70 + info->extack); 71 + if (IS_ERR(phydev)) 72 + return -ENODEV; 73 + 74 + ret = pse_get_pse_attributes(phydev, info->extack, data); 66 75 67 76 ethnl_ops_complete(dev); 68 77 ··· 211 206 /* PSE_SET */ 212 207 213 208 const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = { 214 - [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 209 + [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy), 215 210 [ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] = 216 211 NLA_POLICY_RANGE(NLA_U32, ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED, 217 212 ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED), ··· 224 219 static int 225 220 ethnl_set_pse_validate(struct ethnl_req_info *req_info, struct genl_info *info) 226 221 { 227 - struct net_device *dev = req_info->dev; 228 222 struct nlattr **tb = info->attrs; 229 223 struct phy_device *phydev; 230 224 231 - phydev = dev->phydev; 232 - if (!phydev) { 225 + phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PSE_HEADER], 226 + info->extack); 227 + if (IS_ERR_OR_NULL(phydev)) { 233 228 NL_SET_ERR_MSG(info->extack, "No PHY is attached"); 234 229 return -EOPNOTSUPP; 235 230 } ··· 260 255 static int 261 256 ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info) 262 257 { 263 - struct net_device *dev = req_info->dev; 264 258 struct nlattr **tb = info->attrs; 265 259 struct phy_device *phydev; 266 260 int ret = 0; 267 261 268 - phydev = dev->phydev; 262 + phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PSE_HEADER], 263 + info->extack); 264 + if (IS_ERR_OR_NULL(phydev)) 265 + return -ENODEV; 269 266 270 267 if (tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]) { 271 268 unsigned int pw_limit;
+17 -7
net/ethtool/strset.c
··· 126 126 127 127 const struct nla_policy ethnl_strset_get_policy[] = { 128 128 [ETHTOOL_A_STRSET_HEADER] = 129 - NLA_POLICY_NESTED(ethnl_header_policy), 129 + NLA_POLICY_NESTED(ethnl_header_policy_phy), 130 130 [ETHTOOL_A_STRSET_STRINGSETS] = { .type = NLA_NESTED }, 131 131 [ETHTOOL_A_STRSET_COUNTS_ONLY] = { .type = NLA_FLAG }, 132 132 }; ··· 233 233 } 234 234 235 235 static int strset_prepare_set(struct strset_info *info, struct net_device *dev, 236 - unsigned int id, bool counts_only) 236 + struct phy_device *phydev, unsigned int id, 237 + bool counts_only) 237 238 { 238 239 const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops; 239 240 const struct ethtool_ops *ops = dev->ethtool_ops; 240 241 void *strings; 241 242 int count, ret; 242 243 243 - if (id == ETH_SS_PHY_STATS && dev->phydev && 244 + if (id == ETH_SS_PHY_STATS && phydev && 244 245 !ops->get_ethtool_phy_stats && phy_ops && 245 246 phy_ops->get_sset_count) 246 - ret = phy_ops->get_sset_count(dev->phydev); 247 + ret = phy_ops->get_sset_count(phydev); 247 248 else if (ops->get_sset_count && ops->get_strings) 248 249 ret = ops->get_sset_count(dev, id); 249 250 else ··· 259 258 strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL); 260 259 if (!strings) 261 260 return -ENOMEM; 262 - if (id == ETH_SS_PHY_STATS && dev->phydev && 261 + if (id == ETH_SS_PHY_STATS && phydev && 263 262 !ops->get_ethtool_phy_stats && phy_ops && 264 263 phy_ops->get_strings) 265 - phy_ops->get_strings(dev->phydev, strings); 264 + phy_ops->get_strings(phydev, strings); 266 265 else 267 266 ops->get_strings(dev, id, strings); 268 267 info->strings = strings; ··· 280 279 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 281 280 struct strset_reply_data *data = STRSET_REPDATA(reply_base); 282 281 struct net_device *dev = reply_base->dev; 282 + struct nlattr **tb = info->attrs; 283 + struct phy_device *phydev; 283 284 unsigned int i; 284 285 int ret; 285 286 ··· 299 296 return 0; 300 297 } 301 298 299 + phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_HEADER_FLAGS], 300 + info->extack); 301 + 302 + /* phydev can be NULL, check for errors only */ 303 + if (IS_ERR(phydev)) 304 + return PTR_ERR(phydev); 305 + 302 306 ret = ethnl_ops_begin(dev); 303 307 if (ret < 0) 304 308 goto err_strset; ··· 314 304 !data->sets[i].per_dev) 315 305 continue; 316 306 317 - ret = strset_prepare_set(&data->sets[i], dev, i, 307 + ret = strset_prepare_set(&data->sets[i], dev, phydev, i, 318 308 req_info->counts_only); 319 309 if (ret < 0) 320 310 goto err_ops;