]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BFD session handling rework
authorMaria Matejka <mq@ucw.cz>
Mon, 3 Feb 2025 14:21:52 +0000 (15:21 +0100)
committerMaria Matejka <mq@ucw.cz>
Sun, 23 Feb 2025 19:26:22 +0000 (20:26 +0100)
The original implementation for BIRD 3 was rooted in the first
methods how I tried to go for multithreading and it had several flaws,
mostly incomprehensive notification and request pickup routines.
Also converting to a double-loop architecture where one of the
loops (low-latency) solely runs BFD socket communication, whereas
the other one does all the other shenanigans.

14 files changed:
doc/bird.sgml
nest/bfd.h
proto/bfd/bfd.c
proto/bfd/bfd.h
proto/bfd/config.Y
proto/bfd/packets.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/ospf/neighbor.c
proto/ospf/ospf.h
proto/rip/rip.c
proto/rip/rip.h
proto/static/static.c
proto/static/static.h

index f4d7247f779b85a74190e80537a9d7d2c04a752b..11a12494304880c4760ab1ed5b614bba1e852c24 100644 (file)
@@ -2696,6 +2696,11 @@ with the appropriate unit: <m/num/ <cf/s/|<cf/ms/|<cf/us/. Although microseconds
 are allowed as units, practical minimum values are usually in order of tens of
 milliseconds.
 
+<p>Beware, all BFD instances pick up requests and sessions asynchronously, and
+any instance can pick up any matching request, regardless of the order in the
+configuration file. There may be a future update, allowing for strict matching,
+yet for now, we do not have such an option.
+
 <code>
 protocol bfd [&lt;name&gt;] {
        accept [ipv4|ipv6] [direct|multihop];
index c046152f8438e32616cf40c1f10badea49e2485a..a989e7cf72b84dc39df7c437cb17eb823513c309 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef _BIRD_NBFD_H_
 #define _BIRD_NBFD_H_
 
-#include "lib/lists.h"
+#include "lib/tlists.h"
 #include "lib/resource.h"
 #include "conf/conf.h"
 
@@ -28,9 +28,21 @@ struct bfd_options {
   list *passwords;                     /* Passwords for authentication */
 };
 
-struct bfd_request {
+#define TLIST_PREFIX bfd_request
+#define TLIST_TYPE struct bfd_request
+#define TLIST_ITEM n
+#define TLIST_WANT_ADD_TAIL
+
+/* Reference held by the requestor. Free to unrequest */
+struct bfd_request_ref {
   resource r;
-  node n;
+  struct bfd_request *req;
+};
+
+/* The actual request, allocated by BFD, freed after unrequesting
+ * but assuring safe handling by the low-latency BFD routines. */
+struct bfd_request {
+  TLIST_DEFAULT_NODE;
 
   ip_addr addr;
   ip_addr local;
@@ -38,18 +50,24 @@ struct bfd_request {
   struct iface *vrf;
   struct bfd_options opts;
 
-  void (*hook)(struct bfd_request *);
-  void *data;
-  struct birdloop *target;
+  callback * _Atomic notify;
 
-  struct bfd_session *session;
+  struct bfd_session *_Atomic session;
 
-  u8 state;
-  u8 diag;
-  u8 old_state;
-  u8 down;
+  struct bfd_state_pair {
+    struct bfd_state {
+      u8 state;
+      u8 diag;
+    } loc, rem;
+  } cur, old;
+
+  bool down;
 };
 
+void bfd_request_get_state(struct bfd_request *req);
+
+#include "lib/tlists.h"
+
 #define BGP_BFD_GRACEFUL       2       /* BFD down triggers graceful restart */
 
 #define BFD_STATE_ADMIN_DOWN   0
@@ -63,16 +81,17 @@ static inline struct bfd_options * bfd_new_options(void)
 
 #ifdef CONFIG_BFD
 
-struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, void (*hook)(struct bfd_request *), void *data, struct birdloop *target, const struct bfd_options *opts);
-void bfd_update_request(struct bfd_request *req, const struct bfd_options *opts);
+struct bfd_request_ref * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, callback *notify, const struct bfd_options *opts);
+void bfd_update_request(struct bfd_request_ref *req, const struct bfd_options *opts);
+void bfd_request_update_state(struct bfd_request *req);
 
 static inline void cf_check_bfd(int use UNUSED) { }
 
 #else
 
-static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED, struct birdloop *target UNUSED, const struct bfd_options *opts UNUSED) { return NULL; }
-static inline void bfd_update_request(struct bfd_request *req UNUSED, const struct bfd_options *opts UNUSED) { };
-
+static inline struct bfd_request_ref * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, callback *notify UNUSED, const struct bfd_options *opts UNUSED) { return NULL; }
+static inline void bfd_update_request(struct bfd_request_ref *req UNUSED, const struct bfd_options *opts UNUSED) { };
+static inline void bfd_request_update_state(struct bfd_request *req UNUSED) { bug("BFD not compiled in!"); }
 static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }
 
 #endif /* CONFIG_BFD */
