Skip to content

Commit

Permalink
net: Let the active time stamping layer be selectable.
Browse files Browse the repository at this point in the history
Make the sysfs knob writable, and add checks in the ioctl and time
stamping paths to respect the currently selected time stamping layer.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
  • Loading branch information
richardcochran authored and intel-lab-lkp committed Mar 3, 2023
1 parent 90d54e1 commit 00a0656
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 12 deletions.
5 changes: 4 additions & 1 deletion Documentation/ABI/testing/sysfs-class-net-timestamping
Expand Up @@ -11,7 +11,10 @@ What: /sys/class/net/<iface>/current_timestamping_provider
Date: January 2022
Contact: Richard Cochran <richardcochran@gmail.com>
Description:
Show the current SO_TIMESTAMPING provider.
Shows or sets the current SO_TIMESTAMPING provider.
When changing the value, some packets in the kernel
networking stack may still be delivered with time
stamps from the previous provider.
The possible values are:
- "mac" The MAC provides time stamping.
- "phy" The PHY or MII device provides time stamping.
6 changes: 6 additions & 0 deletions drivers/net/phy/phy_device.c
Expand Up @@ -1451,6 +1451,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,

phydev->phy_link_change = phy_link_change;
if (dev) {
if (phy_has_hwtstamp(phydev))
dev->selected_timestamping_layer = PHY_TIMESTAMPING;
else
dev->selected_timestamping_layer = MAC_TIMESTAMPING;

phydev->attached_dev = dev;
dev->phydev = phydev;

Expand Down Expand Up @@ -1774,6 +1779,7 @@ void phy_detach(struct phy_device *phydev)

phy_suspend(phydev);
if (dev) {
dev->selected_timestamping_layer = MAC_TIMESTAMPING;
phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL;
}
Expand Down
10 changes: 10 additions & 0 deletions include/linux/netdevice.h
Expand Up @@ -1741,6 +1741,11 @@ enum netdev_ml_priv_type {
ML_PRIV_CAN,
};

enum timestamping_layer {
MAC_TIMESTAMPING,
PHY_TIMESTAMPING,
};

/**
* struct net_device - The DEVICE structure.
*
Expand Down Expand Up @@ -1980,6 +1985,9 @@ enum netdev_ml_priv_type {
*
* @threaded: napi threaded mode is enabled
*
* @selected_timestamping_layer: Tracks whether the MAC or the PHY
* performs packet time stamping.
*
* @net_notifier_list: List of per-net netdev notifier block
* that follow this device when it is moved
* to another network namespace.
Expand Down Expand Up @@ -2343,6 +2351,8 @@ struct net_device {
unsigned wol_enabled:1;
unsigned threaded:1;

enum timestamping_layer selected_timestamping_layer;

struct list_head net_notifier_list;

#if IS_ENABLED(CONFIG_MACSEC)
Expand Down
44 changes: 41 additions & 3 deletions net/core/dev_ioctl.c
Expand Up @@ -262,6 +262,43 @@ static int dev_eth_ioctl(struct net_device *dev,
return err;
}

static int dev_hwtstamp_ioctl(struct net_device *dev,
struct ifreq *ifr, unsigned int cmd)
{
const struct net_device_ops *ops = dev->netdev_ops;
int err;

err = dsa_ndo_eth_ioctl(dev, ifr, cmd);
if (err == 0 || err != -EOPNOTSUPP)
return err;

if (!netif_device_present(dev))
return -ENODEV;

switch (dev->selected_timestamping_layer) {

case MAC_TIMESTAMPING:
if (ops->ndo_do_ioctl == phy_do_ioctl) {
/* Some drivers set .ndo_do_ioctl to phy_do_ioctl. */
err = -EOPNOTSUPP;
} else {
err = ops->ndo_eth_ioctl(dev, ifr, cmd);
}
break;

case PHY_TIMESTAMPING:
if (phy_has_hwtstamp(dev->phydev)) {
err = phy_mii_ioctl(dev->phydev, ifr, cmd);
} else {
err = -ENODEV;
WARN_ON(1);
}
break;
}

return err;
}

