]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Resource pools are now bound with domains.
authorMaria Matejka <mq@ucw.cz>
Fri, 21 Apr 2023 13:26:06 +0000 (15:26 +0200)
committerMaria Matejka <mq@ucw.cz>
Mon, 24 Apr 2023 08:33:28 +0000 (10:33 +0200)
Memory allocation is a fragile part of BIRD and we need checking that
everybody is using the resource pools in an appropriate way. To assure
this, all the resource pools are associated with locking domains and
every resource manipulation is thoroughly checked whether the
appropriate locking domain is locked.

With transitive resource manipulation like resource dumping or mass free
operations, domains are locked and unlocked on the go, thus we require
pool domains to have higher order than their parent to allow for this
transitive operations.

Adding pool locking revealed some cases of insecure memory manipulation
and this commit fixes that as well.

27 files changed:
conf/conf.c
lib/hash_test.c
lib/io-loop.h
lib/locking.h
lib/resource.c
lib/resource.h
nest/cli.c
nest/cli.h
nest/iface.c
nest/locks.h
nest/password.h
nest/proto.c
nest/protocol.h
nest/rt-attr.c
nest/rt-show.c
nest/rt-table.c
nest/rt.h
proto/babel/babel.c
proto/bfd/bfd.c
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/ospf/iface.c
proto/ospf/neighbor.c
proto/radv/radv.c
proto/rpki/rpki.c
sysdep/unix/io-loop.c
sysdep/unix/krt.c

