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 {
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
*/
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;
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:
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 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
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;
}
......
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