made singleton
[ana-net.git] / src / xt_fblock.c
blobf3830572991a8b17a9a024a16b5550d82cafde8b
1 /*
2 * Lightweight Autonomic Network Architecture
4 * Global LANA IDP translation tables, core backend.
6 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
7 * Swiss federal institute of technology (ETH Zurich)
8 * Subject to the GPL.
9 */
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/rcupdate.h>
14 #include <linux/atomic.h>
15 #include <linux/types.h>
16 #include <linux/cpu.h>
17 #include <linux/spinlock.h>
18 #include <linux/rwlock.h>
19 #include <linux/slab.h>
20 #include <linux/proc_fs.h>
21 #include <linux/radix-tree.h>
23 #include "xt_fblock.h"
24 #include "xt_idp.h"
25 #include "xt_critbit.h"
27 struct idp_elem {
28 char name[FBNAMSIZ];
29 idp_t idp;
30 struct rcu_head rcu;
31 } ____cacheline_aligned;
33 static struct critbit_tree idpmap;
34 RADIX_TREE(fblmap, GFP_ATOMIC);
35 static atomic64_t idp_counter;
36 static struct kmem_cache *fblock_cache = NULL;
38 extern struct proc_dir_entry *lana_proc_dir;
39 static struct proc_dir_entry *fblocks_proc;
41 static inline idp_t provide_new_fblock_idp(void)
43 return (idp_t) atomic64_inc_return(&idp_counter);
46 static int register_to_fblock_namespace(char *name, idp_t val)
48 struct idp_elem *elem;
49 if (critbit_contains(&idpmap, name))
50 return -EEXIST;
51 elem = kzalloc(sizeof(*elem), GFP_ATOMIC);
52 if (!elem)
53 return -ENOMEM;
54 strlcpy(elem->name, name, sizeof(elem->name));
55 elem->idp = val;
56 return critbit_insert(&idpmap, elem->name);
59 static void fblock_namespace_do_free_rcu(struct rcu_head *rp)
61 struct idp_elem *p = container_of(rp, struct idp_elem, rcu);
62 kfree(p);
65 static int unregister_from_fblock_namespace(char *name)
67 int ret;
68 struct idp_elem *elem;
69 elem = struct_of(critbit_get(&idpmap, name), struct idp_elem);
70 if (!elem)
71 return -ENOENT;
72 ret = critbit_delete(&idpmap, elem->name);
73 if (ret)
74 return ret;
75 call_rcu(&elem->rcu, fblock_namespace_do_free_rcu);
76 return 0;
79 /* Called within RCU read lock! */
80 idp_t __get_fblock_namespace_mapping(char *name)
82 struct idp_elem *elem = struct_of(__critbit_get(&idpmap, name),
83 struct idp_elem);
84 if (unlikely(!elem))
85 return IDP_UNKNOWN;
86 smp_rmb();
87 return elem->idp;
89 EXPORT_SYMBOL_GPL(__get_fblock_namespace_mapping);
91 idp_t get_fblock_namespace_mapping(char *name)
93 idp_t ret;
94 rcu_read_lock();
95 ret = __get_fblock_namespace_mapping(name);
96 rcu_read_unlock();
97 return ret;
99 EXPORT_SYMBOL_GPL(get_fblock_namespace_mapping);
101 /* Called within RCU read lock! */
102 int __change_fblock_namespace_mapping(char *name, idp_t new)
104 struct idp_elem *elem = struct_of(__critbit_get(&idpmap, name),
105 struct idp_elem);
106 if (unlikely(!elem))
107 return -ENOENT;
108 elem->idp = new;
109 smp_wmb();
110 return 0;
112 EXPORT_SYMBOL_GPL(__change_fblock_namespace_mapping);
114 int change_fblock_namespace_mapping(char *name, idp_t new)
116 int ret;
117 rcu_read_lock();
118 ret = __change_fblock_namespace_mapping(name, new);
119 rcu_read_unlock();
120 return ret;
122 EXPORT_SYMBOL_GPL(change_fblock_namespace_mapping);
124 struct fblock *search_fblock(idp_t idp)
126 struct fblock *ret;
127 if (unlikely(idp == IDP_UNKNOWN))
128 return NULL;
129 rcu_read_lock();
130 ret = __search_fblock(idp);
131 rcu_read_unlock();
132 return ret;
134 EXPORT_SYMBOL_GPL(search_fblock);
136 /* Note: user needs to do a put_fblock */
137 struct fblock *search_fblock_n(char *name)
139 idp_t id;
140 struct fblock *ret;
141 if (unlikely(!name))
142 return NULL;
143 rcu_read_lock();
144 id = __get_fblock_namespace_mapping(name);
145 ret = __search_fblock(id);
146 rcu_read_unlock();
147 return ret;
149 EXPORT_SYMBOL_GPL(search_fblock_n);
151 /* opt_string must be of type "key=val" and 0-terminated */
152 int __fblock_set_option(struct fblock *fb, char *opt_string)
154 int ret = 0;
155 char *val = opt_string;
156 struct fblock_opt_msg msg;
157 /* Hack: we let the fb think that this belongs to his own chain to
158 * get the reference back to itself. */
159 struct fblock_notifier fbn;
161 memset(&fbn, 0, sizeof(fbn));
162 memset(&msg, 0, sizeof(msg));
164 msg.key = opt_string;
165 while (*val != '=' && *val != '\0')
166 val++;
167 if (*val == '\0')
168 return -EINVAL;
169 val++;
170 *(val - 1) = '\0';
171 msg.val = val;
172 fbn.self = fb;
174 get_fblock(fb);
175 ret = fb->event_rx(&fbn.nb, FBLOCK_SET_OPT, &msg);
176 put_fblock(fb);
178 return ret;
180 EXPORT_SYMBOL_GPL(__fblock_set_option);
182 int fblock_set_option(struct fblock *fb, char *opt_string)
184 int ret;
185 if (unlikely(!opt_string || !fb))
186 return -EINVAL;
187 rcu_read_lock();
188 ret = __fblock_set_option(fb, opt_string);
189 rcu_read_unlock();
190 return ret;
192 EXPORT_SYMBOL_GPL(fblock_set_option);
194 /* Must already hold spin_lock */
195 static void fblock_update_selfref(struct fblock_notifier *head,
196 struct fblock *self)
198 while (rcu_dereference_raw(head) != NULL) {
199 rcu_assign_pointer(head->self, self);
200 rcu_assign_pointer(head, head->next);
205 * Migrate src to dst, both are of same type, working data is
206 * transferred to dst and droped from src. src gets dsts old data,
207 * so that on free, we do not need to explicitly ignore srcs
208 * private data and dsts remaining data.
210 void fblock_migrate_p(struct fblock *dst, struct fblock *src)
212 void *priv_old;
214 get_fblock(dst);
215 get_fblock(src);
217 rcu_assign_pointer(priv_old, dst->private_data);
218 rcu_assign_pointer(dst->private_data, src->private_data);
219 rcu_assign_pointer(src->private_data, priv_old);
221 put_fblock(dst);
222 put_fblock(src);
224 EXPORT_SYMBOL_GPL(fblock_migrate_p);
226 void fblock_migrate_r(struct fblock *dst, struct fblock *src)
228 struct fblock_notifier *not_old;
229 struct fblock_subscrib *sub_old;
231 get_fblock(dst);
232 get_fblock(src);
234 spin_lock(&dst->lock);
235 spin_lock(&src->lock);
237 dst->idp = src->idp;
238 strlcpy(dst->name, src->name, sizeof(dst->name));
240 rcu_assign_pointer(not_old, dst->notifiers);
241 rcu_assign_pointer(dst->notifiers, src->notifiers);
242 rcu_assign_pointer(src->notifiers, not_old);
244 fblock_update_selfref(dst->notifiers, dst);
245 fblock_update_selfref(src->notifiers, src);
247 rcu_assign_pointer(sub_old, dst->others);
248 rcu_assign_pointer(dst->others, src->others);
249 rcu_assign_pointer(src->others, sub_old);
251 atomic_xchg(&dst->refcnt, atomic_xchg(&src->refcnt,
252 atomic_read(&dst->refcnt)));
254 spin_unlock(&src->lock);
255 spin_unlock(&dst->lock);
257 put_fblock(dst);
258 put_fblock(src);
260 EXPORT_SYMBOL_GPL(fblock_migrate_r);
263 * fb1 on top of fb2 in the stack
265 int __fblock_bind(struct fblock *fb1, struct fblock *fb2)
267 int ret;
268 struct fblock_bind_msg msg;
269 /* Hack: we let the fb think that this belongs to his own chain to
270 * get the reference back to itself. */
271 struct fblock_notifier fbn;
273 memset(&fbn, 0, sizeof(fbn));
274 memset(&msg, 0, sizeof(msg));
276 get_fblock(fb1);
277 get_fblock(fb2);
279 msg.dir = TYPE_EGRESS;
280 msg.idp = fb2->idp;
281 fbn.self = fb1;
282 ret = fb1->event_rx(&fbn.nb, FBLOCK_BIND_IDP, &msg);
283 if (ret != NOTIFY_OK) {
284 put_fblock(fb1);
285 put_fblock(fb2);
286 return -EBUSY;
289 msg.dir = TYPE_INGRESS;
290 msg.idp = fb1->idp;
291 fbn.self = fb2;
292 ret = fb2->event_rx(&fbn.nb, FBLOCK_BIND_IDP, &msg);
293 if (ret != NOTIFY_OK) {
294 /* Release previous binding */
295 msg.dir = TYPE_EGRESS;
296 msg.idp = fb2->idp;
297 fbn.self = fb1;
298 ret = fb1->event_rx(&fbn.nb, FBLOCK_UNBIND_IDP, &msg);
299 if (ret != NOTIFY_OK)
300 panic("Cannot release previously bound fblock!\n");
301 put_fblock(fb1);
302 put_fblock(fb2);
303 return -EBUSY;
306 ret = subscribe_to_remote_fblock(fb1, fb2);
307 if (ret) {
308 __fblock_unbind(fb1, fb2);
309 return -ENOMEM;
312 ret = subscribe_to_remote_fblock(fb2, fb1);
313 if (ret) {
314 __fblock_unbind(fb1, fb2);
315 return -ENOMEM;
318 /* We don't give refcount back! */
319 return 0;
321 EXPORT_SYMBOL_GPL(__fblock_bind);
323 int fblock_bind(struct fblock *fb1, struct fblock *fb2)
325 int ret;
326 rcu_read_lock();
327 ret = __fblock_bind(fb1, fb2);
328 rcu_read_unlock();
329 return ret;
331 EXPORT_SYMBOL_GPL(fblock_bind);
334 * fb1 on top of fb2 in the stack
336 int __fblock_unbind(struct fblock *fb1, struct fblock *fb2)
338 int ret;
339 struct fblock_bind_msg msg;
340 /* Hack: we let the fb think that this belongs to his own chain to
341 * get the reference back to itself. */
342 struct fblock_notifier fbn;
344 /* We still have refcnt, we drop it on exit! */
346 memset(&fbn, 0, sizeof(fbn));
347 memset(&msg, 0, sizeof(msg));
349 msg.dir = TYPE_EGRESS;
350 msg.idp = fb2->idp;
351 fbn.self = fb1;
352 ret = fb1->event_rx(&fbn.nb, FBLOCK_UNBIND_IDP, &msg);
353 if (ret != NOTIFY_OK) {
354 /* We are not bound to fb2 */
355 return -EBUSY;
358 msg.dir = TYPE_INGRESS;
359 msg.idp = fb1->idp;
360 fbn.self = fb2;
361 ret = fb2->event_rx(&fbn.nb, FBLOCK_UNBIND_IDP, &msg);
362 if (ret != NOTIFY_OK) {
363 /* We are not bound to fb1, but fb1 was bound to us, so only
364 * release fb1 */
365 put_fblock(fb1);
366 return -EBUSY;
369 unsubscribe_from_remote_fblock(fb1, fb2);
370 unsubscribe_from_remote_fblock(fb2, fb1);
372 put_fblock(fb2);
373 put_fblock(fb1);
375 return 0;
377 EXPORT_SYMBOL_GPL(__fblock_unbind);
379 int fblock_unbind(struct fblock *fb1, struct fblock *fb2)
381 int ret;
382 rcu_read_lock();
383 ret = __fblock_unbind(fb1, fb2);
384 rcu_read_unlock();
385 return ret;
387 EXPORT_SYMBOL_GPL(fblock_unbind);
390 * register_fblock is called when the idp is preknown to the
391 * caller and has already been registered previously. The previous
392 * registration has then called unregister_fblock to remove the
393 * fblock but to keep the namespace and idp number.
395 int register_fblock(struct fblock *p, idp_t idp)
397 p->idp = idp;
398 return radix_tree_insert(&fblmap, idp, p);
400 EXPORT_SYMBOL_GPL(register_fblock);
403 * register_fblock_namespace is called when a new functional block
404 * instance is registered to the system. Then, its name will be
405 * registered into the namespace and it receives a new idp number.
407 int register_fblock_namespace(struct fblock *p)
409 int ret;
410 p->idp = provide_new_fblock_idp();
411 ret = radix_tree_insert(&fblmap, p->idp, p);
412 if (ret < 0)
413 return ret;
414 return register_to_fblock_namespace(p->name, p->idp);
416 EXPORT_SYMBOL_GPL(register_fblock_namespace);
418 void free_fblock_rcu(struct rcu_head *rp)
420 struct fblock *p = container_of(rp, struct fblock, rcu);
421 cleanup_fblock(p);
422 kfree_fblock(p);
424 EXPORT_SYMBOL_GPL(free_fblock_rcu);
427 * unregister_fblock releases the functional block _only_ from the idp to
428 * fblock translation table, but not from the namespace. The idp can then
429 * later be reused, e.g. by another fblock.
431 void unregister_fblock(struct fblock *p)
433 radix_tree_delete(&fblmap, p->idp);
434 put_fblock(p);
436 EXPORT_SYMBOL_GPL(unregister_fblock);
439 * Removes the functional block from the system along with its namespace
440 * mapping.
442 static void __unregister_fblock_namespace(struct fblock *p, int rcu)
444 radix_tree_delete(&fblmap, p->idp);
445 unregister_from_fblock_namespace(p->name);
446 if (rcu)
447 put_fblock(p);
450 void unregister_fblock_namespace(struct fblock *p)
452 __unregister_fblock_namespace(p, 1);
454 EXPORT_SYMBOL_GPL(unregister_fblock_namespace);
456 void unregister_fblock_namespace_no_rcu(struct fblock *p)
458 __unregister_fblock_namespace(p, 0);
460 EXPORT_SYMBOL_GPL(unregister_fblock_namespace_no_rcu);
462 /* If state changes on 'remote' fb, we ('us') want to be notified. */
463 int subscribe_to_remote_fblock(struct fblock *us, struct fblock *remote)
465 struct fblock_notifier *fn = kmalloc(sizeof(*fn), GFP_ATOMIC);
466 if (!fn)
467 return -ENOMEM;
468 /* hold ref */
469 get_fblock(us);
470 get_fblock(remote);
472 spin_lock(&us->lock);
473 fn->self = us;
474 fn->remote = remote->idp;
475 init_fblock_subscriber(us, &fn->nb);
476 fn->next = rcu_dereference_raw(us->notifiers);
477 rcu_assign_pointer(us->notifiers, fn);
478 spin_unlock(&us->lock);
480 return fblock_register_foreign_subscriber(remote,
481 &rcu_dereference_raw(us->notifiers)->nb);
483 EXPORT_SYMBOL_GPL(subscribe_to_remote_fblock);
485 void unsubscribe_from_remote_fblock(struct fblock *us, struct fblock *remote)
487 int found = 0;
488 struct fblock_notifier *fn;
490 if (unlikely(!rcu_dereference_raw(us->notifiers)))
491 return;
492 spin_lock(&us->lock);
493 fn = rcu_dereference_raw(us->notifiers);
494 if (fn->remote == remote->idp)
495 rcu_assign_pointer(us->notifiers, us->notifiers->next);
496 else {
497 struct fblock_notifier *f1;
498 while ((f1 = fn->next)) {
499 if (f1->remote == remote->idp) {
500 found = 1;
501 fn->next = f1->next;
502 fn = f1; /* free f1 */
503 break;
504 } else
505 fn = f1;
508 spin_unlock(&us->lock);
509 if (found) {
510 fblock_unregister_foreign_subscriber(remote, &fn->nb);
511 kfree(fn);
514 /* drop ref */
515 put_fblock(us);
516 put_fblock(remote);
518 EXPORT_SYMBOL_GPL(unsubscribe_from_remote_fblock);
520 static void ctor_fblock(void *obj)
522 struct fblock *p = obj;
523 memset(p, 0, sizeof(*p));
524 spin_lock_init(&p->lock);
525 p->idp = IDP_UNKNOWN;
528 struct fblock *alloc_fblock(gfp_t flags)
530 struct fblock *fb;
531 #ifndef __USE_KMALLOC
532 fb = kmem_cache_alloc(fblock_cache, flags);
533 if (likely(fb))
534 __module_get(THIS_MODULE);
535 #else
536 fb = kmalloc(sizeof(*fb), flags);
537 if (fb) {
538 ctor_fblock(fb);
539 __module_get(THIS_MODULE);
541 #endif
542 return fb;
544 EXPORT_SYMBOL_GPL(alloc_fblock);
546 int init_fblock(struct fblock *fb, char *name, void __percpu *priv)
548 spin_lock(&fb->lock);
549 strlcpy(fb->name, name, sizeof(fb->name));
550 rcu_assign_pointer(fb->private_data, priv);
551 fb->others = kmalloc(sizeof(*(fb->others)), GFP_ATOMIC);
552 if (!fb->others)
553 return -ENOMEM;
554 ATOMIC_INIT_NOTIFIER_HEAD(&fb->others->subscribers);
555 spin_unlock(&fb->lock);
556 atomic_set(&fb->refcnt, 1);
557 return 0;
559 EXPORT_SYMBOL_GPL(init_fblock);
561 void kfree_fblock(struct fblock *p)
563 #ifndef __USE_KMALLOC
564 kmem_cache_free(fblock_cache, p);
565 #else
566 kfree(p);
567 #endif
568 module_put(THIS_MODULE);
570 EXPORT_SYMBOL_GPL(kfree_fblock);
572 void cleanup_fblock(struct fblock *fb)
574 notify_fblock_subscribers(fb, FBLOCK_DOWN, &fb->idp);
575 fb->factory->dtor(fb);
576 kfree(rcu_dereference_raw(fb->others));
578 EXPORT_SYMBOL_GPL(cleanup_fblock);
580 void cleanup_fblock_ctor(struct fblock *fb)
582 kfree(rcu_dereference_raw(fb->others));
584 EXPORT_SYMBOL_GPL(cleanup_fblock_ctor);
586 static int procfs_fblocks(char *page, char **start, off_t offset,
587 int count, int *eof, void *data)
589 int i, has_sub;
590 off_t len = 0;
591 struct fblock *fb;
592 struct fblock_notifier *fn;
593 long long max = atomic64_read(&idp_counter);
595 len += sprintf(page + len, "name type addr idp refcnt next subscr\n");
596 rcu_read_lock();
597 for (i = 0; i <= max; ++i) {
598 fb = radix_tree_lookup(&fblmap, i);
599 if (!fb)
600 continue;
601 while (fb) {
602 has_sub = 0;
603 len += sprintf(page + len, "%s %s %p %u %d %p [",
604 fb->name, fb->factory->type,
605 fb, fb->idp,
606 atomic_read(&fb->refcnt),
607 rcu_dereference_raw(fb->next));
608 fn = rcu_dereference_raw(fb->notifiers);
609 while (fn) {
610 len += sprintf(page + len, "%u ", fn->remote);
611 rcu_assign_pointer(fn, fn->next);
612 has_sub = 1;
614 len += sprintf(page + len - has_sub, "]\n");
615 fb = rcu_dereference_raw(fb->next);
618 rcu_read_unlock();
620 /* FIXME: fits in page? */
621 *eof = 1;
622 return len;
625 int init_fblock_tables(void)
627 int ret = 0;
629 get_critbit_cache();
630 critbit_init_tree(&idpmap);
631 fblock_cache = kmem_cache_create("fblock", sizeof(struct fblock),
632 0, SLAB_HWCACHE_ALIGN |
633 SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT,
634 ctor_fblock);
635 if (!fblock_cache)
636 goto err;
637 atomic64_set(&idp_counter, 0);
638 fblocks_proc = create_proc_read_entry("fblocks", 0400, lana_proc_dir,
639 procfs_fblocks, NULL);
640 if (!fblocks_proc)
641 goto err2;
642 return 0;
643 err2:
644 kmem_cache_destroy(fblock_cache);
645 err:
646 put_critbit_cache();
647 return ret;
649 EXPORT_SYMBOL_GPL(init_fblock_tables);
651 void cleanup_fblock_tables(void)
653 remove_proc_entry("fblocks", lana_proc_dir);
654 put_critbit_cache();
655 rcu_barrier();
656 kmem_cache_destroy(fblock_cache);
658 EXPORT_SYMBOL_GPL(cleanup_fblock_tables);