diff --git a/nest/iface.h b/nest/iface.h index 5deb5432..6e8c8448 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -153,6 +153,7 @@ typedef struct neighbor { #define NEF_STICKY 1 #define NEF_ONLINK 2 #define NEF_IFACE 4 /* Entry for whole iface */ +#define NEF_NOTIFY_MAIN 0x100 /* Notify from main_birdloop context */ neighbor *neigh_find(struct proto *p, ip_addr a, struct iface *ifa, uint flags); diff --git a/nest/neighbor.c b/nest/neighbor.c index 7466e510..e4d42b4d 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -60,6 +60,7 @@ static slab *neigh_slab; static list neigh_hash_table[NEIGH_HASH_SIZE], sticky_neigh_list; static void neigh_do_notify(void *); +static void neigh_do_notify_main(void *); static inline uint neigh_hash(struct proto *p, ip_addr a, struct iface *i) @@ -270,15 +271,29 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags) n->proto = p; n->flags = flags; n->scope = scope; - n->event = (event) { .hook = neigh_do_notify, .data = n }; + ASSERT_DIE(birdloop_inside(p->loop)); - if (p->loop == &main_birdloop) - n->event.list = &global_event_list; + if (flags & NEF_NOTIFY_MAIN) + n->event = (event) { + .hook = neigh_do_notify_main, + .data = n, + .list = &global_event_list, + }; + else if (p->loop == &main_birdloop) + n->event = (event) { + .hook = neigh_do_notify, + .data = n, + .list = &global_event_list, + }; else { birdloop_link(p->loop); - n->event.list = birdloop_event_list(p->loop); + n->event = (event) { + .hook = neigh_do_notify, + .data = n, + .list = birdloop_event_list(p->loop), + }; } IFACE_UNLOCK; @@ -340,6 +355,14 @@ neigh_notify(neighbor *n) ev_send(n->event.list, &n->event); } +static void +neigh_do_notify_main(void *data) +{ + neighbor *n = data; + PROTO_LOCKED_FROM_MAIN(n->proto) + neigh_do_notify(data); +} + static void neigh_do_notify(void *data) { @@ -382,10 +405,18 @@ neigh_down(neighbor *n) static inline void neigh_free(neighbor *n) { + ASSERT_DIE(birdloop_inside(n->proto->loop)); + + if (n->flags & NEF_NOTIFY_MAIN) + ASSERT_DIE(birdloop_inside(&main_birdloop)); + rem_node(&n->n); rem_node(&n->if_n); + + if (n->event.list != &global_event_list) + birdloop_unlink(n->proto->loop); + ev_postpone(&n->event); - birdloop_unlink(n->proto->loop); sl_free(neigh_slab, n); } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index b825f778..7e803f64 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1541,7 +1541,7 @@ bgp_start_locked(struct object_lock *lock) return; } - neighbor *n = neigh_find(&p->p, p->remote_ip, cf->iface, NEF_STICKY); + neighbor *n = neigh_find(&p->p, p->remote_ip, cf->iface, NEF_STICKY | NEF_NOTIFY_MAIN); if (!n) { log(L_ERR "%s: Invalid remote address %I%J", p->p.name, p->remote_ip, cf->iface); diff --git a/sysdep/unix/io-loop.c b/sysdep/unix/io-loop.c index ec805dce..1d3a555f 100644 --- a/sysdep/unix/io-loop.c +++ b/sysdep/unix/io-loop.c @@ -498,7 +498,9 @@ void birdloop_unlink(struct birdloop *loop) { ASSERT_DIE(birdloop_inside(loop)); - loop->links--; + ASSERT_DIE(loop->links); + if (!--loop->links) + birdloop_ping(loop); } static void @@ -541,7 +543,7 @@ birdloop_main(void *arg) birdloop_enter(loop); - if (loop->stopped) + if (loop->stopped && !loop->links) break; loop_begin = current_time();