index 4997f803ac6081c90442aa0f482ecd0ae2ae74e3..75cc616ad819d610f5d62d66ac92d8a07acdc6cf 100644 (file)
@@ -7,21 +7,13 @@
 /**
  * DOC: Bidirectional Forwarding Detection
  *
- * The BFD protocol is implemented in three files: |bfd.c| containing the
- * protocol logic and the protocol glue with BIRD core, |packets.c| handling BFD
- * packet processing, RX, TX and protocol sockets. |io.c| then contains generic
- * code for the event loop, threads and event sources (sockets, microsecond
- * timers). This generic code will be merged to the main BIRD I/O code in the
- * future.
+ * The BFD protocol is implemented in two files: |bfd.c| containing the
+ * protocol logic and the protocol glue with BIRD core, and |packets.c| handling BFD
+ * packet processing, RX, TX and protocol sockets.
  *
- * The BFD implementation uses a separate thread with an internal event loop for
- * handling the protocol logic, which requires high-res and low-latency timing,
- * so it is not affected by the rest of BIRD, which has several low-granularity
- * hooks in the main loop, uses second-based timers and cannot offer good
- * latency. The core of BFD protocol (the code related to BFD sessions,
- * interfaces and packets) runs in the BFD thread, while the rest (the code
- * related to BFD requests, BFD neighbors and the protocol glue) runs in the
- * main thread.
+ * The BFD implementation uses two birdloops, one standard for request pickup
+ * and session state notification broadcast, and another one, low-latency,
+ * to handle just the packets and timing.
  *
  * BFD sessions are represented by structure &bfd_session that contains a state
  * related to the session and two timers (TX timer for periodic packets and hold
  * @session_hash_ip (by IP addresses of neighbors and associated interfaces).
  * Slab and both hashes are in the main protocol structure &bfd_proto. The
  * protocol logic related to BFD sessions is implemented in internal functions
- * bfd_session_*(), which are expected to be called from the context of BFD
- * thread, and external functions bfd_add_session(), bfd_remove_session() and
+ * bfd_session_*(), which are expected to be called in the low-latency loop,
+ * and external functions bfd_add_session(), bfd_remove_session() and
  * bfd_reconfigure_session(), which form an interface to the BFD core for the
- * rest and are expected to be called from the context of main thread.
+ * rest and are called from the regular loop.
  *
  * Each BFD session has an associated BFD interface, represented by structure
  * &bfd_iface. A BFD interface contains a socket used for TX (the one for RX is
  *
  * BFD requests are the external API for the other protocols. When a protocol
  * wants a BFD session, it calls bfd_request_session(), which creates a
- * structure &bfd_request containing approprite information and an notify hook.
- * This structure is a resource associated with the caller's resource pool. When
- * a BFD protocol is available, a BFD request is submitted to the protocol, an
- * appropriate BFD session is found or created and the request is attached to
- * the session. When a session changes state, all attached requests (and related
- * protocols) are notified. Note that BFD requests do not depend on BFD protocol
+ * structure &bfd_request containing approprite information and a notify callback.
+ * Also a reference structure is allocated, which is a resource associated with
+ * the caller's resource pool. Cancellation of the requests is done by freeing
+ * the reference resource, the request itself is freed later to assure that
+ * the low-latency routine is not activating its callback right now.
+ *
+ * The BFD protocols then pick up the requests, find or create appropriate BFD
+ * sessions and the request is then attached to the session. When a session
+ * changes state, all attached requests (and related protocols) are notified.
+ *
+ * Note that BFD requests do not depend on BFD protocol
  * running. When the BFD protocol is stopped or removed (or not available from
- * beginning), related BFD requests are stored in @bfd_wait_list, where waits
- * for a new protocol.
+ * beginning), related BFD requests are stored in @bfd_global.pickup_list
+ * where they wait for a suitable protocol to emerge.
  *
  * BFD neighbors are just a way to statically configure BFD sessions without
- * requests from other protocol. Structures &bfd_neighbor are part of BFD
+ * requests from another protocol. Structures &bfd_neighbor are part of BFD
  * configuration (like static routes in the static protocol). BFD neighbors are
  * handled by BFD protocol like it is a BFD client -- when a BFD neighbor is
  * ready, the protocol just creates a BFD request like any other protocol.
  *
- * The protocol uses a new generic event loop (structure &birdloop) from |io.c|,
- * which supports sockets, timers and events like the main loop. A birdloop is
- * associated with a thread (field @thread) in which event hooks are executed.
- * Most functions for setting event sources (like sk_start() or tm_start()) must
- * be called from the context of that thread. Birdloop allows to temporarily
- * acquire the context of that thread for the main thread by calling
- * birdloop_enter() and then birdloop_leave(), which also ensures mutual
- * exclusion with all event hooks. Note that resources associated with a
- * birdloop (like timers) should be attached to the independent resource pool,
- * detached from the main resource tree.
+ * Messages are passed around BFD as follows:
+ * 
+ * - Reconfiguration of BFD itself, as well as "show bfd" commands, are synchronous,
+ *   and they directly enter the BFD context.
+ * - Requests from other protocols to BFD are asynchronous; they lock the BFD
+ *   global data structure and send events to the protocols to pickup possibly
+ *   new requests.
+ * - Notifications from BFD to other protocols are also asynchronous; they send
+ *   the given callback when ready.
+ * - Reconfiguration of BFD sessions based on the requests are synchronous.
+ * - Notifications of session state from the session loop to the protocol loop
+ *   are asynchronous, by sending an event.
+ * - The session state itself is stored in an atomic structure (tiny enough to fit
+ *   easily in u64) and accessed locklessly.
+ * - There is a known data race in accessing the session state and last state
+ *   change timestamp, which may happen to be inconsistent, yet we don't care
+ *   much actually. The timestamp is there just for user information.
  *
- * There are two kinds of interaction between the BFD core (running in the BFD
- * thread) and the rest of BFD (running in the main thread). The first kind are
- * configuration calls from main thread to the BFD thread (like bfd_add_session()).
- * These calls are synchronous and use birdloop_enter() mechanism for mutual
- * exclusion. The second kind is a notification about session changes from the
- * BFD thread to the main thread. This is done in an asynchronous way, sesions
- * with pending notifications are linked (in the BFD thread) to @notify_list in
- * &bfd_proto, and then bfd_notify_hook() in the main thread is activated using
- * a standard event sending code. The hook then processes scheduled sessions and
- * calls hooks from associated BFD requests. This @notify_list (and state fields
- * in structure &bfd_session) is protected by a spinlock in &bfd_proto and
- * functions bfd_lock_sessions() / bfd_unlock_sessions().
- *
- * There are few data races (accessing @p->p.debug from TRACE() from the BFD
- * thread and accessing some some private fields of %bfd_session from
- * bfd_show_sessions() from the main thread, but these are harmless (i hope).
+ * There are a few other data races (e.g. accessing @p->p.debug from TRACE()
+ * from the low-latency BFD loop and accessing some some private fields of
+ * %bfd_session from * bfd_show_sessions() from the main thread, but these
+ * should be harmless.
  *
  * TODO: document functions and access restrictions for fields in BFD structures.
  *
 #define HASH_IP_EQ(a1,n1,a2,n2)        ipa_equal(a1, a2) && n1 == n2
 #define HASH_IP_FN(a,n)                ipa_hash(a) ^ u32_hash(n)
 
-#define BFD_LOCK       LOCK_DOMAIN(rtable, bfd_global.lock)
-#define BFD_UNLOCK     UNLOCK_DOMAIN(rtable, bfd_global.lock)
+#define BFD_GLOBAL_PUBLIC \
+  DOMAIN(rtable) lock;   \
+  callback cleanup;      \
+
+struct bfd_global_private {
+  BFD_GLOBAL_PUBLIC;
+  pool *request_pool;
+  slab *request_slab;
+  struct bfd_global_private **locked_at;
+  TLIST_LIST(bfd_request) pickup_list;
+  TLIST_LIST(bfd_proto) proto_list;
+};
 
-static struct {
-  DOMAIN(rtable) lock;
-  list wait_list;
-  list pickup_list;
-  list proto_list;
-  uint pickup_reload;
+static union bfd_global {
+  struct { BFD_GLOBAL_PUBLIC; };
+  struct bfd_global_private priv;
 } bfd_global;
 
+#define BFD_LOCKED(g)  LOBJ_LOCKED(&bfd_global, g, bfd_global, rtable)
+LOBJ_UNLOCK_CLEANUP(bfd_global, rtable);
+
 const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
 
 const char *bfd_diag_names[] = {
@@ -187,36 +189,27 @@ bfd_merge_options(const struct bfd_options *bottom, const struct bfd_options *to
 }
 
 static void
-bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
+bfd_session_update_state(struct bfd_session *s, struct bfd_state_pair new)
 {
   struct bfd_proto *p = s->ifa->bfd;
-  uint old_state = s->loc_state;
-  int notify;
+  struct bfd_state_pair old = atomic_load_explicit(&s->state, memory_order_relaxed);
+  atomic_store_explicit(&s->state, new, memory_order_relaxed);
 
-  if (state == old_state)
+  if (new.loc.state == old.loc.state)
     return;
 
   TRACE(D_EVENTS, "Session to %I changed state from %s to %s",
-       s->addr, bfd_state_names[old_state], bfd_state_names[state]);
+       s->addr, bfd_state_names[old.loc.state], bfd_state_names[new.loc.state]);
 
-  bfd_lock_sessions(p);
-  s->loc_state = state;
-  s->loc_diag = diag;
-  s->last_state_change = current_time();
+  atomic_store_explicit(&s->last_state_change, current_time(), memory_order_relaxed);
 
-  notify = !NODE_VALID(&s->n);
-  if (notify)
-    add_tail(&p->notify_list, &s->n);
-  bfd_unlock_sessions(p);
+  callback_activate(&s->notify);
 
-  if (state == BFD_STATE_UP)
+  if (new.loc.state == BFD_STATE_UP)
     bfd_session_set_min_tx(s, s->cf.min_tx_int);
 
-  if (old_state == BFD_STATE_UP)
+  if (old.loc.state == BFD_STATE_UP)
     bfd_session_set_min_tx(s, s->cf.idle_tx_int);
-
-  if (notify)
-    ev_send(&global_event_list, &p->notify_event);
 }
 
 static void
@@ -234,7 +227,7 @@ bfd_session_update_tx_interval(struct bfd_session *s)
     return;
 
   /* Set timer relative to last tx_timer event */
-  tm_set_in(s->tx_timer, s->last_tx + tx_int_l, s->ifa->bfd->p.loop);
+  tm_set_in(s->tx_timer, s->last_tx + tx_int_l, s->ifa->bfd->eloop);
 }
 
 static void
@@ -248,7 +241,7 @@ bfd_session_update_detection_time(struct bfd_session *s, int kick)
   if (!s->last_rx)
     return;
 
-  tm_set_in(s->hold_timer, s->last_rx + timeout, s->ifa->bfd->p.loop);
+  tm_set_in(s->hold_timer, s->last_rx + timeout, s->ifa->bfd->eloop);
 }
 
 static void
@@ -259,10 +252,12 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset)
   if (s->passive && (s->rem_id == 0))
     goto stop;
 
+  struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+
   if (s->rem_demand_mode &&
       !s->poll_active &&
-      (s->loc_state == BFD_STATE_UP) &&
-      (s->rem_state == BFD_STATE_UP))
+      (sp.loc.state == BFD_STATE_UP) &&
+      (sp.rem.state == BFD_STATE_UP))
     goto stop;
 
   if (s->rem_min_rx_int == 0)
@@ -272,7 +267,7 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset)
   if (reset || !tm_active(s->tx_timer))
   {
     s->last_tx = 0;
-    tm_start_in(s->tx_timer, 0, s->ifa->bfd->p.loop);
+    tm_start_in(s->tx_timer, 0, s->ifa->bfd->eloop);
   }
 
   return;
@@ -318,8 +313,10 @@ bfd_session_terminate_poll(struct bfd_session *s)
 }
 
 void
-bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int)
+bfd_session_process_ctl(struct bfd_session *s, struct bfd_state_pair sp, u8 flags, u32 old_tx_int, u32 old_rx_int)
 {
+  ASSERT_DIE(birdloop_inside(s->ifa->bfd->eloop));
+
   if (s->poll_active && (flags & BFD_FLAG_FINAL))
     bfd_session_terminate_poll(s);
 
@@ -329,31 +326,33 @@ bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old
   bfd_session_update_detection_time(s, 1);
 
   /* Update session state */
-  int next_state = 0;
-  int diag = BFD_DIAG_NOTHING;
+  int orig_loc_state = sp.loc.state;
+  sp.loc.state = 0;
+  sp.loc.diag = BFD_DIAG_NOTHING;
 
-  switch (s->loc_state)
+  switch (orig_loc_state)
   {
   case BFD_STATE_ADMIN_DOWN:
+    atomic_store_explicit(&s->state, sp, memory_order_release);
     return;
 
   case BFD_STATE_DOWN:
-    if (s->rem_state == BFD_STATE_DOWN)                next_state = BFD_STATE_INIT;
-    else if (s->rem_state == BFD_STATE_INIT)   next_state = BFD_STATE_UP;
+    if (sp.rem.state == BFD_STATE_DOWN)                sp.loc.state = BFD_STATE_INIT;
+    else if (sp.rem.state == BFD_STATE_INIT)   sp.loc.state = BFD_STATE_UP;
     break;
 
   case BFD_STATE_INIT:
-    if (s->rem_state == BFD_STATE_ADMIN_DOWN)  next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
-    else if (s->rem_state >= BFD_STATE_INIT)   next_state = BFD_STATE_UP;
+    if (sp.rem.state == BFD_STATE_ADMIN_DOWN)  sp.loc.state = BFD_STATE_DOWN, sp.loc.diag = BFD_DIAG_NEIGHBOR_DOWN;
+    else if (sp.rem.state >= BFD_STATE_INIT)   sp.loc.state = BFD_STATE_UP;
     break;
 
   case BFD_STATE_UP:
-    if (s->rem_state <= BFD_STATE_DOWN)                next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
+    if (sp.rem.state <= BFD_STATE_DOWN)                sp.loc.state = BFD_STATE_DOWN, sp.loc.diag = BFD_DIAG_NEIGHBOR_DOWN;
     break;
   }
 
-  if (next_state)
-    bfd_session_update_state(s, next_state, diag);
+  if (sp.loc.state)
+    bfd_session_update_state(s, sp);
 
   bfd_session_control_tx_timer(s, 0);
 
@@ -368,7 +367,11 @@ bfd_session_timeout(struct bfd_session *s)
 
   TRACE(D_EVENTS, "Session to %I expired", s->addr);
 
-  s->rem_state = BFD_STATE_DOWN;
+  struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+  sp.rem.state = BFD_STATE_DOWN;
+  sp.loc.state = BFD_STATE_DOWN;
+  sp.loc.diag = BFD_DIAG_TIMEOUT;
+
   s->rem_id = 0;
   s->rem_min_tx_int = 0;
   s->rem_min_rx_int = 1;
@@ -379,7 +382,7 @@ bfd_session_timeout(struct bfd_session *s)
   s->poll_active = 0;
   s->poll_scheduled = 0;
 
-  bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
+  bfd_session_update_state(s, sp);
 
   bfd_session_control_tx_timer(s, 1);
 }
