diff options
Diffstat (limited to 'net/tipc/name_table.c')
-rw-r--r-- | net/tipc/name_table.c | 121 |
1 files changed, 66 insertions, 55 deletions
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index b1fe20972aa9..bebe88cae07b 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -136,12 +136,12 @@ static struct tipc_service *tipc_service_create(u32 type, struct hlist_head *hd) } /** - * tipc_service_find_range - find service range matching a service instance + * tipc_service_first_range - find first service range in tree matching instance * * Very time-critical, so binary search through range rb tree */ -static struct service_range *tipc_service_find_range(struct tipc_service *sc, - u32 instance) +static struct service_range *tipc_service_first_range(struct tipc_service *sc, + u32 instance) { struct rb_node *n = sc->ranges.rb_node; struct service_range *sr; @@ -158,6 +158,30 @@ static struct service_range *tipc_service_find_range(struct tipc_service *sc, return NULL; } +/* tipc_service_find_range - find service range matching publication parameters + */ +static struct service_range *tipc_service_find_range(struct tipc_service *sc, + u32 lower, u32 upper) +{ + struct rb_node *n = sc->ranges.rb_node; + struct service_range *sr; + + sr = tipc_service_first_range(sc, lower); + if (!sr) + return NULL; + + /* Look for exact match */ + for (n = &sr->tree_node; n; n = rb_next(n)) { + sr = container_of(n, struct service_range, tree_node); + if (sr->upper == upper) + break; + } + if (!n || sr->lower != lower || sr->upper != upper) + return NULL; + + return sr; +} + static struct service_range *tipc_service_create_range(struct tipc_service *sc, u32 lower, u32 upper) { @@ -238,57 +262,19 @@ err: /** * tipc_service_remove_publ - remove a publication from a service */ -static struct publication *tipc_service_remove_publ(struct net *net, - struct tipc_service *sc, - u32 lower, u32 upper, +static struct publication *tipc_service_remove_publ(struct service_range *sr, u32 node, u32 key) { - struct tipc_subscription *sub, *tmp; - struct service_range *sr; struct publication *p; - bool found = false; - bool last = false; - struct rb_node *n; - sr = tipc_service_find_range(sc, lower); - if (!sr) - return NULL; - - /* Find exact matching service range */ - for (n = &sr->tree_node; n; n = rb_next(n)) { - sr = container_of(n, struct service_range, tree_node); - if (sr->upper == upper) - break; - } - if (!n || sr->lower != lower || sr->upper != upper) - return NULL; - - /* Find publication, if it exists */ list_for_each_entry(p, &sr->all_publ, all_publ) { if (p->key != key || (node && node != p->node)) continue; - found = true; - break; + list_del(&p->all_publ); + list_del(&p->local_publ); + return p; } - if (!found) - return NULL; - - list_del(&p->all_publ); - list_del(&p->local_publ); - - /* Remove service range item if this was its last publication */ - if (list_empty(&sr->all_publ)) { - last = true; - rb_erase(&sr->tree_node, &sc->ranges); - kfree(sr); - } - - /* Notify any waiting subscriptions */ - list_for_each_entry_safe(sub, tmp, &sc->subscriptions, service_list) { - tipc_sub_report_overlap(sub, p->lower, p->upper, TIPC_WITHDRAWN, - p->port, p->node, p->scope, last); - } - return p; + return NULL; } /** @@ -379,19 +365,41 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, u32 node, u32 key) { struct tipc_service *sc = tipc_service_find(net, type); + struct tipc_subscription *sub, *tmp; + struct service_range *sr = NULL; struct publication *p = NULL; + bool last; if (!sc) return NULL; spin_lock_bh(&sc->lock); - p = tipc_service_remove_publ(net, sc, lower, upper, node, key); + sr = tipc_service_find_range(sc, lower, upper); + if (!sr) + goto exit; + p = tipc_service_remove_publ(sr, node, key); + if (!p) + goto exit; + + /* Notify any waiting subscriptions */ + last = list_empty(&sr->all_publ); + list_for_each_entry_safe(sub, tmp, &sc->subscriptions, service_list) { + tipc_sub_report_overlap(sub, lower, upper, TIPC_WITHDRAWN, + p->port, node, p->scope, last); + } + + /* Remove service range item if this was its last publication */ + if (list_empty(&sr->all_publ)) { + rb_erase(&sr->tree_node, &sc->ranges); + kfree(sr); + } /* Delete service item if this no more publications and subscriptions */ if (RB_EMPTY_ROOT(&sc->ranges) && list_empty(&sc->subscriptions)) { hlist_del_init_rcu(&sc->service_list); kfree_rcu(sc, rcu); } +exit: spin_unlock_bh(&sc->lock); return p; } @@ -433,7 +441,7 @@ u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *dnode) goto not_found; spin_lock_bh(&sc->lock); - sr = tipc_service_find_range(sc, instance); + sr = tipc_service_first_range(sc, instance); if (unlikely(!sr)) goto no_match; @@ -480,7 +488,7 @@ bool tipc_nametbl_lookup(struct net *net, u32 type, u32 instance, u32 scope, spin_lock_bh(&sc->lock); - sr = tipc_service_find_range(sc, instance); + sr = tipc_service_first_range(sc, instance); if (!sr) goto no_match; @@ -665,13 +673,14 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, /** * tipc_nametbl_subscribe - add a subscription object to the name table */ -void tipc_nametbl_subscribe(struct tipc_subscription *sub) +bool tipc_nametbl_subscribe(struct tipc_subscription *sub) { struct name_table *nt = tipc_name_table(sub->net); struct tipc_net *tn = tipc_net(sub->net); struct tipc_subscr *s = &sub->evt.s; u32 type = tipc_sub_read(s, seq.type); struct tipc_service *sc; + bool res = true; spin_lock_bh(&tn->nametbl_lock); sc = tipc_service_find(sub->net, type); @@ -685,8 +694,10 @@ void tipc_nametbl_subscribe(struct tipc_subscription *sub) pr_warn("Failed to subscribe for {%u,%u,%u}\n", type, tipc_sub_read(s, seq.lower), tipc_sub_read(s, seq.upper)); + res = false; } spin_unlock_bh(&tn->nametbl_lock); + return res; } /** @@ -744,16 +755,16 @@ int tipc_nametbl_init(struct net *net) static void tipc_service_delete(struct net *net, struct tipc_service *sc) { struct service_range *sr, *tmpr; - struct publication *p, *tmpb; + struct publication *p, *tmp; spin_lock_bh(&sc->lock); rbtree_postorder_for_each_entry_safe(sr, tmpr, &sc->ranges, tree_node) { - list_for_each_entry_safe(p, tmpb, - &sr->all_publ, all_publ) { - tipc_service_remove_publ(net, sc, p->lower, p->upper, - p->node, p->key); + list_for_each_entry_safe(p, tmp, &sr->all_publ, all_publ) { + tipc_service_remove_publ(sr, p->node, p->key); kfree_rcu(p, rcu); } + rb_erase(&sr->tree_node, &sc->ranges); + kfree(sr); } hlist_del_init_rcu(&sc->service_list); spin_unlock_bh(&sc->lock); |