]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge commit '94eb0858' into thread-next
authorMaria Matejka <mq@ucw.cz>
Mon, 18 Jul 2022 10:33:00 +0000 (12:33 +0200)
committerMaria Matejka <mq@ucw.cz>
Mon, 18 Jul 2022 10:33:00 +0000 (12:33 +0200)
29 files changed:
1  2 
filter/filter_test.c
lib/a-set_test.c
lib/birdlib.h
lib/event.c
lib/event_test.c
lib/lists.h
lib/resource.c
lib/resource.h
lib/socket.h
lib/timer.c
nest/config.Y
nest/proto.c
nest/protocol.h
nest/rt-show.c
nest/rt-table.c
nest/rt.h
proto/bfd/Makefile
proto/bfd/bfd.c
proto/bfd/bfd.h
proto/bfd/config.Y
proto/bfd/packets.c
proto/bgp/bgp.c
sysdep/unix/Makefile
sysdep/unix/alloc.c
sysdep/unix/io-loop.c
sysdep/unix/io.c
sysdep/unix/main.c
test/birdtest.c
test/bt-utils.c

Simple merge
index 3280031f78526a5fc60dcd83fd1d7f72908cf38d,efd1b67d5f54b83675cdaa735444eec8dbedaba5..693b8f0897392d964fcafce0b3237138b43aa96a
@@@ -221,6 -240,9 +221,7 @@@ t_set_ec_delete(void
    return 1;
  }
  
 -void resource_sys_init(void);
 -
  int
  main(int argc, char *argv[])
  {
diff --cc lib/birdlib.h
Simple merge
diff --cc lib/event.c
index 33dc00b01350f2bb90c95a30db01b706f0cba492,6c5c8b14491e2d88119b1ae781e29a4e627a6649..b0abd1d0b2ed9d2620def1372d48a19f8422e37a
@@@ -152,15 -158,28 +158,29 @@@ ev_run_list(event_list *l
      {
        event *e = SKIP_BACK(event, n, n);
  
-       /* This is ugly hack, we want to log just events executed from the main I/O loop */
-       if ((l == &global_event_list) || (l == &global_work_list))
+       if (legacy)
+       {
+       /* The legacy way of event execution */
        io_log_event(e->hook, e->data);
-       ev_run(e);
+       ev_postpone(e);
+       e->hook(e->data);
+       }
+       else
+       {
+       // io_log_event(e->hook, e->data); /* TODO: add support for event logging in other io loops */
+       ASSERT_DIE(e->list == l);
+       LOCK_DOMAIN(event, l->lock);
+       rem_node(&e->n);
+       UNLOCK_DOMAIN(event, l->lock);
+       e->hook(e->data);
+       }
 +      tmp_flush();
      }
  
-   return !EMPTY_LIST(*l);
+   LOCK_DOMAIN(event, l->lock);
+   int repeat = ! EMPTY_LIST(l->events);
+   UNLOCK_DOMAIN(event, l->lock);
+   return repeat;
  }
  
  int
@@@ -180,12 -204,9 +205,10 @@@ ev_run_list_limited(event_list *l, uin
        if (!limit)
        break;
  
-       /* This is ugly hack, we want to log just events executed from the main I/O loop */
-       if ((l == &global_event_list) || (l == &global_work_list))
-       io_log_event(e->hook, e->data);
+       io_log_event(e->hook, e->data);
  
        ev_run(e);
 +      tmp_flush();
        limit--;
      }
  
index 3070327de0ebfd9e63a6dfafc307972a6f46a77d,9dda3e2a83f336e5ca02afcf2d091fc6118ee943..5385011a826d880ab2dc70e4ea2690d8ab66effe
@@@ -53,10 -55,12 +53,10 @@@ t_ev_run_list(void
  {
    int i;
  
 -  resource_sys_init();
 -  resource_init();
    olock_init();
-   timer_init();
+   birdloop_init();
 -  io_init();
    rt_init();
 +  io_init();
    if_init();
  //  roa_init();
    config_init();
diff --cc lib/lists.h
Simple merge
diff --cc lib/resource.c
index c31d9889dbb12b8ebb2e460ca57bd60c71199522,e80b315b2c85e276d64eaa86ce13834a64c095d4..a33fd21406ffea7ebc834c9acec9314d73cb9795
   * is freed upon shutdown of the module.
   */
  
--struct pool {
--  resource r;
--  list inside;
 -  struct pool_pages *pages;
--  const char *name;
 -};
 -
 -struct pool_pages {
 -  uint free;
 -  uint used;
 -  void *ptr[0];
--};
 -
 -#define POOL_PAGES_MAX        ((page_size - sizeof(struct pool_pages)) / sizeof (void *))
--
  static void pool_dump(resource *);
  static void pool_free(resource *);
  static resource *pool_lookup(resource *, unsigned long);
diff --cc lib/resource.h
index 8bb264b136c0e1718e14da1aedeb83edca298279,26030aea6781b29cacefdac7ca2c04b0a106a75c..5ad011ecc9a516c2c1553ec304bee8c257b38db3
@@@ -40,7 -34,7 +40,12 @@@ struct resclass 
  
  /* Generic resource manipulation */
  
--typedef struct pool pool;
++typedef struct pool {
++  resource r;
++  list inside;
++  const char *name;
++} pool;
++
  
  void resource_init(void);
  pool *rp_new(pool *, const char *);   /* Create new pool */
diff --cc lib/socket.h
Simple merge
diff --cc lib/timer.c
Simple merge
diff --cc nest/config.Y
Simple merge
diff --cc nest/proto.c
index 3a80ab0edee20821d78346ef5d45e5c899362ae2,ac0fb2322447e988793fa7b80493ec68753a786e..e9bced3bf539be33646316bfff0146ba705f9821
@@@ -15,8 -15,9 +15,9 @@@
  #include "lib/event.h"
  #include "lib/timer.h"
  #include "lib/string.h"
+ #include "lib/coro.h"
  #include "conf/conf.h"
 -#include "nest/route.h"
 +#include "nest/rt.h"
  #include "nest/iface.h"
  #include "nest/cli.h"
  #include "filter/filter.h"
@@@ -467,8 -495,7 +489,9 @@@ channel_start_export(struct channel *c
  
    c->out_req = (struct rt_export_request) {
      .name = rn,
+     .list = proto_work_list(c->proto),
 +    .addr = c->out_subprefix,
 +    .addr_mode = c->out_subprefix ? TE_ADDR_IN : TE_ADDR_NONE,
      .trace_routes = c->debug | c->proto->debug,
      .dump_req = channel_dump_export_req,
      .log_state_change = channel_export_log_state_change,
@@@ -629,19 -851,97 +652,20 @@@ void channel_reload_export_bulk(struct 
  
  /* Called by protocol to activate in_table */
  void
 -channel_setup_in_table(struct channel *c, int best)
 -{
 -  int nlen = sizeof("import") + strlen(c->name) + strlen(c->proto->name) + 3;
 -
 -  struct {
 -    struct channel_aux_table cat;
 -    struct rtable_config tab_cf;
 -    char name[0];
 -  } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen);
 -
 -  bsprintf(cat->name, "%s.%s.import", c->proto->name, c->name);
 -
 -  cat->tab_cf.name = cat->name;
 -  cat->tab_cf.addr_type = c->net_type;
 -
 -  c->in_table = &cat->cat;
 -  c->in_table->push = (struct rt_import_request) {
 -    .name = cat->name,
 -    .trace_routes = c->debug | c->proto->debug,
 -    .dump_req = channel_in_push_dump_req,
 -    .log_state_change = channel_push_log_state_change,
 -    .preimport = channel_in_preimport,
 -  };
 -  c->in_table->get = (struct rt_export_request) {
 -    .name = cat->name,
 -    .list = proto_work_list(c->proto),
 -    .trace_routes = c->debug | c->proto->debug,
 -    .dump_req = channel_in_get_dump_req,
 -    .log_state_change = channel_get_log_state_change,
 -    .export_one = best ? channel_in_export_one_best : channel_in_export_one_any,
 -    .export_bulk = best ? channel_in_export_bulk_best : channel_in_export_bulk_any,
 -  };
 -
 -  c->in_table->c = c;
 -  c->in_table->tab = rt_setup(c->proto->pool, &cat->tab_cf);
 -  self_link(&c->in_table->tab->n);
 -  rt_lock_table(c->in_table->tab);
 -
 -  rt_request_import(c->in_table->tab, &c->in_table->push);
 -  rt_request_export(c->in_table->tab, &c->in_table->get);
 -}
 -
 -/* Called by protocol to activate out_table */
 -void
 -channel_setup_out_table(struct channel *c)
 +channel_setup_in_table(struct channel *c)
  {
 -  int nlen = sizeof("export") + strlen(c->name) + strlen(c->proto->name) + 3;
 -
 -  struct {
 -    struct channel_aux_table cat;
 -    struct rtable_config tab_cf;
 -    char name[0];
 -  } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen);
 -
 -  bsprintf(cat->name, "%s.%s.export", c->proto->name, c->name);
 -
 -  cat->tab_cf.name = cat->name;
 -  cat->tab_cf.addr_type = c->net_type;
 -
 -  c->out_table = &cat->cat;
 -  c->out_table->push = (struct rt_import_request) {
 -    .name = cat->name,
 -    .trace_routes = c->debug | c->proto->debug,
 -    .dump_req = channel_out_push_dump_req,
 -    .log_state_change = channel_push_log_state_change,
 -  };
 -  c->out_table->get = (struct rt_export_request) {
 -    .name = cat->name,
 +  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),
      .trace_routes = c->debug | c->proto->debug,
 -    .dump_req = channel_out_get_dump_req,
 -    .log_state_change = channel_get_log_state_change,
 -    .export_one = (c->ra_mode == RA_ANY) ? channel_out_export_one_any : channel_out_export_one_best,
 -    .export_bulk = channel_out_export_bulk,
 +    .export_bulk = channel_reload_export_bulk,
 +    .dump_req = channel_reload_dump_req,
 +    .log_state_change = channel_reload_log_state_change,
    };
  
 -  c->out_table->c = c;
 -  c->out_table->tab = rt_setup(c->proto->pool, &cat->tab_cf);
 -  self_link(&c->out_table->tab->n);
 -  rt_lock_table(c->out_table->tab);
 -
 -  rt_request_import(c->out_table->tab, &c->out_table->push);
 -  rt_request_export(c->out_table->tab, &c->out_table->get);
 +  c->in_keep |= RIK_PREFILTER;
  }
  
 -static void
 -channel_aux_request_refeed(struct channel_aux_table *cat)
 -{
 -  cat->refeed_pending = 1;
 -  rt_stop_export(&cat->get, channel_aux_export_stopped);
 -}
  
  static void
  channel_do_start(struct channel *c)
@@@ -1113,11 -1458,27 +1152,21 @@@ proto_init(struct proto_config *c, nod
  static void
  proto_start(struct proto *p)
  {
-   /* Here we cannot use p->cf->name since it won't survive reconfiguration */
-   p->pool = rp_new(proto_pool, p->proto->name);
+   DBG("Kicking %s up\n", p->name);
+   PD(p, "Starting");
 -  int ns = strlen("Protocol ") + strlen(p->cf->name) + 1;
 -  void *nb = mb_alloc(proto_pool, ns);
 -  ASSERT_DIE(ns - 1 == bsnprintf(nb, ns, "Protocol %s", p->cf->name));
 -
 -  p->pool = rp_new(proto_pool, nb);
++  p->pool = rp_newf(proto_pool, "Protocol %s", p->cf->name);
  
    if (graceful_restart_state == GRS_INIT)
      p->gr_recovery = 1;
 -    p->loop = birdloop_new(p->pool, p->cf->loop_order, nb);
+   if (p->cf->loop_order != DOMAIN_ORDER(the_bird))
 -  mb_move(nb, p->pool);
 -
++    p->loop = birdloop_new(p->pool, p->cf->loop_order, p->pool->name);
+   p->event->list = proto_event_list(p);
+   PROTO_LOCKED_FROM_MAIN(p)
+     proto_notify_state(p, (p->proto->start ? p->proto->start(p) : PS_UP));
  }
  
  
@@@ -1694,9 -2045,9 +1733,9 @@@ protos_dump_all(void
    WALK_LIST(p, proto_list)
    {
  #define DPF(x)        (p->x ? " " #x : "")
 -    debug("  protocol %s (%p) state %s with %d active channels flags: %s%s%s%s\n",
 +    debug("  protocol %s (%p) state %s with %d active channels flags: %s%s%s%s%s\n",
        p->name, p, p_states[p->proto_state], p->active_channels,
-       DPF(disabled), DPF(active), DPF(do_start), DPF(do_stop), DPF(reconfiguring));
+       DPF(disabled), DPF(active), DPF(do_stop), DPF(reconfiguring));
  #undef DPF
  
      struct channel *c;
diff --cc nest/protocol.h
Simple merge
diff --cc nest/rt-show.c
index 6dfb85f6354580f88bb3fba3caa8779c5756b870,d942b8e182251fc6d8520572bc6c7a09e3cffabf..b784bf833d652805905c276ebdcf2c4c06a7e831
@@@ -279,37 -261,34 +279,38 @@@ rt_show_cont(struct rt_show_data *d
    if (d->running_on_config && (d->running_on_config != config))
    {
      cli_printf(c, 8004, "Stopped due to reconfiguration");
 -    goto done;
 +    return rt_show_done(d);
    }
  
 -  if (!d->table_open)
 -  {
 -    FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib);
 -    d->table_open = 1;
 -    d->table_counter++;
 -    d->kernel = rt_show_get_kernel(d);
 +  d->req = (struct rt_export_request) {
 +    .addr = d->addr,
 +    .name = "CLI Show Route",
++    .list = &global_work_list,
 +    .export_bulk = rt_show_net_export_bulk,
 +    .dump_req = rt_show_dump_req,
 +    .log_state_change = rt_show_log_state_change,
 +    .addr_mode = d->addr_mode,
 +  };
  
 -    d->show_counter_last = d->show_counter;
 -    d->rt_counter_last   = d->rt_counter;
 -    d->net_counter_last  = d->net_counter;
 +  d->table_counter++;
  
 -    if (d->tables_defined_by & RSD_TDB_SET)
 -      rt_show_table(c, d);
 -  }
 +  d->show_counter_last = d->show_counter;
 +  d->rt_counter_last   = d->rt_counter;
 +  d->net_counter_last  = d->net_counter;
  
 -  FIB_ITERATE_START(fib, it, net, n)
 -  {
 -    if (!max--)
 -    {
 -      FIB_ITERATE_PUT(it);
 -      return;
 -    }
 -    rt_show_net(c, n, d);
 -  }
 -  FIB_ITERATE_END;
 +  if (d->tables_defined_by & RSD_TDB_SET)
 +    rt_show_table(d);
 +
 +  rt_request_export(d->tab->table, &d->req);
 +}
 +
 +static void
 +rt_show_export_stopped(struct rt_export_request *req)
 +{
 +  struct rt_show_data *d = SKIP_BACK(struct rt_show_data, req, req);
 +
 +  /* The hook is now invalid */
 +  req->hook = NULL;
  
    if (d->stats)
    {
diff --cc nest/rt-table.c
index 85a6faf73a42523de957bf645f7eb8880dd13e3b,9c12ef565ffde2b30628ea425392dcb43d7f6b7c..ca09267885a67094fe708c60f0d2167b7ff50bea
@@@ -1412,9 -1102,15 +1412,15 @@@ rt_next_export(struct rt_export_hook *h
  
    /* No, therefore we must process the table's first pending export */
    else
 -    return tab->first_export;
 +    return tab->first;
  }
  
+ static inline void
+ rt_send_export_event(struct rt_export_hook *hook)
+ {
+   ev_send(hook->req->list, hook->event);
+ }
  static void
  rt_announce_exports(timer *tm)
  {
@@@ -2093,37 -1731,16 +2099,37 @@@ rt_request_export(struct rt_exporter *r
    atomic_store_explicit(&hook->last_export, rpe, memory_order_relaxed);
  
    hook->n = (node) {};
 -  add_tail(&tab->exports, &hook->n);
 +  add_tail(&re->hooks, &hook->n);
  
 -  FIB_ITERATE_INIT(&hook->feed_fit, &tab->fib);
 +  /* Regular export */
 +  rt_set_export_state(hook, TES_FEEDING);
-   ev_schedule_work(hook->event);
++  rt_send_export_event(hook);
 +}
  
 -  DBG("New export hook %p req %p in table %s uc=%u\n", hook, req, tab->name, tab->use_count);
 +static void
 +rt_table_export_stop(struct rt_export_hook *hook)
 +{
 +  rtable *tab = SKIP_BACK(rtable, exporter, hook->table);
  
 -  hook->event = ev_new_init(p, rt_feed_channel, hook);
 -  rt_send_export_event(hook);
 +  if (atomic_load_explicit(&hook->export_state, memory_order_relaxed) != TES_FEEDING)
 +    return;
  
 -  rt_set_export_state(hook, TES_FEEDING);
 +  switch (hook->req->addr_mode)
 +  {
 +    case TE_ADDR_IN:
 +      if (hook->walk_lock)
 +      {
 +      rt_unlock_trie(tab, hook->walk_lock);
 +      hook->walk_lock = NULL;
 +      mb_free(hook->walk_state);
 +      hook->walk_state = NULL;
 +      break;
 +      }
 +      /* fall through */
 +    case TE_ADDR_NONE:
 +      fit_get(&tab->fib, &hook->feed_fit);
 +      break;
 +  }
  }
  
  void
@@@ -2142,12 -1760,11 +2148,12 @@@ rt_stop_export(struct rt_export_reques
    hook->event->hook = rt_export_stopped;
    hook->stopped = stopped;
  
 -  rt_send_export_event(hook);
 -
 +  /* Update export state */
    rt_set_export_state(hook, TES_STOP);
 -}
  
-   ev_schedule(hook->event);
 +  /* Run the stopped event */
++  rt_send_export_event(hook);
 +}
  
  /**
   * rt_refresh_begin - start a refresh cycle
@@@ -3720,18 -2851,8 +3726,18 @@@ rt_commit(struct config *new, struct co
    DBG("\tdone\n");
  }
  
-   ev_schedule_work(c->event);
 +static void
 +rt_feed_done(struct rt_export_hook *c)
 +{
 +  c->event->hook = rt_export_hook;
 +
 +  rt_set_export_state(c, TES_READY);
 +
++  rt_send_export_event(c);
 +}
 +
  /**
 - * rt_feed_channel - advertise all routes to a channel
 + * rt_feed_by_fib - advertise all routes to a channel by walking a fib
   * @c: channel to be fed
   *
   * This function performs one pass of advertisement of routes to a channel that
diff --cc nest/rt.h
index 20ed0ad05856b39f2eb335163ca294eea7e02ba0,0000000000000000000000000000000000000000..5acbded6ea0dfaf6634bce66ee87cbab2548bb23
mode 100644,000000..100644
--- /dev/null
+++ b/nest/rt.h
@@@ -1,541 -1,0 +1,544 @@@
 +/*
 + *    BIRD Internet Routing Daemon -- Routing Table
 + *
 + *    (c) 1998--2000 Martin Mares <mj@ucw.cz>
 + *    (c) 2019--2021 Maria Matejka <mq@jmq.cz>
 + *
 + *    Can be freely distributed and used under the terms of the GNU GPL.
 + */
 +
 +#ifndef _BIRD_NEST_RT_H_
 +#define _BIRD_NEST_RT_H_
 +
 +#include "lib/lists.h"
 +#include "lib/bitmap.h"
 +#include "lib/resource.h"
 +#include "lib/net.h"
 +#include "lib/type.h"
 +#include "lib/fib.h"
 +#include "lib/route.h"
++#include "lib/event.h"
 +
 +#include <stdatomic.h>
 +
 +struct ea_list;
 +struct protocol;
 +struct proto;
 +struct channel;
 +struct rte_src;
 +struct symbol;
 +struct timer;
 +struct filter;
 +struct f_trie;
 +struct f_trie_walk_state;
 +struct cli;
 +
 +/*
 + *    Master Routing Tables. Generally speaking, each of them contains a FIB
 + *    with each entry pointing to a list of route entries representing routes
 + *    to given network (with the selected one at the head).
 + *
 + *    Each of the RTE's contains variable data (the preference and protocol-dependent
 + *    metrics) and a pointer to a route attribute block common for many routes).
 + *
 + *    It's guaranteed that there is at most one RTE for every (prefix,proto) pair.
 + */
 +
 +struct rtable_config {
 +  node n;
 +  char *name;
 +  struct rtable *table;
 +  struct proto_config *krt_attached;  /* Kernel syncer attached to this table */
 +  uint addr_type;                     /* Type of address data stored in table (NET_*) */
 +  uint gc_threshold;                  /* Maximum number of operations before GC is run */
 +  uint gc_period;                     /* Approximate time between two consecutive GC runs */
 +  byte sorted;                                /* Routes of network are sorted according to rte_better() */
 +  byte trie_used;                     /* Rtable has attached trie */
 +  btime min_settle_time;              /* Minimum settle time for notifications */
 +  btime max_settle_time;              /* Maximum settle time for notifications */
 +  btime export_settle_time;           /* Delay before exports are announced */
 +};
 +
 +struct rt_export_hook;
 +struct rt_export_request;
 +
 +struct rt_exporter {
 +  list hooks;                         /* Registered route export hooks */
 +  uint addr_type;                     /* Type of address data exported (NET_*) */
 +
 +  struct rt_export_hook *(*start)(struct rt_exporter *, struct rt_export_request *);
 +  void (*stop)(struct rt_export_hook *);
 +  void (*done)(struct rt_export_hook *);
 +  void (*used)(struct rt_exporter *);
 +
 +  list pending;                               /* List of packed struct rt_pending_export */
 +  struct timer *export_timer;
 +
 +  struct rt_pending_export *first;    /* First export to announce */
 +  u64 next_seq;                               /* The next export will have this ID */
 +};
 +
 +typedef struct rtable {
 +  resource r;
 +  node n;                             /* Node in list of all tables */
 +  pool *rp;                           /* Resource pool to allocate everything from, including itself */
 +  struct slab *rte_slab;              /* Slab to allocate route objects */
 +  struct fib fib;
 +  struct f_trie *trie;                        /* Trie of prefixes defined in fib */
 +  char *name;                         /* Name of this table */
 +  uint addr_type;                     /* Type of address data stored in table (NET_*) */
 +  int use_count;                      /* Number of protocols using this table */
 +  u32 rt_count;                               /* Number of routes in the table */
 +
 +  list imports;                               /* Registered route importers */
 +  struct rt_exporter exporter;                /* Exporter API structure */
 +
 +  struct hmap id_map;
 +  struct hostcache *hostcache;
 +  struct rtable_config *config;               /* Configuration of this table */
 +  struct config *deleted;             /* Table doesn't exist in current configuration,
 +                                       * delete as soon as use_count becomes 0 and remove
 +                                       * obstacle from this routing table.
 +                                       */
 +  struct event *rt_event;             /* Routing table event */
 +  struct timer *prune_timer;          /* Timer for periodic pruning / GC */
 +  btime last_rt_change;                       /* Last time when route changed */
 +  btime base_settle_time;             /* Start time of rtable settling interval */
 +  btime gc_time;                      /* Time of last GC */
 +  uint gc_counter;                    /* Number of operations since last GC */
 +  byte prune_state;                   /* Table prune state, 1 -> scheduled, 2-> running */
 +  byte prune_trie;                    /* Prune prefix trie during next table prune */
 +  byte hcu_scheduled;                 /* Hostcache update is scheduled */
 +  byte nhu_state;                     /* Next Hop Update state */
 +  byte export_used;                   /* Pending Export pruning is scheduled */
 +  struct fib_iterator prune_fit;      /* Rtable prune FIB iterator */
 +  struct fib_iterator nhu_fit;                /* Next Hop Update FIB iterator */
 +  struct f_trie *trie_new;            /* New prefix trie defined during pruning */
 +  struct f_trie *trie_old;            /* Old prefix trie waiting to be freed */
 +  u32 trie_lock_count;                        /* Prefix trie locked by walks */
 +  u32 trie_old_lock_count;            /* Old prefix trie locked by walks */
 +  struct tbf rl_pipe;                 /* Rate limiting token buffer for pipe collisions */
 +
 +  list subscribers;                   /* Subscribers for notifications */
 +  struct timer *settle_timer;         /* Settle time for notifications */
 +  list flowspec_links;                        /* List of flowspec links, src for NET_IPx and dst for NET_FLOWx */
 +  struct f_trie *flowspec_trie;               /* Trie for evaluation of flowspec notifications */
 +} rtable;
 +
 +struct rt_subscription {
 +  node n;
 +  rtable *tab;
 +  void (*hook)(struct rt_subscription *b);
 +  void *data;
 +};
 +
 +struct rt_flowspec_link {
 +  node n;
 +  rtable *src;
 +  rtable *dst;
 +  u32 uc;
 +};
 +
 +#define NHU_CLEAN     0
 +#define NHU_SCHEDULED 1
 +#define NHU_RUNNING   2
 +#define NHU_DIRTY     3
 +
 +typedef struct network {
 +  struct rte_storage *routes;         /* Available routes for this network */
 +  struct rt_pending_export *first, *last;
 +  struct fib_node n;                  /* FIB flags reserved for kernel syncer */
 +} net;
 +
 +struct hostcache {
 +  slab *slab;                         /* Slab holding all hostentries */
 +  struct hostentry **hash_table;      /* Hash table for hostentries */
 +  unsigned hash_order, hash_shift;
 +  unsigned hash_max, hash_min;
 +  unsigned hash_items;
 +  linpool *lp;                                /* Linpool for trie */
 +  struct f_trie *trie;                        /* Trie of prefixes that might affect hostentries */
 +  list hostentries;                   /* List of all hostentries */
 +  byte update_hostcache;
 +};
 +
 +struct hostentry {
 +  node ln;
 +  ip_addr addr;                               /* IP address of host, part of key */
 +  ip_addr link;                               /* (link-local) IP address of host, used as gw
 +                                         if host is directly attached */
 +  struct rtable *tab;                 /* Dependent table, part of key */
 +  struct hostentry *next;             /* Next in hash chain */
 +  unsigned hash_key;                  /* Hash key */
 +  unsigned uc;                                /* Use count */
 +  ea_list *src;                               /* Source attributes */
 +  byte nexthop_linkable;              /* Nexthop list is completely non-device */
 +  u32 igp_metric;                     /* Chosen route IGP metric */
 +};
 +
 +struct rte_storage {
 +  struct rte_storage *next;           /* Next in chain */
 +  struct rte rte;                     /* Route data */
 +};
 +
 +#define RTE_COPY(r)           ((r) ? (r)->rte : (rte) {})
 +#define RTE_COPY_VALID(r)     (((r) && (rte_is_valid(&(r)->rte))) ? (r)->rte : (rte) {})
 +#define RTE_OR_NULL(r)                ((r) ? &((r)->rte) : NULL)
 +#define RTE_VALID_OR_NULL(r)  (((r) && (rte_is_valid(&(r)->rte))) ? &((r)->rte) : NULL)
 +
 +/* Table-channel connections */
 +
 +struct rt_import_request {
 +  struct rt_import_hook *hook;                /* The table part of importer */
 +  char *name;
 +  u8 trace_routes;
 +
 +  void (*dump_req)(struct rt_import_request *req);
 +  void (*log_state_change)(struct rt_import_request *req, u8 state);
 +  /* Preimport is called when the @new route is just-to-be inserted, replacing @old.
 +   * Return a route (may be different or modified in-place) to continue or NULL to withdraw. */
 +  int (*preimport)(struct rt_import_request *req, struct rte *new, struct rte *old);
 +};
 +
 +struct rt_import_hook {
 +  node n;
 +  rtable *table;                      /* The connected table */
 +  struct rt_import_request *req;      /* The requestor */
 +
 +  struct rt_import_stats {
 +    /* Import - from protocol to core */
 +    u32 pref;                         /* Number of routes selected as best in the (adjacent) routing table */
 +    u32 updates_ignored;              /* Number of route updates rejected as already in route table */
 +    u32 updates_accepted;             /* Number of route updates accepted and imported */
 +    u32 withdraws_ignored;            /* Number of route withdraws rejected as already not in route table */
 +    u32 withdraws_accepted;           /* Number of route withdraws accepted and processed */
 +  } stats;
 +
 +  u64 flush_seq;                      /* Table export seq when the channel announced flushing */
 +  btime last_state_change;            /* Time of last state transition */
 +
 +  u8 import_state;                    /* IS_* */
 +  u8 stale_set;                               /* Set this stale_cycle to imported routes */
 +  u8 stale_valid;                     /* Routes with this stale_cycle and bigger are considered valid */
 +  u8 stale_pruned;                    /* Last prune finished when this value was set at stale_valid */
 +  u8 stale_pruning;                   /* Last prune started when this value was set at stale_valid */
 +
 +  void (*stopped)(struct rt_import_request *);        /* Stored callback when import is stopped */
 +};
 +
 +struct rt_pending_export {
 +  struct rt_pending_export * _Atomic next;    /* Next export for the same destination */
 +  struct rte_storage *new, *new_best, *old, *old_best;
 +  u64 seq;                            /* Sequential ID (table-local) of the pending export */
 +};
 +
 +struct rt_export_request {
 +  struct rt_export_hook *hook;                /* Table part of the export */
 +  char *name;
 +  const net_addr *addr;                       /* Network prefilter address */
 +  u8 trace_routes;
 +  u8 addr_mode;                               /* Network prefilter mode (TE_ADDR_*) */
 +
++  event_list *list;                   /* Where to schedule export events */
++
 +  /* 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.
 +   * Anyway, when feeding, &export_bulk is preferred, falling back to &export_one.
 +   * Thus, for RA_OPTIMAL, &export_one is only set,
 +   *     for RA_MERGED and RA_ACCEPTED, &export_bulk is only set
 +   *     and for RA_ANY, both are set to accomodate for feeding all routes but receiving single changes
 +   */
 +  void (*export_one)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
 +  void (*export_bulk)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
 +
 +  void (*dump_req)(struct rt_export_request *req);
 +  void (*log_state_change)(struct rt_export_request *req, u8);
 +};
 +
 +struct rt_export_hook {
 +  node n;
 +  struct rt_exporter *table;          /* The connected table */
 +
 +  pool *pool;
 +
 +  struct rt_export_request *req;      /* The requestor */
 +
 +  struct rt_export_stats {
 +    /* Export - from core to protocol */
 +    u32 updates_received;             /* Number of route updates received */
 +    u32 withdraws_received;           /* Number of route withdraws received */
 +  } stats;
 +
 +  union {
 +    struct fib_iterator feed_fit;             /* Routing table iterator used during feeding */
 +    struct {
 +      struct f_trie_walk_state *walk_state;   /* Iterator over networks in trie */
 +      struct f_trie *walk_lock;                       /* Locked trie for walking */
 +    };
 +    u32 hash_iter;                            /* Iterator over hash */
 +  };
 +
 +  struct bmap seq_map;                        /* Keep track which exports were already procesed */
 +
 +  struct rt_pending_export * _Atomic last_export;/* Last export processed */
 +  struct rt_pending_export *rpe_next; /* Next pending export to process */
 +
 +  btime last_state_change;            /* Time of last state transition */
 +
 +  u8 refeed_pending;                  /* Refeeding and another refeed is scheduled */
 +  _Atomic u8 export_state;            /* Route export state (TES_*, see below) */
 +  u8 feed_type;                               /* Which feeding method is used (TFT_*, see below) */
 +
 +  struct event *event;                        /* Event running all the export operations */
 +
 +  void (*stopped)(struct rt_export_request *);        /* Stored callback when export is stopped */
 +};
 +
 +#define TIS_DOWN      0
 +#define TIS_UP                1
 +#define TIS_STOP      2
 +#define TIS_FLUSHING  3
 +#define TIS_WAITING   4
 +#define TIS_CLEARED   5
 +#define TIS_MAX               6
 +
 +#define TES_DOWN      0
 +#define TES_FEEDING   2
 +#define TES_READY     3
 +#define TES_STOP      4
 +#define TES_MAX               5
 +
 +/* Value of addr_mode */
 +#define TE_ADDR_NONE  0               /* No address matching */
 +#define TE_ADDR_EQUAL 1               /* Exact query - show route <addr> */
 +#define TE_ADDR_FOR   2               /* Longest prefix match - show route for <addr> */
 +#define TE_ADDR_IN    3               /* Interval query - show route in <addr> */
 +
 +
 +#define TFT_FIB               1
 +#define TFT_TRIE      2
 +#define TFT_HASH      3
 +
 +void rt_request_import(rtable *tab, struct rt_import_request *req);
 +void rt_request_export(struct rt_exporter *tab, struct rt_export_request *req);
 +
 +void rt_export_once(struct rt_exporter *tab, struct rt_export_request *req);
 +
 +void rt_stop_import(struct rt_import_request *, void (*stopped)(struct rt_import_request *));
 +void rt_stop_export(struct rt_export_request *, void (*stopped)(struct rt_export_request *));
 +
 +const char *rt_import_state_name(u8 state);
 +const char *rt_export_state_name(u8 state);
 +
 +static inline u8 rt_import_get_state(struct rt_import_hook *ih) { return ih ? ih->import_state : TIS_DOWN; }
 +static inline u8 rt_export_get_state(struct rt_export_hook *eh) { return eh ? eh->export_state : TES_DOWN; }
 +
 +void rt_set_export_state(struct rt_export_hook *hook, u8 state);
 +
 +void rte_import(struct rt_import_request *req, const net_addr *net, rte *new, struct rte_src *src);
 +
 +/* Get next rpe. If src is given, it must match. */
 +struct rt_pending_export *rpe_next(struct rt_pending_export *rpe, struct rte_src *src);
 +
 +/* Mark the pending export processed */
 +void rpe_mark_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
 +
 +/* Get pending export seen status */
 +int rpe_get_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
 +
 +/* Types of route announcement, also used as flags */
 +#define RA_UNDEF      0               /* Undefined RA type */
 +#define RA_OPTIMAL    1               /* Announcement of optimal route change */
 +#define RA_ACCEPTED   2               /* Announcement of first accepted route */
 +#define RA_ANY                3               /* Announcement of any route change */
 +#define RA_MERGED     4               /* Announcement of optimal route merged with next ones */
 +
 +/* Return value of preexport() callback */
 +#define RIC_ACCEPT    1               /* Accepted by protocol */
 +#define RIC_PROCESS   0               /* Process it through import filter */
 +#define RIC_REJECT    -1              /* Rejected by protocol */
 +#define RIC_DROP      -2              /* Silently dropped by protocol */
 +
 +#define rte_update  channel_rte_import
 +/**
 + * rte_update - enter a new update to a routing table
 + * @c: channel doing the update
 + * @net: network address
 + * @rte: a &rte representing the new route
 + * @src: old route source identifier
 + *
 + * This function imports a new route to the appropriate table (via the channel).
 + * Table keys are @net (obligatory) and @rte->attrs->src.
 + * Both the @net and @rte pointers can be local.
 + *
 + * The route attributes (@rte->attrs) are obligatory. They can be also allocated
 + * locally. Anyway, if you use an already-cached attribute object, you shall
 + * call rta_clone() on that object yourself. (This semantics may change in future.)
 + *
 + * If the route attributes are local, you may set @rte->attrs->src to NULL, then
 + * the protocol's default route source will be supplied.
 + *
 + * When rte_update() gets a route, it automatically validates it. This includes
 + * checking for validity of the given network and next hop addresses and also
 + * checking for host-scope or link-scope routes. Then the import filters are
 + * processed and if accepted, the route is passed to route table recalculation.
 + *
 + * The accepted routes are then inserted into the table, replacing the old route
 + * for the same @net identified by @src. Then the route is announced
 + * to all the channels connected to the table using the standard export mechanism.
 + * Setting @rte to NULL makes this a withdraw, otherwise @rte->src must be the same
 + * as @src.
 + *
 + * All memory used for temporary allocations is taken from a special linpool
 + * @rte_update_pool and freed when rte_update() finishes.
 + */
 +void rte_update(struct channel *c, const net_addr *net, struct rte *rte, struct rte_src *src);
 +
 +extern list routing_tables;
 +struct config;
 +
 +void rt_init(void);
 +void rt_preconfig(struct config *);
 +void rt_postconfig(struct config *);
 +void rt_commit(struct config *new, struct config *old);
 +void rt_lock_table(rtable *);
 +void rt_unlock_table(rtable *);
 +struct f_trie * rt_lock_trie(rtable *tab);
 +void rt_unlock_trie(rtable *tab, struct f_trie *trie);
 +void rt_subscribe(rtable *tab, struct rt_subscription *s);
 +void rt_unsubscribe(struct rt_subscription *s);
 +void rt_flowspec_link(rtable *src, rtable *dst);
 +void rt_flowspec_unlink(rtable *src, rtable *dst);
 +rtable *rt_setup(pool *, struct rtable_config *);
 +static inline void rt_shutdown(rtable *r) { rfree(r->rp); }
 +
 +static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
 +static inline net *net_find_valid(rtable *tab, const net_addr *addr)
 +{ net *n = net_find(tab, addr); return (n && n->routes && rte_is_valid(&n->routes->rte)) ? n : NULL; }
 +static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); }
 +net *net_get(rtable *tab, const net_addr *addr);
 +net *net_route(rtable *tab, const net_addr *n);
 +int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
 +rte *rt_export_merged(struct channel *c, rte ** feed, uint count, linpool *pool, int silent);
 +void rt_refresh_begin(struct rt_import_request *);
 +void rt_refresh_end(struct rt_import_request *);
 +void rt_modify_stale(rtable *t, struct rt_import_request *);
 +void rt_schedule_prune(rtable *t);
 +void rte_dump(struct rte_storage *);
 +void rte_free(struct rte_storage *);
 +struct rte_storage *rte_store(const rte *, net *net, rtable *);
 +void rt_dump(rtable *);
 +void rt_dump_all(void);
 +void rt_dump_hooks(rtable *);
 +void rt_dump_hooks_all(void);
 +int rt_reload_channel(struct channel *c);
 +void rt_reload_channel_abort(struct channel *c);
 +void rt_refeed_channel(struct channel *c);
 +void rt_prune_sync(rtable *t, int all);
 +struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 +
 +static inline int rt_is_ip(rtable *tab)
 +{ return (tab->addr_type == NET_IP4) || (tab->addr_type == NET_IP6); }
 +
 +static inline int rt_is_vpn(rtable *tab)
 +{ return (tab->addr_type == NET_VPN4) || (tab->addr_type == NET_VPN6); }
 +
 +static inline int rt_is_roa(rtable *tab)
 +{ return (tab->addr_type == NET_ROA4) || (tab->addr_type == NET_ROA6); }
 +
 +static inline int rt_is_flow(rtable *tab)
 +{ return (tab->addr_type == NET_FLOW4) || (tab->addr_type == NET_FLOW6); }
 +
 +
 +/* Default limit for ECMP next hops, defined in sysdep code */
 +extern const int rt_default_ecmp;
 +
 +struct rt_show_data_rtable {
 +  node n;
 +  const char *name;
 +  struct rt_exporter *table;
 +  struct channel *export_channel;
 +  struct channel *prefilter;
 +  struct krt_proto *kernel;
 +};
 +
 +struct rt_show_data {
 +  struct cli *cli;                    /* Pointer back to the CLI */
 +  net_addr *addr;
 +  list tables;
 +  struct rt_show_data_rtable *tab;    /* Iterator over table list */
 +  struct rt_show_data_rtable *last_table; /* Last table in output */
 +  struct rt_export_request req;               /* Export request in use */
 +  int verbose, tables_defined_by;
 +  const struct filter *filter;
 +  struct proto *show_protocol;
 +  struct proto *export_protocol;
 +  struct channel *export_channel;
 +  struct config *running_on_config;
 +  struct rt_export_hook *kernel_export_hook;
 +  int export_mode, addr_mode, primary_only, filtered, stats;
 +
 +  int net_counter, rt_counter, show_counter, table_counter;
 +  int net_counter_last, rt_counter_last, show_counter_last;
 +  int show_counter_last_flush;
 +};
 +
 +void rt_show(struct rt_show_data *);
 +struct rt_show_data_rtable * rt_show_add_exporter(struct rt_show_data *d, struct rt_exporter *t, const char *name);
 +struct rt_show_data_rtable * rt_show_add_table(struct rt_show_data *d, struct rtable *t);
 +
 +/* Value of table definition mode in struct rt_show_data */
 +#define RSD_TDB_DEFAULT         0             /* no table specified */
 +#define RSD_TDB_INDIRECT  0           /* show route ... protocol P ... */
 +#define RSD_TDB_ALL     RSD_TDB_SET                   /* show route ... table all ... */
 +#define RSD_TDB_DIRECT          RSD_TDB_SET | RSD_TDB_NMN     /* show route ... table X table Y ... */
 +
 +#define RSD_TDB_SET     0x1           /* internal: show empty tables */
 +#define RSD_TDB_NMN     0x2           /* internal: need matching net */
 +
 +/* Value of export_mode in struct rt_show_data */
 +#define RSEM_NONE     0               /* Export mode not used */
 +#define RSEM_PREEXPORT        1               /* Routes ready for export, before filtering */
 +#define RSEM_EXPORT   2               /* Routes accepted by export filter */
 +#define RSEM_NOEXPORT 3               /* Routes rejected by export filter */
 +#define RSEM_EXPORTED 4               /* Routes marked in export map */
 +
 +/* Host entry: Resolve hook for recursive nexthops */
 +extern struct ea_class ea_gen_hostentry;
 +struct hostentry_adata {
 +  adata ad;
 +  struct hostentry *he;
 +  u32 labels[0];
 +};
 +
 +void
 +ea_set_hostentry(ea_list **to, struct rtable *dep, struct rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum]);
 +
 +void ea_show_hostentry(const struct adata *ad, byte *buf, uint size);
 +void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
 +
 +/*
 + *    Default protocol preferences
 + */
 +
 +#define DEF_PREF_DIRECT               240     /* Directly connected */
 +#define DEF_PREF_STATIC               200     /* Static route */
 +#define DEF_PREF_OSPF         150     /* OSPF intra-area, inter-area and type 1 external routes */
 +#define DEF_PREF_BABEL                130     /* Babel */
 +#define DEF_PREF_RIP          120     /* RIP */
 +#define DEF_PREF_BGP          100     /* BGP */
 +#define DEF_PREF_RPKI         100     /* RPKI */
 +#define DEF_PREF_INHERITED    10      /* Routes inherited from other routing daemons */
 +#define DEF_PREF_UNKNOWN      0       /* Routes with no preference set */
 +
 +/*
 + *    Route Origin Authorization
 + */
 +
 +#define ROA_UNKNOWN   0
 +#define ROA_VALID     1
 +#define ROA_INVALID   2
 +
 +int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
 +
 +#endif
Simple merge
diff --cc proto/bfd/bfd.c
index 871ecf69f010213dc6948b03e31693d7db5128a8,3964c2676424ba16f6c5bcc8e73cceaa92fd61e4..e7d27f7489752ce48a23958b02aa200259088a35
@@@ -508,11 -510,17 +510,17 @@@ bfd_remove_session_locked(struct bfd_pr
    HASH_REMOVE(p->session_hash_id, HASH_ID, s);
    HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
  
-   sl_free(s);
+   TRACE(D_EVENTS, "Session to %I removed", s->addr);
  
-   TRACE(D_EVENTS, "Session to %I removed", ip);
 -  sl_free(p->session_slab, s);
++  sl_free(s);
+ }
  
-   birdloop_leave(p->loop);
+ static void
+ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
+ {
+   birdloop_enter(p->p.loop);
+   bfd_remove_session_locked(p, s);
+   birdloop_leave(p->p.loop);
  }
  
  static void
@@@ -1036,27 -1073,20 +1073,23 @@@ bfd_start(struct proto *P
    init_list(&p->notify_list);
    bfd_notify_init(p);
  
-   add_tail(&bfd_proto_list, &p->bfd_node);
-   birdloop_enter(p->loop);
+   add_tail(&bfd_global.proto_list, &p->bfd_node);
  
 -  if (cf->accept_ipv4 && cf->accept_direct)
 -    p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
 +  if (!cf->strict_bind)
 +  {
 +    if (cf->accept_ipv4 && cf->accept_direct)
 +      p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
  
 -  if (cf->accept_ipv4 && cf->accept_multihop)
 -    p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
 +    if (cf->accept_ipv4 && cf->accept_multihop)
 +      p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
  
 -  if (cf->accept_ipv6 && cf->accept_direct)
 -    p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
 +    if (cf->accept_ipv6 && cf->accept_direct)
 +      p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
  
 -  if (cf->accept_ipv6 && cf->accept_multihop)
 -    p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
 +    if (cf->accept_ipv6 && cf->accept_multihop)
 +      p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
 +  }
  
-   birdloop_leave(p->loop);
    bfd_take_requests(p);
  
    struct bfd_neighbor *n;
@@@ -1107,11 -1130,10 +1133,11 @@@ bfd_reconfigure(struct proto *P, struc
    if ((new->accept_ipv4 != old->accept_ipv4) ||
        (new->accept_ipv6 != old->accept_ipv6) ||
        (new->accept_direct != old->accept_direct) ||
 -      (new->accept_multihop != old->accept_multihop))
 +      (new->accept_multihop != old->accept_multihop) ||
 +      (new->strict_bind != old->strict_bind))
      return 0;
  
-   birdloop_mask_wakeups(p->loop);
+   birdloop_mask_wakeups(p->p.loop);
  
    WALK_LIST(ifa, p->iface_list)
      bfd_reconfigure_iface(p, ifa, new);
@@@ -1191,9 -1214,3 +1217,14 @@@ struct protocol proto_bfd = 
    .reconfigure =      bfd_reconfigure,
    .copy_config =      bfd_copy_config,
  };
 +
 +void
 +bfd_build(void)
 +{
 +  proto_build(&proto_bfd);
++
++  bfd_global.lock = DOMAIN_NEW(rtable, "BFD Global");
++  init_list(&bfd_global.wait_list);
++  init_list(&bfd_global.pickup_list);
++  init_list(&bfd_global.proto_list);
 +}
diff --cc proto/bfd/bfd.h
Simple merge
Simple merge
Simple merge
diff --cc proto/bgp/bgp.c
Simple merge
Simple merge
index edad62093e3817686d25681445a52510fcec52d6,4c9d5eb5d14512ac70da0089a66bc9973e4a5fad..a2384ca8c2e0d7e459fa9d8b55b4f161501955a7
  #endif
  
  long page_size = 0;
 -_Bool alloc_multipage = 0;
  
  #ifdef HAVE_MMAP
 +#define KEEP_PAGES_MAIN_MAX   256
 +#define KEEP_PAGES_MAIN_MIN   8
 +#define CLEANUP_PAGES_BULK    256
 +
 +STATIC_ASSERT(KEEP_PAGES_MAIN_MIN * 4 < KEEP_PAGES_MAIN_MAX);
 +
  static _Bool use_fake = 0;
 +
 +#if DEBUGGING
 +struct free_page {
 +  node unused[42];
 +  node n;
 +};
  #else
 -static _Bool use_fake = 1;
 +struct free_page {
 +  node n;
 +};
 +#endif
 +
 +struct free_pages {
 +  list pages;
 +  u16 min, max;               /* Minimal and maximal number of free pages kept */
 +  uint cnt;           /* Number of empty pages */
 +  event cleanup;
 +};
 +
 +static void global_free_pages_cleanup_event(void *);
 +
 +static struct free_pages global_free_pages = {
 +  .min = KEEP_PAGES_MAIN_MIN,
 +  .max = KEEP_PAGES_MAIN_MAX,
 +  .cleanup = { .hook = global_free_pages_cleanup_event },
 +};
 +
 +uint *pages_kept = &global_free_pages.cnt;
 +
 +static void *
 +alloc_sys_page(void)
 +{
 +  void *ptr = mmap(NULL, page_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 +
 +  if (ptr == MAP_FAILED)
 +    bug("mmap(%lu) failed: %m", page_size);
 +
 +  return ptr;
 +}
 +
 +extern int shutting_down; /* Shutdown requested. */
 +
 +#else // ! HAVE_MMAP
 +#define use_fake  1
  #endif
  
 -void resource_sys_init(void)
 +void *
 +alloc_page(void)
  {
 +  if (use_fake)
 +  {
 +    void *ptr = NULL;
 +    int err = posix_memalign(&ptr, page_size, page_size);
 +
 +    if (err || !ptr)
 +      bug("posix_memalign(%lu) failed", (long unsigned int) page_size);
 +
 +    return ptr;
 +  }
 +
  #ifdef HAVE_MMAP
 -  if (!(page_size = sysconf(_SC_PAGESIZE)))
 -    die("System page size must be non-zero");
 +  struct free_pages *fps = &global_free_pages;
  
 -  if ((u64_popcount(page_size) > 1) || (page_size > 16384))
 +  if (fps->cnt)
    {
 -#endif
 -    /* Too big or strange page, use the aligned allocator instead */
 -    page_size = 4096;
 -    use_fake = 1;
 +    struct free_page *fp = SKIP_BACK(struct free_page, n, HEAD(fps->pages));
 +    rem_node(&fp->n);
 +    if ((--fps->cnt < fps->min) && !shutting_down)
-       ev_schedule(&fps->cleanup);
++      ev_send(&global_work_list, &fps->cleanup);
 +
 +    bzero(fp, page_size);
 +    return fp;
    }
 +
 +  return alloc_sys_page();
 +#endif
  }
  
 -void *
 -alloc_sys_page(void)
 +void
 +free_page(void *ptr)
  {
 -#ifdef HAVE_MMAP
 -  if (!use_fake)
 +  if (use_fake)
    {
 -    if (alloc_multipage)
 -    {
 -      void *big = mmap(NULL, page_size * 2, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 -      if (big == MAP_FAILED)
 -      bug("mmap(%lu) failed: %m", page_size);
 -
 -      uintptr_t offset = ((uintptr_t) big) % page_size;
 -      if (offset)
 -      {
 -      void *ret = big + page_size - offset;
 -      munmap(big, page_size - offset);
 -      munmap(ret + page_size, offset);
 -      return ret;
 -      }
 -      else
 -      {
 -      munmap(big + page_size, page_size);
 -      return big;
 -      }
 -    }
 -
 -    void *ret = mmap(NULL, page_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 -    if (ret == MAP_FAILED)
 -      bug("mmap(%lu) failed: %m", page_size);
 -
 -    return ret;
 +    free(ptr);
 +    return;
    }
 -  else
 +
 +#ifdef HAVE_MMAP
 +  struct free_pages *fps = &global_free_pages;
 +  struct free_page *fp = ptr;
 +
 +  fp->n = (node) {};
 +  add_tail(&fps->pages, &fp->n);
 +
 +  if ((++fps->cnt > fps->max) && !shutting_down)
-     ev_schedule(&fps->cleanup);
++    ev_send(&global_work_list, &fps->cleanup);
  #endif
 +}
 +
 +#ifdef HAVE_MMAP
 +static void
 +global_free_pages_cleanup_event(void *data UNUSED)
 +{
 +  if (shutting_down)
 +    return;
 +
 +  struct free_pages *fps = &global_free_pages;
 +
 +  while (fps->cnt / 2 < fps->min)
    {
 -    void *ret = aligned_alloc(page_size, page_size);
 -    if (!ret)
 -      bug("aligned_alloc(%lu) failed", page_size);
 -    return ret;
 +    struct free_page *fp = alloc_sys_page();
 +    fp->n = (node) {};
 +    add_tail(&fps->pages, &fp->n);
 +    fps->cnt++;
 +  }
 +
 +  for (uint seen = 0; (seen < CLEANUP_PAGES_BULK) && (fps->cnt > fps->max / 2); seen++)
 +  {
 +    struct free_page *fp = SKIP_BACK(struct free_page, n, TAIL(fps->pages));
 +    rem_node(&fp->n);
 +
 +    if (munmap(fp, page_size) == 0)
 +      fps->cnt--;
 +    else if (errno == ENOMEM)
 +      add_head(&fps->pages, &fp->n);
 +    else
 +      bug("munmap(%p) failed: %m", fp);
    }
  }
 +#endif
  
  void
 -free_sys_page(void *ptr)
 +resource_sys_init(void)
  {
  #ifdef HAVE_MMAP
 -  if (!use_fake)
 +  ASSERT_DIE(global_free_pages.cnt == 0);
 +
 +  if (!(page_size = sysconf(_SC_PAGESIZE)))
 +    die("System page size must be non-zero");
 +
 +  if (u64_popcount(page_size) == 1)
    {
 -    if (munmap(ptr, page_size) < 0)
 -      bug("munmap(%p) failed: %m", ptr);
 +    struct free_pages *fps = &global_free_pages;
 +
 +    init_list(&fps->pages);
 +    global_free_pages_cleanup_event(NULL);
 +    return;
    }
 -  else
 +
 +  /* Too big or strange page, use the aligned allocator instead */
 +  log(L_WARN "Got strange memory page size (%lu), using the aligned allocator instead", page_size);
 +  use_fake = 1;
  #endif
 -    free(ptr);
 +
 +  page_size = 4096;
  }
index 2805e0f209125436f96e8d958fc2342f3fe07f70,275d38a01d1c7a4dd9a598829d83ce7d21915e18..dfb2ce49a8dc6452ac210dcbb11d71386a64bef8
@@@ -453,18 -473,13 +473,15 @@@ birdloop_main(void *arg
    timer *t;
    int rv, timeout;
  
-   birdloop_set_current(loop);
+   btime loop_begin = current_time();
  
-   pthread_mutex_lock(&loop->mutex);
 +  tmp_init(loop->pool);
 +
+   birdloop_enter(loop);
    while (1)
    {
-     events_fire(loop);
-     timers_fire(&loop->time);
-     times_update();
-     if (events_waiting(loop))
+     timers_fire(&loop->time, 0);
+     if (ev_run_list(&loop->event_list))
        timeout = 0;
      else if (t = timers_first(&loop->time))
        timeout = (tm_remains(t) TO_MS) + 1;
Simple merge
index 8bc07d94c7f6c2951207b9b2ab5d50c82194d08e,5da27cb68d51f5c3a27dce29886694636b51c8f2..bf9f2be02b0dfe619cf88c80282746c0f7cf0ce1
@@@ -881,10 -913,10 +883,10 @@@ main(int argc, char **argv
    random_init();
    net_init();
    resource_init();
-   timer_init();
+   birdloop_init();
    olock_init();
 -  io_init();
    rt_init();
 +  io_init();
    if_init();
  //  roa_init();
    config_init();
diff --cc test/birdtest.c
index ae05d1a530dc759967338bba2b699c051a4e63f5,c6a09684ed6503ea8478910132b3b1867f37470f..2ae7b51e51a724088f5e039539356f0f84bb152d
@@@ -20,7 -20,6 +20,8 @@@
  
  #include "test/birdtest.h"
  #include "lib/string.h"
 +#include "lib/event.h"
++#include "lib/io-loop.h"
  
  #ifdef HAVE_EXECINFO_H
  #include <execinfo.h>
@@@ -120,9 -122,6 +121,9 @@@ bt_init(int argc, char *argv[]
    clock_gettime(CLOCK_MONOTONIC, &bt_begin);
    bt_suite_case_begin = bt_suite_begin = bt_begin;
  
-   ev_init_list(&global_event_list);
 +  resource_init();
++  ev_init_list(&global_event_list, &main_birdloop, "Global event list in unit tests");
++  ev_init_list(&global_work_list, &main_birdloop, "Global work list in unit tests");
    return;
  
   usage:
diff --cc test/bt-utils.c
index 509b5ed42f1309a8500d50d79530256fcb1e4e53,90815e77f9727b7ecf75f2daede0665d9edc6eee..3d56292ee4cabb79900304fe9b719e797d588320
@@@ -60,10 -63,12 +62,11 @@@ bt_bird_init(void
      log_init_debug("");
    log_switch(bt_verbose != 0, NULL, NULL);
  
 -  resource_init();
+   the_bird_lock();
    olock_init();
-   timer_init();
+   birdloop_init();
 -  io_init();
    rt_init();
 +  io_init();
    if_init();
    config_init();
  
  
  void bt_bird_cleanup(void)
  {
 -  for (int i = 0; i < PROTOCOL__MAX; i++)
 -    class_to_protocol[i] = NULL;
 -
    config = new_config = NULL;
+   the_bird_unlock();
  }
  
  static char *