Merge branch 'introduce-ndo_hwtstamp_get-and-ndo_hwtstamp_set'
authorJakub Kicinski <kuba@kernel.org>
Thu, 3 Aug 2023 02:11:08 +0000 (19:11 -0700)
committerJakub Kicinski <kuba@kernel.org>
Thu, 3 Aug 2023 02:11:09 +0000 (19:11 -0700)
Vladimir Oltean says:

====================
Introduce ndo_hwtstamp_get() and ndo_hwtstamp_set()

Based on previous RFCs from Maxim Georgiev:
https://lore.kernel.org/netdev/20230502043150.17097-1-glipus@gmail.com/

this series attempts to introduce new API for the hardware timestamping
control path (SIOCGHWTSTAMP and SIOCSHWTSTAMP handling).

I don't have any board with phylib hardware timestamping, so I would
appreciate testing (especially on lan966x, the most intricate
conversion). I was, however, able to test netdev level timestamping,
because I also have some more unsubmitted conversions in progress:

https://github.com/vladimiroltean/linux/commits/ndo-hwtstamp-v9

I hope that the concerns expressed in the comments of previous series
were addressed, and that Köry Maincent's series:
https://lore.kernel.org/netdev/20230406173308.401924-1-kory.maincent@bootlin.com/
can make progress in parallel with the conversion of the rest of drivers.
====================

Link: https://lore.kernel.org/r/20230801142824.1772134-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
23 files changed:
MAINTAINERS
drivers/net/bonding/bond_main.c
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fec_ptp.c
drivers/net/ethernet/microchip/lan966x/lan966x_main.c
drivers/net/ethernet/microchip/lan966x/lan966x_main.h
drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
drivers/net/ethernet/microchip/sparx5/sparx5_main.h
drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
drivers/net/macvlan.c
drivers/net/phy/Makefile
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
drivers/net/phy/stubs.c [new file with mode: 0644]
include/linux/net_tstamp.h
include/linux/netdevice.h
include/linux/phy.h
include/linux/phylib_stubs.h [new file with mode: 0644]
net/8021q/vlan_dev.c
net/core/dev_ioctl.c
net/ethtool/common.c

index c4f95a9..069e176 100644 (file)
@@ -7752,6 +7752,7 @@ F:        include/linux/mii.h
 F:     include/linux/of_net.h
 F:     include/linux/phy.h
 F:     include/linux/phy_fixed.h
+F:     include/linux/phylib_stubs.h
 F:     include/linux/platform_data/mdio-bcm-unimac.h
 F:     include/linux/platform_data/mdio-gpio.h
 F:     include/trace/events/mdio.h
index 484c9e3..f04d4f2 100644 (file)
@@ -4446,11 +4446,6 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
 {
        struct bonding *bond = netdev_priv(bond_dev);
        struct mii_ioctl_data *mii = NULL;
-       const struct net_device_ops *ops;
-       struct net_device *real_dev;
-       struct hwtstamp_config cfg;
-       struct ifreq ifrr;
-       int res = 0;
 
        netdev_dbg(bond_dev, "bond_eth_ioctl: cmd=%d\n", cmd);
 
@@ -4477,44 +4472,11 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
                }
 
                break;
-       case SIOCSHWTSTAMP:
-               if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
-                       return -EFAULT;
-
-               if (!(cfg.flags & HWTSTAMP_FLAG_BONDED_PHC_INDEX))
-                       return -EOPNOTSUPP;
-
-               fallthrough;
-       case SIOCGHWTSTAMP:
-               real_dev = bond_option_active_slave_get_rcu(bond);
-               if (!real_dev)
-                       return -EOPNOTSUPP;
-
-               strscpy_pad(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
-               ifrr.ifr_ifru = ifr->ifr_ifru;
-
-               ops = real_dev->netdev_ops;
-               if (netif_device_present(real_dev) && ops->ndo_eth_ioctl) {
-                       res = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
-                       if (res)
-                               return res;
-
-                       ifr->ifr_ifru = ifrr.ifr_ifru;
-                       if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
-                               return -EFAULT;
-
-                       /* Set the BOND_PHC_INDEX flag to notify user space */
-                       cfg.flags |= HWTSTAMP_FLAG_BONDED_PHC_INDEX;
-
-                       return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ?
-                               -EFAULT : 0;
-               }
-               fallthrough;
        default:
-               res = -EOPNOTSUPP;
+               return -EOPNOTSUPP;
        }
 
-       return res;
+       return 0;
 }
 
 static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
@@ -5688,6 +5650,67 @@ static u32 bond_mode_bcast_speed(struct slave *slave, u32 speed)
        return speed;
 }
 