index 2cfe7b157874fbabf5e7063a77310f4f046469c8..c7cd81fefb886cc5dfcd80659cc14ad68c3af0ce 100644 (file)
@@ -90,7 +90,7 @@ int undo_available;                   /* Undo was not requested from last reconfiguration */
 struct config *
 config_alloc(const char *name)
 {
-  pool *p = rp_new(config_pool, "Config");
+  pool *p = rp_new(config_pool, the_bird_domain.the_bird, "Config");
   linpool *l = lp_new_default(p);
   struct config *c = lp_allocz(l, sizeof(struct config));
 
@@ -515,7 +515,7 @@ config_timeout(timer *t UNUSED)
 void
 config_init(void)
 {
-  config_pool = rp_new(&root_pool, "Configurations");
+  config_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Configurations");
 
   config_event = ev_new(config_pool);
   config_event->hook = config_done;
index ecfcdd665adf541e81713d682c8674656fa13f49..30213320e29839b3c47fff4cef1076b8ac937c40 100644 (file)
@@ -61,7 +61,7 @@ dump_nodes(void)
 static void
 init_hash_(uint order)
 {
-  my_pool = rp_new(&root_pool, "Test pool");
+  my_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Test pool");
 
   HASH_INIT(hash, my_pool, order);
 
index b0d3d6ccf84aa161ebe65832350e8feb13f16360..80cd2ea2c5c8308d29427525f2083943817e8aa7 100644 (file)
@@ -31,6 +31,7 @@ event_list *birdloop_event_list(struct birdloop *loop);
 
 /* Get birdloop's time heap */
 struct timeloop *birdloop_time_loop(struct birdloop *loop);
+#define birdloop_domain(l)  (birdloop_time_loop((l))->domain)
 
 /* Get birdloop's pool */
 pool *birdloop_pool(struct birdloop *loop);
index 6bed14bf1e013750d373333e4fafb6fe9aaad216..751eecbd131de9fdce7341f02b43aeb959db08f5 100644 (file)
@@ -13,8 +13,8 @@ struct domain_generic;
 
 /* Here define the global lock order; first to last. */
 struct lock_order {
-  struct domain_generic *meta;
   struct domain_generic *the_bird;
+  struct domain_generic *meta;
   struct domain_generic *control;
   struct domain_generic *proto;
   struct domain_generic *service;
index b1b89bdf2a3c15572dfeeb81e538071a54fe7586..2071a4114ab0228a4371e095ff74088ebe297d21 100644 (file)
@@ -46,6 +46,16 @@ static struct resclass pool_class = {
 
 pool root_pool;
 
+static void
+rp_init(pool *z, struct domain_generic *dom, const char *name)
+{
+  ASSERT_DIE(DG_IS_LOCKED(dom));
+
+  z->name = name;
+  z->domain = dom;
+  z->inside = (TLIST_LIST(resource)) {};
+}
+
 /**
  * rp_new - create a resource pool
  * @p: parent pool
@@ -55,77 +65,106 @@ pool root_pool;
  * parent pool.
  */
 pool *
-rp_new(pool *p, const char *name)
+rp_new(pool *p, struct domain_generic *dom, const char *name)
 {
   pool *z = ralloc(p, &pool_class);
-  z->name = name;
-  init_list(&z->inside);
+
+  if (dg_order(p->domain) > dg_order(dom))
+    bug("Requested reverse order pool creation: %s (%d) can't be a parent of %s (%d)",
+       domain_name(p->domain), dg_order(p->domain),
+       domain_name(dom), dg_order(dom));
+
+  if ((dg_order(p->domain) == dg_order(dom)) && (p->domain != dom))
+    bug("Requested incomparable order pool creation: %s (%d) can't be a parent of %s (%d)",
+       domain_name(p->domain), dg_order(p->domain),
+       domain_name(dom), dg_order(dom));
+
+  rp_init(z, dom, name);
   return z;
 }
 
 pool *
-rp_vnewf(pool *p, const char *fmt, va_list args)
+rp_vnewf(pool *p, struct domain_generic *dom, const char *fmt, va_list args)
 {
-  pool *z = rp_new(p, NULL);
+  pool *z = rp_new(p, dom, NULL);
   z->name = mb_vsprintf(p, fmt, args);
   return z;
 }
 
 pool *
-rp_newf(pool *p, const char *fmt, ...)
+rp_newf(pool *p, struct domain_generic *dom, const char *fmt, ...)
 {
   va_list args;
   va_start(args, fmt);
-  pool *z = rp_vnewf(p, fmt, args);
+  pool *z = rp_vnewf(p, dom, fmt, args);
   va_end(args);
 
   return z;
 }
 
+#define POOL_LOCK                              \
+  struct domain_generic *dom = p->domain;      \
+  int locking = !DG_IS_LOCKED(dom);            \
+  if (locking)                                 \
+    DG_LOCK(dom);                              \
+
+#define POOL_UNLOCK if (locking) DG_UNLOCK(dom);\
+
+void rp_free(pool *p)
+{
+  ASSERT_DIE(DG_IS_LOCKED(p->domain));
+  rfree(p);
+}
 
 static void
 pool_free(resource *P)
 {
   pool *p = (pool *) P;
-  resource *r, *rr;
 
-  r = HEAD(p->inside);
-  while (rr = (resource *) r->n.next)
+  POOL_LOCK;
+  WALK_TLIST_DELSAFE(resource, r, &p->inside)
     {
       r->class->free(r);
       xfree(r);
-      r = rr;
     }
+  POOL_UNLOCK;
 }
 
+
 static void
 pool_dump(resource *P, unsigned indent)
 {
   pool *p = (pool *) P;
-  resource *r;
+
+  POOL_LOCK;
 
   debug("%s\n", p->name);
-  WALK_LIST(r, p->inside)
+  WALK_TLIST_DELSAFE(resource, r, &p->inside)
     rdump(r, indent + 3);
+
+  POOL_UNLOCK;
 }
 
 static struct resmem
 pool_memsize(resource *P)
 {
   pool *p = (pool *) P;
-  resource *r;
   struct resmem sum = {
     .effective = 0,
     .overhead = sizeof(pool) + ALLOC_OVERHEAD,
   };
 
-  WALK_LIST(r, p->inside)
+  POOL_LOCK;
+
+  WALK_TLIST(resource, r, &p->inside)
   {
     struct resmem add = rmemsize(r);
     sum.effective += add.effective;
     sum.overhead += add.overhead;
   }
 
+  POOL_UNLOCK;
+
   return sum;
 }
 
@@ -133,14 +172,25 @@ static resource *
 pool_lookup(resource *P, unsigned long a)
 {
   pool *p = (pool *) P;
-  resource *r, *q;
+  resource *q = NULL;
+
+  POOL_LOCK;
 
-  WALK_LIST(r, p->inside)
+  WALK_TLIST(resource, r, &p->inside)
     if (r->class->lookup && (q = r->class->lookup(r, a)))
-      return q;
-  return NULL;
+      break;
+
+  POOL_UNLOCK;
+  return q;
+}
+
+static pool *
+resource_parent(resource *r)
+{
+  return SKIP_BACK(pool, inside, resource_enlisted(r));
 }
 
+
 /**
  * rmove - move a resource
  * @res: resource
@@ -152,13 +202,13 @@ pool_lookup(resource *P, unsigned long a)
 void rmove(void *res, pool *p)
 {
   resource *r = res;
+  pool *orig = resource_parent(r);
 
-  if (r)
-    {
-      if (r->n.next)
-        rem_node(&r->n);
-      add_tail(&p->inside, &r->n);
-    }
+  ASSERT_DIE(DG_IS_LOCKED(orig->domain));
+  ASSERT_DIE(DG_IS_LOCKED(p->domain));
+
+  resource_rem_node(&orig->inside, r);
+  resource_add_tail(&p->inside, r);
 }
 
 /**
@@ -179,8 +229,10 @@ rfree(void *res)
   if (!r)
     return;
 
-  if (r->n.next)
-    rem_node(&r->n);
+  pool *orig = resource_parent(r);
+  ASSERT_DIE(DG_IS_LOCKED(orig->domain));
+  resource_rem_node(&orig->inside, r);
+
   r->class->free(r);
   r->class = NULL;
   xfree(r);
@@ -239,12 +291,14 @@ rmemsize(void *res)
 void *
 ralloc(pool *p, struct resclass *c)
 {
+  ASSERT_DIE(DG_IS_LOCKED(p->domain));
+
   resource *r = xmalloc(c->size);
   bzero(r, c->size);
 
   r->class = c;
-  if (p)
-    add_tail(&p->inside, &r->n);
+  resource_add_tail(&p->inside, r);
+
   return r;
 }
 
@@ -284,28 +338,29 @@ resource_init(void)
   rcu_init();
   resource_sys_init();
 
-  root_pool.r.class = &pool_class;
-  root_pool.name = "Root";
-  init_list(&root_pool.inside);
-  tmp_init(&root_pool);
+  rp_init(&root_pool, the_bird_domain.the_bird, "Root");
+  tmp_init(&root_pool, the_bird_domain.the_bird);
 }
 
 _Thread_local struct tmp_resources tmp_res;
 
 void
-tmp_init(pool *p)
+tmp_init(pool *p, struct domain_generic *dom)
 {
   tmp_res.lp = lp_new_default(p);
   tmp_res.parent = p;
-  tmp_res.pool = rp_new(p, "TMP");
+  tmp_res.pool = rp_new(p, dom, "TMP");
+  tmp_res.domain = dom;
 }
 
 void
 tmp_flush(void)
 {
+  ASSERT_DIE(DG_IS_LOCKED(tmp_res.domain));
+
   lp_flush(tmp_linpool);
   rp_free(tmp_res.pool);
-  tmp_res.pool = rp_new(tmp_res.parent, "TMP");
+  tmp_res.pool = rp_new(tmp_res.parent, tmp_res.domain, "TMP");
 }
 
 
@@ -385,11 +440,13 @@ static struct resclass mb_class = {
 void *
 mb_alloc(pool *p, unsigned size)
 {
+  ASSERT_DIE(DG_IS_LOCKED(p->domain));
+
   struct mblock *b = xmalloc(sizeof(struct mblock) + size);
 
   b->r.class = &mb_class;
-  b->r.n = (node) {};
-  add_tail(&p->inside, &b->r.n);
+  b->r.n = (struct resource_node) {};
+  resource_add_tail(&p->inside, &b->r);
   b->size = size;
   return b->data;
 }
@@ -434,10 +491,14 @@ void *
 mb_realloc(void *m, unsigned size)
 {
   struct mblock *b = SKIP_BACK(struct mblock, data, m);
+  struct pool *p = resource_parent(&b->r);
+
+  ASSERT_DIE(DG_IS_LOCKED(p->domain));
 
   b = xrealloc(b, sizeof(struct mblock) + size);
-  update_node(&b->r.n);
   b->size = size;
+
+  resource_update_node(&p->inside, &b->r);
   return b->data;
 }
 
index 2adb9de0fd46d0635bab010f3f7615b885f74105..810334c14264fca43c24aa6df25ffb12ba1448bc 100644 (file)
@@ -10,7 +10,8 @@
 #ifndef _BIRD_RESOURCE_H_
 #define _BIRD_RESOURCE_H_
 
-#include "lib/lists.h"
+#include "lib/locking.h"
+#include "lib/tlists.h"
 
 #include <stdarg.h>
 
@@ -21,11 +22,20 @@ struct resmem {
 
 /* Resource */
 
+#define TLIST_PREFIX resource
+#define TLIST_TYPE struct resource
+#define TLIST_ITEM n
+#define TLIST_WANT_WALK
+#define TLIST_WANT_ADD_TAIL
+#define TLIST_WANT_UPDATE_NODE
+
 typedef struct resource {
-  node n;                              /* Inside resource pool */
-  struct resclass *class;              /* Resource class */
+  TLIST_DEFAULT_NODE;                  /* Inside resource pool */
+  const struct resclass *class;                /* Resource class */
 } resource;
 
+#include "lib/tlists.h"
+
 /* Resource class */
 
 struct resclass {
@@ -44,14 +54,13 @@ struct resclass {
 
 typedef struct pool {
   resource r;
-  list inside;
+  TLIST_LIST(resource) inside;
+  struct domain_generic *domain;
   const char *name;
 } pool;
 
 
 void resource_init(void);
-pool *rp_new(pool *, const char *);    /* Create new pool */
-pool *rp_newf(pool *, const char *, ...);      /* Create a new pool with a formatted string as its name */
 void rfree(void *);                    /* Free single resource */
 void rdump(void *, unsigned indent);   /* Dump to debug output */
 struct resmem rmemsize(void *res);             /* Return size of memory used by the resource */
@@ -60,12 +69,10 @@ void rmove(void *, pool *);         /* Move to a different pool */
 
 void *ralloc(pool *, struct resclass *);
 
-pool *rp_new(pool *, const char *);            /* Create a new pool */
-pool *rp_newf(pool *, const char *, ...);      /* Create a new pool with a formatted string as its name */
-pool *rp_vnewf(pool *, const char *, va_list); /* Create a new pool with a formatted string as its name */
-void rp_init(pool *, const char *);            /* Init a new pool */
-void rp_initf(pool *, const char *, ...);      /* Init a new pool with a formatted string as its name */
-static inline void rp_free(pool *p) { rfree(&p->r); }  /* Free the whole pool */
+pool *rp_new(pool *, struct domain_generic *, const char *);           /* Create a new pool */
+pool *rp_newf(pool *, struct domain_generic *, const char *, ...);     /* Create a new pool with a formatted string as its name */
+pool *rp_vnewf(pool *, struct domain_generic *, const char *, va_list);        /* Create a new pool with a formatted string as its name */
+void rp_free(pool *p);                                                 /* Free the whole pool */
 
 extern pool root_pool;
 
@@ -97,6 +104,7 @@ void lp_restore(linpool *m, lp_state *p);    /* Restore state */
 struct tmp_resources {
   pool *pool, *parent;
   linpool *lp;
+  struct domain_generic *domain;
 };
 
 extern _Thread_local struct tmp_resources tmp_res;
@@ -106,7 +114,7 @@ extern _Thread_local struct tmp_resources tmp_res;
 #define tmp_allocu(sz) lp_allocu(tmp_linpool, sz)
 #define tmp_allocz(sz) lp_allocz(tmp_linpool, sz)
 
-void tmp_init(pool *p);
+void tmp_init(pool *p, struct domain_generic *dg);
 void tmp_flush(void);
 
 
index 29591d26e19016ae29278a3110ee8a6cdf7ae708..b74edffbf357bb1a81bcf02db6a52eba0bb35fe7 100644 (file)
@@ -262,7 +262,7 @@ cli_command(struct cli *c)
     log(L_TRACE "CLI: %s", c->rx_buf);
   bzero(&f, sizeof(f));
   f.mem = c->parser_pool;
-  f.pool = rp_new(c->pool, "Config");
+  f.pool = rp_new(c->pool, the_bird_domain.the_bird, "Config");
   init_list(&f.symbols);
   cf_read_hook = cli_cmd_read_hook;
   cli_rh_pos = c->rx_buf;
@@ -309,7 +309,7 @@ cli_event(void *data)
 cli *
 cli_new(struct birdsock *sock)
 {
-  pool *p = rp_new(cli_pool, "CLI");
+  pool *p = rp_new(cli_pool, the_bird_domain.the_bird, "CLI");
   cli *c = mb_alloc(p, sizeof(cli));
 
   bzero(c, sizeof(cli));
@@ -433,7 +433,7 @@ cli_free(cli *c)
 void
 cli_init(void)
 {
-  cli_pool = rp_new(&root_pool, "CLI");
+  cli_pool = rp_new(&root_pool, the_bird_domain.the_bird, "CLI");
   init_list(&cli_log_hooks);
   cli_log_inited = 1;
 }
index 2d876571dd448678d9b906e3de289511a67d7413..c5cbd8d7a414a354a99d06320b8c9f5555b42fd6 100644 (file)
@@ -10,6 +10,7 @@
 #define _BIRD_CLI_H_
 
 #include "lib/resource.h"
+#include "lib/lists.h"
 #include "lib/event.h"
 
 #define CLI_RX_BUF_SIZE 4096
index f1938664f8cc2102ce00ba244475e0a5d5c30948..a024b943192c485ccb0c7f42503edf6bf0c0ce85 100644 (file)
@@ -1018,12 +1018,15 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id)
 void
 if_init(void)
 {
-  if_pool = rp_new(&root_pool, "Interfaces");
+  iface_domain = DOMAIN_NEW(attrs, "Interfaces");
+
+  IFACE_LOCK;
+  if_pool = rp_new(&root_pool, iface_domain.attrs, "Interfaces");
   init_list(&global_iface_list);
   iface_sub_slab = sl_new(if_pool, sizeof(struct iface_notification));
   strcpy(default_vrf.name, "default");
   neigh_init(if_pool);
-  iface_domain = DOMAIN_NEW(attrs, "Interfaces");
+  IFACE_UNLOCK;
 }
 
 /*
index 04571e69ed8948220c8712e748e4f6876603451a..993e296bf401a687e541da0b6145da17d0c345c1 100644 (file)
@@ -10,6 +10,7 @@
 #define _BIRD_LOCKS_H_
 
 #include "lib/resource.h"
+#include "lib/lists.h"
 #include "lib/event.h"
 
 /*
index 53168bb7c1a54798a48fe5b57e4fce0553bf5cce..335b9cd429d3f6a2df60cdf9f4b2ca0ad2420498 100644 (file)
@@ -10,6 +10,8 @@
 #ifndef PASSWORD_H
 #define PASSWORD_H
 
+#include "lib/lists.h"
+
 struct password_item {
   node n;
   const char *password;                        /* Key data, null terminated */
index d62692724c3f4e64252d94cda3378197e4485b5c..32183c9dc729ad027e2a9144a173dea8e8cbe462 100644 (file)
@@ -367,6 +367,7 @@ channel_roa_subscribe(struct channel *c, rtable *tab, int dir)
       .name = mb_sprintf(c->proto->pool, "%s.%s.roa-%s.%s",
          c->proto->name, c->name, dir ? "in" : "out", tab->name),
       .list = proto_work_list(c->proto),
+      .pool = c->proto->pool,
       .trace_routes = c->debug | c->proto->debug,
       .dump_req = channel_dump_roa_req,
       .export_one = channel_export_one_roa,
@@ -495,6 +496,7 @@ channel_start_export(struct channel *c)
   c->out_req = (struct rt_export_request) {
     .name = mb_sprintf(c->proto->pool, "%s.%s", c->proto->name, c->name),
     .list = proto_work_list(c->proto),
+    .pool = c->proto->pool,
     .addr = c->out_subprefix,
     .addr_mode = c->out_subprefix ? TE_ADDR_IN : TE_ADDR_NONE,
     .trace_routes = c->debug | c->proto->debug,
@@ -685,6 +687,7 @@ channel_setup_in_table(struct channel *c)
   c->reload_req = (struct rt_export_request) {
     .name = mb_sprintf(c->proto->pool, "%s.%s.import", c->proto->name, c->name),
     .list = proto_work_list(c->proto),
+    .pool = c->proto->pool,
     .trace_routes = c->debug | c->proto->debug,
     .export_bulk = channel_reload_export_bulk,
     .dump_req = channel_reload_dump_req,
@@ -1132,15 +1135,14 @@ proto_loop_stopped(void *ptr)
 {
   struct proto *p = ptr;
 
-  birdloop_enter(&main_birdloop);
+  ASSERT_DIE(birdloop_inside(&main_birdloop));
+  ASSERT_DIE(p->loop != &main_birdloop);
 
   p->pool = NULL; /* is freed by birdloop_free() */
   birdloop_free(p->loop);
   p->loop = &main_birdloop;
 
   proto_cleanup(p);
-
-  birdloop_leave(&main_birdloop);
 }
 
 static void
@@ -1228,7 +1230,7 @@ proto_start(struct proto *p)
     p->pool = birdloop_pool(p->loop);
   }
   else
-    p->pool = rp_newf(proto_pool, "Protocol %s", p->cf->name);
+    p->pool = rp_newf(proto_pool, the_bird_domain.the_bird, "Protocol %s", p->cf->name);
 
   p->iface_sub.target = proto_event_list(p);
 
@@ -1859,7 +1861,7 @@ protos_build(void)
 {
   protos_build_gen();
 
-  proto_pool = rp_new(&root_pool, "Protocols");
+  proto_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Protocols");
 }
 
 
index eccfcb73c83ebfdfb59f0a7b8c2a0dccb7a16d69..02ec5c155de8ff2238afd23efd8a98a5dba2320e 100644 (file)
@@ -280,6 +280,8 @@ struct proto *proto_iterate_named(struct symbol *sym, struct protocol *proto, st
 
 #define PROTO_LOCKED_FROM_MAIN(p)      for (struct birdloop *_proto_loop = PROTO_ENTER_FROM_MAIN(p); _proto_loop; PROTO_LEAVE_FROM_MAIN(_proto_loop), (_proto_loop = NULL))
 
+static inline struct domain_generic *proto_domain(struct proto *p)
+{ return birdloop_domain(p->loop); }
 
 #define CMD_RELOAD     0
 #define CMD_RELOAD_IN  1
index 903926f65c0c053f00eccfc12a763fb6df9c5c04..38612a4ef3ad944f914fe285d7edf64b3b9da90b 100644 (file)
@@ -605,9 +605,16 @@ ea_register(pool *p, struct ea_class *def)
 struct ea_class_ref *
 ea_register_alloc(pool *p, struct ea_class cl)
 {
+  struct ea_class_ref *ref;
+
+  RTA_LOCK;
   struct ea_class *clp = ea_class_find_by_name(cl.name);
   if (clp && clp->type == cl.type)
-    return ea_ref_class(p, clp);
+  {
+    ref = ea_ref_class(p, clp);
+    RTA_UNLOCK;
+    return ref;
+  }
 
   uint namelen = strlen(cl.name) + 1;
 
@@ -619,14 +626,18 @@ ea_register_alloc(pool *p, struct ea_class cl)
   memcpy(cla->name, cl.name, namelen);
   cla->cl.name = cla->name;
 
-  return ea_register(p, &cla->cl);
+  ref = ea_register(p, &cla->cl);
+  RTA_UNLOCK;
+  return ref;
 }
 
 void
 ea_register_init(struct ea_class *clp)
 {
+  RTA_LOCK;
   ASSERT_DIE(!ea_class_find_by_name(clp->name));
   ea_register(&root_pool, clp);
+  RTA_UNLOCK;
 }
 
 struct ea_class *
@@ -1598,7 +1609,8 @@ rta_init(void)
 {
   attrs_domain = DOMAIN_NEW(attrs, "Attributes");
 
-  rta_pool = rp_new(&root_pool, "Attributes");
+  RTA_LOCK;
+  rta_pool = rp_new(&root_pool, attrs_domain.attrs, "Attributes");
 
   for (uint i=0; i<ARRAY_SIZE(ea_slab_sizes); i++)
     ea_slab[i] = sl_new(rta_pool, ea_slab_sizes[i]);
@@ -1607,6 +1619,8 @@ rta_init(void)
   rte_src_init();
   ea_class_init();
 
+  RTA_UNLOCK;
+
   /* These attributes are required to be first for nice "show route" output */
   ea_register_init(&ea_gen_nexthop);
   ea_register_init(&ea_gen_hostentry);
index eacd4e31f729281f9e40e7f5bb0734534cbd5a98..7a8b629b962a6a5ac37a896d647690b5316bcc87 100644 (file)
@@ -288,6 +288,7 @@ rt_show_cont(struct rt_show_data *d)
     .addr = d->addr,
     .name = "CLI Show Route",
     .list = &global_work_list,
+    .pool = c->pool,
     .export_bulk = rt_show_net_export_bulk,
     .dump_req = rt_show_dump_req,
     .log_state_change = rt_show_log_state_change,
index 9629db2c068adf3c62f24b7293e848093ac4970f..f1e3c8f707e67020a260cb71e57784795eeea139 100644 (file)
@@ -2131,7 +2131,7 @@ rt_table_export_start_locked(struct rtable_private *tab, struct rt_export_reques
   struct rt_exporter *re = &tab->exporter.e;
   rt_lock_table(tab);
 
-  req->hook = rt_alloc_export(re, sizeof(struct rt_table_export_hook));
+  req->hook = rt_alloc_export(re, req->pool, sizeof(struct rt_table_export_hook));
   req->hook->req = req;
 
   struct rt_table_export_hook *hook = SKIP_BACK(struct rt_table_export_hook, h, req->hook);
@@ -2212,9 +2212,9 @@ rt_request_export_other(struct rt_exporter *re, struct rt_export_request *req)
 }
 
 struct rt_export_hook *
-rt_alloc_export(struct rt_exporter *re, uint size)
+rt_alloc_export(struct rt_exporter *re, pool *pp, uint size)
 {
-  pool *p = rp_new(re->rp, "Export hook");
+  pool *p = rp_new(pp, pp->domain, "Export hook");
   struct rt_export_hook *hook = mb_allocz(p, size);
 
   hook->pool = p;
@@ -2709,13 +2709,14 @@ rt_flowspec_link(rtable *src_pub, rtable *dst_pub)
 
     if (!ln)
     {
-      pool *p = src->rp;
+      pool *p = birdloop_pool(dst_pub->loop);
       ln = mb_allocz(p, sizeof(struct rt_flowspec_link));
       ln->src = src_pub;
       ln->dst = dst_pub;
       ln->req = (struct rt_export_request) {
        .name = mb_sprintf(p, "%s.flowspec.notifier", dst_pub->name),
        .list = birdloop_event_list(dst_pub->loop),
+       .pool = p,
        .trace_routes = src->config->debug,
        .dump_req = rt_flowspec_dump_req,
        .log_state_change = rt_flowspec_log_state_change,
@@ -2781,8 +2782,6 @@ rt_free(resource *_r)
 {
   struct rtable_private *r = SKIP_BACK(struct rtable_private, r, _r);
 
-  DOMAIN_FREE(rtable, r->lock);
-
   DBG("Deleting routing table %s\n", r->name);
   ASSERT_DIE(r->use_count == 0);
 
@@ -2847,13 +2846,20 @@ rt_setup(pool *pp, struct rtable_config *cf)
 
   /* Start the service thread */
   struct birdloop *loop = birdloop_new(pp, DOMAIN_ORDER(service), 0, "Routing table service %s", cf->name);
+  birdloop_enter(loop);
   pool *sp = birdloop_pool(loop);
-  pool *p = rp_newf(sp, "Routing table data %s", cf->name);
+
+  /* Create the table domain and pool */
+  DOMAIN(rtable) dom = DOMAIN_NEW(rtable, cf->name);
+  LOCK_DOMAIN(rtable, dom);
+
+  pool *p = rp_newf(sp, dom.rtable, "Routing table data %s", cf->name);
 
   /* Create the actual table */
   struct rtable_private *t = ralloc(p, &rt_class);
   t->rp = p;
   t->loop = loop;
+  t->lock = dom;
 
   t->rte_slab = sl_new(p, sizeof(struct rte_storage));
 
@@ -2864,8 +2870,6 @@ rt_setup(pool *pp, struct rtable_config *cf)
   if (t->id >= rtable_max_id)
     rtable_max_id = t->id + 1;
 
-  t->lock = DOMAIN_NEW(rtable, t->name);
-
   fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
 
   if (cf->trie_used)
@@ -2911,8 +2915,9 @@ rt_setup(pool *pp, struct rtable_config *cf)
     t->flowspec_trie->ipv4 = (t->addr_type == NET_FLOW4);
   }
 
+  UNLOCK_DOMAIN(rtable, dom);
+
   /* Setup the service thread flag handler */
-  birdloop_enter(t->loop);
   birdloop_flag_set_handler(t->loop, &t->fh);
   birdloop_leave(t->loop);
 
@@ -2929,7 +2934,7 @@ void
 rt_init(void)
 {
   rta_init();
-  rt_table_pool = rp_new(&root_pool, "Routing tables");
+  rt_table_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Routing tables");
   init_list(&routing_tables);
   init_list(&deleted_routing_tables);
   ev_init_list(&rt_cork.queue, &main_birdloop, "Route cork release");
@@ -4067,13 +4072,14 @@ rt_shutdown(void *tab_)
 static void
 rt_delete(void *tab_)
 {
-  birdloop_enter(&main_birdloop);
+  ASSERT_DIE(birdloop_inside(&main_birdloop));
 
   /* We assume that nobody holds the table reference now as use_count is zero.
    * Anyway the last holder may still hold the lock. Therefore we lock and
    * unlock it the last time to be sure that nobody is there. */
   struct rtable_private *tab = RT_LOCK((rtable *) tab_);
   struct config *conf = tab->deleted;
+  DOMAIN(rtable) dom = tab->lock;
 
   RT_UNLOCK(RT_PUB(tab));
 
@@ -4081,7 +4087,8 @@ rt_delete(void *tab_)
   birdloop_free(tab->loop);
   config_del_obstacle(conf);
 
-  birdloop_leave(&main_birdloop);
+  /* Also drop the domain */
+  DOMAIN_FREE(rtable, dom);
 }
 
 
@@ -4636,18 +4643,9 @@ rt_init_hostcache(struct rtable_private *tab)
     .data = tab,
   };
 
-  hc->req = (struct rt_export_request) {
-    .name = mb_sprintf(tab->rp, "%s.hcu.notifier", tab->name),
-    .list = birdloop_event_list(tab->loop),
-    .trace_routes = tab->config->debug,
-    .dump_req = hc_notify_dump_req,
-    .log_state_change = hc_notify_log_state_change,
-    .export_one = hc_notify_export_one,
-  };
-
-  rt_table_export_start_locked(tab, &hc->req);
-
   tab->hostcache = hc;
+
+  ev_send_loop(tab->loop, &hc->update);
 }
 
 static void
@@ -4775,9 +4773,24 @@ rt_update_hostcache(void *data)
 
   RT_LOCKED((rtable *) data, tab)
   {
-
   struct hostcache *hc = tab->hostcache;
 
+  /* Finish initialization */
+  if (!hc->req.name)
+  {
+    hc->req = (struct rt_export_request) {
+      .name = mb_sprintf(tab->rp, "%s.hcu.notifier", tab->name),
+      .list = birdloop_event_list(tab->loop),
+      .pool = tab->rp,
+      .trace_routes = tab->config->debug,
+      .dump_req = hc_notify_dump_req,
+      .log_state_change = hc_notify_log_state_change,
+      .export_one = hc_notify_export_one,
+    };
+
+    rt_table_export_start_locked(tab, &hc->req);
+  }
+
   /* Shutdown shortcut */
   if (!hc->req.hook)
     RT_RETURN(tab);
index 01372b8c1d528d1e0ec4f39422618aadbccb00ae..4fe8eb53fd142f6fa76c803ac866230c54abcea2 100644 (file)
--- a/nest/rt.h
+++ b/nest/rt.h
@@ -294,6 +294,7 @@ struct rt_export_request {
   u8 addr_mode;                                /* Network prefilter mode (TE_ADDR_*) */
 
   event_list *list;                    /* Where to schedule export events */
+  pool *pool;                          /* Pool to use for allocations */
 
   /* There are two methods of export. You can either request feeding every single change
    * or feeding the whole route feed. In case of regular export, &export_one is preferred.
@@ -438,7 +439,7 @@ int rpe_get_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
  */
 
 void rt_init_export(struct rt_exporter *re, struct rt_export_hook *hook);
-struct rt_export_hook *rt_alloc_export(struct rt_exporter *re, uint size);
+struct rt_export_hook *rt_alloc_export(struct rt_exporter *re, pool *pool, uint size);
 void rt_stop_export_common(struct rt_export_hook *hook);
 void rt_export_stopped(struct rt_export_hook *hook);
 void rt_exporter_init(struct rt_exporter *re);
index e95a0eadebe851bcc08c2bc780133c718691769c..decaa407f4ec862bd472c4e9c5418d5e5c86d404 100644 (file)
@@ -1826,7 +1826,7 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con
 
   TRACE(D_EVENTS, "Adding interface %s", new->name);
 
-  pool *pool = rp_new(p->p.pool, new->name);
+  pool *pool = rp_new(p->p.pool, proto_domain(&p->p), new->name);
 
   ifa = mb_allocz(pool, sizeof(struct babel_iface));
   ifa->proto = p;
index ddbc4948484ba29a9057b10a52932de24375a2a1..f7d8f8d9f7760491b43cc14389b35ddcd3e8d490 100644 (file)
@@ -1073,7 +1073,7 @@ bfd_start(struct proto *P)
 
   pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
 
-  p->tpool = rp_new(P->pool, "BFD loop pool");
+  p->tpool = birdloop_pool(P->loop);
 
   p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
   HASH_INIT(p->session_hash_id, P->pool, 8);
index 4e6524f48c4b2a5fe0694fc83b4db51bda6e0aa2..82971c0119d6dab176522f119532881dfb3dd075 100644 (file)
@@ -1853,7 +1853,7 @@ bgp_init_pending_tx(struct bgp_channel *c)
 {
   ASSERT_DIE(!c->ptx);
 
-  pool *p = rp_new(c->pool, "BGP Pending TX");
+  pool *p = rp_new(c->pool, proto_domain(c->c.proto), "BGP Pending TX");
   c->ptx = ralloc(p, &bgp_pending_tx_class);
   c->ptx->pool = p;
 
@@ -1981,7 +1981,7 @@ bgp_out_table_feed(void *data)
 static void
 bgp_out_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
 {
-  req->hook = rt_alloc_export(re, sizeof(struct bgp_out_export_hook));
+  req->hook = rt_alloc_export(re, req->pool, sizeof(struct bgp_out_export_hook));
   req->hook->req = req;
 
   struct bgp_out_export_hook *hook = SKIP_BACK(struct bgp_out_export_hook, h, req->hook);
index 0525d50238972badea28069a7e38b8c159242603..e60884bae9506c3334f3305e6b1b86afc1dac4ff 100644 (file)
@@ -922,7 +922,8 @@ bgp_graceful_restart_feed(struct bgp_channel *c)
 {
   c->stale_feed = (struct rt_export_request) {
     .name = "BGP-GR",
-    .list = &global_work_list,
+    .list = proto_event_list(c->c.proto),
+    .pool = c->c.proto->pool,
     .trace_routes = c->c.debug | c->c.proto->debug,
     .dump_req = bgp_graceful_restart_feed_dump_req,
     .log_state_change = bgp_graceful_restart_feed_log_state_change,
index 0eecb43256fc5cb12006b9d42721a88a03df2b0d..c3ec4a4c5d6fc17ad3b83978d6afb23b81e349cf 100644 (file)
@@ -567,7 +567,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
     OSPF_TRACE(D_EVENTS, "Adding interface %s (%N) to area %R",
               iface->name, &addr->prefix, oa->areaid);
 
-  pool = rp_new(p->p.pool, "OSPF Interface");
+  pool = rp_new(p->p.pool, proto_domain(&p->p), "OSPF Interface");
   ifa = mb_allocz(pool, sizeof(struct ospf_iface));
   ifa->iface = iface;
   ifa->addr = addr;
@@ -690,7 +690,7 @@ ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip)
 
   /* Vlink ifname is stored just after the ospf_iface structure */
 
-  pool = rp_new(p->p.pool, "OSPF Vlink");
+  pool = rp_new(p->p.pool, proto_domain(&p->p), "OSPF Vlink");
   ifa = mb_allocz(pool, sizeof(struct ospf_iface) + 16);
   ifa->oa = p->backbone;
   ifa->cf = ip;
index 2c73d251fb6fbfca5113ab6eb9d97ccd07b1e9de..59a60587db82f0c3c2c530fbc7af4a23e600129f 100644 (file)
@@ -80,7 +80,7 @@ struct ospf_neighbor *
 ospf_neighbor_new(struct ospf_iface *ifa)
 {
   struct ospf_proto *p = ifa->oa->po;
-  struct pool *pool = rp_new(p->p.pool, "OSPF Neighbor");
+  struct pool *pool = rp_new(p->p.pool, proto_domain(&p->p), "OSPF Neighbor");
   struct ospf_neighbor *n = mb_allocz(pool, sizeof(struct ospf_neighbor));
 
   n->pool = pool;
index eb15f9cb5fb14c3898187afd4d0da4e0be2281a3..3d5fe5a306ab02f51241e57425d4928b02fb5e48 100644 (file)
@@ -287,7 +287,7 @@ radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_conf
 
   RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
 
-  pool *pool = rp_new(p->p.pool, iface->name);
+  pool *pool = rp_new(p->p.pool, proto_domain(&p->p), iface->name);
   ifa = mb_allocz(pool, sizeof(struct radv_iface));
   ifa->pool = pool;
   ifa->ra = p;
index e5638affcb7925a4967c59fa4cc16cee468b4c06..0fd686b32d4000198c95cb3364b5c47818472a88 100644 (file)
@@ -599,7 +599,7 @@ rpki_check_expire_interval(uint seconds)
 static struct rpki_cache *
 rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf)
 {
-  pool *pool = rp_new(p->p.pool, cf->hostname);
+  pool *pool = rp_new(p->p.pool, proto_domain(&p->p), cf->hostname);
 
   struct rpki_cache *cache = mb_allocz(pool, sizeof(struct rpki_cache));
 
index 701eddf77d84e853c2a277a397723cea6e6b1923..8e95a6987e2d97299d5ffe49c1b018140252568b 100644 (file)
@@ -503,12 +503,14 @@ sockets_fire(struct birdloop *loop)
  */
 
 DEFINE_DOMAIN(resource);
+static void bird_thread_start_event(void *_data);
 
 struct birdloop_pickup_group {
   DOMAIN(resource) domain;
   list loops;
   list threads;
   btime max_latency;
+  event start_threads;
 } pickup_groups[2] = {
   {
     /* all zeroes */
@@ -516,6 +518,8 @@ struct birdloop_pickup_group {
   {
     /* FIXME: make this dynamic, now it copies the loop_max_latency value from proto/bfd/config.Y */
     .max_latency = 10 MS,
+    .start_threads.hook = bird_thread_start_event,
+    .start_threads.data = &pickup_groups[1],
   },
 };
 
@@ -630,13 +634,11 @@ bird_thread_main(void *arg)
   rcu_thread_start(&thr->rcu);
   synchronize_rcu();
 
-  tmp_init(thr->pool);
-  init_list(&thr->loops);
-
-  thr->meta = birdloop_new_no_pickup(thr->pool, DOMAIN_ORDER(meta), "Thread Meta");
-  thr->meta->thread = thr;
   birdloop_enter(thr->meta);
 
+  tmp_init(thr->pool, birdloop_domain(thr->meta));
+  init_list(&thr->loops);
+
   thr->sock_changed = 1;
 
   struct pfd pfd;
@@ -759,15 +761,20 @@ static struct bird_thread *
 bird_thread_start(struct birdloop_pickup_group *group)
 {
   ASSERT_DIE(birdloop_inside(&main_birdloop));
-  ASSERT_DIE(DOMAIN_IS_LOCKED(resource, group->domain));
 
-  pool *p = rp_new(&root_pool, "Thread");
+  struct birdloop *meta = birdloop_new_no_pickup(&root_pool, DOMAIN_ORDER(meta), "Thread Meta");
+  pool *p = birdloop_pool(meta);
+
+  birdloop_enter(meta);
+  LOCK_DOMAIN(resource, group->domain);
 
   struct bird_thread *thr = mb_allocz(p, sizeof(*thr));
   thr->pool = p;
   thr->cleanup_event = (event) { .hook = bird_thread_cleanup, .data = thr, };
   thr->group = group;
   thr->max_latency_ns = (group->max_latency ?: 5 S) TO_NS;
+  thr->meta = meta;
+  thr->meta->thread = thr;
 
   wakeup_init(thr);
   ev_init_list(&thr->priority_events, NULL, "Thread direct event list");
@@ -790,9 +797,18 @@ bird_thread_start(struct birdloop_pickup_group *group)
   if (e = pthread_create(&thr->thread_id, &thr->thread_attr, bird_thread_main, thr))
     die("pthread_create() failed: %M", e);
 
+  UNLOCK_DOMAIN(resource, group->domain);
+  birdloop_leave(meta);
   return thr;
 }
 
+static void
+bird_thread_start_event(void *_data)
+{
+  struct birdloop_pickup_group *group = _data;
+  bird_thread_start(group);
+}
+
 static struct birdloop *thread_dropper;
 static event *thread_dropper_event;
 static uint thread_dropper_goal;
@@ -880,15 +896,14 @@ bird_thread_commit(struct config *new, struct config *old UNUSED)
     int dif = list_length(&group->threads) - (thread_dropper_goal = new->thread_count);
     _Bool thread_dropper_running = !!thread_dropper;
 
+    UNLOCK_DOMAIN(resource, group->domain);
+
     if (dif < 0)
     {
       bird_thread_start(group);
-      UNLOCK_DOMAIN(resource, group->domain);
       continue;
     }
 
-    UNLOCK_DOMAIN(resource, group->domain);
-
     if ((dif > 0) && !thread_dropper_running)
     {
       struct birdloop *tdl = birdloop_new(&root_pool, DOMAIN_ORDER(control), group->max_latency, "Thread dropper");
@@ -1006,12 +1021,13 @@ bird_thread_show(void *data)
 void
 cmd_show_threads(int show_loops)
 {
-  pool *p = rp_new(&root_pool, "Show Threads");
+  DOMAIN(control) lock = DOMAIN_NEW(control, "Show Threads");
+  pool *p = rp_new(&root_pool, lock.control, "Show Threads");
 
   struct bird_thread_show_data *tsd = mb_allocz(p, sizeof(struct bird_thread_show_data));
-  tsd->lock = DOMAIN_NEW(control, "Show Threads");
   tsd->cli = this_cli;
   tsd->pool = p;
+  tsd->lock = lock;
   tsd->show_loops = show_loops;
 
   this_cli->cont = bird_thread_show_cli_cont;
@@ -1112,8 +1128,10 @@ birdloop_stop_internal(struct birdloop *loop)
   /* Request local socket reload */
   this_thread->sock_changed++;
 
-  /* Tail-call the stopped hook */
-  loop->stopped(loop->stop_data);
+  /* Call the stopped hook from the main loop */
+  loop->event.hook = loop->stopped;
+  loop->event.data = loop->stop_data;
+  ev_send_loop(&main_birdloop, &loop->event);
 }
 
 static void
@@ -1187,8 +1205,9 @@ static struct birdloop *
 birdloop_vnew_internal(pool *pp, uint order, struct birdloop_pickup_group *group, const char *name, va_list args)
 {
   struct domain_generic *dg = domain_new(name, order);
+  DG_LOCK(dg);
 
-  pool *p = rp_vnewf(pp, name, args);
+  pool *p = rp_vnewf(pp, dg, name, args);
   struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
   loop->pool = p;
 
@@ -1197,7 +1216,7 @@ birdloop_vnew_internal(pool *pp, uint order, struct birdloop_pickup_group *group
 
   atomic_store_explicit(&loop->thread_transition, 0, memory_order_relaxed);
 
-  birdloop_enter(loop);
+  birdloop_enter_locked(loop);
 
   ev_init_list(&loop->event_list, loop, name);
   timers_init(&loop->time, p);
@@ -1211,8 +1230,7 @@ birdloop_vnew_internal(pool *pp, uint order, struct birdloop_pickup_group *group
     LOCK_DOMAIN(resource, group->domain);
     add_tail(&group->loops, &loop->n);
     if (EMPTY_LIST(group->threads))
-      bird_thread_start(group);
-
+      ev_send(&global_event_list, &group->start_threads);
     wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(group->threads)));
     UNLOCK_DOMAIN(resource, group->domain);
   }
@@ -1277,8 +1295,11 @@ birdloop_free(struct birdloop *loop)
 {
   ASSERT_DIE(loop->thread == NULL);
 
-  domain_free(loop->time.domain);
+  struct domain_generic *dg = loop->time.domain;
+  DG_LOCK(dg);
   rp_free(loop->pool);
+  DG_UNLOCK(dg);
+  domain_free(dg);
 }
 
 static void
index 9e6ddb453c2b36329f5c750e48b7bb5ec3381630..6e94b0b61b9ec0e8d4bd06b9202c372b269088e2 100644 (file)
@@ -74,7 +74,7 @@ static list krt_proto_list;
 void
 krt_io_init(void)
 {
-  krt_pool = rp_new(&root_pool, "Kernel Syncer");
+  krt_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Kernel Syncer");
   krt_filter_lp = lp_new_default(krt_pool);
   init_list(&krt_proto_list);
   krt_sys_io_init();