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

net: remove phy_has_hwtstamp() -> phy_mii_ioctl() decision from converted drivers

It is desirable that the new .ndo_hwtstamp_set() API gives more
uniformity, less overhead and future flexibility w.r.t. the PHY
timestamping behavior.

Currently there are some drivers which allow PHY timestamping through
the procedure mentioned in Documentation/networking/timestamping.rst.
They don't do anything locally if phy_has_hwtstamp() is set, except for
lan966x which installs PTP packet traps.

Centralize that behavior in a new dev_set_hwtstamp_phylib() code
function, which calls either phy_mii_ioctl() for the phylib PHY,
or .ndo_hwtstamp_set() of the netdev, based on a single policy
(currently simplistic: phy_has_hwtstamp()).

Any driver converted to .ndo_hwtstamp_set() will automatically opt into
the centralized phylib timestamping policy. Unconverted drivers still
get to choose whether they let the PHY handle timestamping or not.

Netdev drivers with integrated PHY drivers that don't use phylib
presumably don't set dev->phydev, and those will always see
HWTSTAMP_SOURCE_NETDEV requests even when converted. The timestamping
policy will remain 100% up to them.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Tested-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Link: https://lore.kernel.org/r/20230801142824.1772134-13-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Vladimir Oltean and committed by
Jakub Kicinski
fd770e85 60495b66