+/* Set the BOND_PHC_INDEX flag to notify user space */
+static int bond_set_phc_index_flag(struct kernel_hwtstamp_config *kernel_cfg)
+{
+       struct ifreq *ifr = kernel_cfg->ifr;
+       struct hwtstamp_config cfg;
+
+       if (kernel_cfg->copied_to_user) {
+               /* Lower device has a legacy implementation */
+               if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+                       return -EFAULT;
+
+               cfg.flags |= HWTSTAMP_FLAG_BONDED_PHC_INDEX;
+               if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
+                       return -EFAULT;
+       } else {
+               kernel_cfg->flags |= HWTSTAMP_FLAG_BONDED_PHC_INDEX;
+       }
+
+       return 0;
+}
+
+static int bond_hwtstamp_get(struct net_device *dev,
+                            struct kernel_hwtstamp_config *cfg)
+{
+       struct bonding *bond = netdev_priv(dev);
+       struct net_device *real_dev;
+       int err;
+
+       real_dev = bond_option_active_slave_get_rcu(bond);
+       if (!real_dev)
+               return -EOPNOTSUPP;
+
+       err = generic_hwtstamp_get_lower(real_dev, cfg);
+       if (err)
+               return err;
+
+       return bond_set_phc_index_flag(cfg);
+}
+
+static int bond_hwtstamp_set(struct net_device *dev,
+                            struct kernel_hwtstamp_config *cfg,
+                            struct netlink_ext_ack *extack)
+{
+       struct bonding *bond = netdev_priv(dev);
+       struct net_device *real_dev;
+       int err;
+
+       if (!(cfg->flags & HWTSTAMP_FLAG_BONDED_PHC_INDEX))
+               return -EOPNOTSUPP;
+
+       real_dev = bond_option_active_slave_get_rcu(bond);
+       if (!real_dev)
+               return -EOPNOTSUPP;
+
+       err = generic_hwtstamp_set_lower(real_dev, cfg, extack);
+       if (err)
+               return err;
+
+       return bond_set_phc_index_flag(cfg);
+}
+
 static int bond_ethtool_get_link_ksettings(struct net_device *bond_dev,
                                           struct ethtool_link_ksettings *cmd)
 {
@@ -5836,6 +5859,8 @@ static const struct net_device_ops bond_netdev_ops = {
        .ndo_bpf                = bond_xdp,
        .ndo_xdp_xmit           = bond_xdp_xmit,
        .ndo_xdp_get_xmit_slave = bond_xdp_get_xmit_slave,
+       .ndo_hwtstamp_get       = bond_hwtstamp_get,
+       .ndo_hwtstamp_set       = bond_hwtstamp_set,
 };
 
 static const struct device_type bond_type = {
index 8f1edcc..110f2e9 100644 (file)
@@ -698,9 +698,9 @@ struct fec_enet_private {
 void fec_ptp_init(struct platform_device *pdev, int irq_idx);
 void fec_ptp_stop(struct platform_device *pdev);
 void fec_ptp_start_cyclecounter(struct net_device *ndev);
-void fec_ptp_disable_hwts(struct net_device *ndev);
-int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr);
-int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr);
+int fec_ptp_set(struct net_device *ndev, struct kernel_hwtstamp_config *config,
+               struct netlink_ext_ack *extack);
+void fec_ptp_get(struct net_device *ndev, struct kernel_hwtstamp_config *config);
 
 /****************************************************************************/
 #endif /* FEC_H */
index 14d0dc7..43f14ce 100644 (file)
@@ -3203,33 +3203,6 @@ static const struct ethtool_ops fec_enet_ethtool_ops = {
        .self_test              = net_selftest,
 };
 
-static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-       struct phy_device *phydev = ndev->phydev;
-
-       if (!netif_running(ndev))
-               return -EINVAL;
-
-       if (!phydev)
-               return -ENODEV;
-
-       if (fep->bufdesc_ex) {
-               bool use_fec_hwts = !phy_has_hwtstamp(phydev);
-
-               if (cmd == SIOCSHWTSTAMP) {
-                       if (use_fec_hwts)
-                               return fec_ptp_set(ndev, rq);
-                       fec_ptp_disable_hwts(ndev);
-               } else if (cmd == SIOCGHWTSTAMP) {
-                       if (use_fec_hwts)
-                               return fec_ptp_get(ndev, rq);
-               }
-       }
-
-       return phy_mii_ioctl(phydev, rq, cmd);
-}
-
 static void fec_enet_free_buffers(struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
@@ -3895,6 +3868,37 @@ static int fec_enet_xdp_xmit(struct net_device *dev,
        return sent_frames;
 }
 
+static int fec_hwtstamp_get(struct net_device *ndev,
+                           struct kernel_hwtstamp_config *config)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       if (!netif_running(ndev))
+               return -EINVAL;
+
+       if (!fep->bufdesc_ex)
+               return -EOPNOTSUPP;
+
+       fec_ptp_get(ndev, config);
+
+       return 0;
+}
+
+static int fec_hwtstamp_set(struct net_device *ndev,
+                           struct kernel_hwtstamp_config *config,
+                           struct netlink_ext_ack *extack)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       if (!netif_running(ndev))
+               return -EINVAL;
+
+       if (!fep->bufdesc_ex)
+               return -EOPNOTSUPP;
+
+       return fec_ptp_set(ndev, config, extack);
+}
+
 static const struct net_device_ops fec_netdev_ops = {
        .ndo_open               = fec_enet_open,
        .ndo_stop               = fec_enet_close,
@@ -3904,13 +3908,15 @@ static const struct net_device_ops fec_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_tx_timeout         = fec_timeout,
        .ndo_set_mac_address    = fec_set_mac_address,
-       .ndo_eth_ioctl          = fec_enet_ioctl,
+       .ndo_eth_ioctl          = phy_do_ioctl_running,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = fec_poll_controller,
 #endif
        .ndo_set_features       = fec_set_features,
        .ndo_bpf                = fec_enet_bpf,
        .ndo_xdp_xmit           = fec_enet_xdp_xmit,
+       .ndo_hwtstamp_get       = fec_hwtstamp_get,
+       .ndo_hwtstamp_set       = fec_hwtstamp_set,
 };
 
 static const unsigned short offset_des_active_rxq[] = {
index fc4674c..181d9bf 100644 (file)
@@ -605,28 +605,12 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp,
        }
 }
 
-/**
- * fec_ptp_disable_hwts - disable hardware time stamping
- * @ndev: pointer to net_device
- */
-void fec_ptp_disable_hwts(struct net_device *ndev)
+int fec_ptp_set(struct net_device *ndev, struct kernel_hwtstamp_config *config,
+               struct netlink_ext_ack *extack)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
 
