diff options
Diffstat (limited to 'net/core/net-sysfs.c')
| -rw-r--r-- | net/core/net-sysfs.c | 159 | 
1 files changed, 153 insertions, 6 deletions
| diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index bb7e80f4ced3..bd67c4d0fcfd 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -26,6 +26,7 @@  #include <linux/pm_runtime.h>  #include <linux/of.h>  #include <linux/of_net.h> +#include <linux/cpu.h>  #include "net-sysfs.h" @@ -905,11 +906,20 @@ static const void *rx_queue_namespace(struct kobject *kobj)  	return ns;  } +static void rx_queue_get_ownership(struct kobject *kobj, +				   kuid_t *uid, kgid_t *gid) +{ +	const struct net *net = rx_queue_namespace(kobj); + +	net_ns_get_ownership(net, uid, gid); +} +  static struct kobj_type rx_queue_ktype __ro_after_init = {  	.sysfs_ops = &rx_queue_sysfs_ops,  	.release = rx_queue_release,  	.default_attrs = rx_queue_default_attrs, -	.namespace = rx_queue_namespace +	.namespace = rx_queue_namespace, +	.get_ownership = rx_queue_get_ownership,  };  static int rx_queue_add_kobject(struct net_device *dev, int index) @@ -1047,13 +1057,30 @@ static ssize_t traffic_class_show(struct netdev_queue *queue,  				  char *buf)  {  	struct net_device *dev = queue->dev; -	int index = get_netdev_queue_index(queue); -	int tc = netdev_txq_to_tc(dev, index); +	int index; +	int tc; + +	if (!netif_is_multiqueue(dev)) +		return -ENOENT; +	index = get_netdev_queue_index(queue); + +	/* If queue belongs to subordinate dev use its TC mapping */ +	dev = netdev_get_tx_queue(dev, index)->sb_dev ? : dev; + +	tc = netdev_txq_to_tc(dev, index);  	if (tc < 0)  		return -EINVAL; -	return sprintf(buf, "%u\n", tc); +	/* We can report the traffic class one of two ways: +	 * Subordinate device traffic classes are reported with the traffic +	 * class first, and then the subordinate class so for example TC0 on +	 * subordinate device 2 will be reported as "0-2". If the queue +	 * belongs to the root device it will be reported with just the +	 * traffic class, so just "0" for TC 0 for example. +	 */ +	return dev->num_tc < 0 ? sprintf(buf, "%u%d\n", tc, dev->num_tc) : +				 sprintf(buf, "%u\n", tc);  }  #ifdef CONFIG_XPS @@ -1070,6 +1097,9 @@ static ssize_t tx_maxrate_store(struct netdev_queue *queue,  	int err, index = get_netdev_queue_index(queue);  	u32 rate = 0; +	if (!capable(CAP_NET_ADMIN)) +		return -EPERM; +  	err = kstrtou32(buf, 10, &rate);  	if (err < 0)  		return err; @@ -1214,10 +1244,20 @@ static ssize_t xps_cpus_show(struct netdev_queue *queue,  	cpumask_var_t mask;  	unsigned long index; +	if (!netif_is_multiqueue(dev)) +		return -ENOENT; +  	index = get_netdev_queue_index(queue);  	if (dev->num_tc) { +		/* Do not allow XPS on subordinate device directly */  		num_tc = dev->num_tc; +		if (num_tc < 0) +			return -EINVAL; + +		/* If queue belongs to subordinate dev use its map */ +		dev = netdev_get_tx_queue(dev, index)->sb_dev ? : dev; +  		tc = netdev_txq_to_tc(dev, index);  		if (tc < 0)  			return -EINVAL; @@ -1227,13 +1267,13 @@ static ssize_t xps_cpus_show(struct netdev_queue *queue,  		return -ENOMEM;  	rcu_read_lock(); -	dev_maps = rcu_dereference(dev->xps_maps); +	dev_maps = rcu_dereference(dev->xps_cpus_map);  	if (dev_maps) {  		for_each_possible_cpu(cpu) {  			int i, tci = cpu * num_tc + tc;  			struct xps_map *map; -			map = rcu_dereference(dev_maps->cpu_map[tci]); +			map = rcu_dereference(dev_maps->attr_map[tci]);  			if (!map)  				continue; @@ -1260,6 +1300,9 @@ static ssize_t xps_cpus_store(struct netdev_queue *queue,  	cpumask_var_t mask;  	int err; +	if (!netif_is_multiqueue(dev)) +		return -ENOENT; +  	if (!capable(CAP_NET_ADMIN))  		return -EPERM; @@ -1283,6 +1326,91 @@ static ssize_t xps_cpus_store(struct netdev_queue *queue,  static struct netdev_queue_attribute xps_cpus_attribute __ro_after_init  	= __ATTR_RW(xps_cpus); + +static ssize_t xps_rxqs_show(struct netdev_queue *queue, char *buf) +{ +	struct net_device *dev = queue->dev; +	struct xps_dev_maps *dev_maps; +	unsigned long *mask, index; +	int j, len, num_tc = 1, tc = 0; + +	index = get_netdev_queue_index(queue); + +	if (dev->num_tc) { +		num_tc = dev->num_tc; +		tc = netdev_txq_to_tc(dev, index); +		if (tc < 0) +			return -EINVAL; +	} +	mask = kcalloc(BITS_TO_LONGS(dev->num_rx_queues), sizeof(long), +		       GFP_KERNEL); +	if (!mask) +		return -ENOMEM; + +	rcu_read_lock(); +	dev_maps = rcu_dereference(dev->xps_rxqs_map); +	if (!dev_maps) +		goto out_no_maps; + +	for (j = -1; j = netif_attrmask_next(j, NULL, dev->num_rx_queues), +	     j < dev->num_rx_queues;) { +		int i, tci = j * num_tc + tc; +		struct xps_map *map; + +		map = rcu_dereference(dev_maps->attr_map[tci]); +		if (!map) +			continue; + +		for (i = map->len; i--;) { +			if (map->queues[i] == index) { +				set_bit(j, mask); +				break; +			} +		} +	} +out_no_maps: +	rcu_read_unlock(); + +	len = bitmap_print_to_pagebuf(false, buf, mask, dev->num_rx_queues); +	kfree(mask); + +	return len < PAGE_SIZE ? len : -EINVAL; +} + +static ssize_t xps_rxqs_store(struct netdev_queue *queue, const char *buf, +			      size_t len) +{ +	struct net_device *dev = queue->dev; +	struct net *net = dev_net(dev); +	unsigned long *mask, index; +	int err; + +	if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) +		return -EPERM; + +	mask = kcalloc(BITS_TO_LONGS(dev->num_rx_queues), sizeof(long), +		       GFP_KERNEL); +	if (!mask) +		return -ENOMEM; + +	index = get_netdev_queue_index(queue); + +	err = bitmap_parse(buf, len, mask, dev->num_rx_queues); +	if (err) { +		kfree(mask); +		return err; +	} + +	cpus_read_lock(); +	err = __netif_set_xps_queue(dev, mask, index, true); +	cpus_read_unlock(); + +	kfree(mask); +	return err ? : len; +} + +static struct netdev_queue_attribute xps_rxqs_attribute __ro_after_init +	= __ATTR_RW(xps_rxqs);  #endif /* CONFIG_XPS */  static struct attribute *netdev_queue_default_attrs[] __ro_after_init = { @@ -1290,6 +1418,7 @@ static struct attribute *netdev_queue_default_attrs[] __ro_after_init = {  	&queue_traffic_class.attr,  #ifdef CONFIG_XPS  	&xps_cpus_attribute.attr, +	&xps_rxqs_attribute.attr,  	&queue_tx_maxrate.attr,  #endif  	NULL @@ -1315,11 +1444,20 @@ static const void *netdev_queue_namespace(struct kobject *kobj)  	return ns;  } +static void netdev_queue_get_ownership(struct kobject *kobj, +				       kuid_t *uid, kgid_t *gid) +{ +	const struct net *net = netdev_queue_namespace(kobj); + +	net_ns_get_ownership(net, uid, gid); +} +  static struct kobj_type netdev_queue_ktype __ro_after_init = {  	.sysfs_ops = &netdev_queue_sysfs_ops,  	.release = netdev_queue_release,  	.default_attrs = netdev_queue_default_attrs,  	.namespace = netdev_queue_namespace, +	.get_ownership = netdev_queue_get_ownership,  };  static int netdev_queue_add_kobject(struct net_device *dev, int index) @@ -1509,6 +1647,14 @@ static const void *net_namespace(struct device *d)  	return dev_net(dev);  } +static void net_get_ownership(struct device *d, kuid_t *uid, kgid_t *gid) +{ +	struct net_device *dev = to_net_dev(d); +	const struct net *net = dev_net(dev); + +	net_ns_get_ownership(net, uid, gid); +} +  static struct class net_class __ro_after_init = {  	.name = "net",  	.dev_release = netdev_release, @@ -1516,6 +1662,7 @@ static struct class net_class __ro_after_init = {  	.dev_uevent = netdev_uevent,  	.ns_type = &net_ns_type_operations,  	.namespace = net_namespace, +	.get_ownership = net_get_ownership,  };  #ifdef CONFIG_OF_NET |