diff options
Diffstat (limited to 'net/core/net-sysfs.c')
| -rw-r--r-- | net/core/net-sysfs.c | 867 | 
1 files changed, 461 insertions, 406 deletions
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 85e8b5326dd..1cac29ebb05 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -18,9 +18,10 @@  #include <net/sock.h>  #include <net/net_namespace.h>  #include <linux/rtnetlink.h> -#include <linux/wireless.h>  #include <linux/vmalloc.h> -#include <net/wext.h> +#include <linux/export.h> +#include <linux/jiffies.h> +#include <linux/pm_runtime.h>  #include "net-sysfs.h" @@ -28,6 +29,7 @@  static const char fmt_hex[] = "%#x\n";  static const char fmt_long_hex[] = "%#lx\n";  static const char fmt_dec[] = "%d\n"; +static const char fmt_udec[] = "%u\n";  static const char fmt_ulong[] = "%lu\n";  static const char fmt_u64[] = "%llu\n"; @@ -58,35 +60,42 @@ static ssize_t format_##field(const struct net_device *net, char *buf)	\  {									\  	return sprintf(buf, format_string, net->field);			\  }									\ -static ssize_t show_##field(struct device *dev,				\ +static ssize_t field##_show(struct device *dev,				\  			    struct device_attribute *attr, char *buf)	\  {									\  	return netdev_show(dev, attr, buf, format_##field);		\ -} +}									\ + +#define NETDEVICE_SHOW_RO(field, format_string)				\ +NETDEVICE_SHOW(field, format_string);					\ +static DEVICE_ATTR_RO(field) +#define NETDEVICE_SHOW_RW(field, format_string)				\ +NETDEVICE_SHOW(field, format_string);					\ +static DEVICE_ATTR_RW(field)  /* use same locking and permission rules as SIF* ioctl's */  static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,  			    const char *buf, size_t len,  			    int (*set)(struct net_device *, unsigned long))  { -	struct net_device *net = to_net_dev(dev); -	char *endp; +	struct net_device *netdev = to_net_dev(dev); +	struct net *net = dev_net(netdev);  	unsigned long new;  	int ret = -EINVAL; -	if (!capable(CAP_NET_ADMIN)) +	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))  		return -EPERM; -	new = simple_strtoul(buf, &endp, 0); -	if (endp == buf) +	ret = kstrtoul(buf, 0, &new); +	if (ret)  		goto err;  	if (!rtnl_trylock())  		return restart_syscall(); -	if (dev_isalive(net)) { -		if ((ret = (*set)(net, new)) == 0) +	if (dev_isalive(netdev)) { +		if ((ret = (*set)(netdev, new)) == 0)  			ret = len;  	}  	rtnl_unlock(); @@ -94,17 +103,17 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,  	return ret;  } -NETDEVICE_SHOW(dev_id, fmt_hex); -NETDEVICE_SHOW(addr_assign_type, fmt_dec); -NETDEVICE_SHOW(addr_len, fmt_dec); -NETDEVICE_SHOW(iflink, fmt_dec); -NETDEVICE_SHOW(ifindex, fmt_dec); -NETDEVICE_SHOW(features, fmt_long_hex); -NETDEVICE_SHOW(type, fmt_dec); -NETDEVICE_SHOW(link_mode, fmt_dec); +NETDEVICE_SHOW_RO(dev_id, fmt_hex); +NETDEVICE_SHOW_RO(dev_port, fmt_dec); +NETDEVICE_SHOW_RO(addr_assign_type, fmt_dec); +NETDEVICE_SHOW_RO(addr_len, fmt_dec); +NETDEVICE_SHOW_RO(iflink, fmt_dec); +NETDEVICE_SHOW_RO(ifindex, fmt_dec); +NETDEVICE_SHOW_RO(type, fmt_dec); +NETDEVICE_SHOW_RO(link_mode, fmt_dec);  /* use same locking rules as GIFHWADDR ioctl's */ -static ssize_t show_address(struct device *dev, struct device_attribute *attr, +static ssize_t address_show(struct device *dev, struct device_attribute *attr,  			    char *buf)  {  	struct net_device *net = to_net_dev(dev); @@ -116,17 +125,32 @@ static ssize_t show_address(struct device *dev, struct device_attribute *attr,  	read_unlock(&dev_base_lock);  	return ret;  } +static DEVICE_ATTR_RO(address); -static ssize_t show_broadcast(struct device *dev, -			    struct device_attribute *attr, char *buf) +static ssize_t broadcast_show(struct device *dev, +			      struct device_attribute *attr, char *buf)  {  	struct net_device *net = to_net_dev(dev);  	if (dev_isalive(net))  		return sysfs_format_mac(buf, net->broadcast, net->addr_len);  	return -EINVAL;  } +static DEVICE_ATTR_RO(broadcast); + +static int change_carrier(struct net_device *net, unsigned long new_carrier) +{ +	if (!netif_running(net)) +		return -EINVAL; +	return dev_change_carrier(net, (bool) new_carrier); +} -static ssize_t show_carrier(struct device *dev, +static ssize_t carrier_store(struct device *dev, struct device_attribute *attr, +			     const char *buf, size_t len) +{ +	return netdev_store(dev, attr, buf, len, change_carrier); +} + +static ssize_t carrier_show(struct device *dev,  			    struct device_attribute *attr, char *buf)  {  	struct net_device *netdev = to_net_dev(dev); @@ -135,8 +159,9 @@ static ssize_t show_carrier(struct device *dev,  	}  	return -EINVAL;  } +static DEVICE_ATTR_RW(carrier); -static ssize_t show_speed(struct device *dev, +static ssize_t speed_show(struct device *dev,  			  struct device_attribute *attr, char *buf)  {  	struct net_device *netdev = to_net_dev(dev); @@ -145,19 +170,17 @@ static ssize_t show_speed(struct device *dev,  	if (!rtnl_trylock())  		return restart_syscall(); -	if (netif_running(netdev) && -	    netdev->ethtool_ops && -	    netdev->ethtool_ops->get_settings) { -		struct ethtool_cmd cmd = { ETHTOOL_GSET }; - -		if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) -			ret = sprintf(buf, fmt_dec, ethtool_cmd_speed(&cmd)); +	if (netif_running(netdev)) { +		struct ethtool_cmd cmd; +		if (!__ethtool_get_settings(netdev, &cmd)) +			ret = sprintf(buf, fmt_udec, ethtool_cmd_speed(&cmd));  	}  	rtnl_unlock();  	return ret;  } +static DEVICE_ATTR_RO(speed); -static ssize_t show_duplex(struct device *dev, +static ssize_t duplex_show(struct device *dev,  			   struct device_attribute *attr, char *buf)  {  	struct net_device *netdev = to_net_dev(dev); @@ -166,19 +189,30 @@ static ssize_t show_duplex(struct device *dev,  	if (!rtnl_trylock())  		return restart_syscall(); -	if (netif_running(netdev) && -	    netdev->ethtool_ops && -	    netdev->ethtool_ops->get_settings) { -		struct ethtool_cmd cmd = { ETHTOOL_GSET }; - -		if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) -			ret = sprintf(buf, "%s\n", cmd.duplex ? "full" : "half"); +	if (netif_running(netdev)) { +		struct ethtool_cmd cmd; +		if (!__ethtool_get_settings(netdev, &cmd)) { +			const char *duplex; +			switch (cmd.duplex) { +			case DUPLEX_HALF: +				duplex = "half"; +				break; +			case DUPLEX_FULL: +				duplex = "full"; +				break; +			default: +				duplex = "unknown"; +				break; +			} +			ret = sprintf(buf, "%s\n", duplex); +		}  	}  	rtnl_unlock();  	return ret;  } +static DEVICE_ATTR_RO(duplex); -static ssize_t show_dormant(struct device *dev, +static ssize_t dormant_show(struct device *dev,  			    struct device_attribute *attr, char *buf)  {  	struct net_device *netdev = to_net_dev(dev); @@ -188,6 +222,7 @@ static ssize_t show_dormant(struct device *dev,  	return -EINVAL;  } +static DEVICE_ATTR_RO(dormant);  static const char *const operstates[] = {  	"unknown", @@ -199,7 +234,7 @@ static const char *const operstates[] = {  	"up"  }; -static ssize_t show_operstate(struct device *dev, +static ssize_t operstate_show(struct device *dev,  			      struct device_attribute *attr, char *buf)  {  	const struct net_device *netdev = to_net_dev(dev); @@ -216,35 +251,43 @@ static ssize_t show_operstate(struct device *dev,  	return sprintf(buf, "%s\n", operstates[operstate]);  } +static DEVICE_ATTR_RO(operstate); + +static ssize_t carrier_changes_show(struct device *dev, +				    struct device_attribute *attr, +				    char *buf) +{ +	struct net_device *netdev = to_net_dev(dev); +	return sprintf(buf, fmt_dec, +		       atomic_read(&netdev->carrier_changes)); +} +static DEVICE_ATTR_RO(carrier_changes);  /* read-write attributes */ -NETDEVICE_SHOW(mtu, fmt_dec);  static int change_mtu(struct net_device *net, unsigned long new_mtu)  {  	return dev_set_mtu(net, (int) new_mtu);  } -static ssize_t store_mtu(struct device *dev, struct device_attribute *attr, +static ssize_t mtu_store(struct device *dev, struct device_attribute *attr,  			 const char *buf, size_t len)  {  	return netdev_store(dev, attr, buf, len, change_mtu);  } - -NETDEVICE_SHOW(flags, fmt_hex); +NETDEVICE_SHOW_RW(mtu, fmt_dec);  static int change_flags(struct net_device *net, unsigned long new_flags)  { -	return dev_change_flags(net, (unsigned) new_flags); +	return dev_change_flags(net, (unsigned int) new_flags);  } -static ssize_t store_flags(struct device *dev, struct device_attribute *attr, +static ssize_t flags_store(struct device *dev, struct device_attribute *attr,  			   const char *buf, size_t len)  {  	return netdev_store(dev, attr, buf, len, change_flags);  } - -NETDEVICE_SHOW(tx_queue_len, fmt_ulong); +NETDEVICE_SHOW_RW(flags, fmt_hex);  static int change_tx_queue_len(struct net_device *net, unsigned long new_len)  { @@ -252,21 +295,26 @@ static int change_tx_queue_len(struct net_device *net, unsigned long new_len)  	return 0;  } -static ssize_t store_tx_queue_len(struct device *dev, +static ssize_t tx_queue_len_store(struct device *dev,  				  struct device_attribute *attr,  				  const char *buf, size_t len)  { +	if (!capable(CAP_NET_ADMIN)) +		return -EPERM; +  	return netdev_store(dev, attr, buf, len, change_tx_queue_len);  } +NETDEVICE_SHOW_RW(tx_queue_len, fmt_ulong); -static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr, +static ssize_t ifalias_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);  	size_t count = len;  	ssize_t ret; -	if (!capable(CAP_NET_ADMIN)) +	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))  		return -EPERM;  	/* ignore trailing newline */ @@ -281,7 +329,7 @@ static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr,  	return ret < 0 ? ret : len;  } -static ssize_t show_ifalias(struct device *dev, +static ssize_t ifalias_show(struct device *dev,  			    struct device_attribute *attr, char *buf)  {  	const struct net_device *netdev = to_net_dev(dev); @@ -294,30 +342,70 @@ static ssize_t show_ifalias(struct device *dev,  	rtnl_unlock();  	return ret;  } +static DEVICE_ATTR_RW(ifalias); -static struct device_attribute net_class_attributes[] = { -	__ATTR(addr_assign_type, S_IRUGO, show_addr_assign_type, NULL), -	__ATTR(addr_len, S_IRUGO, show_addr_len, NULL), -	__ATTR(dev_id, S_IRUGO, show_dev_id, NULL), -	__ATTR(ifalias, S_IRUGO | S_IWUSR, show_ifalias, store_ifalias), -	__ATTR(iflink, S_IRUGO, show_iflink, NULL), -	__ATTR(ifindex, S_IRUGO, show_ifindex, NULL), -	__ATTR(features, S_IRUGO, show_features, NULL), -	__ATTR(type, S_IRUGO, show_type, NULL), -	__ATTR(link_mode, S_IRUGO, show_link_mode, NULL), -	__ATTR(address, S_IRUGO, show_address, NULL), -	__ATTR(broadcast, S_IRUGO, show_broadcast, NULL), -	__ATTR(carrier, S_IRUGO, show_carrier, NULL), -	__ATTR(speed, S_IRUGO, show_speed, NULL), -	__ATTR(duplex, S_IRUGO, show_duplex, NULL), -	__ATTR(dormant, S_IRUGO, show_dormant, NULL), -	__ATTR(operstate, S_IRUGO, show_operstate, NULL), -	__ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu), -	__ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags), -	__ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len, -	       store_tx_queue_len), -	{} +static int change_group(struct net_device *net, unsigned long new_group) +{ +	dev_set_group(net, (int) new_group); +	return 0; +} + +static ssize_t group_store(struct device *dev, struct device_attribute *attr, +			   const char *buf, size_t len) +{ +	return netdev_store(dev, attr, buf, len, change_group); +} +NETDEVICE_SHOW(group, fmt_dec); +static DEVICE_ATTR(netdev_group, S_IRUGO | S_IWUSR, group_show, group_store); + +static ssize_t phys_port_id_show(struct device *dev, +				 struct device_attribute *attr, char *buf) +{ +	struct net_device *netdev = to_net_dev(dev); +	ssize_t ret = -EINVAL; + +	if (!rtnl_trylock()) +		return restart_syscall(); + +	if (dev_isalive(netdev)) { +		struct netdev_phys_port_id ppid; + +		ret = dev_get_phys_port_id(netdev, &ppid); +		if (!ret) +			ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id); +	} +	rtnl_unlock(); + +	return ret; +} +static DEVICE_ATTR_RO(phys_port_id); + +static struct attribute *net_class_attrs[] = { +	&dev_attr_netdev_group.attr, +	&dev_attr_type.attr, +	&dev_attr_dev_id.attr, +	&dev_attr_dev_port.attr, +	&dev_attr_iflink.attr, +	&dev_attr_ifindex.attr, +	&dev_attr_addr_assign_type.attr, +	&dev_attr_addr_len.attr, +	&dev_attr_link_mode.attr, +	&dev_attr_address.attr, +	&dev_attr_broadcast.attr, +	&dev_attr_speed.attr, +	&dev_attr_duplex.attr, +	&dev_attr_dormant.attr, +	&dev_attr_operstate.attr, +	&dev_attr_carrier_changes.attr, +	&dev_attr_ifalias.attr, +	&dev_attr_carrier.attr, +	&dev_attr_mtu.attr, +	&dev_attr_flags.attr, +	&dev_attr_tx_queue_len.attr, +	&dev_attr_phys_port_id.attr, +	NULL,  }; +ATTRIBUTE_GROUPS(net_class);  /* Show a given an attribute in the statistics group */  static ssize_t netstat_show(const struct device *d, @@ -343,13 +431,13 @@ static ssize_t netstat_show(const struct device *d,  /* generate a read-only statistics attribute */  #define NETSTAT_ENTRY(name)						\ -static ssize_t show_##name(struct device *d,				\ +static ssize_t name##_show(struct device *d,				\  			   struct device_attribute *attr, char *buf) 	\  {									\  	return netstat_show(d, attr, buf,				\  			    offsetof(struct rtnl_link_stats64, name));	\  }									\ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) +static DEVICE_ATTR_RO(name)  NETSTAT_ENTRY(rx_packets);  NETSTAT_ENTRY(tx_packets); @@ -408,63 +496,8 @@ static struct attribute_group netstat_group = {  	.attrs  = netstat_attrs,  }; -#ifdef CONFIG_WIRELESS_EXT_SYSFS -/* helper function that does all the locking etc for wireless stats */ -static ssize_t wireless_show(struct device *d, char *buf, -			     ssize_t (*format)(const struct iw_statistics *, -					       char *)) -{ -	struct net_device *dev = to_net_dev(d); -	const struct iw_statistics *iw; -	ssize_t ret = -EINVAL; - -	if (!rtnl_trylock()) -		return restart_syscall(); -	if (dev_isalive(dev)) { -		iw = get_wireless_stats(dev); -		if (iw) -			ret = (*format)(iw, buf); -	} -	rtnl_unlock(); - -	return ret; -} - -/* show function template for wireless fields */ -#define WIRELESS_SHOW(name, field, format_string)			\ -static ssize_t format_iw_##name(const struct iw_statistics *iw, char *buf) \ -{									\ -	return sprintf(buf, format_string, iw->field);			\ -}									\ -static ssize_t show_iw_##name(struct device *d,				\ -			      struct device_attribute *attr, char *buf)	\ -{									\ -	return wireless_show(d, buf, format_iw_##name);			\ -}									\ -static DEVICE_ATTR(name, S_IRUGO, show_iw_##name, NULL) - -WIRELESS_SHOW(status, status, fmt_hex); -WIRELESS_SHOW(link, qual.qual, fmt_dec); -WIRELESS_SHOW(level, qual.level, fmt_dec); -WIRELESS_SHOW(noise, qual.noise, fmt_dec); -WIRELESS_SHOW(nwid, discard.nwid, fmt_dec); -WIRELESS_SHOW(crypt, discard.code, fmt_dec); -WIRELESS_SHOW(fragment, discard.fragment, fmt_dec); -WIRELESS_SHOW(misc, discard.misc, fmt_dec); -WIRELESS_SHOW(retries, discard.retries, fmt_dec); -WIRELESS_SHOW(beacon, miss.beacon, fmt_dec); - +#if IS_ENABLED(CONFIG_WIRELESS_EXT) || IS_ENABLED(CONFIG_CFG80211)  static struct attribute *wireless_attrs[] = { -	&dev_attr_status.attr, -	&dev_attr_link.attr, -	&dev_attr_level.attr, -	&dev_attr_noise.attr, -	&dev_attr_nwid.attr, -	&dev_attr_crypt.attr, -	&dev_attr_fragment.attr, -	&dev_attr_retries.attr, -	&dev_attr_misc.attr, -	&dev_attr_beacon.attr,  	NULL  }; @@ -473,19 +506,12 @@ static struct attribute_group wireless_group = {  	.attrs = wireless_attrs,  };  #endif + +#else /* CONFIG_SYSFS */ +#define net_class_groups	NULL  #endif /* CONFIG_SYSFS */ -#ifdef CONFIG_RPS -/* - * RX queue sysfs structures and functions. - */ -struct rx_queue_attribute { -	struct attribute attr; -	ssize_t (*show)(struct netdev_rx_queue *queue, -	    struct rx_queue_attribute *attr, char *buf); -	ssize_t (*store)(struct netdev_rx_queue *queue, -	    struct rx_queue_attribute *attr, const char *buf, size_t len); -}; +#ifdef CONFIG_SYSFS  #define to_rx_queue_attr(_attr) container_of(_attr,		\      struct rx_queue_attribute, attr) @@ -520,6 +546,7 @@ static const struct sysfs_ops rx_queue_sysfs_ops = {  	.store = rx_queue_attr_store,  }; +#ifdef CONFIG_RPS  static ssize_t show_rps_map(struct netdev_rx_queue *queue,  			    struct rx_queue_attribute *attribute, char *buf)  { @@ -550,13 +577,6 @@ static ssize_t show_rps_map(struct netdev_rx_queue *queue,  	return len;  } -static void rps_map_release(struct rcu_head *rcu) -{ -	struct rps_map *map = container_of(rcu, struct rps_map, rcu); - -	kfree(map); -} -  static ssize_t store_rps_map(struct netdev_rx_queue *queue,  		      struct rx_queue_attribute *attribute,  		      const char *buf, size_t len) @@ -578,7 +598,7 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue,  		return err;  	} -	map = kzalloc(max_t(unsigned, +	map = kzalloc(max_t(unsigned int,  	    RPS_MAP_SIZE(cpumask_weight(mask)), L1_CACHE_BYTES),  	    GFP_KERNEL);  	if (!map) { @@ -603,9 +623,12 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue,  	rcu_assign_pointer(queue->rps_map, map);  	spin_unlock(&rps_map_lock); -	if (old_map) -		call_rcu(&old_map->rcu, rps_map_release); - +	if (map) +		static_key_slow_inc(&rps_needed); +	if (old_map) { +		kfree_rcu(old_map, rcu); +		static_key_slow_dec(&rps_needed); +	}  	free_cpumask_var(mask);  	return len;  } @@ -615,65 +638,68 @@ static ssize_t show_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue,  					   char *buf)  {  	struct rps_dev_flow_table *flow_table; -	unsigned int val = 0; +	unsigned long val = 0;  	rcu_read_lock();  	flow_table = rcu_dereference(queue->rps_flow_table);  	if (flow_table) -		val = flow_table->mask + 1; +		val = (unsigned long)flow_table->mask + 1;  	rcu_read_unlock(); -	return sprintf(buf, "%u\n", val); -} - -static void rps_dev_flow_table_release_work(struct work_struct *work) -{ -	struct rps_dev_flow_table *table = container_of(work, -	    struct rps_dev_flow_table, free_work); - -	vfree(table); +	return sprintf(buf, "%lu\n", val);  }  static void rps_dev_flow_table_release(struct rcu_head *rcu)  {  	struct rps_dev_flow_table *table = container_of(rcu,  	    struct rps_dev_flow_table, rcu); - -	INIT_WORK(&table->free_work, rps_dev_flow_table_release_work); -	schedule_work(&table->free_work); +	vfree(table);  }  static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue,  				     struct rx_queue_attribute *attr,  				     const char *buf, size_t len)  { -	unsigned int count; -	char *endp; +	unsigned long mask, count;  	struct rps_dev_flow_table *table, *old_table;  	static DEFINE_SPINLOCK(rps_dev_flow_lock); +	int rc;  	if (!capable(CAP_NET_ADMIN))  		return -EPERM; -	count = simple_strtoul(buf, &endp, 0); -	if (endp == buf) -		return -EINVAL; +	rc = kstrtoul(buf, 0, &count); +	if (rc < 0) +		return rc;  	if (count) { -		int i; - -		if (count > 1<<30) { +		mask = count - 1; +		/* mask = roundup_pow_of_two(count) - 1; +		 * without overflows... +		 */ +		while ((mask | (mask >> 1)) != mask) +			mask |= (mask >> 1); +		/* On 64 bit arches, must check mask fits in table->mask (u32), +		 * and on 32bit arches, must check +		 * RPS_DEV_FLOW_TABLE_SIZE(mask + 1) doesn't overflow. +		 */ +#if BITS_PER_LONG > 32 +		if (mask > (unsigned long)(u32)mask) +			return -EINVAL; +#else +		if (mask > (ULONG_MAX - RPS_DEV_FLOW_TABLE_SIZE(1)) +				/ sizeof(struct rps_dev_flow)) {  			/* Enforce a limit to prevent overflow */  			return -EINVAL;  		} -		count = roundup_pow_of_two(count); -		table = vmalloc(RPS_DEV_FLOW_TABLE_SIZE(count)); +#endif +		table = vmalloc(RPS_DEV_FLOW_TABLE_SIZE(mask + 1));  		if (!table)  			return -ENOMEM; -		table->mask = count - 1; -		for (i = 0; i < count; i++) -			table->flows[i].cpu = RPS_NO_CPU; +		table->mask = mask; +		for (count = 0; count <= mask; count++) +			table->flows[count].cpu = RPS_NO_CPU;  	} else  		table = NULL; @@ -696,40 +722,58 @@ static struct rx_queue_attribute rps_cpus_attribute =  static struct rx_queue_attribute rps_dev_flow_table_cnt_attribute =  	__ATTR(rps_flow_cnt, S_IRUGO | S_IWUSR,  	    show_rps_dev_flow_table_cnt, store_rps_dev_flow_table_cnt); +#endif /* CONFIG_RPS */  static struct attribute *rx_queue_default_attrs[] = { +#ifdef CONFIG_RPS  	&rps_cpus_attribute.attr,  	&rps_dev_flow_table_cnt_attribute.attr, +#endif  	NULL  };  static void rx_queue_release(struct kobject *kobj)  {  	struct netdev_rx_queue *queue = to_rx_queue(kobj); +#ifdef CONFIG_RPS  	struct rps_map *map;  	struct rps_dev_flow_table *flow_table; -	map = rcu_dereference_raw(queue->rps_map); +	map = rcu_dereference_protected(queue->rps_map, 1);  	if (map) {  		RCU_INIT_POINTER(queue->rps_map, NULL); -		call_rcu(&map->rcu, rps_map_release); +		kfree_rcu(map, rcu);  	} -	flow_table = rcu_dereference_raw(queue->rps_flow_table); +	flow_table = rcu_dereference_protected(queue->rps_flow_table, 1);  	if (flow_table) {  		RCU_INIT_POINTER(queue->rps_flow_table, NULL);  		call_rcu(&flow_table->rcu, rps_dev_flow_table_release);  	} +#endif  	memset(kobj, 0, sizeof(*kobj));  	dev_put(queue->dev);  } +static const void *rx_queue_namespace(struct kobject *kobj) +{ +	struct netdev_rx_queue *queue = to_rx_queue(kobj); +	struct device *dev = &queue->dev->dev; +	const void *ns = NULL; + +	if (dev->class && dev->class->ns_type) +		ns = dev->class->namespace(dev); + +	return ns; +} +  static struct kobj_type rx_queue_ktype = {  	.sysfs_ops = &rx_queue_sysfs_ops,  	.release = rx_queue_release,  	.default_attrs = rx_queue_default_attrs, +	.namespace = rx_queue_namespace  };  static int rx_queue_add_kobject(struct net_device *net, int index) @@ -741,25 +785,36 @@ static int rx_queue_add_kobject(struct net_device *net, int index)  	kobj->kset = net->queues_kset;  	error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL,  	    "rx-%u", index); -	if (error) { -		kobject_put(kobj); -		return error; +	if (error) +		goto exit; + +	if (net->sysfs_rx_queue_group) { +		error = sysfs_create_group(kobj, net->sysfs_rx_queue_group); +		if (error) +			goto exit;  	}  	kobject_uevent(kobj, KOBJ_ADD);  	dev_hold(queue->dev);  	return error; +exit: +	kobject_put(kobj); +	return error;  } -#endif /* CONFIG_RPS */ +#endif /* CONFIG_SYSFS */  int  net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num)  { -#ifdef CONFIG_RPS +#ifdef CONFIG_SYSFS  	int i;  	int error = 0; +#ifndef CONFIG_RPS +	if (!net->sysfs_rx_queue_group) +		return 0; +#endif  	for (i = old_num; i < new_num; i++) {  		error = rx_queue_add_kobject(net, i);  		if (error) { @@ -768,8 +823,12 @@ net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num)  		}  	} -	while (--i >= new_num) +	while (--i >= new_num) { +		if (net->sysfs_rx_queue_group) +			sysfs_remove_group(&net->_rx[i].kobj, +					   net->sysfs_rx_queue_group);  		kobject_put(&net->_rx[i].kobj); +	}  	return error;  #else @@ -777,7 +836,7 @@ net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num)  #endif  } -#ifdef CONFIG_XPS +#ifdef CONFIG_SYSFS  /*   * netdev_queue sysfs structures and functions.   */ @@ -823,15 +882,139 @@ static const struct sysfs_ops netdev_queue_sysfs_ops = {  	.store = netdev_queue_attr_store,  }; -static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue) +static ssize_t show_trans_timeout(struct netdev_queue *queue, +				  struct netdev_queue_attribute *attribute, +				  char *buf)  { -	struct net_device *dev = queue->dev; -	int i; +	unsigned long trans_timeout; -	for (i = 0; i < dev->num_tx_queues; i++) -		if (queue == &dev->_tx[i]) -			break; +	spin_lock_irq(&queue->_xmit_lock); +	trans_timeout = queue->trans_timeout; +	spin_unlock_irq(&queue->_xmit_lock); + +	return sprintf(buf, "%lu", trans_timeout); +} + +static struct netdev_queue_attribute queue_trans_timeout = +	__ATTR(tx_timeout, S_IRUGO, show_trans_timeout, NULL); + +#ifdef CONFIG_BQL +/* + * Byte queue limits sysfs structures and functions. + */ +static ssize_t bql_show(char *buf, unsigned int value) +{ +	return sprintf(buf, "%u\n", value); +} + +static ssize_t bql_set(const char *buf, const size_t count, +		       unsigned int *pvalue) +{ +	unsigned int value; +	int err; +	if (!strcmp(buf, "max") || !strcmp(buf, "max\n")) +		value = DQL_MAX_LIMIT; +	else { +		err = kstrtouint(buf, 10, &value); +		if (err < 0) +			return err; +		if (value > DQL_MAX_LIMIT) +			return -EINVAL; +	} + +	*pvalue = value; + +	return count; +} + +static ssize_t bql_show_hold_time(struct netdev_queue *queue, +				  struct netdev_queue_attribute *attr, +				  char *buf) +{ +	struct dql *dql = &queue->dql; + +	return sprintf(buf, "%u\n", jiffies_to_msecs(dql->slack_hold_time)); +} + +static ssize_t bql_set_hold_time(struct netdev_queue *queue, +				 struct netdev_queue_attribute *attribute, +				 const char *buf, size_t len) +{ +	struct dql *dql = &queue->dql; +	unsigned int value; +	int err; + +	err = kstrtouint(buf, 10, &value); +	if (err < 0) +		return err; + +	dql->slack_hold_time = msecs_to_jiffies(value); + +	return len; +} + +static struct netdev_queue_attribute bql_hold_time_attribute = +	__ATTR(hold_time, S_IRUGO | S_IWUSR, bql_show_hold_time, +	    bql_set_hold_time); + +static ssize_t bql_show_inflight(struct netdev_queue *queue, +				 struct netdev_queue_attribute *attr, +				 char *buf) +{ +	struct dql *dql = &queue->dql; + +	return sprintf(buf, "%u\n", dql->num_queued - dql->num_completed); +} + +static struct netdev_queue_attribute bql_inflight_attribute = +	__ATTR(inflight, S_IRUGO, bql_show_inflight, NULL); + +#define BQL_ATTR(NAME, FIELD)						\ +static ssize_t bql_show_ ## NAME(struct netdev_queue *queue,		\ +				 struct netdev_queue_attribute *attr,	\ +				 char *buf)				\ +{									\ +	return bql_show(buf, queue->dql.FIELD);				\ +}									\ +									\ +static ssize_t bql_set_ ## NAME(struct netdev_queue *queue,		\ +				struct netdev_queue_attribute *attr,	\ +				const char *buf, size_t len)		\ +{									\ +	return bql_set(buf, len, &queue->dql.FIELD);			\ +}									\ +									\ +static struct netdev_queue_attribute bql_ ## NAME ## _attribute =	\ +	__ATTR(NAME, S_IRUGO | S_IWUSR, bql_show_ ## NAME,		\ +	    bql_set_ ## NAME); + +BQL_ATTR(limit, limit) +BQL_ATTR(limit_max, max_limit) +BQL_ATTR(limit_min, min_limit) + +static struct attribute *dql_attrs[] = { +	&bql_limit_attribute.attr, +	&bql_limit_max_attribute.attr, +	&bql_limit_min_attribute.attr, +	&bql_hold_time_attribute.attr, +	&bql_inflight_attribute.attr, +	NULL +}; + +static struct attribute_group dql_group = { +	.name  = "byte_queue_limits", +	.attrs  = dql_attrs, +}; +#endif /* CONFIG_BQL */ + +#ifdef CONFIG_XPS +static unsigned int get_netdev_queue_index(struct netdev_queue *queue) +{ +	struct net_device *dev = queue->dev; +	unsigned int i; + +	i = queue - dev->_tx;  	BUG_ON(i >= dev->num_tx_queues);  	return i; @@ -883,37 +1066,14 @@ static ssize_t show_xps_map(struct netdev_queue *queue,  	return len;  } -static void xps_map_release(struct rcu_head *rcu) -{ -	struct xps_map *map = container_of(rcu, struct xps_map, rcu); - -	kfree(map); -} - -static void xps_dev_maps_release(struct rcu_head *rcu) -{ -	struct xps_dev_maps *dev_maps = -	    container_of(rcu, struct xps_dev_maps, rcu); - -	kfree(dev_maps); -} - -static DEFINE_MUTEX(xps_map_mutex); -#define xmap_dereference(P)		\ -	rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) -  static ssize_t store_xps_map(struct netdev_queue *queue,  		      struct netdev_queue_attribute *attribute,  		      const char *buf, size_t len)  {  	struct net_device *dev = queue->dev; -	cpumask_var_t mask; -	int err, i, cpu, pos, map_len, alloc_len, need_set;  	unsigned long index; -	struct xps_map *map, *new_map; -	struct xps_dev_maps *dev_maps, *new_dev_maps; -	int nonempty = 0; -	int numa_node = -2; +	cpumask_var_t mask; +	int err;  	if (!capable(CAP_NET_ADMIN))  		return -EPERM; @@ -929,169 +1089,50 @@ static ssize_t store_xps_map(struct netdev_queue *queue,  		return err;  	} -	new_dev_maps = kzalloc(max_t(unsigned, -	    XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES), GFP_KERNEL); -	if (!new_dev_maps) { -		free_cpumask_var(mask); -		return -ENOMEM; -	} - -	mutex_lock(&xps_map_mutex); - -	dev_maps = xmap_dereference(dev->xps_maps); - -	for_each_possible_cpu(cpu) { -		map = dev_maps ? -			xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; -		new_map = map; -		if (map) { -			for (pos = 0; pos < map->len; pos++) -				if (map->queues[pos] == index) -					break; -			map_len = map->len; -			alloc_len = map->alloc_len; -		} else -			pos = map_len = alloc_len = 0; - -		need_set = cpu_isset(cpu, *mask) && cpu_online(cpu); -#ifdef CONFIG_NUMA -		if (need_set) { -			if (numa_node == -2) -				numa_node = cpu_to_node(cpu); -			else if (numa_node != cpu_to_node(cpu)) -				numa_node = -1; -		} -#endif -		if (need_set && pos >= map_len) { -			/* Need to add queue to this CPU's map */ -			if (map_len >= alloc_len) { -				alloc_len = alloc_len ? -				    2 * alloc_len : XPS_MIN_MAP_ALLOC; -				new_map = kzalloc_node(XPS_MAP_SIZE(alloc_len), -						       GFP_KERNEL, -						       cpu_to_node(cpu)); -				if (!new_map) -					goto error; -				new_map->alloc_len = alloc_len; -				for (i = 0; i < map_len; i++) -					new_map->queues[i] = map->queues[i]; -				new_map->len = map_len; -			} -			new_map->queues[new_map->len++] = index; -		} else if (!need_set && pos < map_len) { -			/* Need to remove queue from this CPU's map */ -			if (map_len > 1) -				new_map->queues[pos] = -				    new_map->queues[--new_map->len]; -			else -				new_map = NULL; -		} -		RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], new_map); -	} - -	/* Cleanup old maps */ -	for_each_possible_cpu(cpu) { -		map = dev_maps ? -			xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; -		if (map && xmap_dereference(new_dev_maps->cpu_map[cpu]) != map) -			call_rcu(&map->rcu, xps_map_release); -		if (new_dev_maps->cpu_map[cpu]) -			nonempty = 1; -	} - -	if (nonempty) -		rcu_assign_pointer(dev->xps_maps, new_dev_maps); -	else { -		kfree(new_dev_maps); -		rcu_assign_pointer(dev->xps_maps, NULL); -	} - -	if (dev_maps) -		call_rcu(&dev_maps->rcu, xps_dev_maps_release); - -	netdev_queue_numa_node_write(queue, (numa_node >= 0) ? numa_node : -1); - -	mutex_unlock(&xps_map_mutex); +	err = netif_set_xps_queue(dev, mask, index);  	free_cpumask_var(mask); -	return len; -error: -	mutex_unlock(&xps_map_mutex); - -	if (new_dev_maps) -		for_each_possible_cpu(i) -			kfree(rcu_dereference_protected( -				new_dev_maps->cpu_map[i], -				1)); -	kfree(new_dev_maps); -	free_cpumask_var(mask); -	return -ENOMEM; +	return err ? : len;  }  static struct netdev_queue_attribute xps_cpus_attribute =      __ATTR(xps_cpus, S_IRUGO | S_IWUSR, show_xps_map, store_xps_map); +#endif /* CONFIG_XPS */  static struct attribute *netdev_queue_default_attrs[] = { +	&queue_trans_timeout.attr, +#ifdef CONFIG_XPS  	&xps_cpus_attribute.attr, +#endif  	NULL  };  static void netdev_queue_release(struct kobject *kobj)  {  	struct netdev_queue *queue = to_netdev_queue(kobj); -	struct net_device *dev = queue->dev; -	struct xps_dev_maps *dev_maps; -	struct xps_map *map; -	unsigned long index; -	int i, pos, nonempty = 0; - -	index = get_netdev_queue_index(queue); -	mutex_lock(&xps_map_mutex); -	dev_maps = xmap_dereference(dev->xps_maps); - -	if (dev_maps) { -		for_each_possible_cpu(i) { -			map = xmap_dereference(dev_maps->cpu_map[i]); -			if (!map) -				continue; - -			for (pos = 0; pos < map->len; pos++) -				if (map->queues[pos] == index) -					break; - -			if (pos < map->len) { -				if (map->len > 1) -					map->queues[pos] = -					    map->queues[--map->len]; -				else { -					RCU_INIT_POINTER(dev_maps->cpu_map[i], -					    NULL); -					call_rcu(&map->rcu, xps_map_release); -					map = NULL; -				} -			} -			if (map) -				nonempty = 1; -		} +	memset(kobj, 0, sizeof(*kobj)); +	dev_put(queue->dev); +} -		if (!nonempty) { -			RCU_INIT_POINTER(dev->xps_maps, NULL); -			call_rcu(&dev_maps->rcu, xps_dev_maps_release); -		} -	} +static const void *netdev_queue_namespace(struct kobject *kobj) +{ +	struct netdev_queue *queue = to_netdev_queue(kobj); +	struct device *dev = &queue->dev->dev; +	const void *ns = NULL; -	mutex_unlock(&xps_map_mutex); +	if (dev->class && dev->class->ns_type) +		ns = dev->class->namespace(dev); -	memset(kobj, 0, sizeof(*kobj)); -	dev_put(queue->dev); +	return ns;  }  static struct kobj_type netdev_queue_ktype = {  	.sysfs_ops = &netdev_queue_sysfs_ops,  	.release = netdev_queue_release,  	.default_attrs = netdev_queue_default_attrs, +	.namespace = netdev_queue_namespace,  };  static int netdev_queue_add_kobject(struct net_device *net, int index) @@ -1103,22 +1144,29 @@ static int netdev_queue_add_kobject(struct net_device *net, int index)  	kobj->kset = net->queues_kset;  	error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL,  	    "tx-%u", index); -	if (error) { -		kobject_put(kobj); -		return error; -	} +	if (error) +		goto exit; + +#ifdef CONFIG_BQL +	error = sysfs_create_group(kobj, &dql_group); +	if (error) +		goto exit; +#endif  	kobject_uevent(kobj, KOBJ_ADD);  	dev_hold(queue->dev); +	return 0; +exit: +	kobject_put(kobj);  	return error;  } -#endif /* CONFIG_XPS */ +#endif /* CONFIG_SYSFS */  int  netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num)  { -#ifdef CONFIG_XPS +#ifdef CONFIG_SYSFS  	int i;  	int error = 0; @@ -1130,27 +1178,30 @@ netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num)  		}  	} -	while (--i >= new_num) -		kobject_put(&net->_tx[i].kobj); +	while (--i >= new_num) { +		struct netdev_queue *queue = net->_tx + i; + +#ifdef CONFIG_BQL +		sysfs_remove_group(&queue->kobj, &dql_group); +#endif +		kobject_put(&queue->kobj); +	}  	return error;  #else  	return 0; -#endif +#endif /* CONFIG_SYSFS */  }  static int register_queue_kobjects(struct net_device *net)  {  	int error = 0, txq = 0, rxq = 0, real_rx = 0, real_tx = 0; -#if defined(CONFIG_RPS) || defined(CONFIG_XPS) +#ifdef CONFIG_SYSFS  	net->queues_kset = kset_create_and_add("queues",  	    NULL, &net->dev.kobj);  	if (!net->queues_kset)  		return -ENOMEM; -#endif - -#ifdef CONFIG_RPS  	real_rx = net->real_num_rx_queues;  #endif  	real_tx = net->real_num_tx_queues; @@ -1177,21 +1228,33 @@ static void remove_queue_kobjects(struct net_device *net)  {  	int real_rx = 0, real_tx = 0; -#ifdef CONFIG_RPS +#ifdef CONFIG_SYSFS  	real_rx = net->real_num_rx_queues;  #endif  	real_tx = net->real_num_tx_queues;  	net_rx_queue_update_kobjects(net, real_rx, 0);  	netdev_queue_update_kobjects(net, real_tx, 0); -#if defined(CONFIG_RPS) || defined(CONFIG_XPS) +#ifdef CONFIG_SYSFS  	kset_unregister(net->queues_kset);  #endif  } -static const void *net_current_ns(void) +static bool net_current_may_mount(void) +{ +	struct net *net = current->nsproxy->net_ns; + +	return ns_capable(net->user_ns, CAP_SYS_ADMIN); +} + +static void *net_grab_current_ns(void)  { -	return current->nsproxy->net_ns; +	struct net *ns = current->nsproxy->net_ns; +#ifdef CONFIG_NET_NS +	if (ns) +		atomic_inc(&ns->passive); +#endif +	return ns;  }  static const void *net_initial_ns(void) @@ -1206,23 +1269,14 @@ static const void *net_netlink_ns(struct sock *sk)  struct kobj_ns_type_operations net_ns_type_operations = {  	.type = KOBJ_NS_TYPE_NET, -	.current_ns = net_current_ns, +	.current_may_mount = net_current_may_mount, +	.grab_current_ns = net_grab_current_ns,  	.netlink_ns = net_netlink_ns,  	.initial_ns = net_initial_ns, +	.drop_ns = net_drop_ns,  };  EXPORT_SYMBOL_GPL(net_ns_type_operations); -static void net_kobj_ns_exit(struct net *net) -{ -	kobj_ns_exit(KOBJ_NS_TYPE_NET, net); -} - -static struct pernet_operations kobj_net_ops = { -	.exit = net_kobj_ns_exit, -}; - - -#ifdef CONFIG_HOTPLUG  static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)  {  	struct net_device *dev = to_net_dev(d); @@ -1241,7 +1295,6 @@ static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)  exit:  	return retval;  } -#endif  /*   *	netdev_release -- destroy and free a dead device. @@ -1254,7 +1307,7 @@ static void netdev_release(struct device *d)  	BUG_ON(dev->reg_state != NETREG_RELEASED);  	kfree(dev->ifalias); -	kfree((char *)dev - dev->padded); +	netdev_freemem(dev);  }  static const void *net_namespace(struct device *d) @@ -1267,12 +1320,8 @@ static const void *net_namespace(struct device *d)  static struct class net_class = {  	.name = "net",  	.dev_release = netdev_release, -#ifdef CONFIG_SYSFS -	.dev_attrs = net_class_attributes, -#endif /* CONFIG_SYSFS */ -#ifdef CONFIG_HOTPLUG +	.dev_groups = net_class_groups,  	.dev_uevent = netdev_uevent, -#endif  	.ns_type = &net_ns_type_operations,  	.namespace = net_namespace,  }; @@ -1288,6 +1337,8 @@ void netdev_unregister_kobject(struct net_device * net)  	remove_queue_kobjects(net); +	pm_runtime_set_memalloc_noio(dev, false); +  	device_del(dev);  } @@ -1311,10 +1362,11 @@ int netdev_register_kobject(struct net_device *net)  		groups++;  	*groups++ = &netstat_group; -#ifdef CONFIG_WIRELESS_EXT_SYSFS + +#if IS_ENABLED(CONFIG_WIRELESS_EXT) || IS_ENABLED(CONFIG_CFG80211)  	if (net->ieee80211_ptr)  		*groups++ = &wireless_group; -#ifdef CONFIG_WIRELESS_EXT +#if IS_ENABLED(CONFIG_WIRELESS_EXT)  	else if (net->wireless_handlers)  		*groups++ = &wireless_group;  #endif @@ -1331,24 +1383,27 @@ int netdev_register_kobject(struct net_device *net)  		return error;  	} +	pm_runtime_set_memalloc_noio(dev, true); +  	return error;  } -int netdev_class_create_file(struct class_attribute *class_attr) +int netdev_class_create_file_ns(struct class_attribute *class_attr, +				const void *ns)  { -	return class_create_file(&net_class, class_attr); +	return class_create_file_ns(&net_class, class_attr, ns);  } -EXPORT_SYMBOL(netdev_class_create_file); +EXPORT_SYMBOL(netdev_class_create_file_ns); -void netdev_class_remove_file(struct class_attribute *class_attr) +void netdev_class_remove_file_ns(struct class_attribute *class_attr, +				 const void *ns)  { -	class_remove_file(&net_class, class_attr); +	class_remove_file_ns(&net_class, class_attr, ns);  } -EXPORT_SYMBOL(netdev_class_remove_file); +EXPORT_SYMBOL(netdev_class_remove_file_ns); -int netdev_kobject_init(void) +int __init netdev_kobject_init(void)  {  	kobj_ns_type_register(&net_ns_type_operations); -	register_pernet_subsys(&kobj_net_ops);  	return class_register(&net_class);  }  | 