-       fep->hwts_tx_en = 0;
-       fep->hwts_rx_en = 0;
-}
-
-int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr)
-{
-       struct fec_enet_private *fep = netdev_priv(ndev);
-
-       struct hwtstamp_config config;
-
-       if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
-               return -EFAULT;
-
-       switch (config.tx_type) {
+       switch (config->tx_type) {
        case HWTSTAMP_TX_OFF:
                fep->hwts_tx_en = 0;
                break;
@@ -637,33 +621,28 @@ int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr)
                return -ERANGE;
        }
 
-       switch (config.rx_filter) {
+       switch (config->rx_filter) {
        case HWTSTAMP_FILTER_NONE:
                fep->hwts_rx_en = 0;
                break;
 
        default:
                fep->hwts_rx_en = 1;
-               config.rx_filter = HWTSTAMP_FILTER_ALL;
+               config->rx_filter = HWTSTAMP_FILTER_ALL;
                break;
        }
 
-       return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-           -EFAULT : 0;
+       return 0;
 }
 
-int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr)
+void fec_ptp_get(struct net_device *ndev, struct kernel_hwtstamp_config *config)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
-       struct hwtstamp_config config;
-
-       config.flags = 0;
-       config.tx_type = fep->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-       config.rx_filter = (fep->hwts_rx_en ?
-                           HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE);
 
-       return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-               -EFAULT : 0;
+       config->flags = 0;
+       config->tx_type = fep->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       config->rx_filter = (fep->hwts_rx_en ?
+                            HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE);
 }
 
 /*
index 73f2068..4a1acc7 100644 (file)
@@ -450,39 +450,46 @@ static int lan966x_port_get_parent_id(struct net_device *dev,
        return 0;
 }
 
-static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr,
-                             int cmd)
+static int lan966x_port_hwtstamp_get(struct net_device *dev,
+                                    struct kernel_hwtstamp_config *cfg)
+{
+       struct lan966x_port *port = netdev_priv(dev);
+
+       if (!port->lan966x->ptp)
+               return -EOPNOTSUPP;
+
+       lan966x_ptp_hwtstamp_get(port, cfg);
+
+       return 0;
+}
+
+static int lan966x_port_hwtstamp_set(struct net_device *dev,
+                                    struct kernel_hwtstamp_config *cfg,
+                                    struct netlink_ext_ack *extack)
 {
        struct lan966x_port *port = netdev_priv(dev);
        int err;
 
-       if (cmd == SIOCSHWTSTAMP) {
-               err = lan966x_ptp_setup_traps(port, ifr);
-               if (err)
-                       return err;
-       }
+       if (cfg->source != HWTSTAMP_SOURCE_NETDEV &&
+           cfg->source != HWTSTAMP_SOURCE_PHYLIB)
+               return -EOPNOTSUPP;
 
-       if (!phy_has_hwtstamp(dev->phydev) && port->lan966x->ptp) {
-               switch (cmd) {
-               case SIOCSHWTSTAMP:
-                       err = lan966x_ptp_hwtstamp_set(port, ifr);
-                       if (err)
-                               lan966x_ptp_del_traps(port);
+       err = lan966x_ptp_setup_traps(port, cfg);
+       if (err)
+               return 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;
-               case SIOCGHWTSTAMP:
-                       return lan966x_ptp_hwtstamp_get(port, ifr);
                }
        }
 
-       if (!dev->phydev)
-               return -ENODEV;
-
-       err = phy_mii_ioctl(dev->phydev, ifr, cmd);
-       if (err && cmd == SIOCSHWTSTAMP)
-               lan966x_ptp_del_traps(port);
-
-       return err;
+       return 0;
 }
 
 static const struct net_device_ops lan966x_port_netdev_ops = {
@@ -495,10 +502,12 @@ static const struct net_device_ops lan966x_port_netdev_ops = {
        .ndo_get_stats64                = lan966x_stats_get,
        .ndo_set_mac_address            = lan966x_port_set_mac_address,
        .ndo_get_port_parent_id         = lan966x_port_get_parent_id,
-       .ndo_eth_ioctl                  = lan966x_port_ioctl,
+       .ndo_eth_ioctl                  = phy_do_ioctl,
        .ndo_setup_tc                   = lan966x_tc_setup,
        .ndo_bpf                        = lan966x_xdp,
        .ndo_xdp_xmit                   = lan966x_xdp_xmit,
+       .ndo_hwtstamp_get               = lan966x_port_hwtstamp_get,
+       .ndo_hwtstamp_set               = lan966x_port_hwtstamp_set,
 };
 
 bool lan966x_netdevice_check(const struct net_device *dev)
@@ -808,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);
index 27f2728..b538d49 100644 (file)
@@ -298,7 +298,7 @@ struct lan966x_phc {
        struct ptp_clock *clock;
        struct ptp_clock_info info;
        struct ptp_pin_desc pins[LAN966X_PHC_PINS_NUM];
-       struct hwtstamp_config hwtstamp_config;
+       struct kernel_hwtstamp_config hwtstamp_config;
        struct lan966x *lan966x;
        u8 index;
 };
@@ -578,8 +578,11 @@ void lan966x_mdb_restore_entries(struct lan966x *lan966x);
 
 int lan966x_ptp_init(struct lan966x *lan966x);
 void lan966x_ptp_deinit(struct lan966x *lan966x);
-int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr);
-int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr);
+int lan966x_ptp_hwtstamp_set(struct lan966x_port *port,
+                            struct kernel_hwtstamp_config *cfg,
+                            struct netlink_ext_ack *extack);
+void lan966x_ptp_hwtstamp_get(struct lan966x_port *port,
+                             struct kernel_hwtstamp_config *cfg);
 void lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb,
                          u64 src_port, u64 timestamp);
 int lan966x_ptp_txtstamp_request(struct lan966x_port *port,
@@ -590,7 +593,8 @@ irqreturn_t lan966x_ptp_irq_handler(int irq, void *args);
 irqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args);
 u32 lan966x_ptp_get_period_ps(void);
 int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts);
-int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr);
+int lan966x_ptp_setup_traps(struct lan966x_port *port,
+                           struct kernel_hwtstamp_config *cfg);
 int lan966x_ptp_del_traps(struct lan966x_port *port);
 
 int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev);
index 266a21a..60bd0cf 100644 (file)
@@ -248,29 +248,23 @@ int lan966x_ptp_del_traps(struct lan966x_port *port)
        return err;
 }
 
-int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr)
+int lan966x_ptp_setup_traps(struct lan966x_port *port,
+                           struct kernel_hwtstamp_config *cfg)
 {
-       struct hwtstamp_config cfg;
-
-       if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
-               return -EFAULT;
-
-       if (cfg.rx_filter == HWTSTAMP_FILTER_NONE)
+       if (cfg->rx_filter == HWTSTAMP_FILTER_NONE)
                return lan966x_ptp_del_traps(port);
        else
                return lan966x_ptp_add_traps(port);
 }
 
-int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
+int lan966x_ptp_hwtstamp_set(struct lan966x_port *port,
+                            struct kernel_hwtstamp_config *cfg,
+                            struct netlink_ext_ack *extack)
 {
        struct lan966x *lan966x = port->lan966x;
-       struct hwtstamp_config cfg;
        struct lan966x_phc *phc;
 
-       if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
-               return -EFAULT;
-
-       switch (cfg.tx_type) {
+       switch (cfg->tx_type) {
        case HWTSTAMP_TX_ON:
                port->ptp_tx_cmd = IFH_REW_OP_TWO_STEP_PTP;
                break;
@@ -284,7 +278,7 @@ int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
                return -ERANGE;
        }
 
-       switch (cfg.rx_filter) {
+       switch (cfg->rx_filter) {
        case HWTSTAMP_FILTER_NONE:
                port->ptp_rx_cmd = false;
                break;
@@ -303,7 +297,7 @@ int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
        case HWTSTAMP_FILTER_NTP_ALL:
                port->ptp_rx_cmd = true;
-               cfg.rx_filter = HWTSTAMP_FILTER_ALL;
+               cfg->rx_filter = HWTSTAMP_FILTER_ALL;
                break;
        default:
                return -ERANGE;
@@ -312,20 +306,20 @@ int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
        /* Commit back the result & save it */
        mutex_lock(&lan966x->ptp_lock);
        phc = &lan966x->phc[LAN966X_PHC_PORT];