@@ -395,7 +398,8 @@ bfd_session_set_min_tx(struct bfd_session *s, u32 val)
   s->des_min_tx_new = val;
 
   /* Postpone timer update if des_min_tx_int increases and the session is up */
-  if ((s->loc_state != BFD_STATE_UP) || (val < s->des_min_tx_int))
+  struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+  if ((sp.loc.state != BFD_STATE_UP) || (val < s->des_min_tx_int))
   {
     s->des_min_tx_int = val;
     bfd_session_update_tx_interval(s);
@@ -415,7 +419,8 @@ bfd_session_set_min_rx(struct bfd_session *s, u32 val)
   s->req_min_rx_new = val;
 
   /* Postpone timer update if req_min_rx_int decreases and the session is up */
-  if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int))
+  struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+  if ((sp.loc.state != BFD_STATE_UP) || (val > s->req_min_rx_int))
   {
     s->req_min_rx_int = val;
     bfd_session_update_detection_time(s, 0);
@@ -427,15 +432,46 @@ bfd_session_set_min_rx(struct bfd_session *s, u32 val)
 struct bfd_session *
 bfd_find_session_by_id(struct bfd_proto *p, u32 id)
 {
+  ASSERT_DIE(birdloop_inside(p->eloop));
   return HASH_FIND(p->session_hash_id, HASH_ID, id);
 }
 
 struct bfd_session *
 bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex)
 {
+  ASSERT_DIE(birdloop_inside(p->eloop));
   return HASH_FIND(p->session_hash_ip, HASH_IP, addr, ifindex);
 }
 
+static void
+bfd_notify_request(struct bfd_session *s, struct bfd_request *req)
+{
+  rcu_read_lock();
+  callback *notify = atomic_load_explicit(&req->notify, memory_order_acquire);
+  if (notify)
+    callback_activate(notify);
+  rcu_read_unlock();
+  if (!notify)
+  {
+    bfd_request_rem_node(&s->request_list, req);
+    BFD_LOCKED(g)
+      sl_free(req);
+  }
+}
+
+static void
+bfd_notify_session(callback *cb)
+{
+  SKIP_BACK_DECLARE(struct bfd_session, s, notify, cb);
+
+  struct bfd_proto *p = s->ifa->bfd;
+  ASSERT_DIE(birdloop_inside(p->p.loop));
+  ASSERT_DIE(!birdloop_inside(p->eloop));
+
+  WALK_TLIST_DELSAFE(bfd_request, req, &s->request_list)
+    bfd_notify_request(s, req);
+}
+
 static void
 bfd_tx_timer_hook(timer *t)
 {
@@ -473,16 +509,30 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
   s->addr = addr;
   s->ifa = ifa;
   s->ifindex = iface ? iface->index : 0;
+
+  callback_init(&s->notify, bfd_notify_session, p->p.loop);
+
+  s->cf = bfd_merge_options(&ifa->cf->opts, opts);
+
+  atomic_store_explicit(&s->last_state_change, current_time(), memory_order_relaxed);
+
+  s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
+  s->hold_timer = tm_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
+
+  /* This must be freakingly fast */
+  birdloop_enter(p->eloop);
+
   s->loc_id = bfd_get_free_id(p);
 
   HASH_INSERT(p->session_hash_id, HASH_ID, s);
   HASH_INSERT(p->session_hash_ip, HASH_IP, s);
 
-  s->cf = bfd_merge_options(&ifa->cf->opts, opts);
-
   /* Initialization of state variables - see RFC 5880 6.8.1 */
-  s->loc_state = BFD_STATE_DOWN;
-  s->rem_state = BFD_STATE_DOWN;
+  atomic_store_explicit(&s->state, ((struct bfd_state_pair) {
+      .loc = { .state = BFD_STATE_DOWN },
+      .rem = { .state = BFD_STATE_DOWN },
+      }), memory_order_relaxed);
+
   s->des_min_tx_int = s->des_min_tx_new = s->cf.idle_tx_int;
   s->req_min_rx_int = s->req_min_rx_new = s->cf.min_rx_int;
   s->rem_min_rx_int = 1;
@@ -490,46 +540,17 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
   s->passive = s->cf.passive;
   s->tx_csn = random_u32();
 
-  s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
-  s->hold_timer = tm_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
   bfd_session_update_tx_interval(s);
   bfd_session_control_tx_timer(s, 1);
 
-  init_list(&s->request_list);
-  s->last_state_change = current_time();
+  /* End of the fast part */
+  birdloop_leave(p->eloop);
 
   TRACE(D_EVENTS, "Session to %I added", s->addr);
 
   return s;
 }
 
-/*
-static void
-bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
-{
-  birdloop_enter(p->p.loop);
-
-  s->opened = 1;
-
-  bfd_session_control_tx_timer(s);
-
-  birdloop_leave(p->p.loop);
-}
-
-static void
-bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
-{
-  birdloop_enter(p->p.loop);
-
-  s->opened = 0;
-
-  bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN);
-  bfd_session_control_tx_timer(s);
-
-  birdloop_leave(p->p.loop);
-}
-*/
-
 static void
 bfd_remove_session_locked(struct bfd_proto *p, struct bfd_session *s)
 {
@@ -545,6 +566,8 @@ bfd_remove_session_locked(struct bfd_proto *p, struct bfd_session *s)
   rfree(s->tx_timer);
   rfree(s->hold_timer);
 
+  callback_cancel(&s->notify);
+
   HASH_REMOVE(p->session_hash_id, HASH_ID, s);
   HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
 
@@ -553,14 +576,6 @@ bfd_remove_session_locked(struct bfd_proto *p, struct bfd_session *s)
   sl_free(s);
 }
 
