diff options
| -rw-r--r-- | include/linux/net_tstamp.h | 33 | ||||
| -rw-r--r-- | include/linux/netdevice.h | 9 | ||||
| -rw-r--r-- | include/net/dsa.h | 51 | ||||
| -rw-r--r-- | net/core/dev.c | 8 | ||||
| -rw-r--r-- | net/core/dev_ioctl.c | 110 | ||||
| -rw-r--r-- | net/dsa/master.c | 50 | ||||
| -rw-r--r-- | net/dsa/master.h | 3 | ||||
| -rw-r--r-- | net/dsa/port.c | 10 | ||||
| -rw-r--r-- | net/dsa/port.h | 2 | ||||
| -rw-r--r-- | net/dsa/slave.c | 11 | 
10 files changed, 147 insertions, 140 deletions
| diff --git a/include/linux/net_tstamp.h b/include/linux/net_tstamp.h new file mode 100644 index 000000000000..fd67f3cc0c4b --- /dev/null +++ b/include/linux/net_tstamp.h @@ -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_ */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 62e093a6d6d1..a740be3bb911 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.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 */ diff --git a/include/net/dsa.h b/include/net/dsa.h index a15f17a38eca..8903053fa5aa 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -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); diff --git a/net/core/dev.c b/net/core/dev.c index 0c4b21291348..7ce5985be84b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -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; diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 846669426236..6d772837eb3f 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -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;  } diff --git a/net/dsa/master.c b/net/dsa/master.c index 22d3f16b0e6d..c2cabe6248b1 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -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); diff --git a/net/dsa/master.h b/net/dsa/master.h index 3fc0e610b5b5..80842f4e27f7 100644 --- a/net/dsa/master.h +++ b/net/dsa/master.h @@ -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 diff --git a/net/dsa/port.c b/net/dsa/port.c index 15cee17769e9..71ba30538411 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -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;  } diff --git a/net/dsa/port.h b/net/dsa/port.h index 9c218660d223..dc812512fd0e 100644 --- a/net/dsa/port.h +++ b/net/dsa/port.h @@ -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); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 165bb2cb8431..8abc1658ac47 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -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;  	} | 
