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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
858e5b0640
33
include/linux/net_tstamp.h
Normal file
33
include/linux/net_tstamp.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* 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 {
|
||||
NETDEV_OFFLOAD_XSTATS_REPORT_USED,
|
||||
NETDEV_OFFLOAD_XSTATS_REPORT_DELTA,
|
||||
NETDEV_XDP_FEAT_CHANGE,
|
||||
NETDEV_PRE_CHANGE_HWTSTAMP,
|
||||
};
|
||||
const char *netdev_cmd_to_name(enum netdev_cmd cmd);
|
||||
|
||||
@ -2928,6 +2929,11 @@ struct netdev_notifier_pre_changeaddr_info {
|
||||
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 {
|
||||
NETDEV_OFFLOAD_XSTATS_TYPE_L3 = 1,
|
||||
};
|
||||
@ -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_info(unsigned long val,
|
||||
struct netdev_notifier_info *info);
|
||||
|
||||
extern rwlock_t dev_base_lock; /* Device list lock */
|
||||
|
||||
|
@ -109,16 +109,6 @@ struct dsa_device_ops {
|
||||
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 net_device *dev;
|
||||
unsigned int id;
|
||||
@ -317,11 +307,6 @@ struct dsa_port {
|
||||
*/
|
||||
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.
|
||||
* 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,
|
||||
#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);
|
||||
int dsa_register_switch(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;
|
||||
struct list_head ptype_all __read_mostly; /* Taps */
|
||||
|
||||
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,
|
||||
struct net_device *dev,
|
||||
struct netlink_ext_ack *extack);
|
||||
@ -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(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE)
|
||||
N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA)
|
||||
N(XDP_FEAT_CHANGE)
|
||||
N(XDP_FEAT_CHANGE) N(PRE_CHANGE_HWTSTAMP)
|
||||
}
|
||||
#undef N
|
||||
return "UNKNOWN_NETDEV_EVENT";
|
||||
@ -1919,8 +1917,8 @@ static void move_netdevice_notifiers_dev_net(struct net_device *dev,
|
||||
* are as for raw_notifier_call_chain().
|
||||
*/
|
||||
|
||||
static int call_netdevice_notifiers_info(unsigned long val,
|
||||
struct netdev_notifier_info *info)
|
||||
int call_netdevice_notifiers_info(unsigned long val,
|
||||
struct netdev_notifier_info *info)
|
||||
{
|
||||
struct net *net = dev_net(info->dev);
|
||||
int ret;
|
||||
|
@ -183,22 +183,18 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
|
||||
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_rx_filters rx_filter;
|
||||
int tx_type_valid = 0;
|
||||
int rx_filter_valid = 0;
|
||||
|
||||
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (cfg.flags & ~HWTSTAMP_FLAG_MASK)
|
||||
if (cfg->flags & ~HWTSTAMP_FLAG_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
tx_type = cfg.tx_type;
|
||||
rx_filter = cfg.rx_filter;
|
||||
tx_type = cfg->tx_type;
|
||||
rx_filter = cfg->rx_filter;
|
||||
|
||||
switch (tx_type) {
|
||||
case HWTSTAMP_TX_OFF:
|
||||
@ -246,20 +242,53 @@ static int dev_eth_ioctl(struct net_device *dev,
|
||||
struct ifreq *ifr, unsigned int cmd)
|
||||
{
|
||||
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;
|
||||
|
||||
err = dsa_ndo_eth_ioctl(dev, ifr, cmd);
|
||||
if (err == 0 || err != -EOPNOTSUPP)
|
||||
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
hwtstamp_config_to_kernel(&kernel_cfg, &cfg);
|
||||
|
||||
err = net_hwtstamp_validate(&kernel_cfg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ops->ndo_eth_ioctl) {
|
||||
if (netif_device_present(dev))
|
||||
err = ops->ndo_eth_ioctl(dev, ifr, cmd);
|
||||
else
|
||||
err = -ENODEV;
|
||||
info.info.extack = &extack;
|
||||
info.config = &kernel_cfg;
|
||||
|
||||
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,
|
||||
@ -391,36 +420,31 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
|
||||
rtnl_lock();
|
||||
return err;
|
||||
|
||||
case SIOCDEVPRIVATE ... SIOCDEVPRIVATE + 15:
|
||||
return dev_siocdevprivate(dev, ifr, data, cmd);
|
||||
|
||||
case SIOCSHWTSTAMP:
|
||||
err = net_hwtstamp_validate(ifr);
|
||||
if (err)
|
||||
return err;
|
||||
fallthrough;
|
||||
return dev_set_hwtstamp(dev, ifr);
|
||||
|
||||
/*
|
||||
* Unknown or private ioctl
|
||||
*/
|
||||
case SIOCGHWTSTAMP:
|
||||
return dev_get_hwtstamp(dev, ifr);
|
||||
|
||||
case SIOCGMIIPHY:
|
||||
case SIOCGMIIREG:
|
||||
case SIOCSMIIREG:
|
||||
return dev_eth_ioctl(dev, ifr, cmd);
|
||||
|
||||
case SIOCBONDENSLAVE:
|
||||
case SIOCBONDRELEASE:
|
||||
case SIOCBONDSETHWADDR:
|
||||
case SIOCBONDSLAVEINFOQUERY:
|
||||
case SIOCBONDINFOQUERY:
|
||||
case SIOCBONDCHANGEACTIVE:
|
||||
return dev_siocbond(dev, ifr, cmd);
|
||||
|
||||
/* Unknown ioctl */
|
||||
default:
|
||||
if (cmd >= SIOCDEVPRIVATE &&
|
||||
cmd <= SIOCDEVPRIVATE + 15)
|
||||
return dev_siocdevprivate(dev, ifr, data, cmd);
|
||||
|
||||
if (cmd == SIOCGMIIPHY ||
|
||||
cmd == SIOCGMIIREG ||
|
||||
cmd == SIOCSMIIREG ||
|
||||
cmd == SIOCSHWTSTAMP ||
|
||||
cmd == SIOCGHWTSTAMP) {
|
||||
err = dev_eth_ioctl(dev, ifr, cmd);
|
||||
} else if (cmd == SIOCBONDENSLAVE ||
|
||||
cmd == SIOCBONDRELEASE ||
|
||||
cmd == SIOCBONDSETHWADDR ||
|
||||
cmd == SIOCBONDSLAVEINFOQUERY ||
|
||||
cmd == SIOCBONDINFOQUERY ||
|
||||
cmd == SIOCBONDCHANGEACTIVE) {
|
||||
err = dev_siocbond(dev, ifr, cmd);
|
||||
} else
|
||||
err = -EINVAL;
|
||||
|
||||
err = -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -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_switch *ds = cpu_dp->ds;
|
||||
struct dsa_switch_tree *dst;
|
||||
int err = -EOPNOTSUPP;
|
||||
struct dsa_port *dp;
|
||||
|
||||
dst = ds->dst;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCGHWTSTAMP:
|
||||
case SIOCSHWTSTAMP:
|
||||
/* Deny PTP operations on master if there is at least one
|
||||
* 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;
|
||||
break;
|
||||
list_for_each_entry(dp, &dst->ports, list) {
|
||||
if (dsa_port_supports_hwtstamp(dp)) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"HW timestamping not allowed on DSA master when switch supports the operation");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->netdev_ops->ndo_eth_ioctl)
|
||||
err = dev->netdev_ops->ndo_eth_ioctl(dev, ifr, cmd);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct dsa_port *cpu_dp = dev->dsa_ptr;
|
||||
@ -267,15 +260,6 @@ static void dsa_master_ethtool_teardown(struct net_device *dev)
|
||||
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
|
||||
* (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
|
||||
@ -414,16 +398,13 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
|
||||
if (ret)
|
||||
goto out_err_reset_promisc;
|
||||
|
||||
dsa_netdev_ops_set(dev, &dsa_netdev_ops);
|
||||
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &dsa_group);
|
||||
if (ret)
|
||||
goto out_err_ndo_teardown;
|
||||
goto out_err_ethtool_teardown;
|
||||
|
||||
return ret;
|
||||
|
||||
out_err_ndo_teardown:
|
||||
dsa_netdev_ops_set(dev, NULL);
|
||||
out_err_ethtool_teardown:
|
||||
dsa_master_ethtool_teardown(dev);
|
||||
out_err_reset_promisc:
|
||||
dsa_master_set_promiscuity(dev, -1);
|
||||
@ -433,7 +414,6 @@ out_err_reset_promisc:
|
||||
void dsa_master_teardown(struct net_device *dev)
|
||||
{
|
||||
sysfs_remove_group(&dev->dev.kobj, &dsa_group);
|
||||
dsa_netdev_ops_set(dev, NULL);
|
||||
dsa_master_ethtool_teardown(dev);
|
||||
dsa_master_reset_mtu(dev);
|
||||
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,
|
||||
struct netlink_ext_ack *extack);
|
||||
void dsa_master_lag_teardown(struct net_device *lag_dev,
|
||||
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
|
||||
|
@ -114,19 +114,21 @@ static bool dsa_port_can_configure_learning(struct dsa_port *dp)
|
||||
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 ifreq ifr = {};
|
||||
int err;
|
||||
|
||||
if (!ds->ops->port_hwtstamp_get || !ds->ops->port_hwtstamp_set)
|
||||
return false;
|
||||
|
||||
/* "See through" shim implementations of the "get" method.
|
||||
* This will clobber the ifreq structure, but we will either return an
|
||||
* error, or the master will overwrite it with proper values.
|
||||
* Since we can't cook up a complete ioctl request structure, this will
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ struct switchdev_obj_port_mdb;
|
||||
struct switchdev_vlan_msti;
|
||||
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,
|
||||
const struct dsa_device_ops *tag_ops);
|
||||
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,
|
||||
static int dsa_slave_netdevice_event(struct notifier_block *nb,
|
||||
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);
|
||||
|
||||
switch (event) {
|
||||
@ -3418,6 +3419,16 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
|
||||
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user