-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);
-}
-
 struct bfd_reconfigure_sessions_deferred_call {
   struct deferred_call dc;
   struct bfd_proto *p;
@@ -575,12 +590,15 @@ bfd_reconfigure_sessions(struct deferred_call *dc)
 
   struct bfd_proto *p = brsdc->p;
   birdloop_enter(p->p.loop);
+  birdloop_enter(p->eloop);
+  
+  /* Very much hoping that this is not too slow to cause significant delays. */
 
   HASH_WALK(p->session_hash_id, next_id, s)
   {
-    if (!EMPTY_LIST(s->request_list))
+    if (!EMPTY_TLIST(bfd_request, &s->request_list))
     {
-      SKIP_BACK_DECLARE(struct bfd_request, req, n, HEAD(s->request_list));
+      struct bfd_request *req = THEAD(bfd_request, &s->request_list);
       struct bfd_options opts = bfd_merge_options(&s->ifa->cf->opts, &req->opts);
 
 #define CHK(x) (opts.x != s->cf.x) ||
@@ -596,7 +614,8 @@ bfd_reconfigure_sessions(struct deferred_call *dc)
 
       if (reload)
       {
-       u32 tx = (s->loc_state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int;
+       struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+       u32 tx = (sp.loc.state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int;
        bfd_session_set_min_tx(s, tx);
        bfd_session_set_min_rx(s, s->cf.min_rx_int);
        s->detect_mult = s->cf.multiplier;
@@ -609,6 +628,8 @@ bfd_reconfigure_sessions(struct deferred_call *dc)
     }
   }
   HASH_WALK_END;
+
+  birdloop_leave(p->eloop);
   birdloop_leave(p->p.loop);
 
   /* Now the config is clean */
@@ -657,11 +678,13 @@ bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface)
   ifa->cf = ic;
   ifa->bfd = p;
 
+  birdloop_enter(p->eloop);
   ifa->sk = bfd_open_tx_sk(p, local, iface);
   ifa->uc = 1;
 
   if (cf->strict_bind)
     ifa->rx = bfd_open_rx_sk_bound(p, local, iface);
+  birdloop_leave(p->eloop);
 
   add_tail(&p->iface_list, &ifa->n);
 
@@ -671,6 +694,7 @@ bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface)
 static void
 bfd_free_iface(struct bfd_iface *ifa)
 {
+  ASSERT_DIE(birdloop_inside(ifa->bfd->eloop));
   if (!ifa || --ifa->uc)
     return;
 
@@ -689,204 +713,207 @@ bfd_free_iface(struct bfd_iface *ifa)
  *     BFD requests
  */
 
-static void
-bfd_request_notify(struct bfd_request *req, u8 state, u8 remote, u8 diag)
+void
+bfd_request_update_state(struct bfd_request *req)
 {
-  u8 old_state = req->state;
-
-  if (state == old_state)
-    return;
-
-  req->state = state;
-  req->diag = diag;
-  req->old_state = old_state;
-  req->down = (old_state == BFD_STATE_UP) && (state == BFD_STATE_DOWN) && (remote != BFD_STATE_ADMIN_DOWN);
+  req->old = req->cur;
 
-  if (req->hook)
-  {
-    struct birdloop *target = !birdloop_inside(req->target) ? req->target : NULL;
+  rcu_read_lock();
 
-    if (target)
-      birdloop_enter(target);
+  struct bfd_session *s = atomic_load_explicit(&req->session, memory_order_acquire);
+  if (s)
+    req->cur = atomic_load_explicit(&s->state, memory_order_acquire);
+  else
+    req->cur = (struct bfd_state_pair) {};
 
-    req->hook(req);
+  rcu_read_unlock();
 
-    if (target)
-      birdloop_leave(target);
-  }
+  req->down =
+       (req->old.loc.state == BFD_STATE_UP)
+    && (req->cur.loc.state == BFD_STATE_DOWN)
+    && (req->cur.rem.state != BFD_STATE_ADMIN_DOWN);
 }
 
-static int
-bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
+static struct bfd_request *
+bfd_pick_request(struct bfd_proto *p)
 {
-  struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
+  BFD_LOCKED(g)
+    WALK_TLIST(bfd_request, req, &g->pickup_list)
+    {
+      SKIP_BACK_DECLARE(struct bfd_config, cf, c, p->p.cf);
+      if (p->p.vrf && (p->p.vrf != req->vrf))
+      {
+       TRACE(D_EVENTS, "Not accepting request to %I with different VRF", req->addr);
+       continue;
+      }
 
-  if (p->p.vrf && (p->p.vrf != req->vrf))
-  {
-    TRACE(D_EVENTS, "Not accepting request to %I with different VRF", req->addr);
-    return 0;
-  }
+      if (ipa_is_ip4(req->addr) ? !cf->accept_ipv4 : !cf->accept_ipv6)
+      {
+       TRACE(D_EVENTS, "Not accepting request to %I (AF limit)", req->addr);
+       continue;
+      }
 
-  if (ipa_is_ip4(req->addr) ? !cf->accept_ipv4 : !cf->accept_ipv6)
-  {
-    TRACE(D_EVENTS, "Not accepting request to %I (AF limit)", req->addr);
-    return 0;
-  }
+      if (req->iface ? !cf->accept_direct : !cf->accept_multihop)
+      {
+       TRACE(D_EVENTS, "Not accepting %s request to %I", req->iface ? "direct" : "multihop", req->addr);
+       continue;
+      }
 
-  if (req->iface ? !cf->accept_direct : !cf->accept_multihop)
-  {
-    TRACE(D_EVENTS, "Not accepting %s request to %I", req->iface ? "direct" : "multihop", req->addr);
-    return 0;
-  }
+      bfd_request_rem_node(&g->pickup_list, req);
+      return req;
+    }
 
+  return NULL;
+}
+
+static void
+bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
+{
   uint ifindex = req->iface ? req->iface->index : 0;
+
+  birdloop_enter(p->eloop);
   struct bfd_session *s = bfd_find_session_by_addr(p, req->addr, ifindex);
+  birdloop_leave(p->eloop);
 
   if (s)
     TRACE(D_EVENTS, "Session to %I reused", s->addr);
   else
     s = bfd_add_session(p, req->addr, req->local, req->iface, &req->opts);
 
-  rem_node(&req->n);
-  add_tail(&s->request_list, &req->n);
-  req->session = s;
-
-  bfd_lock_sessions(p);
+  /* Register the request in the session */
+  bfd_request_add_tail(&s->request_list, req);
+  atomic_store_explicit(&req->session, s, memory_order_release);
 
-  int notify = !NODE_VALID(&s->n);
-  if (notify)
-    add_tail(&p->notify_list, &s->n);
-
-  bfd_unlock_sessions(p);
-
-  if (notify)
-    ev_send(&global_event_list, &p->notify_event);
-
-  return 1;
+  /* Inform the requestor */
+  bfd_notify_request(s, req);
 }
 
 static void
-bfd_pickup_requests(void *_data UNUSED)
+bfd_pickup_requests(callback *cb)
 {
-  /* NOTE TO MY FUTURE SELF
-   *
-   * Functions bfd_take_requests() and bfd_drop_requests() need to have
-   * consistent &bfd_global.wait_list and this is ensured only by having these
-   * functions called from bfd_start() and bfd_shutdown() which are both called
-   * in PROTO_LOCKED_FROM_MAIN context, i.e. always from &main_birdloop.
-   *
-   * This pickup event is also called in &main_birdloop, therefore we can
-   * freely do BFD_LOCK/BFD_UNLOCK while processing all the requests. All BFD
-   * protocols capable of bfd_add_request() are either started before this code
-   * happens or after that.
-   *
-   * If BFD protocols could start in parallel with this routine, they might
-   * miss some of the waiting requests, thus if anybody tries to start
-   * protocols or run this pickup event outside &main_birdloop in future, they
-   * shall ensure that this race condition is mitigated somehow.
-   *
-   * Thank you, my future self, for understanding. Have a nice day!
-   */
-
-  DBG("BFD pickup loop starting");
-
-  BFD_LOCK;
-  do {
-    bfd_global.pickup_reload = 0;
-    BFD_UNLOCK;
-
-    node *n;
-    WALK_LIST(n, bfd_global.proto_list)
-    {
-      SKIP_BACK_DECLARE(struct bfd_proto, p, bfd_node, n);
-      birdloop_enter(p->p.loop);
-      BFD_LOCK;
-
-      TRACE(D_EVENTS, "Picking up new requests (%d available)", list_length(&bfd_global.pickup_list));
+  SKIP_BACK_DECLARE(struct bfd_proto, p, pickup, cb);
+  for (
+      struct bfd_request *req;
+      req = bfd_pick_request(p);
+      )
+    bfd_add_request(p, req);
+}
 
-      node *rn, *rnxt;
-      WALK_LIST_DELSAFE(rn, rnxt, bfd_global.pickup_list)
-       bfd_add_request(p, SKIP_BACK(struct bfd_request, n, rn));
+static void
+bfd_cleanup_requests(callback *cb)
+{
+  SKIP_BACK_DECLARE(struct bfd_proto, p, cleanup, cb);
 
-      BFD_UNLOCK;
+  ASSERT_DIE(p->p.proto_state == PS_UP);
 
-      /* Remove sessions with no requests */
-      HASH_WALK_DELSAFE(p->session_hash_id, next_id, s)
+  birdloop_enter(p->eloop);
+  HASH_WALK_DELSAFE(p->session_hash_id, next_id, s)
+  {
+    WALK_TLIST_DELSAFE(bfd_request, req, &s->request_list)
+      if (!atomic_load_explicit(&req->notify, memory_order_acquire))
       {
-       if (EMPTY_LIST(s->request_list))
-         bfd_remove_session_locked(p, s);
+       bfd_request_rem_node(&s->request_list, req);
+       BFD_LOCKED(g)
+         sl_free(req);
       }
-      HASH_WALK_END;
-
-      birdloop_leave(p->p.loop);
-    }
 
-    BFD_LOCK;
-  } while (bfd_global.pickup_reload);
-
-  list tmp_list;
-  init_list(&tmp_list);
-  add_tail_list(&tmp_list, &bfd_global.pickup_list);
+    if (EMPTY_TLIST(bfd_request, &s->request_list))
+      bfd_remove_session_locked(p, s);
+  }
+  HASH_WALK_END;
+  birdloop_leave(p->eloop);
+}
 
-  init_list(&bfd_global.pickup_list);
-  BFD_UNLOCK;
+static void
+bfd_drop_requests(struct bfd_proto *p)
+{
+  birdloop_enter(p->eloop);
+  HASH_WALK_DELSAFE(p->session_hash_id, next_id, s)
+  {
+    WALK_TLIST_DELSAFE(bfd_request, req, &s->request_list)
+    {
+      bfd_request_rem_node(&s->request_list, req);
+      atomic_store_explicit(&req->session, NULL, memory_order_release);
+      BFD_LOCKED(g)
+       bfd_request_add_tail(&g->pickup_list, req);
+    }
+  }
+  HASH_WALK_END;
 
-  log(L_TRACE "No protocol for %d BFD requests", list_length(&tmp_list));
+  synchronize_rcu();
+  HASH_WALK_DELSAFE(p->session_hash_id, next_id, s)
+    bfd_remove_session_locked(p, s);
+  HASH_WALK_END;
 
-  node *n;
-  WALK_LIST(n, tmp_list)
-    bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), BFD_STATE_ADMIN_DOWN, BFD_STATE_ADMIN_DOWN, 0);
+  birdloop_leave(p->eloop);
 
-  BFD_LOCK;
-  add_tail_list(&bfd_global.wait_list, &tmp_list);
-  BFD_UNLOCK;
+  BFD_LOCKED(g)
+    WALK_TLIST(bfd_proto, p, &g->proto_list)
+      callback_activate(&p->pickup);
 }
 
-static event bfd_pickup_event = { .hook = bfd_pickup_requests };
-
 static void
-bfd_take_requests(struct bfd_proto *p)
+bfd_request_ref_free(resource *r)
 {
-  node *n, *nn;
-  BFD_LOCK;
-  WALK_LIST_DELSAFE(n, nn, bfd_global.wait_list)
-    bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n));
-  BFD_UNLOCK;
+  struct bfd_request *req = SKIP_BACK(struct bfd_request_ref, r, r)->req;
+  
+  callback *cb = atomic_exchange_explicit(&req->notify, NULL, memory_order_release);
+  synchronize_rcu();
+
+  /* Now if anybody wanted to activate the callback, they did it
+   * and the cancellation won't race. */
+  callback_cancel(cb);
+
+  /* Request global BFD cleanup. We can't reliably detect where the request
+   * ended up, so we rather ping all. */
+  callback_activate(&bfd_global.cleanup);
 }
 
 static void