+117 -33
-8
drivers/net/ethernet/freescale/fec_main.c
··· 3872 3872 struct kernel_hwtstamp_config *config) 3873 3873 { 3874 3874 struct fec_enet_private *fep = netdev_priv(ndev); 3875 - struct phy_device *phydev = ndev->phydev; 3876 - 3877 - if (phy_has_hwtstamp(phydev)) 3878 - return phy_mii_ioctl(phydev, config->ifr, SIOCGHWTSTAMP); 3879 3875 3880 3876 if (!netif_running(ndev)) 3881 3877 return -EINVAL; ··· 3889 3893 struct netlink_ext_ack *extack) 3890 3894 { 3891 3895 struct fec_enet_private *fep = netdev_priv(ndev); 3892 - struct phy_device *phydev = ndev->phydev; 3893 - 3894 - if (phy_has_hwtstamp(phydev)) 3895 - return phy_mii_ioctl(phydev, config->ifr, SIOCSHWTSTAMP); 3896 3896 3897 3897 if (!netif_running(ndev)) 3898 3898 return -EINVAL;
+14 -11
drivers/net/ethernet/microchip/lan966x/lan966x_main.c
··· 455 455 { 456 456 struct lan966x_port *port = netdev_priv(dev); 457 457 458 - if (phy_has_hwtstamp(dev->phydev)) 459 - return phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCGHWTSTAMP); 460 - 461 458 if (!port->lan966x->ptp) 462 459 return -EOPNOTSUPP; 463 460 ··· 470 473 struct lan966x_port *port = netdev_priv(dev); 471 474 int err; 472 475 476 + if (cfg->source != HWTSTAMP_SOURCE_NETDEV && 477 + cfg->source != HWTSTAMP_SOURCE_PHYLIB) 478 + return -EOPNOTSUPP; 479 + 473 480 err = lan966x_ptp_setup_traps(port, cfg); 474 481 if (err) 475 482 return err; 476 483 477 - if (phy_has_hwtstamp(dev->phydev)) { 478 - err = phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCSHWTSTAMP); 479 - if (err) 484 + if (cfg->source == HWTSTAMP_SOURCE_NETDEV) { 485 + if (!port->lan966x->ptp) 486 + return -EOPNOTSUPP; 487 + 488 + err = lan966x_ptp_hwtstamp_set(port, cfg, extack); 489 + if (err) { 480 490 lan966x_ptp_del_traps(port); 481 - return err; 491 + return err; 492 + } 482 493 } 483 494 484 - if (!port->lan966x->ptp) 485 - return -EOPNOTSUPP; 486 - 487 - return lan966x_ptp_hwtstamp_set(port, cfg, extack); 495 + return 0; 488 496 } 489 497 490 498 static const struct net_device_ops lan966x_port_netdev_ops = { ··· 817 815 NETIF_F_HW_VLAN_STAG_TX | 818 816 NETIF_F_HW_TC; 819 817 dev->hw_features |= NETIF_F_HW_TC; 818 + dev->priv_flags |= IFF_SEE_ALL_HWTSTAMP_REQUESTS; 820 819 dev->needed_headroom = IFH_LEN_BYTES; 821 820 822 821 eth_hw_addr_gen(dev, lan966x->base_mac, p + 1);
-6
drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
··· 216 216 struct sparx5_port *sparx5_port = netdev_priv(dev); 217 217 struct sparx5 *sparx5 = sparx5_port->sparx5; 218 218 219 - if (phy_has_hwtstamp(dev->phydev)) 220 - return phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCGHWTSTAMP); 221 - 222 219 if (!sparx5->ptp) 223 220 return -EOPNOTSUPP; 224 221 ··· 230 233 { 231 234 struct sparx5_port *sparx5_port = netdev_priv(dev); 232 235 struct sparx5 *sparx5 = sparx5_port->sparx5; 233 - 234 - if (phy_has_hwtstamp(dev->phydev)) 235 - return phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCSHWTSTAMP); 236 236 237 237 if (!sparx5->ptp) 238 238 return -EOPNOTSUPP;
+16
include/linux/net_tstamp.h
··· 5 5 6 6 #include <uapi/linux/net_tstamp.h> 7 7 8 + enum hwtstamp_source { 9 + HWTSTAMP_SOURCE_NETDEV, 10 + HWTSTAMP_SOURCE_PHYLIB, 11 + }; 12 + 8 13 /** 9 14 * struct kernel_hwtstamp_config - Kernel copy of struct hwtstamp_config 10 15 * ··· 20 15 * a legacy implementation of a lower driver 21 16 * @copied_to_user: request was passed to a legacy implementation which already 22 17 * copied the ioctl request back to user space 18 + * @source: indication whether timestamps should come from the netdev or from 19 + * an attached phylib PHY 23 20 * 24 21 * Prefer using this structure for in-kernel processing of hardware 25 22 * timestamping configuration, over the inextensible struct hwtstamp_config ··· 33 26 int rx_filter; 34 27 struct ifreq *ifr; 35 28 bool copied_to_user; 29 + enum hwtstamp_source source; 36 30 }; 37 31 38 32 static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kernel_cfg, ··· 50 42 cfg->flags = kernel_cfg->flags; 51 43 cfg->tx_type = kernel_cfg->tx_type; 52 44 cfg->rx_filter = kernel_cfg->rx_filter; 45 + } 46 + 47 + static inline bool kernel_hwtstamp_config_changed(const struct kernel_hwtstamp_config *a, 48 + const struct kernel_hwtstamp_config *b) 49 + { 50 + return a->flags != b->flags || 51 + a->tx_type != b->tx_type || 52 + a->rx_filter != b->rx_filter; 53 53 } 54 54 55 55 #endif /* _LINUX_NET_TIMESTAMPING_H_ */
+4
include/linux/netdevice.h
··· 1724 1724 * @IFF_TX_SKB_NO_LINEAR: device/driver is capable of xmitting frames with 1725 1725 * skb_headlen(skb) == 0 (data starts from frag0) 1726 1726 * @IFF_CHANGE_PROTO_DOWN: device supports setting carrier via IFLA_PROTO_DOWN 1727 + * @IFF_SEE_ALL_HWTSTAMP_REQUESTS: device wants to see calls to 1728 + * ndo_hwtstamp_set() for all timestamp requests regardless of source, 1729 + * even if those aren't HWTSTAMP_SOURCE_NETDEV. 1727 1730 */ 1728 1731 enum netdev_priv_flags { 1729 1732 IFF_802_1Q_VLAN = 1<<0, ··· 1762 1759 IFF_NO_ADDRCONF = BIT_ULL(30), 1763 1760 IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), 1764 1761 IFF_CHANGE_PROTO_DOWN = BIT_ULL(32), 1762 + IFF_SEE_ALL_HWTSTAMP_REQUESTS = BIT_ULL(33), 1765 1763 }; 1766 1764 1767 1765 #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
+83 -8
net/core/dev_ioctl.c
··· 5 5 #include <linux/etherdevice.h> 6 6 #include <linux/rtnetlink.h> 7 7 #include <linux/net_tstamp.h> 8 + #include <linux/phylib_stubs.h> 8 9 #include <linux/wireless.h> 9 10 #include <linux/if_bridge.h> 10 11 #include <net/dsa_stubs.h> ··· 253 252 return ops->ndo_eth_ioctl(dev, ifr, cmd); 254 253 } 255 254 255 + /** 256 + * dev_get_hwtstamp_phylib() - Get hardware timestamping settings of NIC 257 + * or of attached phylib PHY 258 + * @dev: Network device 259 + * @cfg: Timestamping configuration structure 260 + * 261 + * Helper for enforcing a common policy that phylib timestamping, if available, 262 + * should take precedence in front of hardware timestamping provided by the 263 + * netdev. 264 + * 265 + * Note: phy_mii_ioctl() only handles SIOCSHWTSTAMP (not SIOCGHWTSTAMP), and 266 + * there only exists a phydev->mii_ts->hwtstamp() method. So this will return 267 + * -EOPNOTSUPP for phylib for now, which is still more accurate than letting 268 + * the netdev handle the GET request. 269 + */ 270 + static int dev_get_hwtstamp_phylib(struct net_device *dev, 271 + struct kernel_hwtstamp_config *cfg) 272 + { 273 + if (phy_has_hwtstamp(dev->phydev)) 274 + return phy_hwtstamp_get(dev->phydev, cfg); 275 + 276 + return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg); 277 + } 278 + 256 279 static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr) 257 280 { 258 281 const struct net_device_ops *ops = dev->netdev_ops; ··· 291 266 return -ENODEV; 292 267 293 268 kernel_cfg.ifr = ifr; 294 - err = ops->ndo_hwtstamp_get(dev, &kernel_cfg); 269 + err = dev_get_hwtstamp_phylib(dev, &kernel_cfg); 295 270 if (err) 296 271 return err; 297 272 ··· 303 278 304 279 if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg))) 305 280 return -EFAULT; 281 + } 282 + 283 + return 0; 284 + } 285 + 286 + /** 287 + * dev_set_hwtstamp_phylib() - Change hardware timestamping of NIC 288 + * or of attached phylib PHY 289 + * @dev: Network device 290 + * @cfg: Timestamping configuration structure 291 + * @extack: Netlink extended ack message structure, for error reporting 292 + * 293 + * Helper for enforcing a common policy that phylib timestamping, if available, 294 + * should take precedence in front of hardware timestamping provided by the 295 + * netdev. If the netdev driver needs to perform specific actions even for PHY 296 + * timestamping to work properly (a switch port must trap the timestamped 297 + * frames and not forward them), it must set IFF_SEE_ALL_HWTSTAMP_REQUESTS in 298 + * dev->priv_flags. 299 + */ 300 + static int dev_set_hwtstamp_phylib(struct net_device *dev, 301 + struct kernel_hwtstamp_config *cfg, 302 + struct netlink_ext_ack *extack) 303 + { 304 + const struct net_device_ops *ops = dev->netdev_ops; 305 + bool phy_ts = phy_has_hwtstamp(dev->phydev); 306 + struct kernel_hwtstamp_config old_cfg = {}; 307 + bool changed = false; 308 + int err; 309 + 310 + cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV; 311 + 312 + if (!phy_ts || (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) { 313 + err = ops->ndo_hwtstamp_get(dev, &old_cfg); 314 + if (err) 315 + return err; 316 + 317 + err = ops->ndo_hwtstamp_set(dev, cfg, extack); 318 + if (err) { 319 + if (extack->_msg) 320 + netdev_err(dev, "%s\n", extack->_msg); 321 + return err; 322 + } 323 + 324 + changed = kernel_hwtstamp_config_changed(&old_cfg, cfg); 325 + } 326 + 327 + if (phy_ts) { 328 + err = phy_hwtstamp_set(dev->phydev, cfg, extack); 329 + if (err) { 330 + if (changed) 331 + ops->ndo_hwtstamp_set(dev, &old_cfg, NULL); 332 + return err; 333 + } 306 334 } 307 335 308 336 return 0; ··· 392 314 if (!netif_device_present(dev)) 393 315 return -ENODEV; 394 316 395 - err = ops->ndo_hwtstamp_set(dev, &kernel_cfg, &extack); 396 - if (err) { 397 - if (extack._msg) 398 - netdev_err(dev, "%s\n", extack._msg); 317 + err = dev_set_hwtstamp_phylib(dev, &kernel_cfg, &extack); 318 + if (err) 399 319 return err; 400 - } 401 320 402 321 /* The driver may have modified the configuration, so copy the 403 322 * updated version of it back to user space ··· 437 362 return -ENODEV; 438 363 439 364 if (ops->ndo_hwtstamp_get) 440 - return ops->ndo_hwtstamp_get(dev, kernel_cfg); 365 + return dev_get_hwtstamp_phylib(dev, kernel_cfg); 441 366 442 367 /* Legacy path: unconverted lower driver */ 443 368 return generic_hwtstamp_ioctl_lower(dev, SIOCGHWTSTAMP, kernel_cfg); ··· 454 379 return -ENODEV; 455 380 456 381 if (ops->ndo_hwtstamp_set) 457 - return ops->ndo_hwtstamp_set(dev, kernel_cfg, extack); 382 + return dev_set_hwtstamp_phylib(dev, kernel_cfg, extack); 458 383 459 384 /* Legacy path: unconverted lower driver */ 460 385 return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);