Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.4-rc2 337 lines 7.6 kB view raw
1/* 2 * net/core/netprio_cgroup.c Priority Control Group 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: Neil Horman <nhorman@tuxdriver.com> 10 */ 11 12#include <linux/module.h> 13#include <linux/slab.h> 14#include <linux/types.h> 15#include <linux/string.h> 16#include <linux/errno.h> 17#include <linux/skbuff.h> 18#include <linux/cgroup.h> 19#include <linux/rcupdate.h> 20#include <linux/atomic.h> 21#include <net/rtnetlink.h> 22#include <net/pkt_cls.h> 23#include <net/sock.h> 24#include <net/netprio_cgroup.h> 25 26static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp); 27static void cgrp_destroy(struct cgroup *cgrp); 28static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp); 29 30struct cgroup_subsys net_prio_subsys = { 31 .name = "net_prio", 32 .create = cgrp_create, 33 .destroy = cgrp_destroy, 34 .populate = cgrp_populate, 35#ifdef CONFIG_NETPRIO_CGROUP 36 .subsys_id = net_prio_subsys_id, 37#endif 38 .module = THIS_MODULE 39}; 40 41#define PRIOIDX_SZ 128 42 43static unsigned long prioidx_map[PRIOIDX_SZ]; 44static DEFINE_SPINLOCK(prioidx_map_lock); 45static atomic_t max_prioidx = ATOMIC_INIT(0); 46 47static inline struct cgroup_netprio_state *cgrp_netprio_state(struct cgroup *cgrp) 48{ 49 return container_of(cgroup_subsys_state(cgrp, net_prio_subsys_id), 50 struct cgroup_netprio_state, css); 51} 52 53static int get_prioidx(u32 *prio) 54{ 55 unsigned long flags; 56 u32 prioidx; 57 58 spin_lock_irqsave(&prioidx_map_lock, flags); 59 prioidx = find_first_zero_bit(prioidx_map, sizeof(unsigned long) * PRIOIDX_SZ); 60 if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) { 61 spin_unlock_irqrestore(&prioidx_map_lock, flags); 62 return -ENOSPC; 63 } 64 set_bit(prioidx, prioidx_map); 65 spin_unlock_irqrestore(&prioidx_map_lock, flags); 66 atomic_set(&max_prioidx, prioidx); 67 *prio = prioidx; 68 return 0; 69} 70 71static void put_prioidx(u32 idx) 72{ 73 unsigned long flags; 74 75 spin_lock_irqsave(&prioidx_map_lock, flags); 76 clear_bit(idx, prioidx_map); 77 spin_unlock_irqrestore(&prioidx_map_lock, flags); 78} 79 80static void extend_netdev_table(struct net_device *dev, u32 new_len) 81{ 82 size_t new_size = sizeof(struct netprio_map) + 83 ((sizeof(u32) * new_len)); 84 struct netprio_map *new_priomap = kzalloc(new_size, GFP_KERNEL); 85 struct netprio_map *old_priomap; 86 int i; 87 88 old_priomap = rtnl_dereference(dev->priomap); 89 90 if (!new_priomap) { 91 printk(KERN_WARNING "Unable to alloc new priomap!\n"); 92 return; 93 } 94 95 for (i = 0; 96 old_priomap && (i < old_priomap->priomap_len); 97 i++) 98 new_priomap->priomap[i] = old_priomap->priomap[i]; 99 100 new_priomap->priomap_len = new_len; 101 102 rcu_assign_pointer(dev->priomap, new_priomap); 103 if (old_priomap) 104 kfree_rcu(old_priomap, rcu); 105} 106 107static void update_netdev_tables(void) 108{ 109 struct net_device *dev; 110 u32 max_len = atomic_read(&max_prioidx) + 1; 111 struct netprio_map *map; 112 113 rtnl_lock(); 114 for_each_netdev(&init_net, dev) { 115 map = rtnl_dereference(dev->priomap); 116 if ((!map) || 117 (map->priomap_len < max_len)) 118 extend_netdev_table(dev, max_len); 119 } 120 rtnl_unlock(); 121} 122 123static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp) 124{ 125 struct cgroup_netprio_state *cs; 126 int ret; 127 128 cs = kzalloc(sizeof(*cs), GFP_KERNEL); 129 if (!cs) 130 return ERR_PTR(-ENOMEM); 131 132 if (cgrp->parent && cgrp_netprio_state(cgrp->parent)->prioidx) { 133 kfree(cs); 134 return ERR_PTR(-EINVAL); 135 } 136 137 ret = get_prioidx(&cs->prioidx); 138 if (ret != 0) { 139 printk(KERN_WARNING "No space in priority index array\n"); 140 kfree(cs); 141 return ERR_PTR(ret); 142 } 143 144 return &cs->css; 145} 146 147static void cgrp_destroy(struct cgroup *cgrp) 148{ 149 struct cgroup_netprio_state *cs; 150 struct net_device *dev; 151 struct netprio_map *map; 152 153 cs = cgrp_netprio_state(cgrp); 154 rtnl_lock(); 155 for_each_netdev(&init_net, dev) { 156 map = rtnl_dereference(dev->priomap); 157 if (map) 158 map->priomap[cs->prioidx] = 0; 159 } 160 rtnl_unlock(); 161 put_prioidx(cs->prioidx); 162 kfree(cs); 163} 164 165static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft) 166{ 167 return (u64)cgrp_netprio_state(cgrp)->prioidx; 168} 169 170static int read_priomap(struct cgroup *cont, struct cftype *cft, 171 struct cgroup_map_cb *cb) 172{ 173 struct net_device *dev; 174 u32 prioidx = cgrp_netprio_state(cont)->prioidx; 175 u32 priority; 176 struct netprio_map *map; 177 178 rcu_read_lock(); 179 for_each_netdev_rcu(&init_net, dev) { 180 map = rcu_dereference(dev->priomap); 181 priority = map ? map->priomap[prioidx] : 0; 182 cb->fill(cb, dev->name, priority); 183 } 184 rcu_read_unlock(); 185 return 0; 186} 187 188static int write_priomap(struct cgroup *cgrp, struct cftype *cft, 189 const char *buffer) 190{ 191 char *devname = kstrdup(buffer, GFP_KERNEL); 192 int ret = -EINVAL; 193 u32 prioidx = cgrp_netprio_state(cgrp)->prioidx; 194 unsigned long priority; 195 char *priostr; 196 struct net_device *dev; 197 struct netprio_map *map; 198 199 if (!devname) 200 return -ENOMEM; 201 202 /* 203 * Minimally sized valid priomap string 204 */ 205 if (strlen(devname) < 3) 206 goto out_free_devname; 207 208 priostr = strstr(devname, " "); 209 if (!priostr) 210 goto out_free_devname; 211 212 /* 213 *Separate the devname from the associated priority 214 *and advance the priostr poitner to the priority value 215 */ 216 *priostr = '\0'; 217 priostr++; 218 219 /* 220 * If the priostr points to NULL, we're at the end of the passed 221 * in string, and its not a valid write 222 */ 223 if (*priostr == '\0') 224 goto out_free_devname; 225 226 ret = kstrtoul(priostr, 10, &priority); 227 if (ret < 0) 228 goto out_free_devname; 229 230 ret = -ENODEV; 231 232 dev = dev_get_by_name(&init_net, devname); 233 if (!dev) 234 goto out_free_devname; 235 236 update_netdev_tables(); 237 ret = 0; 238 rcu_read_lock(); 239 map = rcu_dereference(dev->priomap); 240 if (map) 241 map->priomap[prioidx] = priority; 242 rcu_read_unlock(); 243 dev_put(dev); 244 245out_free_devname: 246 kfree(devname); 247 return ret; 248} 249 250static struct cftype ss_files[] = { 251 { 252 .name = "prioidx", 253 .read_u64 = read_prioidx, 254 }, 255 { 256 .name = "ifpriomap", 257 .read_map = read_priomap, 258 .write_string = write_priomap, 259 }, 260}; 261 262static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp) 263{ 264 return cgroup_add_files(cgrp, ss, ss_files, ARRAY_SIZE(ss_files)); 265} 266 267static int netprio_device_event(struct notifier_block *unused, 268 unsigned long event, void *ptr) 269{ 270 struct net_device *dev = ptr; 271 struct netprio_map *old; 272 273 /* 274 * Note this is called with rtnl_lock held so we have update side 275 * protection on our rcu assignments 276 */ 277 278 switch (event) { 279 case NETDEV_UNREGISTER: 280 old = rtnl_dereference(dev->priomap); 281 RCU_INIT_POINTER(dev->priomap, NULL); 282 if (old) 283 kfree_rcu(old, rcu); 284 break; 285 } 286 return NOTIFY_DONE; 287} 288 289static struct notifier_block netprio_device_notifier = { 290 .notifier_call = netprio_device_event 291}; 292 293static int __init init_cgroup_netprio(void) 294{ 295 int ret; 296 297 ret = cgroup_load_subsys(&net_prio_subsys); 298 if (ret) 299 goto out; 300#ifndef CONFIG_NETPRIO_CGROUP 301 smp_wmb(); 302 net_prio_subsys_id = net_prio_subsys.subsys_id; 303#endif 304 305 register_netdevice_notifier(&netprio_device_notifier); 306 307out: 308 return ret; 309} 310 311static void __exit exit_cgroup_netprio(void) 312{ 313 struct netprio_map *old; 314 struct net_device *dev; 315 316 unregister_netdevice_notifier(&netprio_device_notifier); 317 318 cgroup_unload_subsys(&net_prio_subsys); 319 320#ifndef CONFIG_NETPRIO_CGROUP 321 net_prio_subsys_id = -1; 322 synchronize_rcu(); 323#endif 324 325 rtnl_lock(); 326 for_each_netdev(&init_net, dev) { 327 old = rtnl_dereference(dev->priomap); 328 RCU_INIT_POINTER(dev->priomap, NULL); 329 if (old) 330 kfree_rcu(old, rcu); 331 } 332 rtnl_unlock(); 333} 334 335module_init(init_cgroup_netprio); 336module_exit(exit_cgroup_netprio); 337MODULE_LICENSE("GPL v2");