-       memcpy(&phc->hwtstamp_config, &cfg, sizeof(cfg));
+       phc->hwtstamp_config = *cfg;
        mutex_unlock(&lan966x->ptp_lock);
 
-       return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+       return 0;
 }
 
-int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr)
+void lan966x_ptp_hwtstamp_get(struct lan966x_port *port,
+                             struct kernel_hwtstamp_config *cfg)
 {
        struct lan966x *lan966x = port->lan966x;
        struct lan966x_phc *phc;
 
        phc = &lan966x->phc[LAN966X_PHC_PORT];
-       return copy_to_user(ifr->ifr_data, &phc->hwtstamp_config,
-                           sizeof(phc->hwtstamp_config)) ? -EFAULT : 0;
+       *cfg = phc->hwtstamp_config;
 }
 
 static int lan966x_ptp_classify(struct lan966x_port *port, struct sk_buff *skb)
index 62c8546..89a9a7a 100644 (file)
@@ -205,7 +205,7 @@ enum sparx5_core_clockfreq {
 struct sparx5_phc {
        struct ptp_clock *clock;
        struct ptp_clock_info info;
-       struct hwtstamp_config hwtstamp_config;
+       struct kernel_hwtstamp_config hwtstamp_config;
        struct sparx5 *sparx5;
        u8 index;
 };
@@ -388,8 +388,11 @@ void sparx5_unregister_netdevs(struct sparx5 *sparx5);
 /* sparx5_ptp.c */
 int sparx5_ptp_init(struct sparx5 *sparx5);
 void sparx5_ptp_deinit(struct sparx5 *sparx5);
-int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr);
-int sparx5_ptp_hwtstamp_get(struct sparx5_port *port, struct ifreq *ifr);
+int sparx5_ptp_hwtstamp_set(struct sparx5_port *port,
+                           struct kernel_hwtstamp_config *cfg,
+                           struct netlink_ext_ack *extack);
+void sparx5_ptp_hwtstamp_get(struct sparx5_port *port,
+                            struct kernel_hwtstamp_config *cfg);
 void sparx5_ptp_rxtstamp(struct sparx5 *sparx5, struct sk_buff *skb,
                         u64 timestamp);
 int sparx5_ptp_txtstamp_request(struct sparx5_port *port,
index d078156..705a004 100644 (file)
@@ -210,22 +210,31 @@ static int sparx5_get_port_parent_id(struct net_device *dev,
        return 0;
 }
 
-static int sparx5_port_ioctl(struct net_device *dev, struct ifreq *ifr,
-                            int cmd)
+static int sparx5_port_hwtstamp_get(struct net_device *dev,
+                                   struct kernel_hwtstamp_config *cfg)
 {
        struct sparx5_port *sparx5_port = netdev_priv(dev);
        struct sparx5 *sparx5 = sparx5_port->sparx5;
 
-       if (!phy_has_hwtstamp(dev->phydev) && sparx5->ptp) {
-               switch (cmd) {
-               case SIOCSHWTSTAMP:
-                       return sparx5_ptp_hwtstamp_set(sparx5_port, ifr);
-               case SIOCGHWTSTAMP:
-                       return sparx5_ptp_hwtstamp_get(sparx5_port, ifr);
-               }
-       }
+       if (!sparx5->ptp)
+               return -EOPNOTSUPP;
+
+       sparx5_ptp_hwtstamp_get(sparx5_port, cfg);
+
+       return 0;
+}
+
+static int sparx5_port_hwtstamp_set(struct net_device *dev,
+                                   struct kernel_hwtstamp_config *cfg,
+                                   struct netlink_ext_ack *extack)
+{
+       struct sparx5_port *sparx5_port = netdev_priv(dev);
+       struct sparx5 *sparx5 = sparx5_port->sparx5;
+
+       if (!sparx5->ptp)
+               return -EOPNOTSUPP;
 
-       return phy_mii_ioctl(dev->phydev, ifr, cmd);
+       return sparx5_ptp_hwtstamp_set(sparx5_port, cfg, extack);
 }
 
 static const struct net_device_ops sparx5_port_netdev_ops = {
@@ -238,8 +247,10 @@ static const struct net_device_ops sparx5_port_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_get_stats64        = sparx5_get_stats64,
        .ndo_get_port_parent_id = sparx5_get_port_parent_id,
-       .ndo_eth_ioctl          = sparx5_port_ioctl,
+       .ndo_eth_ioctl          = phy_do_ioctl,
        .ndo_setup_tc           = sparx5_port_setup_tc,
+       .ndo_hwtstamp_get       = sparx5_port_hwtstamp_get,
+       .ndo_hwtstamp_set       = sparx5_port_hwtstamp_set,
 };
 
 bool sparx5_netdevice_check(const struct net_device *dev)
index 0edb98c..5a93246 100644 (file)
@@ -74,10 +74,11 @@ static u64 sparx5_ptp_get_nominal_value(struct sparx5 *sparx5)
        return res;
 }
 
