aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/netprio_cgroup.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-11-22 10:32:46 -0500
committerTejun Heo <tj@kernel.org>2012-11-22 10:32:46 -0500
commit4a6ee25c7ea24decdf17af6fa2f2ab00acc7e4bf (patch)
treecbb106de43c38c9a44c17d1d9f48671dd334bbbc /net/core/netprio_cgroup.c
parent52bca930c913c85ed1157ebc8f9dd9bc38a8c2c3 (diff)
netprio_cgroup: reimplement priomap expansion
netprio kept track of the highest prioidx allocated and resized priomaps accordingly when necessary. This makes it necessary to keep track of prioidx allocation and may end up resizing on every new prioidx. Update extend_netdev_table() such that it takes @target_idx which the priomap should be able to accomodate. If the priomap is large enough, nothing happens; otherwise, the size is doubled until @target_idx can be accomodated. This makes max_prioidx and write_update_netdev_table() unnecessary. write_priomap() now calls extend_netdev_table() directly. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Neil Horman <nhorman@tuxdriver.com> Tested-and-Acked-by: Daniel Wagner <daniel.wagner@bmw-carit.de> Acked-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/netprio_cgroup.c')
-rw-r--r--net/core/netprio_cgroup.c56
1 files changed, 33 insertions, 23 deletions
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index 92cc54c81f0b..569d83da53d0 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -27,11 +27,11 @@
27 27
28#include <linux/fdtable.h> 28#include <linux/fdtable.h>
29 29
30#define PRIOMAP_MIN_SZ 128
30#define PRIOIDX_SZ 128 31#define PRIOIDX_SZ 128
31 32
32static unsigned long prioidx_map[PRIOIDX_SZ]; 33static unsigned long prioidx_map[PRIOIDX_SZ];
33static DEFINE_SPINLOCK(prioidx_map_lock); 34static DEFINE_SPINLOCK(prioidx_map_lock);
34static atomic_t max_prioidx = ATOMIC_INIT(0);
35 35
36static inline struct cgroup_netprio_state *cgrp_netprio_state(struct cgroup *cgrp) 36static inline struct cgroup_netprio_state *cgrp_netprio_state(struct cgroup *cgrp)
37{ 37{
@@ -51,8 +51,6 @@ static int get_prioidx(u32 *prio)
51 return -ENOSPC; 51 return -ENOSPC;
52 } 52 }
53 set_bit(prioidx, prioidx_map); 53 set_bit(prioidx, prioidx_map);
54 if (atomic_read(&max_prioidx) < prioidx)
55 atomic_set(&max_prioidx, prioidx);
56 spin_unlock_irqrestore(&prioidx_map_lock, flags); 54 spin_unlock_irqrestore(&prioidx_map_lock, flags);
57 *prio = prioidx; 55 *prio = prioidx;
58 return 0; 56 return 0;
@@ -67,15 +65,40 @@ static void put_prioidx(u32 idx)
67 spin_unlock_irqrestore(&prioidx_map_lock, flags); 65 spin_unlock_irqrestore(&prioidx_map_lock, flags);
68} 66}
69 67
70static int extend_netdev_table(struct net_device *dev, u32 new_len) 68/*
69 * Extend @dev->priomap so that it's large enough to accomodate
70 * @target_idx. @dev->priomap.priomap_len > @target_idx after successful
71 * return. Must be called under rtnl lock.
72 */
73static int extend_netdev_table(struct net_device *dev, u32 target_idx)
71{ 74{
72 size_t new_size = sizeof(struct netprio_map) + 75 struct netprio_map *old, *new;
73 ((sizeof(u32) * new_len)); 76 size_t new_sz, new_len;
74 struct netprio_map *new = kzalloc(new_size, GFP_KERNEL);
75 struct netprio_map *old;
76 77
78 /* is the existing priomap large enough? */
77 old = rtnl_dereference(dev->priomap); 79 old = rtnl_dereference(dev->priomap);
80 if (old && old->priomap_len > target_idx)
81 return 0;
82
83 /*
84 * Determine the new size. Let's keep it power-of-two. We start
85 * from PRIOMAP_MIN_SZ and double it until it's large enough to
86 * accommodate @target_idx.
87 */
88 new_sz = PRIOMAP_MIN_SZ;
89 while (true) {
90 new_len = (new_sz - offsetof(struct netprio_map, priomap)) /
91 sizeof(new->priomap[0]);
92 if (new_len > target_idx)
93 break;
94 new_sz *= 2;
95 /* overflowed? */
96 if (WARN_ON(new_sz < PRIOMAP_MIN_SZ))
97 return -ENOSPC;
98 }
78 99
100 /* allocate & copy */
101 new = kzalloc(new_sz, GFP_KERNEL);
79 if (!new) { 102 if (!new) {
80 pr_warn("Unable to alloc new priomap!\n"); 103 pr_warn("Unable to alloc new priomap!\n");
81 return -ENOMEM; 104 return -ENOMEM;
@@ -87,26 +110,13 @@ static int extend_netdev_table(struct net_device *dev, u32 new_len)
87 110
88 new->priomap_len = new_len; 111 new->priomap_len = new_len;
89 112
113 /* install the new priomap */
90 rcu_assign_pointer(dev->priomap, new); 114 rcu_assign_pointer(dev->priomap, new);
91 if (old) 115 if (old)
92 kfree_rcu(old, rcu); 116 kfree_rcu(old, rcu);
93 return 0; 117 return 0;
94} 118}
95 119
96static int write_update_netdev_table(struct net_device *dev)
97{
98 int ret = 0;
99 u32 max_len;
100 struct netprio_map *map;
101
102 max_len = atomic_read(&max_prioidx) + 1;
103 map = rtnl_dereference(dev->priomap);
104 if (!map || map->priomap_len < max_len)
105 ret = extend_netdev_table(dev, max_len);
106
107 return ret;
108}
109
110static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) 120static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp)
111{ 121{
112 struct cgroup_netprio_state *cs; 122 struct cgroup_netprio_state *cs;
@@ -191,7 +201,7 @@ static int write_priomap(struct cgroup *cgrp, struct cftype *cft,
191 201
192 rtnl_lock(); 202 rtnl_lock();
193 203
194 ret = write_update_netdev_table(dev); 204 ret = extend_netdev_table(dev, prioidx);
195 if (ret) 205 if (ret)
196 goto out_unlock; 206 goto out_unlock;
197 207