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 set_bit(prioidx, prioidx_map); 62 spin_unlock_irqrestore(&prioidx_map_lock, flags); 63 if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) 64 return -ENOSPC; 65 66 atomic_set(&max_prioidx, prioidx); 67 *prio = prioidx; 68 return 0; 69 } 70 71 static 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 80 static 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 107 static void update_netdev_tables(void) 108 { 109 struct net_device *dev; 110 u32 max_len = atomic_read(&max_prioidx); 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 123 static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, 124 struct cgroup *cgrp) 125 { 126 struct cgroup_netprio_state *cs; 127 int ret; 128 129 cs = kzalloc(sizeof(*cs), GFP_KERNEL); 130 if (!cs) 131 return ERR_PTR(-ENOMEM); 132 133 if (cgrp->parent && cgrp_netprio_state(cgrp->parent)->prioidx) { 134 kfree(cs); 135 return ERR_PTR(-EINVAL); 136 } 137 138 ret = get_prioidx(&cs->prioidx); 139 if (ret != 0) { 140 printk(KERN_WARNING "No space in priority index array\n"); 141 kfree(cs); 142 return ERR_PTR(ret); 143 } 144 145 return &cs->css; 146 } 147 148 static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) 149 { 150 struct cgroup_netprio_state *cs; 151 struct net_device *dev; 152 struct netprio_map *map; 153 154 cs = cgrp_netprio_state(cgrp); 155 rtnl_lock(); 156 for_each_netdev(&init_net, dev) { 157 map = rtnl_dereference(dev->priomap); 158 if (map) 159 map->priomap[cs->prioidx] = 0; 160 } 161 rtnl_unlock(); 162 put_prioidx(cs->prioidx); 163 kfree(cs); 164 } 165 166 static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft) 167 { 168 return (u64)cgrp_netprio_state(cgrp)->prioidx; 169 } 170 171 static int read_priomap(struct cgroup *cont, struct cftype *cft, 172 struct cgroup_map_cb *cb) 173 { 174 struct net_device *dev; 175 u32 prioidx = cgrp_netprio_state(cont)->prioidx; 176 u32 priority; 177 struct netprio_map *map; 178 179 rcu_read_lock(); 180 for_each_netdev_rcu(&init_net, dev) { 181 map = rcu_dereference(dev->priomap); 182 priority = map ? map->priomap[prioidx] : 0; 183 cb->fill(cb, dev->name, priority); 184 } 185 rcu_read_unlock(); 186 return 0; 187 } 188 189 static int write_priomap(struct cgroup *cgrp, struct cftype *cft, 190 const char *buffer) 191 { 192 char *devname = kstrdup(buffer, GFP_KERNEL); 193 int ret = -EINVAL; 194 u32 prioidx = cgrp_netprio_state(cgrp)->prioidx; 195 unsigned long priority; 196 char *priostr; 197 struct net_device *dev; 198 struct netprio_map *map; 199 200 if (!devname) 201 return -ENOMEM; 202 203 /* 204 * Minimally sized valid priomap string 205 */ 206 if (strlen(devname) < 3) 207 goto out_free_devname; 208 209 priostr = strstr(devname, " "); 210 if (!priostr) 211 goto out_free_devname; 212 213 /* 214 *Separate the devname from the associated priority 215 *and advance the priostr poitner to the priority value 216 */ 217 *priostr = '\0'; 218 priostr++; 219 220 /* 221 * If the priostr points to NULL, we're at the end of the passed 222 * in string, and its not a valid write 223 */ 224 if (*priostr == '\0') 225 goto out_free_devname; 226 227 ret = kstrtoul(priostr, 10, &priority); 228 if (ret < 0) 229 goto out_free_devname; 230 231 ret = -ENODEV; 232 233 dev = dev_get_by_name(&init_net, devname); 234 if (!dev) 235 goto out_free_devname; 236 237 update_netdev_tables(); 238 ret = 0; 239 rcu_read_lock(); 240 map = rcu_dereference(dev->priomap); 241 if (map) 242 map->priomap[prioidx] = priority; 243 rcu_read_unlock(); 244 dev_put(dev); 245 246 out_free_devname: 247 kfree(devname); 248 return ret; 249 } 250 251 static struct cftype ss_files[] = { 252 { 253 .name = "prioidx", 254 .read_u64 = read_prioidx, 255 }, 256 { 257 .name = "ifpriomap", 258 .read_map = read_priomap, 259 .write_string = write_priomap, 260 }, 261 }; 262 263 static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp) 264 { 265 return cgroup_add_files(cgrp, ss, ss_files, ARRAY_SIZE(ss_files)); 266 } 267 268 static int netprio_device_event(struct notifier_block *unused, 269 unsigned long event, void *ptr) 270 { 271 struct net_device *dev = ptr; 272 struct netprio_map *old; 273 u32 max_len = atomic_read(&max_prioidx); 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 282 case NETDEV_REGISTER: 283 if (max_len) 284 extend_netdev_table(dev, max_len); 285 break; 286 case NETDEV_UNREGISTER: 287 old = rtnl_dereference(dev->priomap); 288 RCU_INIT_POINTER(dev->priomap, NULL); 289 if (old) 290 kfree_rcu(old, rcu); 291 break; 292 } 293 return NOTIFY_DONE; 294 } 295 296 static struct notifier_block netprio_device_notifier = { 297 .notifier_call = netprio_device_event 298 }; 299 300 static int __init init_cgroup_netprio(void) 301 { 302 int ret; 303 304 ret = cgroup_load_subsys(&net_prio_subsys); 305 if (ret) 306 goto out; 307 #ifndef CONFIG_NETPRIO_CGROUP 308 smp_wmb(); 309 net_prio_subsys_id = net_prio_subsys.subsys_id; 310 #endif 311 312 register_netdevice_notifier(&netprio_device_notifier); 313 314 out: 315 return ret; 316 } 317 318 static void __exit exit_cgroup_netprio(void) 319 { 320 struct netprio_map *old; 321 struct net_device *dev; 322 323 unregister_netdevice_notifier(&netprio_device_notifier); 324 325 cgroup_unload_subsys(&net_prio_subsys); 326 327 #ifndef CONFIG_NETPRIO_CGROUP 328 net_prio_subsys_id = -1; 329 synchronize_rcu(); 330 #endif 331 332 rtnl_lock(); 333 for_each_netdev(&init_net, dev) { 334 old = rtnl_dereference(dev->priomap); 335 RCU_INIT_POINTER(dev->priomap, NULL); 336 if (old) 337 kfree_rcu(old, rcu); 338 } 339 rtnl_unlock(); 340 } 341 342 module_init(init_cgroup_netprio); 343 module_exit(exit_cgroup_netprio); 344 MODULE_LICENSE("GPL v2"); 345