-bfd_drop_requests(struct bfd_proto *p)
+bfd_request_ref_dump(struct dump_request *dreq, resource *r)
 {
-  node *n;
-  BFD_LOCK;
-  HASH_WALK_DELSAFE(p->session_hash_id, next_id, s)
-  {
-    WALK_LIST_FIRST(n, s->request_list)
-    {
-      SKIP_BACK_DECLARE(struct bfd_request, req, n, n);
-      rem_node(&req->n);
-      add_tail(&bfd_global.pickup_list, &req->n);
-      req->session = NULL;
-    }
+  struct bfd_request *req = SKIP_BACK(struct bfd_request_ref, r, r)->req;
 
-    ev_send(&global_event_list, &bfd_pickup_event);
+  rcu_read_lock();
+  struct bfd_session *s = atomic_load_explicit(&req->session, memory_order_acquire);
+  RDUMP("addr=%I local=%I iface=%s vrf=%s notify=%p session=%p",
+      req->addr, req->local,
+      req->iface ? req->iface->name : "(none)",
+      req->vrf ? req->vrf->name : "(none)",
+      atomic_load_explicit(&req->notify, memory_order_relaxed),
+      s);
 
-    bfd_remove_session_locked(p, s);
+  if (s)
+  {
+    struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+    btime last_update = atomic_load_explicit(&s->last_state_change, memory_order_relaxed);
+    RDUMP("state=(loc %u d%u rem %u d%u) changed=%t\n", 
+       sp.loc.state, sp.loc.diag, sp.rem.state, sp.rem.diag,
+       last_update);
   }
-  HASH_WALK_END;
-  BFD_UNLOCK;
+  else
+    RDUMP("\n");
+
+  rcu_read_unlock();
 }
 
-static struct resclass bfd_request_class;
+static struct resclass bfd_request_ref_class = {
+  .name = "BFD request reference",
+  .size = sizeof(struct bfd_request_ref),
+  .free = bfd_request_ref_free,
+  .dump = bfd_request_ref_dump,
+};
 
-struct bfd_request *
+struct bfd_request_ref *
 bfd_request_session(pool *p, ip_addr addr, ip_addr local,
                    struct iface *iface, struct iface *vrf,
-                   void (*hook)(struct bfd_request *), void *data,
-                   struct birdloop *target,
+                   callback *notify,
                    const struct bfd_options *opts)
 {
-  struct bfd_request *req = ralloc(p, &bfd_request_class);
+  struct bfd_request *req;
+
+  BFD_LOCKED(g)
+    req = sl_allocz(g->request_slab);
 
   req->addr = addr;
   req->local = local;
@@ -896,58 +923,46 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
   if (opts)
     req->opts = *opts;
 
-  ASSERT_DIE(target || !hook);
-  req->hook = hook;
-  req->data = data;
-  req->target = target;
+  ASSERT_DIE(notify);
+  atomic_store_explicit(&req->notify, notify, memory_order_relaxed);
+  atomic_store_explicit(&req->session, NULL, memory_order_relaxed);
 
-  req->session = NULL;
-
-  BFD_LOCK;
-  bfd_global.pickup_reload++;
-  add_tail(&bfd_global.pickup_list, &req->n);
-  ev_send(&global_event_list, &bfd_pickup_event);
-  DBG("New BFD request enlisted.\n");
-  BFD_UNLOCK;
+  BFD_LOCKED(g)
+  {
+    bfd_request_add_tail(&g->pickup_list, req);
+    WALK_TLIST(bfd_proto, p, &g->proto_list)
+      callback_activate(&p->pickup);
+  }
 
-  return req;
+  struct bfd_request_ref *rr = ralloc(p, &bfd_request_ref_class);
+  rr->req = req;
+  return rr;
 }
 
 void
-bfd_update_request(struct bfd_request *req, const struct bfd_options *opts)
+bfd_update_request(struct bfd_request_ref *rr, const struct bfd_options *opts)
 {
-  req->opts = *opts;
-}
-
-static void
-bfd_request_free(resource *r)
-{
-  struct bfd_request *req = (struct bfd_request *) r;
-
-  BFD_LOCK;
-  rem_node(&req->n);
-  BFD_UNLOCK;
-
-  ev_send(&global_event_list, &bfd_pickup_event);
+  rr->req->opts = *opts;
 }
 
 static void
-bfd_request_dump(struct dump_request *dreq, resource *r)
+bfd_cleanup_unpicked_requests(callback *cb)
 {
-  struct bfd_request *req = (struct bfd_request *) r;
+  ASSERT_DIE(cb == &bfd_global.cleanup);
+  BFD_LOCKED(g)
+  {
+    WALK_TLIST_DELSAFE(bfd_request, req, &g->pickup_list)
+      if (!atomic_load_explicit(&req->notify, memory_order_acquire))
+      {
+       bfd_request_rem_node(&g->pickup_list, req);
+       sl_free(req);
+      }
 
-  RDUMP("(code %p, data %p)\n", req->hook, req->data);
+    WALK_TLIST_DELSAFE(bfd_proto, p, &g->proto_list)
+      callback_activate(&p->cleanup);
+  }
 }
 
-static struct resclass bfd_request_class = {
-  "BFD request",
-  sizeof(struct bfd_request),
-  bfd_request_free,
-  bfd_request_dump,
-  NULL,
-  NULL,
-};
-
 
 /*
  *     BFD neighbors
@@ -965,7 +980,7 @@ bfd_neigh_notify(struct neighbor *nb)
   if ((nb->scope > 0) && !n->req)
   {
     ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip;
-    n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL, NULL, NULL);
+    n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, &n->notify, NULL);
   }
 
   if ((nb->scope <= 0) && n->req)
@@ -982,7 +997,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
 
   if (n->multihop)
   {
-    n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL, NULL, NULL);
+    n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, &n->notify, NULL);
     return;
   }
 
@@ -1024,6 +1039,15 @@ bfd_stop_neighbor(struct bfd_proto *p UNUSED, struct bfd_neighbor *n)
   n->req = NULL;
 }
 
+void
+bfd_neighbor_notify(callback *cb UNUSED)
+{
+  // SKIP_BACK_DECLARE(struct bfd_neighbor, n, bfd_notify, cb);
+  // bfd_request_update_state(n->req->req);
+  /* This may, in future, push the changed BFD state to our API.
+   * We don't have any API yet, tho. Time to make some? */
+}
+
 static inline int
 bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
 {
@@ -1035,11 +1059,10 @@ static void
 bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new)
 {
   struct bfd_config *old = (struct bfd_config *) (p->p.cf);
-  struct bfd_neighbor *on, *nn;
 
-  WALK_LIST(on, old->neigh_list)
+  WALK_TLIST(bfd_neighbor, on, &old->neigh_list)
   {
-    WALK_LIST(nn, new->neigh_list)
+    WALK_TLIST(bfd_neighbor, nn, &new->neigh_list)
       if (bfd_same_neighbor(nn, on))
       {
        nn->neigh = on->neigh;
@@ -1055,51 +1078,12 @@ bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new)
   next:;
   }
 
-  WALK_LIST(nn, new->neigh_list)
+  WALK_TLIST(bfd_neighbor, nn, &new->neigh_list)
     if (!nn->active)
       bfd_start_neighbor(p, nn);
 }
 
 
-/*
- *     BFD notify socket
- */
-
-/* This core notify code should be replaced after main loop transition to birdloop */
-
-static void
-bfd_notify_hook(void *data)
-{
-  struct bfd_proto *p = data;
-  struct bfd_session *s;
-  list tmp_list;
-  u8 loc_state, rem_state, diag;
-  node *n, *nn;
-
-  bfd_lock_sessions(p);
-  init_list(&tmp_list);
-  add_tail_list(&tmp_list, &p->notify_list);
-  init_list(&p->notify_list);
-  bfd_unlock_sessions(p);
-
-  WALK_LIST_FIRST(s, tmp_list)
-  {
-    bfd_lock_sessions(p);
-    rem_node(&s->n);
-    loc_state = s->loc_state;
-    rem_state = s->rem_state;
-    diag = s->loc_diag;
-    bfd_unlock_sessions(p);
-
-    WALK_LIST_DELSAFE(n, nn, s->request_list)
-      bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), loc_state, rem_state, diag);
-
-    /* Remove the session if all requests were removed in notify hooks */
-    if (EMPTY_LIST(s->request_list))
-      bfd_remove_session(p, s);
-  }
-}
-
 /*
  *     BFD protocol glue
  */
@@ -1120,9 +1104,8 @@ bfd_start(struct proto *P)
   struct bfd_proto *p = (struct bfd_proto *) P;
   struct bfd_config *cf = (struct bfd_config *) (P->cf);
 
-  pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
-
   p->tpool = birdloop_pool(P->loop);
+  p->eloop = birdloop_new(P->pool, DOMAIN_ORDER(service), cf->express_thread_group->group, "BFD Express %s", P->name);
 
   p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
   HASH_INIT(p->session_hash_id, P->pool, 8);
@@ -1130,16 +1113,15 @@ bfd_start(struct proto *P)
 
   init_list(&p->iface_list);
 
-  init_list(&p->notify_list);
-  p->notify_event = (event) {
-    .hook = bfd_notify_hook,
-    .data = p,
-  };
+  callback_init(&p->pickup, bfd_pickup_requests, P->loop);
+  callback_init(&p->cleanup, bfd_cleanup_requests, P->loop);
 
-  add_tail(&bfd_global.proto_list, &p->bfd_node);
+  BFD_LOCKED(g)
+    bfd_proto_add_tail(&g->proto_list, p);
 
   if (!cf->strict_bind)
   {
+    birdloop_enter(p->eloop);
     if (cf->accept_ipv4 && cf->accept_direct)
       p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
 
@@ -1151,32 +1133,47 @@ bfd_start(struct proto *P)
 
     if (cf->accept_ipv6 && cf->accept_multihop)
       p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
+    birdloop_leave(p->eloop);
   }
 
-  bfd_take_requests(p);
+  callback_activate(&p->pickup);
 