-int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
+int sparx5_ptp_hwtstamp_set(struct sparx5_port *port,
+                           struct kernel_hwtstamp_config *cfg,
+                           struct netlink_ext_ack *extack)
 {
        struct sparx5 *sparx5 = port->sparx5;
-       struct hwtstamp_config cfg;
        struct sparx5_phc *phc;
 
        /* For now don't allow to run ptp on ports that are part of a bridge,
@@ -88,10 +89,7 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
        if (test_bit(port->portno, sparx5->bridge_mask))
                return -EINVAL;
 
-       if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
-               return -EFAULT;
-
-       switch (cfg.tx_type) {
+       switch (cfg->tx_type) {
        case HWTSTAMP_TX_ON:
                port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
                break;
@@ -105,7 +103,7 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
                return -ERANGE;
        }
 
-       switch (cfg.rx_filter) {
+       switch (cfg->rx_filter) {
        case HWTSTAMP_FILTER_NONE:
                break;
        case HWTSTAMP_FILTER_ALL:
@@ -122,7 +120,7 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
        case HWTSTAMP_FILTER_NTP_ALL:
-               cfg.rx_filter = HWTSTAMP_FILTER_ALL;
+               cfg->rx_filter = HWTSTAMP_FILTER_ALL;
                break;
        default:
                return -ERANGE;
@@ -131,20 +129,20 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
        /* Commit back the result & save it */
        mutex_lock(&sparx5->ptp_lock);
        phc = &sparx5->phc[SPARX5_PHC_PORT];
-       memcpy(&phc->hwtstamp_config, &cfg, sizeof(cfg));
+       phc->hwtstamp_config = *cfg;
        mutex_unlock(&sparx5->ptp_lock);
 
-       return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+       return 0;
 }
 