static int dev_siocbond(struct net_device *dev,
struct ifreq *ifr, unsigned int cmd)
{
Expand Down Expand Up @@ -397,6 +434,9 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
return err;
fallthrough;

case SIOCGHWTSTAMP:
return dev_hwtstamp_ioctl(dev, ifr, cmd);

/*
* Unknown or private ioctl
*/
Expand All @@ -407,9 +447,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,

if (cmd == SIOCGMIIPHY ||
cmd == SIOCGMIIREG ||
cmd == SIOCSMIIREG ||
cmd == SIOCSHWTSTAMP ||
cmd == SIOCGHWTSTAMP) {
cmd == SIOCSMIIREG) {
err = dev_eth_ioctl(dev, ifr, cmd);
} else if (cmd == SIOCBONDENSLAVE ||
cmd == SIOCBONDRELEASE ||
Expand Down
50 changes: 46 additions & 4 deletions net/core/net-sysfs.c
Expand Up @@ -666,17 +666,59 @@ static ssize_t current_timestamping_provider_show(struct device *dev,
if (!rtnl_trylock())
return restart_syscall();

if (phy_has_tsinfo(phydev)) {
ret = sprintf(buf, "%s\n", "phy");
} else {
switch (netdev->selected_timestamping_layer) {
case MAC_TIMESTAMPING:
ret = sprintf(buf, "%s\n", "mac");
break;
case PHY_TIMESTAMPING:
ret = sprintf(buf, "%s\n", "phy");
break;
}

rtnl_unlock();

return ret;
}
static DEVICE_ATTR_RO(current_timestamping_provider);

static ssize_t current_timestamping_provider_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct net_device *netdev = to_net_dev(dev);
struct net *net = dev_net(netdev);
enum timestamping_layer flavor;

if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;

if (sysfs_streq(buf, "mac"))
flavor = MAC_TIMESTAMPING;
else if (sysfs_streq(buf, "phy"))
flavor = PHY_TIMESTAMPING;
else
return -EINVAL;

if (!rtnl_trylock())
return restart_syscall();

if (!dev_isalive(netdev))
goto out;

if (netdev->selected_timestamping_layer != flavor) {
const struct net_device_ops *ops = netdev->netdev_ops;
struct ifreq ifr = {0};

/* Disable time stamping in the current layer. */
if (netif_device_present(netdev) && ops->ndo_eth_ioctl)
ops->ndo_eth_ioctl(netdev, &ifr, SIOCSHWTSTAMP);

netdev->selected_timestamping_layer = flavor;
}
out:
rtnl_unlock();
return len;
}
static DEVICE_ATTR_RW(current_timestamping_provider);

static struct attribute *net_class_attrs[] __ro_after_init = {
&dev_attr_netdev_group.attr,
Expand Down
6 changes: 6 additions & 0 deletions net/core/timestamping.c
Expand Up @@ -28,6 +28,9 @@ void skb_clone_tx_timestamp(struct sk_buff *skb)
if (!skb->sk)
return;

if (skb->dev->selected_timestamping_layer != PHY_TIMESTAMPING)
return;

type = classify(skb);
if (type == PTP_CLASS_NONE)
return;
Expand All @@ -50,6 +53,9 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb)
if (!skb->dev || !skb->dev->phydev || !skb->dev->phydev->mii_ts)
return false;

if (skb->dev->selected_timestamping_layer != PHY_TIMESTAMPING)
return false;

if (skb_headroom(skb) < ETH_HLEN)
return false;

Expand Down
18 changes: 14 additions & 4 deletions net/ethtool/common.c
Expand Up @@ -629,10 +629,20 @@ int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
memset(info, 0, sizeof(*info));
info->cmd = ETHTOOL_GET_TS_INFO;

if (phy_has_tsinfo(phydev))
return phy_ts_info(phydev, info);
if (ops->get_ts_info)
return ops->get_ts_info(dev, info);
switch (dev->selected_timestamping_layer) {

case MAC_TIMESTAMPING:
if (ops->get_ts_info)
return ops->get_ts_info(dev, info);
break;

case PHY_TIMESTAMPING:
if (phy_has_tsinfo(phydev)) {
return phy_ts_info(phydev, info);
}
WARN_ON(1);
return -ENODEV;
}

info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
Expand Down

0 comments on commit 00a0656

Please sign in to comment.