From: Maria Matejka Date: Mon, 3 Feb 2025 14:21:52 +0000 (+0100) Subject: BFD session handling rework X-Git-Tag: v3.1.0~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=38acb415fbbf21d2f9992680a913c03cc88e9787;p=thirdparty%2Fbird.git BFD session handling rework 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. --- diff --git a/doc/bird.sgml b/doc/bird.sgml index 1ec20ab5a..9c0d350ef 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2699,6 +2699,11 @@ with the appropriate unit: 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. + protocol bfd [<name>] { accept [ipv4|ipv6] [direct|multihop]; diff --git a/nest/bfd.h b/nest/bfd.h index c046152f8..a989e7cf7 100644 --- a/nest/bfd.h +++ b/nest/bfd.h @@ -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 */ diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 4997f803a..75cc616ad 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -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 @@ -30,10 +22,10 @@ * @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 @@ -47,49 +39,49 @@ * * 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. * @@ -113,17 +105,27 @@ #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)); + } } diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h index 107829b72..71f1090df 100644 --- a/proto/bfd/bfd.h +++ b/proto/bfd/bfd.h @@ -21,6 +21,7 @@ #include "lib/resource.h" #include "lib/socket.h" #include "lib/string.h" +#include "lib/tlists.h" #include "nest/bfd.h" @@ -34,42 +35,48 @@ #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); diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y index 0eeb84a1a..024a0b997 100644 --- a/proto/bfd/config.Y +++ b/proto/bfd/config.Y @@ -21,7 +21,7 @@ extern struct bfd_config *bfd_cf; CF_DECLS -CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE, ADDRESS, +CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE, ADDRESS, EXPRESS, INTERFACE, MULTIHOP, NEIGHBOR, DEV, ALL, 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; diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c index f8bd63d73..8bc08fe17 100644 --- a/proto/bfd/packets.c +++ b/proto/bfd/packets.c @@ -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; diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 0721be1f9..db03e355c 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1613,10 +1613,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))) { @@ -1659,9 +1663,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"); } @@ -1826,6 +1830,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; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 8059c150d..5d9abc0ca 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -384,7 +384,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 */ diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index 59a60587d..acc0acfb2 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -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) { diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 75552953b..d99026e85 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -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 */ diff --git a/proto/rip/rip.c b/proto/rip/rip.c index ce8436156..003c0355d 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -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) diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 0cf2afd34..cdef670e8 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -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 */ diff --git a/proto/static/static.c b/proto/static/static.c index 2f997503c..c3ced7132 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -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); diff --git a/proto/static/static.h b/proto/static/static.h index 22810a5e1..31307240a 100644 --- a/proto/static/static.h +++ b/proto/static/static.h @@ -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 */