-  struct bfd_neighbor *n;
-  WALK_LIST(n, cf->neigh_list)
+  WALK_TLIST(bfd_neighbor, n, &cf->neigh_list)
     bfd_start_neighbor(p, n);
 
   return PS_UP;
 }
 
+static void
+bfd_cleanup_eloop(void *_p)
+{
+  struct bfd_proto *p = _p;
+
+  birdloop_enter(p->p.loop);
+  birdloop_free(p->eloop);
+  proto_notify_state(&p->p, PS_FLUSH);
+  birdloop_leave(p->p.loop);
+}
+
 static int
 bfd_shutdown(struct proto *P)
 {
   struct bfd_proto *p = (struct bfd_proto *) P;
   struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
 
-  rem_node(&p->bfd_node);
+  BFD_LOCKED(g)
+    bfd_proto_rem_node(&g->proto_list, p);
+
+  callback_cancel(&p->cleanup);
+  callback_cancel(&p->pickup);
 
-  struct bfd_neighbor *bn;
-  WALK_LIST(bn, cf->neigh_list)
+  WALK_TLIST(bfd_neighbor, bn, &cf->neigh_list)
     bfd_stop_neighbor(p, bn);
 
   bfd_drop_requests(p);
+  birdloop_stop(p->eloop, bfd_cleanup_eloop, p);
 
-  return PS_FLUSH;
+  return PS_STOP;
 }
 
 static int
@@ -1224,18 +1221,13 @@ bfd_copy_config(struct proto_config *dest, struct proto_config *src UNUSED)
 
   /* We clean up patt_list and neigh_list, neighbors and ifaces are non-sharable */
   init_list(&d->patt_list);
-  init_list(&d->neigh_list);
+  d->neigh_list = (TLIST_LIST(bfd_neighbor)) {};
 }
 
 void
 bfd_show_session(struct bfd_session *s, int details)
 {
-  /* FIXME: this is thread-unsafe, but perhaps harmless */
-
-  u8 loc_state = s->loc_state;
-  u8 rem_state = s->rem_state;
-  u8 loc_diag = s->loc_diag;
-  u8 rem_diag = s->rem_diag;
+  struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
   uint loc_id = s->loc_id;
   uint rem_id = s->rem_id;
 
@@ -1244,21 +1236,22 @@ bfd_show_session(struct bfd_session *s, int details)
   btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
   u8 auth_type = s->cf.auth_type;
 
-  loc_state = (loc_state < 4) ? loc_state : 0;
-  rem_state = (rem_state < 4) ? rem_state : 0;
+  sp.loc.state = (sp.loc.state < 4) ? sp.loc.state : 0;
+  sp.rem.state = (sp.rem.state < 4) ? sp.rem.state : 0;
 
   byte dbuf[BFD_DIAG_BUFFER_SIZE];
   byte tbuf[TM_DATETIME_BUFFER_SIZE];
 
   rcu_read_lock();
   struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed);
-  tm_format_time(tbuf, this_cli->tf ?: &gr->tf_proto, s->last_state_change);
+  tm_format_time(tbuf, this_cli->tf ?: &gr->tf_proto,
+      atomic_load_explicit(&s->last_state_change, memory_order_relaxed));
   rcu_read_unlock();
 
   if (!details)
   {
     cli_msg(-1020, "%-25I %-10s %-10s %-12s  %7t  %7t",
-           s->addr, ifname, bfd_state_names[loc_state], tbuf, tx_int, timeout);
+           s->addr, ifname, bfd_state_names[sp.loc.state], tbuf, tx_int, timeout);
 
     return;
   }
@@ -1266,11 +1259,11 @@ bfd_show_session(struct bfd_session *s, int details)
   cli_msg(-1020, "  %-21s %I", "Address:", s->addr);
   cli_msg(-1020, "  %-21s %s", "Interface:", ifname);
   cli_msg(-1020, "  %-21s %s", "Session type:", s->ifa->iface ? "Direct" : "Multihop");
-  cli_msg(-1020, "  %-21s %s", "Session state:", bfd_state_names[loc_state]);
-  cli_msg(-1020, "  %-21s %s", "Remote state:", bfd_state_names[rem_state]);
+  cli_msg(-1020, "  %-21s %s", "Session state:", bfd_state_names[sp.loc.state]);
+  cli_msg(-1020, "  %-21s %s", "Remote state:", bfd_state_names[sp.rem.state]);
   cli_msg(-1020, "  %-21s %s", "Last state change:", tbuf);
-  cli_msg(-1020, "  %-21s %s", "Local diagnostic:", bfd_diag_name(loc_diag, dbuf));
-  cli_msg(-1020, "  %-21s %s", "Remote diagnostic:", bfd_diag_name(rem_diag, dbuf));
+  cli_msg(-1020, "  %-21s %s", "Local diagnostic:", bfd_diag_name(sp.loc.diag, dbuf));
+  cli_msg(-1020, "  %-21s %s", "Remote diagnostic:", bfd_diag_name(sp.rem.diag, dbuf));
   cli_msg(-1020, "  %-21s %u", "Local discriminator:", loc_id);
   cli_msg(-1020, "  %-21s %u", "Remote discriminator:", rem_id);
 
@@ -1359,10 +1352,14 @@ bfd_build(void)
 {
   proto_build(&proto_bfd);
 
+  callback_init(&bfd_global.cleanup, bfd_cleanup_unpicked_requests, &main_birdloop);
+
   bfd_global.lock = DOMAIN_NEW(rtable);
   DOMAIN_SETUP(rtable, bfd_global.lock, "BFD Global", NULL);
 
-  init_list(&bfd_global.wait_list);
-  init_list(&bfd_global.pickup_list);
-  init_list(&bfd_global.proto_list);
+  BFD_LOCKED(g)
+  {
+    g->request_pool = rp_new(&root_pool, g->lock.rtable, "BFD Global");
+    g->request_slab = sl_new(g->request_pool, sizeof(struct bfd_request));
+  }
 }
index 107829b723f5d7661b11d866cbb37f64004c180e..71f1090dfe25232cfb71c3ff7f884be97f301e6e 100644 (file)
@@ -21,6 +21,7 @@
 #include "lib/resource.h"
 #include "lib/socket.h"
 #include "lib/string.h"
+#include "lib/tlists.h"
 
 #include "nest/bfd.h"
 
 #define BFD_DEFAULT_IDLE_TX_INT        (1 S_)
 #define BFD_DEFAULT_MULTIPLIER 5
 
-
-struct bfd_iface_config;
-
-struct bfd_config
-{
-  struct proto_config c;
-  list patt_list;              /* List of iface configs (struct bfd_iface_config) */
-  list neigh_list;             /* List of configured neighbors (struct bfd_neighbor) */
-  struct bfd_iface_config *multihop; /* Multihop pseudoiface config */
-  u8 accept_ipv4;
-  u8 accept_ipv6;
-  u8 accept_direct;
-  u8 accept_multihop;
-  u8 strict_bind;
-  u8 zero_udp6_checksum_rx;
-};
-
 struct bfd_iface_config
 {
-  struct iface_patt i;
+  struct iface_patt i;                 /* contains list node (!) */
   struct bfd_options opts;
 };
 
+#define TLIST_PREFIX bfd_neighbor
+#define TLIST_TYPE struct bfd_neighbor
+#define TLIST_ITEM n
+#define TLIST_WANT_ADD_TAIL
 struct bfd_neighbor
 {
-  node n;
+  TLIST_DEFAULT_NODE;
   ip_addr addr;
   ip_addr local;
   struct iface *iface;
 
   struct neighbor *neigh;
-  struct bfd_request *req;
+  struct bfd_request_ref *req;
+
+  callback notify;
 
   u8 multihop;
   u8 active;
 };
+#include "lib/tlists.h"
+
+struct bfd_config
+{
+  struct proto_config c;
+  struct thread_group_config *express_thread_group;
+  list patt_list;      /* List of iface configs (struct bfd_iface_config) */
+  TLIST_LIST(bfd_neighbor) neigh_list;         /* List of configured neighbors */
+  struct bfd_iface_config *multihop; /* Multihop pseudoiface config */
+  u8 accept_ipv4;
+  u8 accept_ipv6;
+  u8 accept_direct;
+  u8 accept_multihop;
+  u8 strict_bind;
+  u8 zero_udp6_checksum_rx;
+};
+
 
 struct bfd_proto
 {
@@ -79,14 +86,16 @@ struct bfd_proto
 
   pool *tpool;
 
-  node bfd_node;
+  struct birdloop *eloop;
+
+  TLIST_NODE(bfd_proto, struct bfd_proto) bfd_node;
 
   slab *session_slab;
   HASH(struct bfd_session) session_hash_id;
   HASH(struct bfd_session) session_hash_ip;
 
-  event notify_event;
-  list notify_list;
+  callback pickup;
+  callback cleanup;
 
   sock *rx4_1;
   sock *rx6_1;
@@ -95,6 +104,12 @@ struct bfd_proto
   list iface_list;
 };
 