-int sparx5_ptp_hwtstamp_get(struct sparx5_port *port, struct ifreq *ifr)
+void sparx5_ptp_hwtstamp_get(struct sparx5_port *port,
+                            struct kernel_hwtstamp_config *cfg)
 {
        struct sparx5 *sparx5 = port->sparx5;
        struct sparx5_phc *phc;
 
        phc = &sparx5->phc[SPARX5_PHC_PORT];
-       return copy_to_user(ifr->ifr_data, &phc->hwtstamp_config,
-                           sizeof(phc->hwtstamp_config)) ? -EFAULT : 0;
+       *cfg = phc->hwtstamp_config;
 }
 
 static void sparx5_ptp_classify(struct sparx5_port *port, struct sk_buff *skb,
index ed90816..02bd201 100644 (file)
@@ -868,31 +868,24 @@ static int macvlan_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
-static int macvlan_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+static int macvlan_hwtstamp_get(struct net_device *dev,
+                               struct kernel_hwtstamp_config *cfg)
 {
        struct net_device *real_dev = macvlan_dev_real_dev(dev);
-       const struct net_device_ops *ops = real_dev->netdev_ops;
-       struct ifreq ifrr;
-       int err = -EOPNOTSUPP;
 
-       strscpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
-       ifrr.ifr_ifru = ifr->ifr_ifru;
+       return generic_hwtstamp_get_lower(real_dev, cfg);
+}
 
-       switch (cmd) {
-       case SIOCSHWTSTAMP:
-               if (!net_eq(dev_net(dev), &init_net))
-                       break;
-               fallthrough;
-       case SIOCGHWTSTAMP:
-               if (netif_device_present(real_dev) && ops->ndo_eth_ioctl)
-                       err = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
-               break;
-       }
+static int macvlan_hwtstamp_set(struct net_device *dev,
+                               struct kernel_hwtstamp_config *cfg,
+                               struct netlink_ext_ack *extack)
+{
+       struct net_device *real_dev = macvlan_dev_real_dev(dev);
 
-       if (!err)
-               ifr->ifr_ifru = ifrr.ifr_ifru;
+       if (!net_eq(dev_net(dev), &init_net))
+               return -EOPNOTSUPP;
 
-       return err;
+       return generic_hwtstamp_set_lower(real_dev, cfg, extack);
 }
 
 /*
@@ -1193,7 +1186,6 @@ static const struct net_device_ops macvlan_netdev_ops = {
        .ndo_stop               = macvlan_stop,
        .ndo_start_xmit         = macvlan_start_xmit,
        .ndo_change_mtu         = macvlan_change_mtu,
-       .ndo_eth_ioctl          = macvlan_eth_ioctl,
        .ndo_fix_features       = macvlan_fix_features,
        .ndo_change_rx_flags    = macvlan_change_rx_flags,
        .ndo_set_mac_address    = macvlan_set_mac_address,
@@ -1212,6 +1204,8 @@ static const struct net_device_ops macvlan_netdev_ops = {
 #endif
        .ndo_get_iflink         = macvlan_dev_get_iflink,
        .ndo_features_check     = passthru_features_check,
+       .ndo_hwtstamp_get       = macvlan_hwtstamp_get,
+       .ndo_hwtstamp_set       = macvlan_hwtstamp_set,
 };
 
 static void macvlan_dev_free(struct net_device *dev)
index 3514278..c945ed9 100644 (file)
@@ -14,6 +14,8 @@ endif
 # dedicated loadable module, so we bundle them all together into libphy.ko
 ifdef CONFIG_PHYLIB
 libphy-y                       += $(mdio-bus-y)
+# the stubs are built-in whenever PHYLIB is built-in or module
+obj-y                          += stubs.o
 else
 obj-$(CONFIG_MDIO_DEVICE)      += mdio-bus.o
 endif
index bdf00b2..8aec8e8 100644 (file)
@@ -456,6 +456,40 @@ int phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd)
 EXPORT_SYMBOL(phy_do_ioctl_running);
 
 /**
+ * __phy_hwtstamp_get - Get hardware timestamping configuration from PHY
+ *
+ * @phydev: the PHY device structure
+ * @config: structure holding the timestamping configuration
+ *
+ * Query the PHY device for its current hardware timestamping configuration.
+ */
+int __phy_hwtstamp_get(struct phy_device *phydev,
+                      struct kernel_hwtstamp_config *config)
+{
+       if (!phydev)
+               return -ENODEV;
+
+       return phy_mii_ioctl(phydev, config->ifr, SIOCGHWTSTAMP);
+}
+
+/**
+ * __phy_hwtstamp_set - Modify PHY hardware timestamping configuration
+ *
+ * @phydev: the PHY device structure
+ * @config: structure holding the timestamping configuration
+ * @extack: netlink extended ack structure, for error reporting
+ */
+int __phy_hwtstamp_set(struct phy_device *phydev,
+                      struct kernel_hwtstamp_config *config,
+                      struct netlink_ext_ack *extack)
+{
+       if (!phydev)
+               return -ENODEV;
+
+       return phy_mii_ioctl(phydev, config->ifr, SIOCSHWTSTAMP);
+}
+
+/**
  * phy_queue_state_machine - Trigger the state machine to run soon
  *
  * @phydev: the phy_device struct
index 61921d4..e19c4fe 100644 (file)
 #include <linux/of.h>
 #include <linux/netdevice.h>
 #include <linux/phy.h>
+#include <linux/phylib_stubs.h>
 #include <linux/phy_led_triggers.h>
 #include <linux/pse-pd/pse.h>
 #include <linux/property.h>
+#include <linux/rtnetlink.h>
 #include <linux/sfp.h>
 #include <linux/skbuff.h>
 #include <linux/slab.h>
@@ -3447,11 +3449,29 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
        .start_cable_test_tdr   = phy_start_cable_test_tdr,
 };
 
+static const struct phylib_stubs __phylib_stubs = {
+       .hwtstamp_get = __phy_hwtstamp_get,
+       .hwtstamp_set = __phy_hwtstamp_set,
+};
+
+static void phylib_register_stubs(void)
+{
+       phylib_stubs = &__phylib_stubs;
+}
+
+static void phylib_unregister_stubs(void)
+{
+       phylib_stubs = NULL;
+}
+
 static int __init phy_init(void)
 {
        int rc;
 
+       rtnl_lock();
        ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops);
+       phylib_register_stubs();
+       rtnl_unlock();
 
        rc = mdio_bus_init();
        if (rc)
@@ -3474,7 +3494,10 @@ err_c45:
 err_mdio_bus:
        mdio_bus_exit();
 err_ethtool_phy_ops:
+       rtnl_lock();
+       phylib_unregister_stubs();
        ethtool_set_ethtool_phy_ops(NULL);
+       rtnl_unlock();
 
        return rc;
 }
@@ -3484,7 +3507,10 @@ static void __exit phy_exit(void)
        phy_driver_unregister(&genphy_c45_driver);
        phy_driver_unregister(&genphy_driver);
        mdio_bus_exit();
+       rtnl_lock();
+       phylib_unregister_stubs();
        ethtool_set_ethtool_phy_ops(NULL);
+       rtnl_unlock();
 }
 
 subsys_initcall(phy_init);
diff --git a/drivers/net/phy/stubs.c b/drivers/net/phy/stubs.c
new file mode 100644 (file)
index 0000000..cfb9f27
--- /dev/null
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Stubs for PHY library functionality called by the core network stack.
+ * These are necessary because CONFIG_PHYLIB can be a module, and built-in
+ * code cannot directly call symbols exported by modules.
+ */
+#include <linux/phylib_stubs.h>
+
+const struct phylib_stubs *phylib_stubs;
+EXPORT_SYMBOL_GPL(phylib_stubs);
index fd67f3c..eb01c37 100644 (file)
@@ -5,12 +5,23 @@
 
 #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
  *
  * @flags: see struct hwtstamp_config
  * @tx_type: see struct hwtstamp_config
  * @rx_filter: see struct hwtstamp_config
+ * @ifr: pointer to ifreq structure from the original ioctl request, to pass to
+ *     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
@@ -20,6 +31,9 @@ struct kernel_hwtstamp_config {
        int flags;
        int tx_type;
        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,
@@ -30,4 +44,20 @@ static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kern
        kernel_cfg->rx_filter = cfg->rx_filter;
 }
 
+static inline void hwtstamp_config_from_kernel(struct hwtstamp_config *cfg,
+                                              const struct kernel_hwtstamp_config *kernel_cfg)
+{
+       cfg->flags = kernel_cfg->flags;
+       cfg->tx_type = kernel_cfg->tx_type;
+       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_ */
index 84c36a7..85d5944 100644 (file)
@@ -57,6 +57,7 @@
 struct netpoll_info;
 struct device;
 struct ethtool_ops;
+struct kernel_hwtstamp_config;
 struct phy_device;
 struct dsa_port;
 struct ip_tunnel_parm;
@@ -1418,6 +1419,16 @@ struct netdev_net_notifier {
  *     Get hardware timestamp based on normal/adjustable time or free running
  *     cycle counter. This function is required if physical clock supports a
  *     free running cycle counter.
+ *
+ * int (*ndo_hwtstamp_get)(struct net_device *dev,
+ *                        struct kernel_hwtstamp_config *kernel_config);
+ *     Get the currently configured hardware timestamping parameters for the
+ *     NIC device.
+ *
+ * int (*ndo_hwtstamp_set)(struct net_device *dev,
+ *                        struct kernel_hwtstamp_config *kernel_config,
+ *                        struct netlink_ext_ack *extack);
+ *     Change the hardware timestamping parameters for NIC device.
  */
 struct net_device_ops {
        int                     (*ndo_init)(struct net_device *dev);
@@ -1652,6 +1663,11 @@ struct net_device_ops {
        ktime_t                 (*ndo_get_tstamp)(struct net_device *dev,
                                                  const struct skb_shared_hwtstamps *hwtstamps,
                                                  bool cycles);
+       int                     (*ndo_hwtstamp_get)(struct net_device *dev,
+                                                   struct kernel_hwtstamp_config *kernel_config);
+       int                     (*ndo_hwtstamp_set)(struct net_device *dev,
+                                                   struct kernel_hwtstamp_config *kernel_config,
+                                                   struct netlink_ext_ack *extack);
 };
 
 struct xdp_metadata_ops {
@@ -1708,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,
@@ -1743,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
@@ -3934,6 +3954,11 @@ int put_user_ifreq(struct ifreq *ifr, void __user *arg);
 int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
                void __user *data, bool *need_copyout);
 int dev_ifconf(struct net *net, struct ifconf __user *ifc);
+int generic_hwtstamp_get_lower(struct net_device *dev,
+                              struct kernel_hwtstamp_config *kernel_cfg);
+int generic_hwtstamp_set_lower(struct net_device *dev,
+                              struct kernel_hwtstamp_config *kernel_cfg,
+                              struct netlink_ext_ack *extack);
 int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *userdata);
 unsigned int dev_get_flags(const struct net_device *);
 int __dev_change_flags(struct net_device *dev, unsigned int flags,
index b254848..ba08b0e 100644 (file)
@@ -298,6 +298,7 @@ static inline const char *phy_modes(phy_interface_t interface)
 #define MII_BUS_ID_SIZE        61
 
 struct device;
+struct kernel_hwtstamp_config;
 struct phylink;
 struct sfp_bus;
 struct sfp_upstream_ops;
@@ -1955,6 +1956,12 @@ int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
 int phy_ethtool_get_plca_status(struct phy_device *phydev,
                                struct phy_plca_status *plca_st);
 
+int __phy_hwtstamp_get(struct phy_device *phydev,
+                      struct kernel_hwtstamp_config *config);
+int __phy_hwtstamp_set(struct phy_device *phydev,
+                      struct kernel_hwtstamp_config *config,
+                      struct netlink_ext_ack *extack);
+
 static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
 {
        struct phy_package_shared *shared = phydev->shared;
diff --git a/include/linux/phylib_stubs.h b/include/linux/phylib_stubs.h
new file mode 100644 (file)
index 0000000..1279f48
--- /dev/null
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Stubs for the Network PHY library
+ */
+
+#include <linux/rtnetlink.h>
+
+struct kernel_hwtstamp_config;
+struct netlink_ext_ack;
+struct phy_device;
+
+#if IS_ENABLED(CONFIG_PHYLIB)
+
+extern const struct phylib_stubs *phylib_stubs;
+
+struct phylib_stubs {
+       int (*hwtstamp_get)(struct phy_device *phydev,
+                           struct kernel_hwtstamp_config *config);
+       int (*hwtstamp_set)(struct phy_device *phydev,
+                           struct kernel_hwtstamp_config *config,
+                           struct netlink_ext_ack *extack);
+};
+
+static inline int phy_hwtstamp_get(struct phy_device *phydev,
+                                  struct kernel_hwtstamp_config *config)
+{
+       /* phylib_register_stubs() and phylib_unregister_stubs()
+        * also run under rtnl_lock().
+        */
+       ASSERT_RTNL();
+
+       if (!phylib_stubs)
+               return -EOPNOTSUPP;
+
+       return phylib_stubs->hwtstamp_get(phydev, config);
+}
+
+static inline int phy_hwtstamp_set(struct phy_device *phydev,
+                                  struct kernel_hwtstamp_config *config,
+                                  struct netlink_ext_ack *extack)
+{
+       /* phylib_register_stubs() and phylib_unregister_stubs()
+        * also run under rtnl_lock().
+        */
+       ASSERT_RTNL();
+
+       if (!phylib_stubs)
+               return -EOPNOTSUPP;
+
+       return phylib_stubs->hwtstamp_set(phydev, config, extack);
+}
+
+#else
+
+static inline int phy_hwtstamp_get(struct phy_device *phydev,
+                                  struct kernel_hwtstamp_config *config)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int phy_hwtstamp_set(struct phy_device *phydev,
+                                  struct kernel_hwtstamp_config *config,
+                                  struct netlink_ext_ack *extack)
+{
+       return -EOPNOTSUPP;
+}
+
+#endif
index b90781b..2a7f1b1 100644 (file)
@@ -354,6 +354,26 @@ out:
        return 0;
 }
 
+static int vlan_hwtstamp_get(struct net_device *dev,
+                            struct kernel_hwtstamp_config *cfg)
+{
+       struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
+
+       return generic_hwtstamp_get_lower(real_dev, cfg);
+}
+
+static int vlan_hwtstamp_set(struct net_device *dev,
+                            struct kernel_hwtstamp_config *cfg,
+                            struct netlink_ext_ack *extack)
+{
+       struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
+
+       if (!net_eq(dev_net(dev), dev_net(real_dev)))
+               return -EOPNOTSUPP;
+
+       return generic_hwtstamp_set_lower(real_dev, cfg, extack);
+}
+
 static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
@@ -365,14 +385,9 @@ static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        ifrr.ifr_ifru = ifr->ifr_ifru;
 
        switch (cmd) {
-       case SIOCSHWTSTAMP:
-               if (!net_eq(dev_net(dev), dev_net(real_dev)))
-                       break;
-               fallthrough;
        case SIOCGMIIPHY:
        case SIOCGMIIREG:
        case SIOCSMIIREG:
-       case SIOCGHWTSTAMP:
                if (netif_device_present(real_dev) && ops->ndo_eth_ioctl)
                        err = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
                break;
@@ -1081,6 +1096,8 @@ static const struct net_device_ops vlan_netdev_ops = {
        .ndo_fix_features       = vlan_dev_fix_features,
        .ndo_get_iflink         = vlan_dev_get_iflink,
        .ndo_fill_forward_path  = vlan_dev_fill_forward_path,
+       .ndo_hwtstamp_get       = vlan_hwtstamp_get,
+       .ndo_hwtstamp_set       = vlan_hwtstamp_set,
 };
 
 static void vlan_dev_free(struct net_device *dev)
index 3730945..72e0770 100644 (file)
@@ -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,14 +253,118 @@ 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)
 {
-       return dev_eth_ioctl(dev, ifr, SIOCGHWTSTAMP);
+       const struct net_device_ops *ops = dev->netdev_ops;
+       struct kernel_hwtstamp_config kernel_cfg = {};
+       struct hwtstamp_config cfg;
+       int err;
+
+       if (!ops->ndo_hwtstamp_get)
+               return dev_eth_ioctl(dev, ifr, SIOCGHWTSTAMP); /* legacy */
+
+       if (!netif_device_present(dev))
+               return -ENODEV;
+
+       kernel_cfg.ifr = ifr;
+       err = dev_get_hwtstamp_phylib(dev, &kernel_cfg);
+       if (err)
+               return err;
+
+       /* If the request was resolved through an unconverted driver, omit
+        * the copy_to_user(), since the implementation has already done that
+        */
+       if (!kernel_cfg.copied_to_user) {
+               hwtstamp_config_from_kernel(&cfg, &kernel_cfg);
+
+               if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
+                       return -EFAULT;
+       }
+
+       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)
 {
-       struct kernel_hwtstamp_config kernel_cfg;
+       const struct net_device_ops *ops = dev->netdev_ops;
+       struct kernel_hwtstamp_config kernel_cfg = {};
        struct netlink_ext_ack extack = {};
        struct hwtstamp_config cfg;
        int err;
@@ -268,6 +373,7 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
                return -EFAULT;
 
        hwtstamp_config_to_kernel(&kernel_cfg, &cfg);
+       kernel_cfg.ifr = ifr;
 
        err = net_hwtstamp_validate(&kernel_cfg);
        if (err)
@@ -280,8 +386,80 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
                return err;
        }
 
-       return dev_eth_ioctl(dev, ifr, SIOCSHWTSTAMP);
+       if (!ops->ndo_hwtstamp_set)
+               return dev_eth_ioctl(dev, ifr, SIOCSHWTSTAMP); /* legacy */
+
+       if (!netif_device_present(dev))
+               return -ENODEV;
+
+       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
+        */
+       if (!kernel_cfg.copied_to_user) {
+               hwtstamp_config_from_kernel(&cfg, &kernel_cfg);
+
+               if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int generic_hwtstamp_ioctl_lower(struct net_device *dev, int cmd,
+                                       struct kernel_hwtstamp_config *kernel_cfg)
+{
+       struct ifreq ifrr;
+       int err;
+
+       strscpy_pad(ifrr.ifr_name, dev->name, IFNAMSIZ);
+       ifrr.ifr_ifru = kernel_cfg->ifr->ifr_ifru;
+
+       err = dev_eth_ioctl(dev, &ifrr, cmd);
+       if (err)
+               return err;
+
+       kernel_cfg->ifr->ifr_ifru = ifrr.ifr_ifru;
+       kernel_cfg->copied_to_user = true;
+
+       return 0;
+}
+
+int generic_hwtstamp_get_lower(struct net_device *dev,
+                              struct kernel_hwtstamp_config *kernel_cfg)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+
+       if (!netif_device_present(dev))
+               return -ENODEV;
+
+       if (ops->ndo_hwtstamp_get)
+               return dev_get_hwtstamp_phylib(dev, kernel_cfg);
+
+       /* Legacy path: unconverted lower driver */
+       return generic_hwtstamp_ioctl_lower(dev, SIOCGHWTSTAMP, kernel_cfg);
+}
+EXPORT_SYMBOL(generic_hwtstamp_get_lower);
+
+int generic_hwtstamp_set_lower(struct net_device *dev,
+                              struct kernel_hwtstamp_config *kernel_cfg,
+                              struct netlink_ext_ack *extack)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+
+       if (!netif_device_present(dev))
+               return -ENODEV;
+
+       if (ops->ndo_hwtstamp_set)
+               return dev_set_hwtstamp_phylib(dev, kernel_cfg, extack);
+
+       /* Legacy path: unconverted lower driver */
+       return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);
 }
+EXPORT_SYMBOL(generic_hwtstamp_set_lower);
 
 static int dev_siocbond(struct net_device *dev,
                        struct ifreq *ifr, unsigned int cmd)
index 5fb1905..f5598c5 100644 (file)
@@ -665,9 +665,8 @@ const struct ethtool_phy_ops *ethtool_phy_ops;
 
 void ethtool_set_ethtool_phy_ops(const struct ethtool_phy_ops *ops)
 {
-       rtnl_lock();
+       ASSERT_RTNL();
        ethtool_phy_ops = ops;
-       rtnl_unlock();
 }
 EXPORT_SYMBOL_GPL(ethtool_set_ethtool_phy_ops);