Message ID | 20230801142824.1772134-13-vladimir.oltean@nxp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Introduce ndo_hwtstamp_get() and ndo_hwtstamp_set() | expand |
On Tue, Aug 1, 2023 at 4:29 PM Vladimir Oltean <vladimir.oltean@nxp.com> wrote: > > 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> > --- > +/** > + * dev_set_hwtstamp_phylib() - Change hardware timestamping of NIC > + * or of attached phylib PHY > + * @dev: Network device > + * @cfg: Timestamping configuration structure > + * @extack: Netlink extended ack message structure, for error reporting > + * > + * Helper for enforcing a common policy that phylib timestamping, if available, > + * should take precedence in front of hardware timestamping provided by the > + * netdev. If the netdev driver needs to perform specific actions even for PHY > + * timestamping to work properly (a switch port must trap the timestamped > + * frames and not forward them), it must set IFF_SEE_ALL_HWTSTAMP_REQUESTS in > + * dev->priv_flags. > + */ > +static int dev_set_hwtstamp_phylib(struct net_device *dev, > + struct kernel_hwtstamp_config *cfg, > + struct netlink_ext_ack *extack) > +{ > + const struct net_device_ops *ops = dev->netdev_ops; > + bool phy_ts = phy_has_hwtstamp(dev->phydev); > + struct kernel_hwtstamp_config old_cfg = {}; > + bool changed = false; > + int err; > + > + cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV; > + > + if (!phy_ts || (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) { > + err = ops->ndo_hwtstamp_get(dev, &old_cfg); old_cfg.ifr is NULL at this point. This causes a crash later in generic_hwtstamp_ioctl_lower() ifrr.ifr_ifru = kernel_cfg->ifr->ifr_ifru; > + if (err) > + return err; > + > + err = ops->ndo_hwtstamp_set(dev, cfg, extack); > + if (err) { > + if (extack->_msg) > + netdev_err(dev, "%s\n", extack->_msg); > + return err; > + } > + > + changed = kernel_hwtstamp_config_changed(&old_cfg, cfg); > + } > + > + if (phy_ts) { > + err = phy_hwtstamp_set(dev->phydev, cfg, extack); > + if (err) { > + if (changed) > + ops->ndo_hwtstamp_set(dev, &old_cfg, NULL); > + return err; > + } > + } > + > + return 0; > +} > + > static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr) > { > const struct net_device_ops *ops = dev->netdev_ops; > @@ -314,12 +392,9 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr) > if (!netif_device_present(dev)) > return -ENODEV; > > - err = ops->ndo_hwtstamp_set(dev, &kernel_cfg, &extack); > - if (err) { > - if (extack._msg) > - netdev_err(dev, "%s\n", extack._msg); > + err = dev_set_hwtstamp_phylib(dev, &kernel_cfg, &extack); > + if (err) > return err; > - } > > /* The driver may have modified the configuration, so copy the > * updated version of it back to user space > @@ -362,7 +437,7 @@ int generic_hwtstamp_get_lower(struct net_device *dev, > return -ENODEV; > > if (ops->ndo_hwtstamp_get) > - return ops->ndo_hwtstamp_get(dev, kernel_cfg); > + return dev_get_hwtstamp_phylib(dev, kernel_cfg); > > /* Legacy path: unconverted lower driver */ > return generic_hwtstamp_ioctl_lower(dev, SIOCGHWTSTAMP, kernel_cfg); > @@ -379,7 +454,7 @@ int generic_hwtstamp_set_lower(struct net_device *dev, > return -ENODEV; > > if (ops->ndo_hwtstamp_set) > - return ops->ndo_hwtstamp_set(dev, kernel_cfg, extack); > + return dev_set_hwtstamp_phylib(dev, kernel_cfg, extack); > > /* Legacy path: unconverted lower driver */ > return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg); > -- > 2.34.1 >
On Fri, Aug 04, 2023 at 09:21:28AM +0200, Eric Dumazet wrote: > > +/** > > + * dev_set_hwtstamp_phylib() - Change hardware timestamping of NIC > > + * or of attached phylib PHY > > + * @dev: Network device > > + * @cfg: Timestamping configuration structure > > + * @extack: Netlink extended ack message structure, for error reporting > > + * > > + * Helper for enforcing a common policy that phylib timestamping, if available, > > + * should take precedence in front of hardware timestamping provided by the > > + * netdev. If the netdev driver needs to perform specific actions even for PHY > > + * timestamping to work properly (a switch port must trap the timestamped > > + * frames and not forward them), it must set IFF_SEE_ALL_HWTSTAMP_REQUESTS in > > + * dev->priv_flags. > > + */ > > +static int dev_set_hwtstamp_phylib(struct net_device *dev, > > + struct kernel_hwtstamp_config *cfg, > > + struct netlink_ext_ack *extack) > > +{ > > + const struct net_device_ops *ops = dev->netdev_ops; > > + bool phy_ts = phy_has_hwtstamp(dev->phydev); > > + struct kernel_hwtstamp_config old_cfg = {}; > > + bool changed = false; > > + int err; > > + > > + cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV; > > + > > + if (!phy_ts || (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) { > > + err = ops->ndo_hwtstamp_get(dev, &old_cfg); > > old_cfg.ifr is NULL at this point. > > This causes a crash later in generic_hwtstamp_ioctl_lower() > > ifrr.ifr_ifru = kernel_cfg->ifr->ifr_ifru; Thanks for reporting. My control flow is all wrong, it seems. I've sent this patch for addressing the breakage: https://lore.kernel.org/netdev/20230804134939.3109763-1-vladimir.oltean@nxp.com/
On Tue, 1 Aug 2023 17:28:24 +0300 Vladimir Oltean <vladimir.oltean@nxp.com> wrote: > 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. > +static int dev_set_hwtstamp_phylib(struct net_device *dev, > + struct kernel_hwtstamp_config *cfg, > + struct netlink_ext_ack *extack) > +{ ... > + if (phy_ts) { > + err = phy_hwtstamp_set(dev->phydev, cfg, extack); > + if (err) { > + if (changed) > + ops->ndo_hwtstamp_set(dev, &old_cfg, NULL); > + return err; > + } > + } In this case the copy_from_user function will be call 2 times, one in dev_set_hwtstamp and one in the mii_ts.hwtstamp callback of the PHY driver. Should we create also a copied_from_user flag? Other idea? Regards,
On Thu, 28 Sep 2023 12:12:14 +0200 Köry Maincent <kory.maincent@bootlin.com> wrote: > On Tue, 1 Aug 2023 17:28:24 +0300 > Vladimir Oltean <vladimir.oltean@nxp.com> wrote: > > > 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. > > > +static int dev_set_hwtstamp_phylib(struct net_device *dev, > > + struct kernel_hwtstamp_config *cfg, > > + struct netlink_ext_ack *extack) > > +{ > ... > > > + if (phy_ts) { > > + err = phy_hwtstamp_set(dev->phydev, cfg, extack); > > + if (err) { > > + if (changed) > > + ops->ndo_hwtstamp_set(dev, &old_cfg, NULL); > > + return err; > > + } > > + } > > In this case the copy_from_user function will be call 2 times, one in > dev_set_hwtstamp and one in the mii_ts.hwtstamp callback of the PHY driver. > Should we create also a copied_from_user flag? Other idea? oops sorry for the noise the issue I face seems elsewhere. If I understand it well, two call of copy_from_user consecutive will behave the same.
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 6d81fff0227e..43f14cec91e9 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3872,10 +3872,6 @@ static int fec_hwtstamp_get(struct net_device *ndev, struct kernel_hwtstamp_config *config) { struct fec_enet_private *fep = netdev_priv(ndev); - struct phy_device *phydev = ndev->phydev; - - if (phy_has_hwtstamp(phydev)) - return phy_mii_ioctl(phydev, config->ifr, SIOCGHWTSTAMP); if (!netif_running(ndev)) return -EINVAL; @@ -3893,10 +3889,6 @@ static int fec_hwtstamp_set(struct net_device *ndev, struct netlink_ext_ack *extack) { struct fec_enet_private *fep = netdev_priv(ndev); - struct phy_device *phydev = ndev->phydev; - - if (phy_has_hwtstamp(phydev)) - return phy_mii_ioctl(phydev, config->ifr, SIOCSHWTSTAMP); if (!netif_running(ndev)) return -EINVAL; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 1baa94a98fe3..4a1acc7234f6 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -455,9 +455,6 @@ static int lan966x_port_hwtstamp_get(struct net_device *dev, { struct lan966x_port *port = netdev_priv(dev); - if (phy_has_hwtstamp(dev->phydev)) - return phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCGHWTSTAMP); - if (!port->lan966x->ptp) return -EOPNOTSUPP; @@ -473,21 +470,26 @@ static int lan966x_port_hwtstamp_set(struct net_device *dev, struct lan966x_port *port = netdev_priv(dev); int err; + if (cfg->source != HWTSTAMP_SOURCE_NETDEV && + cfg->source != HWTSTAMP_SOURCE_PHYLIB) + return -EOPNOTSUPP; + err = lan966x_ptp_setup_traps(port, cfg); if (err) return err; - if (phy_has_hwtstamp(dev->phydev)) { - err = phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCSHWTSTAMP); - if (err) + if (cfg->source == HWTSTAMP_SOURCE_NETDEV) { + if (!port->lan966x->ptp) + return -EOPNOTSUPP; + + err = lan966x_ptp_hwtstamp_set(port, cfg, extack); + if (err) { lan966x_ptp_del_traps(port); - return err; + return err; + } } - if (!port->lan966x->ptp) - return -EOPNOTSUPP; - - return lan966x_ptp_hwtstamp_set(port, cfg, extack); + return 0; } static const struct net_device_ops lan966x_port_netdev_ops = { @@ -815,6 +817,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_TC; dev->hw_features |= NETIF_F_HW_TC; + dev->priv_flags |= IFF_SEE_ALL_HWTSTAMP_REQUESTS; dev->needed_headroom = IFH_LEN_BYTES; eth_hw_addr_gen(dev, lan966x->base_mac, p + 1); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c index e01d3b1e17e0..705a004b324f 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c @@ -216,9 +216,6 @@ static int sparx5_port_hwtstamp_get(struct net_device *dev, struct sparx5_port *sparx5_port = netdev_priv(dev); struct sparx5 *sparx5 = sparx5_port->sparx5; - if (phy_has_hwtstamp(dev->phydev)) - return phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCGHWTSTAMP); - if (!sparx5->ptp) return -EOPNOTSUPP; @@ -234,9 +231,6 @@ static int sparx5_port_hwtstamp_set(struct net_device *dev, struct sparx5_port *sparx5_port = netdev_priv(dev); struct sparx5 *sparx5 = sparx5_port->sparx5; - if (phy_has_hwtstamp(dev->phydev)) - return phy_mii_ioctl(dev->phydev, cfg->ifr, SIOCSHWTSTAMP); - if (!sparx5->ptp) return -EOPNOTSUPP; diff --git a/include/linux/net_tstamp.h b/include/linux/net_tstamp.h index 03e922814851..eb01c37e71e0 100644 --- a/include/linux/net_tstamp.h +++ b/include/linux/net_tstamp.h @@ -5,6 +5,11 @@ #include <uapi/linux/net_tstamp.h> +enum hwtstamp_source { + HWTSTAMP_SOURCE_NETDEV, + HWTSTAMP_SOURCE_PHYLIB, +}; + /** * struct kernel_hwtstamp_config - Kernel copy of struct hwtstamp_config * @@ -15,6 +20,8 @@ * a legacy implementation of a lower driver * @copied_to_user: request was passed to a legacy implementation which already * copied the ioctl request back to user space + * @source: indication whether timestamps should come from the netdev or from + * an attached phylib PHY * * Prefer using this structure for in-kernel processing of hardware * timestamping configuration, over the inextensible struct hwtstamp_config @@ -26,6 +33,7 @@ struct kernel_hwtstamp_config { int rx_filter; struct ifreq *ifr; bool copied_to_user; + enum hwtstamp_source source; }; static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kernel_cfg, @@ -44,4 +52,12 @@ static inline void hwtstamp_config_from_kernel(struct hwtstamp_config *cfg, cfg->rx_filter = kernel_cfg->rx_filter; } +static inline bool kernel_hwtstamp_config_changed(const struct kernel_hwtstamp_config *a, + const struct kernel_hwtstamp_config *b) +{ + return a->flags != b->flags || + a->tx_type != b->tx_type || + a->rx_filter != b->rx_filter; +} + #endif /* _LINUX_NET_TIMESTAMPING_H_ */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 23e335f245cf..85d594460c66 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1724,6 +1724,9 @@ struct xdp_metadata_ops { * @IFF_TX_SKB_NO_LINEAR: device/driver is capable of xmitting frames with * skb_headlen(skb) == 0 (data starts from frag0) * @IFF_CHANGE_PROTO_DOWN: device supports setting carrier via IFLA_PROTO_DOWN + * @IFF_SEE_ALL_HWTSTAMP_REQUESTS: device wants to see calls to + * ndo_hwtstamp_set() for all timestamp requests regardless of source, + * even if those aren't HWTSTAMP_SOURCE_NETDEV. */ enum netdev_priv_flags { IFF_802_1Q_VLAN = 1<<0, @@ -1759,6 +1762,7 @@ enum netdev_priv_flags { IFF_NO_ADDRCONF = BIT_ULL(30), IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), IFF_CHANGE_PROTO_DOWN = BIT_ULL(32), + IFF_SEE_ALL_HWTSTAMP_REQUESTS = BIT_ULL(33), }; #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index d0223ecd6f6f..72e077022348 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -5,6 +5,7 @@ #include <linux/etherdevice.h> #include <linux/rtnetlink.h> #include <linux/net_tstamp.h> +#include <linux/phylib_stubs.h> #include <linux/wireless.h> #include <linux/if_bridge.h> #include <net/dsa_stubs.h> @@ -252,6 +253,30 @@ static int dev_eth_ioctl(struct net_device *dev, return ops->ndo_eth_ioctl(dev, ifr, cmd); } +/** + * dev_get_hwtstamp_phylib() - Get hardware timestamping settings of NIC + * or of attached phylib PHY + * @dev: Network device + * @cfg: Timestamping configuration structure + * + * Helper for enforcing a common policy that phylib timestamping, if available, + * should take precedence in front of hardware timestamping provided by the + * netdev. + * + * Note: phy_mii_ioctl() only handles SIOCSHWTSTAMP (not SIOCGHWTSTAMP), and + * there only exists a phydev->mii_ts->hwtstamp() method. So this will return + * -EOPNOTSUPP for phylib for now, which is still more accurate than letting + * the netdev handle the GET request. + */ +static int dev_get_hwtstamp_phylib(struct net_device *dev, + struct kernel_hwtstamp_config *cfg) +{ + if (phy_has_hwtstamp(dev->phydev)) + return phy_hwtstamp_get(dev->phydev, cfg); + + return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg); +} + static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr) { const struct net_device_ops *ops = dev->netdev_ops; @@ -266,7 +291,7 @@ static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr) return -ENODEV; kernel_cfg.ifr = ifr; - err = ops->ndo_hwtstamp_get(dev, &kernel_cfg); + err = dev_get_hwtstamp_phylib(dev, &kernel_cfg); if (err) return err; @@ -283,6 +308,59 @@ static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr) return 0; } +/** + * dev_set_hwtstamp_phylib() - Change hardware timestamping of NIC + * or of attached phylib PHY + * @dev: Network device + * @cfg: Timestamping configuration structure + * @extack: Netlink extended ack message structure, for error reporting + * + * Helper for enforcing a common policy that phylib timestamping, if available, + * should take precedence in front of hardware timestamping provided by the + * netdev. If the netdev driver needs to perform specific actions even for PHY + * timestamping to work properly (a switch port must trap the timestamped + * frames and not forward them), it must set IFF_SEE_ALL_HWTSTAMP_REQUESTS in + * dev->priv_flags. + */ +static int dev_set_hwtstamp_phylib(struct net_device *dev, + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) +{ + const struct net_device_ops *ops = dev->netdev_ops; + bool phy_ts = phy_has_hwtstamp(dev->phydev); + struct kernel_hwtstamp_config old_cfg = {}; + bool changed = false; + int err; + + cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV; + + if (!phy_ts || (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) { + err = ops->ndo_hwtstamp_get(dev, &old_cfg); + if (err) + return err; + + err = ops->ndo_hwtstamp_set(dev, cfg, extack); + if (err) { + if (extack->_msg) + netdev_err(dev, "%s\n", extack->_msg); + return err; + } + + changed = kernel_hwtstamp_config_changed(&old_cfg, cfg); + } + + if (phy_ts) { + err = phy_hwtstamp_set(dev->phydev, cfg, extack); + if (err) { + if (changed) + ops->ndo_hwtstamp_set(dev, &old_cfg, NULL); + return err; + } + } + + return 0; +} + static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr) { const struct net_device_ops *ops = dev->netdev_ops; @@ -314,12 +392,9 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr) if (!netif_device_present(dev)) return -ENODEV; - err = ops->ndo_hwtstamp_set(dev, &kernel_cfg, &extack); - if (err) { - if (extack._msg) - netdev_err(dev, "%s\n", extack._msg); + err = dev_set_hwtstamp_phylib(dev, &kernel_cfg, &extack); + if (err) return err; - } /* The driver may have modified the configuration, so copy the * updated version of it back to user space @@ -362,7 +437,7 @@ int generic_hwtstamp_get_lower(struct net_device *dev, return -ENODEV; if (ops->ndo_hwtstamp_get) - return ops->ndo_hwtstamp_get(dev, kernel_cfg); + return dev_get_hwtstamp_phylib(dev, kernel_cfg); /* Legacy path: unconverted lower driver */ return generic_hwtstamp_ioctl_lower(dev, SIOCGHWTSTAMP, kernel_cfg); @@ -379,7 +454,7 @@ int generic_hwtstamp_set_lower(struct net_device *dev, return -ENODEV; if (ops->ndo_hwtstamp_set) - return ops->ndo_hwtstamp_set(dev, kernel_cfg, extack); + return dev_set_hwtstamp_phylib(dev, kernel_cfg, extack); /* Legacy path: unconverted lower driver */ return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);