+#define TLIST_PREFIX bfd_proto
+#define TLIST_TYPE struct bfd_proto
+#define TLIST_ITEM bfd_node
+#define TLIST_WANT_ADD_TAIL
+#include "lib/tlists.h"
+
 struct bfd_iface
 {
   node n;
@@ -122,10 +137,7 @@ struct bfd_session
   u8 poll_active;
   u8 poll_scheduled;
 
-  u8 loc_state;
-  u8 rem_state;
-  u8 loc_diag;
-  u8 rem_diag;
+  struct bfd_state_pair _Atomic state;
   u32 loc_id;                          /* Local session ID (local discriminator) */
   u32 rem_id;                          /* Remote session ID (remote discriminator) */
 
@@ -149,8 +161,10 @@ struct bfd_session
   timer *tx_timer;                     /* Periodic control packet timer */
   timer *hold_timer;                   /* Timer for session down detection time */
 
-  list request_list;                   /* List of client requests (struct bfd_request) */
-  btime last_state_change;             /* Time of last state change */
+  TLIST_LIST(bfd_request) request_list;        /* List of client requests (struct bfd_request) */
+  _Atomic btime last_state_change;     /* Time of last state change */
+
+  callback notify;                     /* Sent to the main protocol loop */
 
   u8 rx_csn_known;                     /* Received crypto sequence number is known */
   u32 rx_csn;                          /* Last received crypto sequence number */
@@ -207,15 +221,12 @@ extern const char *bfd_state_names[];
 
 extern const u8 bfd_auth_type_to_hash_alg[];
 
-
-static inline void bfd_lock_sessions(struct bfd_proto *p) { pthread_spin_lock(&p->lock); }
-static inline void bfd_unlock_sessions(struct bfd_proto *p) { pthread_spin_unlock(&p->lock); }
-
 /* bfd.c */
 struct bfd_session * bfd_find_session_by_id(struct bfd_proto *p, u32 id);
 struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex);
-void bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int);
+void bfd_session_process_ctl(struct bfd_session *s, struct bfd_state_pair sp, u8 flags, u32 old_tx_int, u32 old_rx_int);
 void bfd_show_sessions(struct proto *P, struct bfd_show_sessions_cmd *args);
+void bfd_neighbor_notify(callback *);
 
 /* packets.c */
 void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final);
index 9d336d6650c0db1513b84963fd79b46d43121818..9a4b1d0961cfacd35625d529e9506403120c191b 100644 (file)
@@ -21,7 +21,7 @@ extern struct bfd_config *bfd_cf;
 
 CF_DECLS
 
-CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
+CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE, EXPRESS,
        INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
        NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT,
        STRICT, BIND, ZERO, UDP6, CHECKSUM, RX)
@@ -40,24 +40,28 @@ bfd_proto_start: proto_start BFD
   this_proto = proto_config_new(&proto_bfd, $1);
   this_proto->loop_order = DOMAIN_ORDER(proto);
   if (new_config->thread_group_simple)
-    this_proto->thread_group = TTAIL(thread_group, &new_config->thread_group);
+    BFD_CFG->express_thread_group = TTAIL(thread_group, &new_config->thread_group);
   else
   {
     struct symbol *sym = cf_find_symbol(new_config, "express");
     if (sym && sym->class == SYM_THREAD_GROUP)
-      this_proto->thread_group = sym->thread_group;
+      BFD_CFG->express_thread_group = sym->thread_group;
     else
-      this_proto->thread_group = NULL;
+      BFD_CFG->express_thread_group = NULL;
   }
 
   init_list(&BFD_CFG->patt_list);
-  init_list(&BFD_CFG->neigh_list);
   BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1;
   BFD_CFG->accept_direct = BFD_CFG->accept_multihop = 1;
 };
 
 bfd_proto_item:
    proto_item
+ | EXPRESS THREAD GROUP symbol {
+    if ($4->class != SYM_THREAD_GROUP)
+      cf_error("Unexpected symbol %s as a thread group name", $4->name);
+    BFD_CFG->express_thread_group = $4->thread_group;
+ }
  | ACCEPT bfd_accept
  | INTERFACE bfd_iface
  | MULTIHOP bfd_multihop
@@ -73,8 +77,8 @@ bfd_proto_opts:
 
 bfd_proto:
    bfd_proto_start proto_name '{' bfd_proto_opts '}' {
-  if (!this_proto->thread_group)
-    cf_error("Thread group not configured and \"express\" not found.");
+  if (!BFD_CFG->express_thread_group)
+    cf_error("Express thread group not configured and \"express\" not found.");
 } ;
 
 
@@ -172,7 +176,9 @@ bfd_neigh_multihop:
 bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
 {
   this_bfd_neighbor = cfg_allocz(sizeof(struct bfd_neighbor));
-  add_tail(&BFD_CFG->neigh_list, NODE this_bfd_neighbor);
+  bfd_neighbor_add_tail(&BFD_CFG->neigh_list, this_bfd_neighbor);
+
+  callback_init(&BFD_NEIGHBOR->notify, bfd_neighbor_notify, &main_birdloop);
 
   BFD_NEIGHBOR->addr = $1;
   BFD_NEIGHBOR->local = $3;
index f8bd63d7364395b661c9bd627630d7740af05b2c..8bc08fe171be8cbe1a6c4d8224c950a35ee2a394 100644 (file)
@@ -290,9 +290,11 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
   if (!sk)
     return;
 
+  struct bfd_state_pair sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+
   pkt = (struct bfd_ctl_packet *) sk->tbuf;
-  pkt->vdiag = bfd_pack_vdiag(1, s->loc_diag);
-  pkt->flags = bfd_pack_flags(s->loc_state, 0);
+  pkt->vdiag = bfd_pack_vdiag(1, sp.loc.diag);
+  pkt->flags = bfd_pack_flags(sp.loc.state, 0);
   pkt->detect_mult = s->detect_mult;
   pkt->length = BFD_BASE_LEN;
   pkt->snd_id = htonl(s->loc_id);
@@ -313,7 +315,7 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
     log(L_WARN "%s: Old packet overwritten in TX buffer", p->p.name);
 
   TRACE(D_PACKETS, "Sending CTL to %I [%s%s]", s->addr,
-       bfd_state_names[s->loc_state], bfd_format_flags(pkt->flags, fb));
+       bfd_state_names[sp.loc.state], bfd_format_flags(pkt->flags, fb));
 
   sk_send_to(sk, pkt->length, s->addr, sk->dport);
 }
@@ -351,6 +353,7 @@ bfd_rx_hook(sock *sk, uint len)
     DROP("invalid my discriminator", 0);
 
   struct bfd_session *s;
+  struct bfd_state_pair sp;
   u32 id = ntohl(pkt->rcv_id);
 
   if (id)
@@ -359,6 +362,8 @@ bfd_rx_hook(sock *sk, uint len)
 
     if (!s)
       DROP("unknown session id", id);
+
+    sp = atomic_load_explicit(&s->state, memory_order_relaxed);
   }
   else
   {
@@ -375,8 +380,10 @@ bfd_rx_hook(sock *sk, uint len)
     if (!s)
       return 1;
 
+    sp = atomic_load_explicit(&s->state, memory_order_relaxed);
+
     /* For active sessions we require matching remote id */
-    if ((s->loc_state == BFD_STATE_UP) && (ntohl(pkt->snd_id) != s->rem_id))
+    if ((sp.loc.state == BFD_STATE_UP) && (ntohl(pkt->snd_id) != s->rem_id))
       DROP("mismatched remote id", ntohl(pkt->snd_id));
   }
 
@@ -388,17 +395,17 @@ bfd_rx_hook(sock *sk, uint len)
   u32 old_rx_int = s->rem_min_rx_int;
 
   s->rem_id = ntohl(pkt->snd_id);
-  s->rem_state = bfd_pkt_get_state(pkt);
-  s->rem_diag = bfd_pkt_get_diag(pkt);
+  sp.rem.state = bfd_pkt_get_state(pkt);
+  sp.rem.diag = bfd_pkt_get_diag(pkt);
   s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND;
   s->rem_min_tx_int = ntohl(pkt->des_min_tx_int);
   s->rem_min_rx_int = ntohl(pkt->req_min_rx_int);
   s->rem_detect_mult = pkt->detect_mult;
 
   TRACE(D_PACKETS, "CTL received from %I [%s%s]", sk->faddr,
-       bfd_state_names[s->rem_state], bfd_format_flags(pkt->flags, fb));
+       bfd_state_names[sp.rem.state], bfd_format_flags(pkt->flags, fb));
 
-  bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int);
+  bfd_session_process_ctl(s, sp, pkt->flags, old_tx_int, old_rx_int);
   return 1;
 
 drop:
@@ -437,7 +444,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
   if (cf->zero_udp6_checksum_rx)
     sk->flags |= SKF_UDP6_NO_CSUM_RX;
 
-  if (sk_open(sk, p->p.loop) < 0)
+  if (sk_open(sk, p->eloop) < 0)
     goto err;
 
   return sk;
@@ -473,7 +480,7 @@ bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa)
   if (cf->zero_udp6_checksum_rx)
     sk->flags |= SKF_UDP6_NO_CSUM_RX;
 
-  if (sk_open(sk, p->p.loop) < 0)
+  if (sk_open(sk, p->eloop) < 0)
     goto err;
 
   return sk;
@@ -504,7 +511,7 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
   sk->ttl = ifa ? 255 : -1;
   sk->flags = SKF_BIND | SKF_HIGH_PORT;
 
-  if (sk_open(sk, p->p.loop) < 0)
+  if (sk_open(sk, p->eloop) < 0)
     goto err;
 
   return sk;
index 859f7bc167712d67c0c9fd156fe93f63f580a8a0..d738b68a82d10ea77195bf2f33122d58a498369d 100644 (file)
@@ -1608,10 +1608,14 @@ bgp_neigh_notify(neighbor *n)
 }
 
 static void
