1
0
mirror of https://gitlab.labs.nic.cz/labs/bird.git synced 2024-05-11 16:54:54 +00:00

Interface updates are asynchronous

Instead of propagating interface updates as they are loaded from kernel,
they are enqueued and all the notifications are called from a
protocol-specific event. This change allows to break the locking loop
between protocols and interfaces.

Anyway, this change is based on v2 branch to keep the changes between v2
and v3 smaller.
This commit is contained in:
Maria Matejka
2023-01-31 13:07:46 +01:00
parent 64e0877525
commit c354e8f4c1
15 changed files with 315 additions and 63 deletions

View File

@@ -34,6 +34,9 @@
#include "conf/conf.h"
#include "sysdep/unix/krt.h"
static TLIST_LIST(ifsub) iface_sub_list;
static slab *iface_sub_slab;
static pool *if_pool;
list iface_list;
@@ -140,13 +143,51 @@ if_copy(struct iface *to, struct iface *from)
to->flags = from->flags | (to->flags & IF_TMP_DOWN);
to->mtu = from->mtu;
to->master_index = from->master_index;
to->master = from->master;
if_unlink(to->master);
if_link(to->master = from->master);
}
void
if_enqueue_notify_to(struct iface_notification x, struct iface_subscription *s)
{
switch (x.type) {
case IFNOT_ADDRESS:
if (!s->ifa_notify) return;
ifa_link(x.a);
break;
case IFNOT_INTERFACE:
if (!s->if_notify) return;
if_link(x.i);
break;
case IFNOT_NEIGHBOR:
if (!s->neigh_notify) return;
neigh_link(x.n);
break;
default:
bug("Unknown interface notification type: %d", x.type);
}
struct iface_notification *in = sl_alloc(iface_sub_slab);
*in = x;
ifnot_add_tail(&s->queue, in);
ev_schedule(&s->event);
}
void
if_enqueue_notify(struct iface_notification x)
{
WALK_TLIST(ifsub, s, &iface_sub_list)
if_enqueue_notify_to(x, s);
}
static inline void
ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
ifa_send_notify(struct iface_subscription *s, unsigned c, struct ifa *a)
{
if (p->ifa_notify &&
struct proto *p = SKIP_BACK(struct proto, iface_sub, s);
if (s->ifa_notify &&
(p->proto_state != PS_DOWN) &&
(!p->vrf || p->vrf == a->iface->master))
{
@@ -154,19 +195,21 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
log(L_TRACE "%s < address %N on interface %s %s",
p->name, &a->prefix, a->iface->name,
(c & IF_CHANGE_UP) ? "added" : "removed");
p->ifa_notify(p, c, a);
s->ifa_notify(p, c, a);
}
}
static void
ifa_notify_change_(unsigned c, struct ifa *a)
{
struct proto *p;
DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip);
WALK_LIST(p, proto_list)
ifa_send_notify(p, c, a);
if_enqueue_notify((struct iface_notification) {
.type = IFNOT_ADDRESS,
.a = a,
.flags = c,
});
}
static inline void
@@ -182,9 +225,11 @@ ifa_notify_change(unsigned c, struct ifa *a)
}
static inline void
if_send_notify(struct proto *p, unsigned c, struct iface *i)
if_send_notify(struct iface_subscription *s, unsigned c, struct iface *i)
{
if (p->if_notify &&
struct proto *p = SKIP_BACK(struct proto, iface_sub, s);
if (s->if_notify &&
(p->proto_state != PS_DOWN) &&
(!p->vrf || p->vrf == i->master))
{
@@ -197,14 +242,13 @@ if_send_notify(struct proto *p, unsigned c, struct iface *i)
(c & IF_CHANGE_PREFERRED) ? "changes preferred address" :
(c & IF_CHANGE_CREATE) ? "created" :
"sends unknown event");
p->if_notify(p, c, i);
s->if_notify(p, c, i);
}
}
static void
if_notify_change(unsigned c, struct iface *i)
{
struct proto *p;
struct ifa *a;
if (i->flags & IF_JUST_CREATED)
@@ -225,8 +269,11 @@ if_notify_change(unsigned c, struct iface *i)
WALK_LIST(a, i->addrs)
ifa_notify_change_(IF_CHANGE_DOWN, a);
WALK_LIST(p, proto_list)
if_send_notify(p, c, i);
if_enqueue_notify((struct iface_notification) {
.type = IFNOT_INTERFACE,
.i = i,
.flags = c,
});
if (c & IF_CHANGE_UP)
WALK_LIST(a, i->addrs)
@@ -320,6 +367,7 @@ if_update(struct iface *new)
new->llv6 = i->llv6;
new->sysdep = i->sysdep;
memcpy(&new->addrs, &i->addrs, sizeof(i->addrs));
memcpy(&new->neighbors, &i->neighbors, sizeof(i->neighbors));
memcpy(i, new, sizeof(*i));
i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */
goto newif;
@@ -334,9 +382,10 @@ if_update(struct iface *new)
}
i = mb_alloc(if_pool, sizeof(struct iface));
memcpy(i, new, sizeof(*i));
if_link(i->master);
init_list(&i->addrs);
newif:
init_list(&i->neighbors);
newif:
i->flags |= IF_UPDATED | IF_TMP_DOWN; /* Tmp down as we don't have addresses yet */
add_tail(&iface_list, &i->n);
return i;
@@ -386,31 +435,116 @@ if_end_update(void)
}
}
void
if_link(struct iface *i)
{
if (i)
i->uc++;
}
void
if_unlink(struct iface *i)
{
if (i)
i->uc--;
/* TODO: Do some interface object cleanup */
}
static void
iface_notify_hook(void *_s)
{
struct iface_subscription *s = _s;
while (!EMPTY_TLIST(ifnot, &s->queue))
{
struct iface_notification *n = THEAD(ifnot, &s->queue);
switch (n->type) {
case IFNOT_ADDRESS:
ifa_send_notify(s, n->flags, n->a);
ifa_unlink(n->a);
break;
case IFNOT_INTERFACE:
if_send_notify(s, n->flags, n->i);
if_unlink(n->i);
break;
case IFNOT_NEIGHBOR:
s->neigh_notify(n->n);
neigh_unlink(n->n);
break;
default:
bug("Bad interface notification type: %d", n->type);
}
ifnot_rem_node(&s->queue, n);
sl_free(n);
}
}
/**
* if_feed_baby - advertise interfaces to a new protocol
* @p: protocol to feed
* iface_subscribe - request interface updates
* @s: subscription structure
*
* When a new protocol starts, this function sends it a series
* of notifications about all existing interfaces.
*/
void
if_feed_baby(struct proto *p)
iface_subscribe(struct iface_subscription *s)
{
struct iface *i;
struct ifa *a;
ifsub_add_tail(&iface_sub_list, s);
s->event = (event) {
.hook = iface_notify_hook,
.data = s,
};
if (!p->if_notify && !p->ifa_notify) /* shortcut */
if (!s->if_notify && !s->ifa_notify) /* shortcut */
return;
struct iface *i;
DBG("Announcing interfaces to new protocol %s\n", p->name);
WALK_LIST(i, iface_list)
{
if_send_notify(p, IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), i);
if_send_notify(s, IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), i);
struct ifa *a;
if (i->flags & IF_UP)
WALK_LIST(a, i->addrs)
ifa_send_notify(p, IF_CHANGE_CREATE | IF_CHANGE_UP, a);
ifa_send_notify(s, IF_CHANGE_CREATE | IF_CHANGE_UP, a);
}
}
/**
* iface_unsubscribe - unsubscribe from interface updates
* @s: subscription structure
*/
void
iface_unsubscribe(struct iface_subscription *s)
{
ifsub_rem_node(&iface_sub_list, s);
ev_postpone(&s->event);
WALK_TLIST_DELSAFE(ifnot, n, &s->queue)
{
switch (n->type)
{
case IFNOT_ADDRESS:
ifa_unlink(n->a);
break;
case IFNOT_INTERFACE:
if_unlink(n->i);
break;
case IFNOT_NEIGHBOR:
neigh_unlink(n->n);
break;
default:
bug("Bad interface notification type: %d", n->type);
}
ifnot_rem_node(&s->queue, n);
sl_free(n);
}
}
/**
* if_find_by_index - find interface by ifindex
* @idx: ifindex
@@ -600,6 +734,8 @@ ifa_update(struct ifa *a)
b = mb_alloc(if_pool, sizeof(struct ifa));
memcpy(b, a, sizeof(struct ifa));
ifa_link(b);
if_link(i);
add_tail(&i->addrs, &b->n);
b->flags |= IA_UPDATED;
@@ -646,11 +782,29 @@ ifa_delete(struct ifa *a)
if (i->flags & IF_UP)
ifa_notify_change(IF_CHANGE_DOWN, b);
mb_free(b);
ifa_unlink(b);
return;
}
}
void ifa_link(struct ifa *a)
{
if (a)
a->uc++;
}
void ifa_unlink(struct ifa *a)
{
if (!a)
return;
if (--a->uc)
return;
if_unlink(a->iface);
mb_free(a);
}
u32
if_choose_router_id(struct iface_patt *mask, u32 old_id)
{
@@ -706,6 +860,7 @@ if_init(void)
{
if_pool = rp_new(&root_pool, "Interfaces");
init_list(&iface_list);
iface_sub_slab = sl_new(if_pool, sizeof(struct iface_notification));
strcpy(default_vrf.name, "default");
neigh_init(if_pool);
}