diff options
Diffstat (limited to 'net/core/netprio_cgroup.c')
| -rw-r--r-- | net/core/netprio_cgroup.c | 78 | 
1 files changed, 58 insertions, 20 deletions
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 5b8aa2fae48..b2e9caa1ad1 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -49,8 +49,9 @@ static int get_prioidx(u32 *prio)  		return -ENOSPC;  	}  	set_bit(prioidx, prioidx_map); +	if (atomic_read(&max_prioidx) < prioidx) +		atomic_set(&max_prioidx, prioidx);  	spin_unlock_irqrestore(&prioidx_map_lock, flags); -	atomic_set(&max_prioidx, prioidx);  	*prio = prioidx;  	return 0;  } @@ -64,7 +65,7 @@ static void put_prioidx(u32 idx)  	spin_unlock_irqrestore(&prioidx_map_lock, flags);  } -static void extend_netdev_table(struct net_device *dev, u32 new_len) +static int extend_netdev_table(struct net_device *dev, u32 new_len)  {  	size_t new_size = sizeof(struct netprio_map) +  			   ((sizeof(u32) * new_len)); @@ -76,7 +77,7 @@ static void extend_netdev_table(struct net_device *dev, u32 new_len)  	if (!new_priomap) {  		pr_warn("Unable to alloc new priomap!\n"); -		return; +		return -ENOMEM;  	}  	for (i = 0; @@ -89,46 +90,79 @@ static void extend_netdev_table(struct net_device *dev, u32 new_len)  	rcu_assign_pointer(dev->priomap, new_priomap);  	if (old_priomap)  		kfree_rcu(old_priomap, rcu); +	return 0;  } -static void update_netdev_tables(void) +static int write_update_netdev_table(struct net_device *dev)  { +	int ret = 0; +	u32 max_len; +	struct netprio_map *map; + +	rtnl_lock(); +	max_len = atomic_read(&max_prioidx) + 1; +	map = rtnl_dereference(dev->priomap); +	if (!map || map->priomap_len < max_len) +		ret = extend_netdev_table(dev, max_len); +	rtnl_unlock(); + +	return ret; +} + +static int update_netdev_tables(void) +{ +	int ret = 0;  	struct net_device *dev; -	u32 max_len = atomic_read(&max_prioidx) + 1; +	u32 max_len;  	struct netprio_map *map;  	rtnl_lock(); +	max_len = atomic_read(&max_prioidx) + 1;  	for_each_netdev(&init_net, dev) {  		map = rtnl_dereference(dev->priomap); -		if ((!map) || -		    (map->priomap_len < max_len)) -			extend_netdev_table(dev, max_len); +		/* +		 * don't allocate priomap if we didn't +		 * change net_prio.ifpriomap (map == NULL), +		 * this will speed up skb_update_prio. +		 */ +		if (map && map->priomap_len < max_len) { +			ret = extend_netdev_table(dev, max_len); +			if (ret < 0) +				break; +		}  	}  	rtnl_unlock(); +	return ret;  }  static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp)  {  	struct cgroup_netprio_state *cs; -	int ret; +	int ret = -EINVAL;  	cs = kzalloc(sizeof(*cs), GFP_KERNEL);  	if (!cs)  		return ERR_PTR(-ENOMEM); -	if (cgrp->parent && cgrp_netprio_state(cgrp->parent)->prioidx) { -		kfree(cs); -		return ERR_PTR(-EINVAL); -	} +	if (cgrp->parent && cgrp_netprio_state(cgrp->parent)->prioidx) +		goto out;  	ret = get_prioidx(&cs->prioidx); -	if (ret != 0) { +	if (ret < 0) {  		pr_warn("No space in priority index array\n"); -		kfree(cs); -		return ERR_PTR(ret); +		goto out; +	} + +	ret = update_netdev_tables(); +	if (ret < 0) { +		put_prioidx(cs->prioidx); +		goto out;  	}  	return &cs->css; +out: +	kfree(cs); +	return ERR_PTR(ret);  }  static void cgrp_destroy(struct cgroup *cgrp) @@ -141,7 +175,7 @@ static void cgrp_destroy(struct cgroup *cgrp)  	rtnl_lock();  	for_each_netdev(&init_net, dev) {  		map = rtnl_dereference(dev->priomap); -		if (map) +		if (map && cs->prioidx < map->priomap_len)  			map->priomap[cs->prioidx] = 0;  	}  	rtnl_unlock(); @@ -165,7 +199,7 @@ static int read_priomap(struct cgroup *cont, struct cftype *cft,  	rcu_read_lock();  	for_each_netdev_rcu(&init_net, dev) {  		map = rcu_dereference(dev->priomap); -		priority = map ? map->priomap[prioidx] : 0; +		priority = (map && prioidx < map->priomap_len) ? map->priomap[prioidx] : 0;  		cb->fill(cb, dev->name, priority);  	}  	rcu_read_unlock(); @@ -220,13 +254,17 @@ static int write_priomap(struct cgroup *cgrp, struct cftype *cft,  	if (!dev)  		goto out_free_devname; -	update_netdev_tables(); -	ret = 0; +	ret = write_update_netdev_table(dev); +	if (ret < 0) +		goto out_put_dev; +  	rcu_read_lock();  	map = rcu_dereference(dev->priomap);  	if (map)  		map->priomap[prioidx] = priority;  	rcu_read_unlock(); + +out_put_dev:  	dev_put(dev);  out_free_devname:  |