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