diff --git a/nest/route.h b/nest/route.h index 0e5cd20f..7850a54c 100644 --- a/nest/route.h +++ b/nest/route.h @@ -92,6 +92,7 @@ extern uint rtable_max_id; struct birdloop *loop; /* Service thread */ \ netindex_hash *netindex; /* Prefix index for this table */ \ event *nhu_event; /* Nexthop updater */ \ + event *hcu_event; /* Hostcache updater */ \ /* The complete rtable structure */ struct rtable_private { @@ -121,6 +122,7 @@ struct rtable_private { * obstacle from this routing table. */ struct event *nhu_uncork_event; /* Helper event to schedule NHU on uncork */ + struct event *hcu_uncork_event; /* Helper event to schedule HCU on uncork */ struct timer *prune_timer; /* Timer for periodic pruning / GC */ struct event *prune_event; /* Event for prune execution */ struct birdloop_flag_handler fh; /* Handler for simple events */ @@ -503,14 +505,15 @@ struct hostentry { rtable *owner; /* Nexthop owner table */ struct hostentry *next; /* Next in hash chain */ unsigned hash_key; /* Hash key */ - unsigned uc; /* Use count */ - ea_list *src; /* Source attributes */ - byte nexthop_linkable; /* Nexthop list is completely non-device */ u32 igp_metric; /* Chosen route IGP metric */ + ea_list *src; /* Source attributes */ + struct lfuc uc; /* Use count */ + byte nexthop_linkable; /* Nexthop list is completely non-device */ }; struct hostcache { slab *slab; /* Slab holding all hostentries */ + rtable *tab; /* Parent routing table */ struct hostentry **hash_table; /* Hash table for hostentries */ unsigned hash_order, hash_shift; unsigned hash_max, hash_min; @@ -518,7 +521,6 @@ struct hostcache { linpool *lp; /* Linpool for trie */ struct f_trie *trie; /* Trie of prefixes that might affect hostentries */ list hostentries; /* List of all hostentries */ - event update; struct rt_export_request req; /* Notifier */ }; diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 1df1f96d..fa300106 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -143,14 +143,14 @@ struct ea_class ea_gen_nexthop = { ea_gen_hostentry_stored(const eattr *ea) { struct hostentry_adata *had = (struct hostentry_adata *) ea->u.ptr; - had->he->uc++; + lfuc_lock(&had->he->uc); } static void ea_gen_hostentry_freed(const eattr *ea) { struct hostentry_adata *had = (struct hostentry_adata *) ea->u.ptr; - had->he->uc--; + lfuc_unlock(&had->he->uc, birdloop_event_list(had->he->owner->loop), had->he->owner->hcu_event); } struct ea_class ea_gen_hostentry = { diff --git a/nest/rt-table.c b/nest/rt-table.c index be0ecb0a..23c9549e 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -3812,8 +3812,8 @@ rt_commit(struct config *new, struct config *old) rt_check_cork_low(tab); - if (tab->hostcache && ev_get_list(&tab->hostcache->update) == &rt_cork.queue) - ev_postpone(&tab->hostcache->update); + if (tab->hcu_event && (ev_get_list(tab->hcu_event) == &rt_cork.queue)) + ev_postpone(tab->hcu_event); /* Force one more loop run */ birdloop_flag(tab->loop, RTF_DELETE); @@ -4223,7 +4223,7 @@ static void hc_notify_log_state_change(struct rt_export_request *req, u8 state) { struct hostcache *hc = SKIP_BACK(struct hostcache, req, req); - rt_trace((rtable *) hc->update.data, D_STATES, "HCU Export state changed to %s", rt_export_state_name(state)); + rt_trace(hc->tab, D_STATES, "HCU Export state changed to %s", rt_export_state_name(state)); } static void @@ -4231,8 +4231,8 @@ hc_notify_export_one(struct rt_export_request *req, const net_addr *net, struct { struct hostcache *hc = SKIP_BACK(struct hostcache, req, req); - RT_LOCKED((rtable *) hc->update.data, tab) - if (ev_active(&hc->update) || !trie_match_net(hc->trie, net)) + RT_LOCKED(hc->tab, tab) + if (ev_active(tab->hcu_event) || !trie_match_net(hc->trie, net)) /* No interest in this update, mark seen only */ rpe_mark_seen_all(req->hook, first, NULL, NULL); else @@ -4250,8 +4250,8 @@ hc_notify_export_one(struct rt_export_request *req, const net_addr *net, struct /* Yes, something has actually changed. Do the hostcache update. */ if ((o != RTE_VALID_OR_NULL(new_best)) && (atomic_load_explicit(&req->hook->export_state, memory_order_acquire) == TES_READY) - && !ev_active(&hc->update)) - ev_send_loop(tab->loop, &hc->update); + && !ev_active(tab->hcu_event)) + ev_send_loop(tab->loop, tab->hcu_event); } } @@ -4269,14 +4269,13 @@ rt_init_hostcache(struct rtable_private *tab) hc->lp = lp_new(tab->rp); hc->trie = f_new_trie(hc->lp, 0); - hc->update = (event) { - .hook = rt_update_hostcache, - .data = tab, - }; + hc->tab = RT_PUB(tab); + tab->hcu_event = ev_new_init(tab->rp, rt_update_hostcache, tab); + tab->hcu_uncork_event = ev_new_init(tab->rp, rt_update_hostcache, tab); tab->hostcache = hc; - ev_send_loop(tab->loop, &hc->update); + ev_send_loop(tab->loop, tab->hcu_event); } static void @@ -4290,7 +4289,7 @@ rt_free_hostcache(struct rtable_private *tab) struct hostentry *he = SKIP_BACK(struct hostentry, ln, n); rta_free(he->src); - if (he->uc) + if (!lfuc_finished(&he->uc)) log(L_ERR "Hostcache is not empty in table %s", tab->name); } @@ -4426,7 +4425,7 @@ rt_update_hostcache(void *data) if (!hc->req.hook) return; - if (rt_cork_check(&hc->update)) + if (rt_cork_check(tab->hcu_uncork_event)) { rt_trace(tab, D_STATES, "Hostcache update corked"); return; @@ -4445,11 +4444,11 @@ rt_update_hostcache(void *data) WALK_LIST_DELSAFE(n, x, hc->hostentries) { he = SKIP_BACK(struct hostentry, ln, n); - if (!he->uc) - { - hc_delete_hostentry(hc, tab->rp, he); - continue; - } + if (lfuc_finished(&he->uc)) + { + hc_delete_hostentry(hc, tab->rp, he); + continue; + } if (rt_update_hostentry(tab, he)) nhu_pending[he->tab->id] = he->tab; @@ -4462,36 +4461,6 @@ rt_update_hostcache(void *data) rt_schedule_nhu(dst); } -struct hostentry_tmp_lock { - resource r; - rtable *tab; - struct hostentry *he; -}; - -static void -hostentry_tmp_unlock(resource *r) -{ - struct hostentry_tmp_lock *l = SKIP_BACK(struct hostentry_tmp_lock, r, r); - RT_LOCKED(l->tab, tab) - l->he->uc--; -} - -static void -hostentry_tmp_lock_dump(resource *r, unsigned indent UNUSED) -{ - struct hostentry_tmp_lock *l = SKIP_BACK(struct hostentry_tmp_lock, r, r); - debug("he=%p tab=%s\n", l->he, l->tab->name); -} - -struct resclass hostentry_tmp_lock_class = { - .name = "Temporary hostentry lock", - .size = sizeof(struct hostentry_tmp_lock), - .free = hostentry_tmp_unlock, - .dump = hostentry_tmp_lock_dump, - .lookup = NULL, - .memsize = NULL, -}; - static struct hostentry * rt_get_hostentry(struct rtable_private *tab, ip_addr a, ip_addr ll, rtable *dep) { @@ -4507,17 +4476,19 @@ rt_get_hostentry(struct rtable_private *tab, ip_addr a, ip_addr ll, rtable *dep) if (ipa_equal(he->addr, a) && ipa_equal(he->link, link) && (he->tab == dep)) break; - if (!he) + if (he) + lfuc_lock(&he->uc); + else { he = hc_new_hostentry(hc, tab->rp, a, link, dep, k); + lfuc_lock_revive(&he->uc); + he->owner = RT_PUB(tab); rt_update_hostentry(tab, he); } - struct hostentry_tmp_lock *l = ralloc(tmp_res.pool, &hostentry_tmp_lock_class); - l->he = he; - l->tab = RT_PUB(tab); - l->he->uc++; + /* Free the hostentry if filtered out */ + lfuc_unlock(&he->uc, birdloop_event_list(tab->loop), tab->hcu_event); return he; }