-bgp_bfd_notify(struct bfd_request *req)
+bgp_bfd_notify(callback *cb)
 {
-  struct bgp_proto *p = req->data;
+  SKIP_BACK_DECLARE(struct bgp_proto, p, bfd_notify, cb);
+  ASSERT_DIE(birdloop_inside(p->p.loop));
+
   int ps = p->p.proto_state;
+  struct bfd_request *req = p->bfd_req->req;
+  bfd_request_update_state(req);
 
   if (req->down && ((ps == PS_START) || (ps == PS_UP)))
   {
@@ -1654,9 +1658,9 @@ bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd)
 
   if (bfd && !p->bfd_req && !bgp_is_dynamic(p))
   {
-    p->bfd_req = bfd_request_session(p->p.pool, p->remote_ip, p->local_ip,
+    p->bfd_req = bfd_request_session(p->p.pool_up, p->remote_ip, p->local_ip,
                                     p->cf->multihop ? NULL : p->neigh->iface,
-                                    p->p.vrf, bgp_bfd_notify, p, p->p.loop, bfd);
+                                    p->p.vrf, &p->bfd_notify, bfd);
     BGP_TRACE(D_EVENTS, "Requesting a new BFD session");
   }
 
@@ -1821,6 +1825,7 @@ bgp_start(struct proto *P)
   p->incoming_conn.state = BS_IDLE;
   p->neigh = NULL;
   p->bfd_req = NULL;
+  callback_init(&p->bfd_notify, bgp_bfd_notify, p->p.loop);
   p->gr_ready = 0;
   p->gr_active_num = 0;
 
index dac6e84eaa415792c0347bb07cc909dc999c84ef..15bcb448812ee01f2a222a7e25b7bc792d29d299 100644 (file)
@@ -377,7 +377,8 @@ struct bgp_proto {
   struct object_lock *lock;            /* Lock for neighbor connection */
   struct neighbor *neigh;              /* Neighbor entry corresponding to remote ip, NULL if multihop */
   struct bgp_listen_request listen;    /* Shared listening socket */
-  struct bfd_request *bfd_req;         /* BFD request, if BFD is used */
+  struct bfd_request_ref *bfd_req;     /* BFD request, if BFD is used */
+  callback bfd_notify;                 /* BFD notification callback */
   struct birdsock *postponed_sk;       /* Postponed incoming socket for dynamic BGP */
   struct rt_uncork_callback uncork;    /* Uncork hook */
   struct bgp_stats stats;              /* BGP statistics */
index 59a60587db82f0c3c2c530fbc7af4a23e600129f..acc0acfb2e461146af308bc03286ec7c9d88350c 100644 (file)
@@ -24,6 +24,7 @@ const char *ospf_inm_names[] = {
 
 static int can_do_adj(struct ospf_neighbor *n);
 static void inactivity_timer_hook(timer * timer);
+static void ospf_neigh_bfd_hook(callback *cb);
 static void dbdes_timer_hook(timer *t);
 static void lsrq_timer_hook(timer *t);
 static void lsrt_timer_hook(timer *t);
@@ -100,6 +101,8 @@ ospf_neighbor_new(struct ospf_iface *ifa)
   n->lsrt_timer = tm_new_init(pool, lsrt_timer_hook, n, ifa->rxmtint S, 0);
   n->ackd_timer = tm_new_init(pool, ackd_timer_hook, n, ifa->rxmtint S / 2, 0);
 
+  callback_init(&n->bfd_notify, ospf_neigh_bfd_hook, p->p.loop);
+
   return (n);
 }
 
@@ -756,9 +759,12 @@ inactivity_timer_hook(timer * timer)
 }
 
 static void
-ospf_neigh_bfd_hook(struct bfd_request *req)
+ospf_neigh_bfd_hook(callback *cb)
 {
-  struct ospf_neighbor *n = req->data;
+  SKIP_BACK_DECLARE(struct ospf_neighbor, n, bfd_notify, cb);
+  struct bfd_request *req = n->bfd_req->req;
+  bfd_request_update_state(req);
+
   struct ospf_proto *p = n->ifa->oa->po;
 
   if (req->down)
@@ -777,7 +783,7 @@ ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd)
   if (use_bfd && !n->bfd_req)
     n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip,
                                     n->ifa->iface, p->p.vrf,
-                                    ospf_neigh_bfd_hook, n, p->p.loop, NULL);
+                                    &n->bfd_notify, NULL);
 
   if (!use_bfd && n->bfd_req)
   {
index 75552953b5a92ddecae9d1c69499789bc271ea60..d99026e85f8b12a6cdcc2fafa6a2ac37d3394669 100644 (file)
@@ -407,7 +407,8 @@ struct ospf_neighbor
 #define ACKL_DELAY 1
   timer *ackd_timer;           /* Delayed ack timer */
   timer *gr_timer;             /* Graceful restart timer, non-NULL only if gr_active */
-  struct bfd_request *bfd_req; /* BFD request, if BFD is used */
+  struct bfd_request_ref *bfd_req;     /* BFD request, if BFD is used */
+  callback bfd_notify;         /* BFD notification callback */
   void *ldd_buffer;            /* Last database description packet */
   u32 ldd_bsize;               /* Buffer size for ldd_buffer */
   u32 csn;                     /* OSPFv2: Last received crypt seq number */
index ce8436156c149b173969925fe520fd14a339e22b..003c0355d42bc07e6acc62b3c13608b3ea4fa8ae 100644 (file)
@@ -82,6 +82,7 @@
 #include "lib/macro.h"
 
 
+static void rip_bfd_notify(callback *cb);
 static inline void rip_lock_neighbor(struct rip_neighbor *n);
 static inline void rip_unlock_neighbor(struct rip_neighbor *n);
 static inline int rip_iface_link_up(struct rip_iface *ifa);
@@ -459,6 +460,8 @@ rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa)
   nbr->data = n;
   n->csn = nbr->aux;
 
+  callback_init(&n->bfd_notify, rip_bfd_notify, p->p.loop);
+
   add_tail(&ifa->neigh_list, NODE n);
 
   return n;
@@ -525,10 +528,11 @@ rip_neigh_notify(struct neighbor *nbr)
 }
 
 static void
-rip_bfd_notify(struct bfd_request *req)
+rip_bfd_notify(callback *cb)
 {
-  struct rip_neighbor *n = req->data;
+  SKIP_BACK_DECLARE(struct rip_neighbor, n, bfd_notify, cb);
   struct rip_proto *p = n->ifa->rip;
+  struct bfd_request *req = n->bfd_req->req;
 
   if (req->down)
   {
@@ -551,9 +555,9 @@ rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n)
      * may cause problems if two link-local addresses are assigned to one iface.
      */
     ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip;
-    n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr,
+    n->bfd_req = bfd_request_session(p->p.pool_up, n->nbr->addr, saddr,
                                     n->nbr->iface, p->p.vrf,
-                                    rip_bfd_notify, n, p->p.loop, NULL);
+                                    &n->bfd_notify, NULL);
   }
 
   if (!use_bfd && n->bfd_req)
index 0cf2afd3424cff619b7483f6fdb12cec6837f460..cdef670e81ced5980007b1ae5443060bc63ddddb 100644 (file)
@@ -149,7 +149,8 @@ struct rip_neighbor
   node n;
   struct rip_iface *ifa;               /* Associated interface, may be NULL if stale */
   struct neighbor *nbr;                        /* Associaded core neighbor, may be NULL if stale */
-  struct bfd_request *bfd_req;         /* BFD request, if BFD is used */
+  struct bfd_request_ref *bfd_req;     /* BFD request, if BFD is used */
+  callback bfd_notify;                 /* Callback for BFD notifications */
   btime last_seen;                     /* Time of last received and accepted message */
   u32 uc;                              /* Use count, number of routes linking the neighbor */
   u32 csn;                             /* Last received crypto sequence number */
index 2f997503c36d463dcb1521ee5d1963957f3afd2f..c3ced71323ad5f0512a1bf1d05829addf6a8dceb 100644 (file)
@@ -252,7 +252,7 @@ static_announce_marked(void *P)
 }
 
 static void
-static_bfd_notify(struct bfd_request *req);
+static_bfd_notify(callback *cb);
 
 static void
 static_update_bfd(struct static_proto *p, struct static_route *r)
@@ -265,9 +265,10 @@ static_update_bfd(struct static_proto *p, struct static_route *r)
   if (bfd_up && !r->bfd_req)
   {
     // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
-    r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip,
+    callback_init(&r->bfd_notify, static_bfd_notify, p->p.loop);
+    r->bfd_req = bfd_request_session(p->p.pool_up, r->via, nb->ifa->ip,
                                     nb->iface, p->p.vrf,
-                                    static_bfd_notify, r, p->p.loop, NULL);
+                                    &r->bfd_notify, NULL);
   }
 
   if (!bfd_up && r->bfd_req)
@@ -291,8 +292,12 @@ static_decide(struct static_proto *p, struct static_route *r)
   if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
     goto fail;
 
-  if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP))
-    goto fail;
+  if (r->bfd_req)
+  {
+    bfd_request_update_state(r->bfd_req->req);
+    if (r->bfd_req->req->cur.loc.state != BFD_STATE_UP)
+      goto fail;
+  }
 
   r->active = 1;
   return !old_active;
@@ -449,9 +454,9 @@ static_neigh_notify(struct neighbor *n)
 }
 
 static void
-static_bfd_notify(struct bfd_request *req)
+static_bfd_notify(callback *cb)
 {
-  struct static_route *r = req->data;
+  SKIP_BACK_DECLARE(struct static_route, r, bfd_notify, cb);
   struct static_proto *p = (void *) r->neigh->proto;
 
   // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);
index 22810a5e1be8e56b1fe7ea30121234e316d016a4..31307240a2bcd8b2e8fc578a3379c507a2866585 100644 (file)
@@ -49,7 +49,8 @@ struct static_route {
   byte weight;                         /* Multipath next hop weight */
   byte use_bfd;                                /* Configured to use BFD */
   uint mpls_label;                     /* Local MPLS label, -1 if unused */
-  struct bfd_request *bfd_req;         /* BFD request, if BFD is used */
+  struct bfd_request_ref *bfd_req;     /* BFD request, if BFD is used */
+  callback bfd_notify;                 /* Notifier for BFD */
   union {
     adata *mls;                                /* MPLS label stack; may be NULL */
     adata *aspa;                       /* ASPA provider list; may be NULL */