]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
net: Add the possibility to support a selected hwtstamp in netdevice
authorKory Maincent <kory.maincent@bootlin.com>
Thu, 12 Dec 2024 17:06:43 +0000 (18:06 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 16 Dec 2024 12:51:40 +0000 (12:51 +0000)
Introduce the description of a hwtstamp provider, mainly defined with a
the hwtstamp source and the phydev pointer.

Add a hwtstamp provider description within the netdev structure to
allow saving the hwtstamp we want to use. This prepares for future
support of an ethtool netlink command to select the desired hwtstamp
provider. By default, the old API that does not support hwtstamp
selectability is used, meaning the hwtstamp provider pointer is unset.

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/phy_device.c
include/linux/net_tstamp.h
include/linux/netdevice.h
include/uapi/linux/net_tstamp.h
net/core/dev_ioctl.c
net/core/timestamping.c

index b26bb33cd1d4827e266b846f8ce7af0f94e899bd..1a908af4175b2c6a0dd856a531359cf751839a36 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/phy_link_topology.h>
 #include <linux/pse-pd/pse.h>
 #include <linux/property.h>
+#include <linux/ptp_clock_kernel.h>
 #include <linux/rtnetlink.h>
 #include <linux/sfp.h>
 #include <linux/skbuff.h>
@@ -1998,6 +1999,15 @@ void phy_detach(struct phy_device *phydev)
 
        phy_suspend(phydev);
        if (dev) {
+               struct hwtstamp_provider *hwprov;
+
+               hwprov = rtnl_dereference(dev->hwprov);
+               /* Disable timestamp if it is the one selected */
+               if (hwprov && hwprov->phydev == phydev) {
+                       rcu_assign_pointer(dev->hwprov, NULL);
+                       kfree_rcu(hwprov, rcu_head);
+               }
+
                phydev->attached_dev->phydev = NULL;
                phydev->attached_dev = NULL;
                phy_link_topo_del_phy(dev, phydev);
index 662074b08c94c0fe0eca352ba9cdc7ee1c09d132..ff0758e88ea1008efe533cde003b12719bf4fcd3 100644 (file)
@@ -19,6 +19,33 @@ enum hwtstamp_source {
        HWTSTAMP_SOURCE_PHYLIB,
 };
 
+/**
+ * struct hwtstamp_provider_desc - hwtstamp provider description
+ *
+ * @index: index of the hwtstamp provider.
+ * @qualifier: hwtstamp provider qualifier.
+ */
+struct hwtstamp_provider_desc {
+       int index;
+       enum hwtstamp_provider_qualifier qualifier;
+};
+
+/**
+ * struct hwtstamp_provider - hwtstamp provider object
+ *
+ * @rcu_head: RCU callback used to free the struct.
+ * @source: source of the hwtstamp provider.
+ * @phydev: pointer of the phydev source in case a PTP coming from phylib
+ * @desc: hwtstamp provider description.
+ */
+
+struct hwtstamp_provider {
+       struct rcu_head rcu_head;
+       enum hwtstamp_source source;
+       struct phy_device *phydev;
+       struct hwtstamp_provider_desc desc;
+};
+
 /**
  * struct kernel_hwtstamp_config - Kernel copy of struct hwtstamp_config
  *
@@ -31,6 +58,7 @@ enum hwtstamp_source {
  *     copied the ioctl request back to user space
  * @source: indication whether timestamps should come from the netdev or from
  *     an attached phylib PHY
+ * @qualifier: qualifier of the hwtstamp provider
  *
  * Prefer using this structure for in-kernel processing of hardware
  * timestamping configuration, over the inextensible struct hwtstamp_config
@@ -43,6 +71,7 @@ struct kernel_hwtstamp_config {
        struct ifreq *ifr;
        bool copied_to_user;
        enum hwtstamp_source source;
+       enum hwtstamp_provider_qualifier qualifier;
 };
 
 static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kernel_cfg,
index d917949bba03fc8f60e188546e886aebddf1499a..2593019ad5b1614f3b8c037afb4ba4fa740c7d51 100644 (file)
@@ -82,6 +82,7 @@ struct xdp_metadata_ops;
 struct xdp_md;
 struct ethtool_netdev_state;
 struct phy_link_topology;
+struct hwtstamp_provider;
 
 typedef u32 xdp_features_t;
 
@@ -2045,6 +2046,7 @@ enum netdev_reg_state {
  *
  *     @neighbours:    List heads pointing to this device's neighbours'
  *                     dev_list, one per address-family.
+ *     @hwprov: Tracks which PTP performs hardware packet time stamping.
  *
  *     FIXME: cleanup struct net_device such that network protocol info
  *     moves out.
@@ -2457,6 +2459,8 @@ struct net_device {
 
        struct hlist_head neighbours[NEIGH_NR_TABLES];
 
+       struct hwtstamp_provider __rcu  *hwprov;
+
        u8                      priv[] ____cacheline_aligned
                                       __counted_by(priv_len);
 } ____cacheline_aligned;
index 858339d1c1c4c1967c0ec76390719dec61e04a3c..55b0ab51096c153c6e1f81d0b0c231dddfa13494 100644 (file)
 #include <linux/types.h>
 #include <linux/socket.h>   /* for SO_TIMESTAMPING */
 
+/*
+ * Possible type of hwtstamp provider. Mainly "precise" the default one
+ * is for IEEE 1588 quality and "approx" is for NICs DMA point.
+ */
+enum hwtstamp_provider_qualifier {
+       HWTSTAMP_PROVIDER_QUALIFIER_PRECISE,
+       HWTSTAMP_PROVIDER_QUALIFIER_APPROX,
+
+       HWTSTAMP_PROVIDER_QUALIFIER_CNT,
+};
+
 /* SO_TIMESTAMPING flags */
 enum {
        SOF_TIMESTAMPING_TX_HARDWARE = (1<<0),
index 1f09930fca26d6f222622125ed023ef94a98bffa..087a57b7e4fab1b1d02fa07b99c69d4c59e2ad67 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/net_tstamp.h>
 #include <linux/phylib_stubs.h>
+#include <linux/ptp_clock_kernel.h>
 #include <linux/wireless.h>
 #include <linux/if_bridge.h>
 #include <net/dsa_stubs.h>
@@ -269,6 +270,21 @@ static int dev_eth_ioctl(struct net_device *dev,
 int dev_get_hwtstamp_phylib(struct net_device *dev,
                            struct kernel_hwtstamp_config *cfg)
 {
+       struct hwtstamp_provider *hwprov;
+
+       hwprov = rtnl_dereference(dev->hwprov);
+       if (hwprov) {
+               cfg->qualifier = hwprov->desc.qualifier;
+               if (hwprov->source == HWTSTAMP_SOURCE_PHYLIB &&
+                   hwprov->phydev)
+                       return phy_hwtstamp_get(hwprov->phydev, cfg);
+
+               if (hwprov->source == HWTSTAMP_SOURCE_NETDEV)
+                       return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg);
+
+               return -EOPNOTSUPP;
+       }
+
        if (phy_is_default_hwtstamp(dev->phydev))
                return phy_hwtstamp_get(dev->phydev, cfg);
 
@@ -324,11 +340,32 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,
                            struct netlink_ext_ack *extack)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
-       bool phy_ts = phy_is_default_hwtstamp(dev->phydev);
        struct kernel_hwtstamp_config old_cfg = {};
+       struct hwtstamp_provider *hwprov;
+       struct phy_device *phydev;
        bool changed = false;
+       bool phy_ts;
        int err;
 
+       hwprov = rtnl_dereference(dev->hwprov);
+       if (hwprov) {
+               if (hwprov->source == HWTSTAMP_SOURCE_PHYLIB &&
+                   hwprov->phydev) {
+                       phy_ts = true;
+                       phydev = hwprov->phydev;
+               } else if (hwprov->source == HWTSTAMP_SOURCE_NETDEV) {
+                       phy_ts = false;
+               } else {
+                       return -EOPNOTSUPP;
+               }
+
+               cfg->qualifier = hwprov->desc.qualifier;
+       } else {
+               phy_ts = phy_is_default_hwtstamp(dev->phydev);
+               if (phy_ts)
+                       phydev = dev->phydev;
+       }
+
        cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV;
 
        if (phy_ts && dev->see_all_hwtstamp_requests) {
@@ -350,7 +387,7 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,
                changed = kernel_hwtstamp_config_changed(&old_cfg, cfg);
 
        if (phy_ts) {
-               err = phy_hwtstamp_set(dev->phydev, cfg, extack);
+               err = phy_hwtstamp_set(phydev, cfg, extack);
                if (err) {
                        if (changed)
                                ops->ndo_hwtstamp_set(dev, &old_cfg, NULL);
index 3717fb152ecc4f8b81aeb6252b9f4d8a2242da69..a50a7ef49ae894bfd23462f2a9eb3c762483af26 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/ptp_classify.h>
 #include <linux/skbuff.h>
 #include <linux/export.h>
+#include <linux/ptp_clock_kernel.h>
 
 static unsigned int classify(const struct sk_buff *skb)
 {
@@ -21,19 +22,39 @@ static unsigned int classify(const struct sk_buff *skb)
 
 void skb_clone_tx_timestamp(struct sk_buff *skb)
 {
+       struct hwtstamp_provider *hwprov;
        struct mii_timestamper *mii_ts;
+       struct phy_device *phydev;
        struct sk_buff *clone;
        unsigned int type;
 
-       if (!skb->sk || !skb->dev ||
-           !phy_is_default_hwtstamp(skb->dev->phydev))
+       if (!skb->sk || !skb->dev)
                return;
 
+       rcu_read_lock();
+       hwprov = rcu_dereference(skb->dev->hwprov);
+       if (hwprov) {
+               if (hwprov->source != HWTSTAMP_SOURCE_PHYLIB ||
+                   !hwprov->phydev) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               phydev = hwprov->phydev;
+       } else {
+               phydev = skb->dev->phydev;
+               if (!phy_is_default_hwtstamp(phydev)) {
+                       rcu_read_unlock();
+                       return;
+               }
+       }
+       rcu_read_unlock();
+
        type = classify(skb);
        if (type == PTP_CLASS_NONE)
                return;
 
-       mii_ts = skb->dev->phydev->mii_ts;
+       mii_ts = phydev->mii_ts;
        if (likely(mii_ts->txtstamp)) {
                clone = skb_clone_sk(skb);
                if (!clone)
@@ -45,12 +66,33 @@ EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
 
 bool skb_defer_rx_timestamp(struct sk_buff *skb)
 {
+       struct hwtstamp_provider *hwprov;
        struct mii_timestamper *mii_ts;
+       struct phy_device *phydev;
        unsigned int type;
 
-       if (!skb->dev || !phy_is_default_hwtstamp(skb->dev->phydev))
+       if (!skb->dev)
                return false;
 
+       rcu_read_lock();
+       hwprov = rcu_dereference(skb->dev->hwprov);
+       if (hwprov) {
+               if (hwprov->source != HWTSTAMP_SOURCE_PHYLIB ||
+                   !hwprov->phydev) {
+                       rcu_read_unlock();
+                       return false;
+               }
+
+               phydev = hwprov->phydev;
+       } else {
+               phydev = skb->dev->phydev;
+               if (!phy_is_default_hwtstamp(phydev)) {
+                       rcu_read_unlock();
+                       return false;
+               }
+       }
+       rcu_read_unlock();
+
        if (skb_headroom(skb) < ETH_HLEN)
                return false;
 
@@ -63,7 +105,7 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb)
        if (type == PTP_CLASS_NONE)
                return false;
 
-       mii_ts = skb->dev->phydev->mii_ts;
+       mii_ts = phydev->mii_ts;
        if (likely(mii_ts->rxtstamp))
                return mii_ts->rxtstamp(mii_ts, skb, type);