Commit 858e5b06 authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa_master_ioctl-notifier'

Vladimir Oltean says:

====================
net: Convert dsa_master_ioctl() to netdev notifier

This is preparatory work in order for Maxim Georgiev to be able to start
the API conversion process of hardware timestamping from ndo_eth_ioctl()
to ndo_hwtstamp_set():
https://lore.kernel.org/netdev/20230331045619.40256-1-glipus@gmail.com/

In turn, Maxim Georgiev's work is a preparation so that Köry Maincent is
able to make the active hardware timestamping layer selectable by user
space.
https://lore.kernel.org/netdev/20230308135936.761794-1-kory.maincent@bootlin.com/

So, quite some dependency chain.

Before this patch set, DSA prevented the conversion of any networking
driver from the ndo_eth_ioctl() API to the ndo_hwtstamp_set() API,
because it wanted to validate the hwtstamping settings on the DSA
master, and it was only coded up to do this using the old API.

After this patch set, a new netdev notifier exists, which does not
depend on anything that would constitute the "soon-to-be-legacy" API,
but rather, it uses a newly introduced struct kernel_hwtstamp_config,
and it doesn't issue any ioctl at all, being thus compatible both with
ndo_eth_ioctl(), and with the not-yet-introduced, but now possible,
ndo_hwtstamp_set().
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 51aaa682 88c0a6b5
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_NET_TIMESTAMPING_H_
#define _LINUX_NET_TIMESTAMPING_H_
#include <uapi/linux/net_tstamp.h>
/**
* 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
*
* Prefer using this structure for in-kernel processing of hardware
* timestamping configuration, over the inextensible struct hwtstamp_config
* exposed to the %SIOCGHWTSTAMP and %SIOCSHWTSTAMP ioctl UAPI.
*/
struct kernel_hwtstamp_config {
int flags;
int tx_type;
int rx_filter;
};
static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kernel_cfg,
const struct hwtstamp_config *cfg)
{
kernel_cfg->flags = cfg->flags;
kernel_cfg->tx_type = cfg->tx_type;
kernel_cfg->rx_filter = cfg->rx_filter;
}
#endif /* _LINUX_NET_TIMESTAMPING_H_ */
...@@ -2878,6 +2878,7 @@ enum netdev_cmd { ...@@ -2878,6 +2878,7 @@ enum netdev_cmd {
NETDEV_OFFLOAD_XSTATS_REPORT_USED, NETDEV_OFFLOAD_XSTATS_REPORT_USED,
NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, NETDEV_OFFLOAD_XSTATS_REPORT_DELTA,
NETDEV_XDP_FEAT_CHANGE, NETDEV_XDP_FEAT_CHANGE,
NETDEV_PRE_CHANGE_HWTSTAMP,
}; };
const char *netdev_cmd_to_name(enum netdev_cmd cmd); const char *netdev_cmd_to_name(enum netdev_cmd cmd);
...@@ -2928,6 +2929,11 @@ struct netdev_notifier_pre_changeaddr_info { ...@@ -2928,6 +2929,11 @@ struct netdev_notifier_pre_changeaddr_info {
const unsigned char *dev_addr; const unsigned char *dev_addr;
}; };
struct netdev_notifier_hwtstamp_info {
struct netdev_notifier_info info; /* must be first */
struct kernel_hwtstamp_config *config;
};
enum netdev_offload_xstats_type { enum netdev_offload_xstats_type {
NETDEV_OFFLOAD_XSTATS_TYPE_L3 = 1, NETDEV_OFFLOAD_XSTATS_TYPE_L3 = 1,
}; };
...@@ -2984,7 +2990,8 @@ netdev_notifier_info_to_extack(const struct netdev_notifier_info *info) ...@@ -2984,7 +2990,8 @@ netdev_notifier_info_to_extack(const struct netdev_notifier_info *info)
} }
int call_netdevice_notifiers(unsigned long val, struct net_device *dev); int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
int call_netdevice_notifiers_info(unsigned long val,
struct netdev_notifier_info *info);
extern rwlock_t dev_base_lock; /* Device list lock */ extern rwlock_t dev_base_lock; /* Device list lock */
......
...@@ -109,16 +109,6 @@ struct dsa_device_ops { ...@@ -109,16 +109,6 @@ struct dsa_device_ops {
bool promisc_on_master; bool promisc_on_master;
}; };
/* This structure defines the control interfaces that are overlayed by the
* DSA layer on top of the DSA CPU/management net_device instance. This is
* used by the core net_device layer while calling various net_device_ops
* function pointers.
*/
struct dsa_netdevice_ops {
int (*ndo_eth_ioctl)(struct net_device *dev, struct ifreq *ifr,
int cmd);
};
struct dsa_lag { struct dsa_lag {
struct net_device *dev; struct net_device *dev;
unsigned int id; unsigned int id;
...@@ -317,11 +307,6 @@ struct dsa_port { ...@@ -317,11 +307,6 @@ struct dsa_port {
*/ */
const struct ethtool_ops *orig_ethtool_ops; const struct ethtool_ops *orig_ethtool_ops;
/*
* Original copy of the master netdev net_device_ops
*/
const struct dsa_netdevice_ops *netdev_ops;
/* List of MAC addresses that must be forwarded on this port. /* List of MAC addresses that must be forwarded on this port.
* These are only valid on CPU ports and DSA links. * These are only valid on CPU ports and DSA links.
*/ */
...@@ -1339,42 +1324,6 @@ static inline void dsa_tag_generic_flow_dissect(const struct sk_buff *skb, ...@@ -1339,42 +1324,6 @@ static inline void dsa_tag_generic_flow_dissect(const struct sk_buff *skb,
#endif #endif
} }
#if IS_ENABLED(CONFIG_NET_DSA)
static inline int __dsa_netdevice_ops_check(struct net_device *dev)
{
int err = -EOPNOTSUPP;
if (!dev->dsa_ptr)
return err;
if (!dev->dsa_ptr->netdev_ops)
return err;
return 0;
}
static inline int dsa_ndo_eth_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
{
const struct dsa_netdevice_ops *ops;
int err;
err = __dsa_netdevice_ops_check(dev);
if (err)
return err;
ops = dev->dsa_ptr->netdev_ops;
return ops->ndo_eth_ioctl(dev, ifr, cmd);
}
#else
static inline int dsa_ndo_eth_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
{
return -EOPNOTSUPP;
}
#endif
void dsa_unregister_switch(struct dsa_switch *ds); void dsa_unregister_switch(struct dsa_switch *ds);
int dsa_register_switch(struct dsa_switch *ds); int dsa_register_switch(struct dsa_switch *ds);
void dsa_switch_shutdown(struct dsa_switch *ds); void dsa_switch_shutdown(struct dsa_switch *ds);
......
...@@ -160,8 +160,6 @@ struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; ...@@ -160,8 +160,6 @@ struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly; /* Taps */ struct list_head ptype_all __read_mostly; /* Taps */
static int netif_rx_internal(struct sk_buff *skb); static int netif_rx_internal(struct sk_buff *skb);
static int call_netdevice_notifiers_info(unsigned long val,
struct netdev_notifier_info *info);
static int call_netdevice_notifiers_extack(unsigned long val, static int call_netdevice_notifiers_extack(unsigned long val,
struct net_device *dev, struct net_device *dev,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
...@@ -1614,7 +1612,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd) ...@@ -1614,7 +1612,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd)
N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO)
N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE)
N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA) N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA)
N(XDP_FEAT_CHANGE) N(XDP_FEAT_CHANGE) N(PRE_CHANGE_HWTSTAMP)
} }
#undef N #undef N
return "UNKNOWN_NETDEV_EVENT"; return "UNKNOWN_NETDEV_EVENT";
...@@ -1919,7 +1917,7 @@ static void move_netdevice_notifiers_dev_net(struct net_device *dev, ...@@ -1919,7 +1917,7 @@ static void move_netdevice_notifiers_dev_net(struct net_device *dev,
* are as for raw_notifier_call_chain(). * are as for raw_notifier_call_chain().
*/ */
static int call_netdevice_notifiers_info(unsigned long val, int call_netdevice_notifiers_info(unsigned long val,
struct netdev_notifier_info *info) struct netdev_notifier_info *info)
{ {
struct net *net = dev_net(info->dev); struct net *net = dev_net(info->dev);
......
...@@ -183,22 +183,18 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm ...@@ -183,22 +183,18 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
return err; return err;
} }
static int net_hwtstamp_validate(struct ifreq *ifr) static int net_hwtstamp_validate(const struct kernel_hwtstamp_config *cfg)
{ {
struct hwtstamp_config cfg;
enum hwtstamp_tx_types tx_type; enum hwtstamp_tx_types tx_type;
enum hwtstamp_rx_filters rx_filter; enum hwtstamp_rx_filters rx_filter;
int tx_type_valid = 0; int tx_type_valid = 0;
int rx_filter_valid = 0; int rx_filter_valid = 0;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) if (cfg->flags & ~HWTSTAMP_FLAG_MASK)
return -EFAULT;
if (cfg.flags & ~HWTSTAMP_FLAG_MASK)
return -EINVAL; return -EINVAL;
tx_type = cfg.tx_type; tx_type = cfg->tx_type;
rx_filter = cfg.rx_filter; rx_filter = cfg->rx_filter;
switch (tx_type) { switch (tx_type) {
case HWTSTAMP_TX_OFF: case HWTSTAMP_TX_OFF:
...@@ -246,20 +242,53 @@ static int dev_eth_ioctl(struct net_device *dev, ...@@ -246,20 +242,53 @@ static int dev_eth_ioctl(struct net_device *dev,
struct ifreq *ifr, unsigned int cmd) struct ifreq *ifr, unsigned int cmd)
{ {
const struct net_device_ops *ops = dev->netdev_ops; const struct net_device_ops *ops = dev->netdev_ops;
if (!ops->ndo_eth_ioctl)
return -EOPNOTSUPP;
if (!netif_device_present(dev))
return -ENODEV;
return ops->ndo_eth_ioctl(dev, ifr, cmd);
}
static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr)
{
return dev_eth_ioctl(dev, ifr, SIOCGHWTSTAMP);
}
static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
{
struct netdev_notifier_hwtstamp_info info = {
.info.dev = dev,
};
struct kernel_hwtstamp_config kernel_cfg;
struct netlink_ext_ack extack = {};
struct hwtstamp_config cfg;
int err; int err;
err = dsa_ndo_eth_ioctl(dev, ifr, cmd); if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
if (err == 0 || err != -EOPNOTSUPP) return -EFAULT;
hwtstamp_config_to_kernel(&kernel_cfg, &cfg);
err = net_hwtstamp_validate(&kernel_cfg);
if (err)
return err; return err;
if (ops->ndo_eth_ioctl) { info.info.extack = &extack;
if (netif_device_present(dev)) info.config = &kernel_cfg;
err = ops->ndo_eth_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
err = call_netdevice_notifiers_info(NETDEV_PRE_CHANGE_HWTSTAMP,
&info.info);
err = notifier_to_errno(err);
if (err) {
if (extack._msg)
netdev_err(dev, "%s\n", extack._msg);
return err; return err;
}
return dev_eth_ioctl(dev, ifr, SIOCSHWTSTAMP);
} }
static int dev_siocbond(struct net_device *dev, static int dev_siocbond(struct net_device *dev,
...@@ -391,36 +420,31 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data, ...@@ -391,36 +420,31 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
rtnl_lock(); rtnl_lock();
return err; return err;
case SIOCDEVPRIVATE ... SIOCDEVPRIVATE + 15:
return dev_siocdevprivate(dev, ifr, data, cmd);
case SIOCSHWTSTAMP: case SIOCSHWTSTAMP:
err = net_hwtstamp_validate(ifr); return dev_set_hwtstamp(dev, ifr);
if (err)
return err;
fallthrough;
/* case SIOCGHWTSTAMP:
* Unknown or private ioctl return dev_get_hwtstamp(dev, ifr);
*/
default:
if (cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15)
return dev_siocdevprivate(dev, ifr, data, cmd);
if (cmd == SIOCGMIIPHY || case SIOCGMIIPHY:
cmd == SIOCGMIIREG || case SIOCGMIIREG:
cmd == SIOCSMIIREG || case SIOCSMIIREG:
cmd == SIOCSHWTSTAMP || return dev_eth_ioctl(dev, ifr, cmd);
cmd == SIOCGHWTSTAMP) {
err = dev_eth_ioctl(dev, ifr, cmd); case SIOCBONDENSLAVE:
} else if (cmd == SIOCBONDENSLAVE || case SIOCBONDRELEASE:
cmd == SIOCBONDRELEASE || case SIOCBONDSETHWADDR:
cmd == SIOCBONDSETHWADDR || case SIOCBONDSLAVEINFOQUERY:
cmd == SIOCBONDSLAVEINFOQUERY || case SIOCBONDINFOQUERY:
cmd == SIOCBONDINFOQUERY || case SIOCBONDCHANGEACTIVE:
cmd == SIOCBONDCHANGEACTIVE) { return dev_siocbond(dev, ifr, cmd);
err = dev_siocbond(dev, ifr, cmd);
} else
err = -EINVAL;
/* Unknown ioctl */
default:
err = -EINVAL;
} }
return err; return err;
} }
......
...@@ -195,38 +195,31 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, ...@@ -195,38 +195,31 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset,
} }
} }
static int dsa_master_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Deny PTP operations on master if there is at least one switch in the tree
* that is PTP capable.
*/
int dsa_master_pre_change_hwtstamp(struct net_device *dev,
const struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{ {
struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_port *cpu_dp = dev->dsa_ptr;
struct dsa_switch *ds = cpu_dp->ds; struct dsa_switch *ds = cpu_dp->ds;
struct dsa_switch_tree *dst; struct dsa_switch_tree *dst;
int err = -EOPNOTSUPP;
struct dsa_port *dp; struct dsa_port *dp;
dst = ds->dst; dst = ds->dst;
switch (cmd) { list_for_each_entry(dp, &dst->ports, list) {
case SIOCGHWTSTAMP: if (dsa_port_supports_hwtstamp(dp)) {
case SIOCSHWTSTAMP: NL_SET_ERR_MSG(extack,
/* Deny PTP operations on master if there is at least one "HW timestamping not allowed on DSA master when switch supports the operation");
* switch in the tree that is PTP capable.
*/
list_for_each_entry(dp, &dst->ports, list)
if (dsa_port_supports_hwtstamp(dp, ifr))
return -EBUSY; return -EBUSY;
break; }
} }
if (dev->netdev_ops->ndo_eth_ioctl) return 0;
err = dev->netdev_ops->ndo_eth_ioctl(dev, ifr, cmd);
return err;
} }
static const struct dsa_netdevice_ops dsa_netdev_ops = {
.ndo_eth_ioctl = dsa_master_ioctl,
};
static int dsa_master_ethtool_setup(struct net_device *dev) static int dsa_master_ethtool_setup(struct net_device *dev)
{ {
struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_port *cpu_dp = dev->dsa_ptr;
...@@ -267,15 +260,6 @@ static void dsa_master_ethtool_teardown(struct net_device *dev) ...@@ -267,15 +260,6 @@ static void dsa_master_ethtool_teardown(struct net_device *dev)
cpu_dp->orig_ethtool_ops = NULL; cpu_dp->orig_ethtool_ops = NULL;
} }
static void dsa_netdev_ops_set(struct net_device *dev,
const struct dsa_netdevice_ops *ops)
{
if (netif_is_lag_master(dev))
return;
dev->dsa_ptr->netdev_ops = ops;
}
/* Keep the master always promiscuous if the tagging protocol requires that /* Keep the master always promiscuous if the tagging protocol requires that
* (garbles MAC DA) or if it doesn't support unicast filtering, case in which * (garbles MAC DA) or if it doesn't support unicast filtering, case in which
* it would revert to promiscuous mode as soon as we call dev_uc_add() on it * it would revert to promiscuous mode as soon as we call dev_uc_add() on it
...@@ -414,16 +398,13 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) ...@@ -414,16 +398,13 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
if (ret) if (ret)
goto out_err_reset_promisc; goto out_err_reset_promisc;
dsa_netdev_ops_set(dev, &dsa_netdev_ops);
ret = sysfs_create_group(&dev->dev.kobj, &dsa_group); ret = sysfs_create_group(&dev->dev.kobj, &dsa_group);
if (ret) if (ret)
goto out_err_ndo_teardown; goto out_err_ethtool_teardown;
return ret; return ret;
out_err_ndo_teardown: out_err_ethtool_teardown:
dsa_netdev_ops_set(dev, NULL);
dsa_master_ethtool_teardown(dev); dsa_master_ethtool_teardown(dev);
out_err_reset_promisc: out_err_reset_promisc:
dsa_master_set_promiscuity(dev, -1); dsa_master_set_promiscuity(dev, -1);
...@@ -433,7 +414,6 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) ...@@ -433,7 +414,6 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
void dsa_master_teardown(struct net_device *dev) void dsa_master_teardown(struct net_device *dev)
{ {
sysfs_remove_group(&dev->dev.kobj, &dsa_group); sysfs_remove_group(&dev->dev.kobj, &dsa_group);
dsa_netdev_ops_set(dev, NULL);
dsa_master_ethtool_teardown(dev); dsa_master_ethtool_teardown(dev);
dsa_master_reset_mtu(dev); dsa_master_reset_mtu(dev);
dsa_master_set_promiscuity(dev, -1); dsa_master_set_promiscuity(dev, -1);
......
...@@ -15,5 +15,8 @@ int dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp, ...@@ -15,5 +15,8 @@ int dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
void dsa_master_lag_teardown(struct net_device *lag_dev, void dsa_master_lag_teardown(struct net_device *lag_dev,
struct dsa_port *cpu_dp); struct dsa_port *cpu_dp);
int dsa_master_pre_change_hwtstamp(struct net_device *dev,
const struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
#endif #endif
...@@ -114,19 +114,21 @@ static bool dsa_port_can_configure_learning(struct dsa_port *dp) ...@@ -114,19 +114,21 @@ static bool dsa_port_can_configure_learning(struct dsa_port *dp)
return !err; return !err;
} }
bool dsa_port_supports_hwtstamp(struct dsa_port *dp, struct ifreq *ifr) bool dsa_port_supports_hwtstamp(struct dsa_port *dp)
{ {
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
struct ifreq ifr = {};
int err; int err;
if (!ds->ops->port_hwtstamp_get || !ds->ops->port_hwtstamp_set) if (!ds->ops->port_hwtstamp_get || !ds->ops->port_hwtstamp_set)
return false; return false;
/* "See through" shim implementations of the "get" method. /* "See through" shim implementations of the "get" method.
* This will clobber the ifreq structure, but we will either return an * Since we can't cook up a complete ioctl request structure, this will
* error, or the master will overwrite it with proper values. * fail in copy_to_user() with -EFAULT, which hopefully is enough to
* detect a valid implementation.
*/ */
err = ds->ops->port_hwtstamp_get(ds, dp->index, ifr); err = ds->ops->port_hwtstamp_get(ds, dp->index, &ifr);
return err != -EOPNOTSUPP; return err != -EOPNOTSUPP;
} }
......
...@@ -15,7 +15,7 @@ struct switchdev_obj_port_mdb; ...@@ -15,7 +15,7 @@ struct switchdev_obj_port_mdb;
struct switchdev_vlan_msti; struct switchdev_vlan_msti;
struct phy_device; struct phy_device;
bool dsa_port_supports_hwtstamp(struct dsa_port *dp, struct ifreq *ifr); bool dsa_port_supports_hwtstamp(struct dsa_port *dp);
void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,
const struct dsa_device_ops *tag_ops); const struct dsa_device_ops *tag_ops);
int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age); int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age);
......
...@@ -3289,6 +3289,7 @@ static int dsa_master_changeupper(struct net_device *dev, ...@@ -3289,6 +3289,7 @@ static int dsa_master_changeupper(struct net_device *dev,
static int dsa_slave_netdevice_event(struct notifier_block *nb, static int dsa_slave_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
switch (event) { switch (event) {
...@@ -3418,6 +3419,16 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, ...@@ -3418,6 +3419,16 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
return NOTIFY_OK; return NOTIFY_OK;
} }
case NETDEV_PRE_CHANGE_HWTSTAMP: {
struct netdev_notifier_hwtstamp_info *info = ptr;
int err;
if (!netdev_uses_dsa(dev))
return NOTIFY_DONE;
err = dsa_master_pre_change_hwtstamp(dev, info->config, extack);
return notifier_from_errno(err);
}
default: default:
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment