/* * net/tipc/name_table.c: TIPC name table code * * Copyright (c) 2000-2006, 2014-2018, Ericsson AB * Copyright (c) 2004-2008, 2010-2014, Wind River Systems * Copyright (c) 2020-2021, Red Hat Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE.
*/
/** * struct service_range - container for all bindings of a service range * @lower: service range lower bound * @upper: service range upper bound * @tree_node: member of service range RB tree * @max: largest 'upper' in this node subtree * @local_publ: list of identical publications made from this node * Used by closest_first lookup and multicast lookup algorithm * @all_publ: all publications identical to this one, whatever node and scope * Used by round-robin lookup algorithm
*/ struct service_range {
u32 lower;
u32 upper; struct rb_node tree_node;
u32 max; struct list_head local_publ; struct list_head all_publ;
};
/** * struct tipc_service - container for all published instances of a service type * @type: 32 bit 'type' value for service * @publ_cnt: increasing counter for publications in this service * @ranges: rb tree containing all service ranges for this service * @service_list: links to adjacent name ranges in hash chain * @subscriptions: list of subscriptions for this service type * @lock: spinlock controlling access to pertaining service ranges/publications * @rcu: RCU callback head used for deferred freeing
*/ struct tipc_service {
u32 type;
u32 publ_cnt; struct rb_root ranges; struct hlist_node service_list; struct list_head subscriptions;
spinlock_t lock; /* Covers service range list */ struct rcu_head rcu;
};
/** * service_range_foreach_match - iterate over tipc service rbtree for each * range match * @sr: the service range pointer as a loop cursor * @sc: the pointer to tipc service which holds the service range rbtree * @start: beginning of the search range (end >= start) for matching * @end: end of the search range (end >= start) for matching
*/ #define service_range_foreach_match(sr, sc, start, end) \ for (sr = service_range_match_first((sc)->ranges.rb_node, \
start, \
end); \
sr; \
sr = service_range_match_next(&(sr)->tree_node, \
start, \
end))
/** * service_range_match_first - find first service range matching a range * @n: the root node of service range rbtree for searching * @start: beginning of the search range (end >= start) for matching * @end: end of the search range (end >= start) for matching * * Return: the leftmost service range node in the rbtree that overlaps the * specific range if any. Otherwise, returns NULL.
*/ staticstruct service_range *service_range_match_first(struct rb_node *n,
u32 start, u32 end)
{ struct service_range *sr; struct rb_node *l, *r;
/* Non overlaps in tree at all? */ if (!n || service_range_entry(n)->max < start) return NULL;
while (n) {
l = n->rb_left; if (l && service_range_entry(l)->max >= start) { /* A leftmost overlap range node must be one in the left * subtree. If not, it has lower > end, then nodes on * the right side cannot satisfy the condition either.
*/
n = l; continue;
}
/* No one in the left subtree can match, return if this node is * an overlap i.e. leftmost.
*/
sr = service_range_entry(n); if (service_range_overlap(sr, start, end)) return sr;
/* Ok, try to lookup on the right side */
r = n->rb_right; if (sr->lower <= end &&
r && service_range_entry(r)->max >= start) {
n = r; continue;
} break;
}
return NULL;
}
/** * service_range_match_next - find next service range matching a range * @n: a node in service range rbtree from which the searching starts * @start: beginning of the search range (end >= start) for matching * @end: end of the search range (end >= start) for matching * * Return: the next service range node to the given node in the rbtree that * overlaps the specific range if any. Otherwise, returns NULL.
*/ staticstruct service_range *service_range_match_next(struct rb_node *n,
u32 start, u32 end)
{ struct service_range *sr; struct rb_node *p, *r;
while (n) {
r = n->rb_right; if (r && service_range_entry(r)->max >= start) /* A next overlap range node must be one in the right * subtree. If not, it has lower > end, then any next * successor (- an ancestor) of this node cannot * satisfy the condition either.
*/ return service_range_match_first(r, start, end);
/* No one in the right subtree can match, go up to find an * ancestor of this node which is parent of a left-hand child.
*/ while ((p = rb_parent(n)) && n == p->rb_right)
n = p; if (!p) break;
/* Return if this ancestor is an overlap */
sr = service_range_entry(p); if (service_range_overlap(sr, start, end)) return sr;
/* Ok, try to lookup more from this ancestor */ if (sr->lower <= end) {
n = p; continue;
} break;
}
/** * tipc_publ_create - create a publication structure * @ua: the service range the user is binding to * @sk: the address of the socket that is bound * @key: publication key
*/ staticstruct publication *tipc_publ_create(struct tipc_uaddr *ua, struct tipc_socket_addr *sk,
u32 key)
{ struct publication *p = kzalloc(sizeof(*p), GFP_ATOMIC);
/** * tipc_service_create - create a service structure for the specified 'type' * @net: network namespace * @ua: address representing the service to be bound * * Allocates a single range structure and sets it to all 0's.
*/ staticstruct tipc_service *tipc_service_create(struct net *net, struct tipc_uaddr *ua)
{ struct name_table *nt = tipc_name_table(net); struct tipc_service *service; struct hlist_head *hd;
service = kzalloc(sizeof(*service), GFP_ATOMIC); if (!service) {
pr_warn("Service creation failed, no memory\n"); return NULL;
}
/** * tipc_service_subscribe - attach a subscription, and optionally * issue the prescribed number of events if there is any service * range overlapping with the requested range * @service: the tipc_service to attach the @sub to * @sub: the subscription to attach
*/ staticvoid tipc_service_subscribe(struct tipc_service *service, struct tipc_subscription *sub)
{ struct publication *p, *first, *tmp; struct list_head publ_list; struct service_range *sr;
u32 filter, lower, upper;
sc = tipc_service_find(net, ua); if (!sc) gotoexit;
spin_lock_bh(&sc->lock);
sr = tipc_service_find_range(sc, ua); if (!sr) goto unlock;
p = tipc_service_remove_publ(sr, sk, key); if (!p) goto unlock;
/* 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, p, TIPC_WITHDRAWN, last);
}
/* Remove service range item if this was its last publication */ if (list_empty(&sr->all_publ)) {
rb_erase_augmented(&sr->tree_node, &sc->ranges, &sr_callbacks);
kfree(sr);
}
/* Delete service item if 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);
}
unlock:
spin_unlock_bh(&sc->lock); exit: if (!p) {
pr_err("Failed to remove unknown binding: %u,%u,%u/%u:%u/%u\n",
ua->sr.type, ua->sr.lower, ua->sr.upper,
sk->node, sk->ref, key);
} return p;
}
/** * tipc_nametbl_lookup_anycast - perform service instance to socket translation * @net: network namespace * @ua: service address to look up * @sk: address to socket we want to find * * On entry, a non-zero 'sk->node' indicates the node where we want lookup to be * performed, which may not be this one. * * On exit: * * - If lookup is deferred to another node, leave 'sk->node' unchanged and * return 'true'. * - If lookup is successful, set the 'sk->node' and 'sk->ref' (== portid) which * represent the bound socket and return 'true'. * - If lookup fails, return 'false' * * Note that for legacy users (node configured with Z.C.N address format) the * 'closest-first' lookup algorithm must be maintained, i.e., if sk.node is 0 * we must look in the local binding list first
*/ bool tipc_nametbl_lookup_anycast(struct net *net, struct tipc_uaddr *ua, struct tipc_socket_addr *sk)
{ struct tipc_net *tn = tipc_net(net); bool legacy = tn->legacy_addr_format;
u32 self = tipc_own_addr(net);
u32 inst = ua->sa.instance; struct service_range *r; struct tipc_service *sc; struct publication *p; struct list_head *l; bool res = false;
if (!tipc_in_scope(legacy, sk->node, self)) returntrue;
rcu_read_lock();
sc = tipc_service_find(net, ua); if (unlikely(!sc)) gotoexit;
spin_lock_bh(&sc->lock);
service_range_foreach_match(r, sc, inst, inst) { /* Select lookup algo: local, closest-first or round-robin */ if (sk->node == self) {
l = &r->local_publ; if (list_empty(l)) continue;
p = list_first_entry(l, struct publication, local_publ);
list_move_tail(&p->local_publ, &r->local_publ);
} elseif (legacy && !sk->node && !list_empty(&r->local_publ)) {
l = &r->local_publ;
p = list_first_entry(l, struct publication, local_publ);
list_move_tail(&p->local_publ, &r->local_publ);
} else {
l = &r->all_publ;
p = list_first_entry(l, struct publication, all_publ);
list_move_tail(&p->all_publ, &r->all_publ);
}
*sk = p->sk;
res = true; /* Todo: as for legacy, pick the first matching range only, a * "true" round-robin will be performed as needed.
*/ break;
}
spin_unlock_bh(&sc->lock);
exit:
rcu_read_unlock(); return res;
}
/* tipc_nametbl_lookup_group(): lookup destinaton(s) in a communication group * Returns a list of one (== group anycast) or more (== group multicast) * destination socket/node pairs matching the given address. * The requester may or may not want to exclude himself from the list.
*/ bool tipc_nametbl_lookup_group(struct net *net, struct tipc_uaddr *ua, struct list_head *dsts, int *dstcnt,
u32 exclude, bool mcast)
{
u32 self = tipc_own_addr(net);
u32 inst = ua->sa.instance; struct service_range *sr; struct tipc_service *sc; struct publication *p;
/* Todo: a full search i.e. service_range_foreach_match() instead? */
sr = service_range_match_first(sc->ranges.rb_node, inst, inst); if (!sr) goto no_match;
/* tipc_nametbl_lookup_mcast_sockets(): look up node local destinaton sockets * matching the given address * Used on nodes which have received a multicast/broadcast message * Returns a list of local sockets
*/ void tipc_nametbl_lookup_mcast_sockets(struct net *net, struct tipc_uaddr *ua, struct list_head *dports)
{ struct service_range *sr; struct tipc_service *sc; struct publication *p;
u8 scope = ua->scope;
rcu_read_lock();
sc = tipc_service_find(net, ua); if (!sc) gotoexit;
/* tipc_nametbl_lookup_mcast_nodes(): look up all destination nodes matching * the given address. Used in sending node. * Used on nodes which are sending out a multicast/broadcast message * Returns a list of nodes, including own node if applicable
*/ void tipc_nametbl_lookup_mcast_nodes(struct net *net, struct tipc_uaddr *ua, struct tipc_nlist *nodes)
{ struct service_range *sr; struct tipc_service *sc; struct publication *p;
rcu_read_lock();
sc = tipc_service_find(net, ua); if (!sc) gotoexit;
/* Delete service item if 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);
}
spin_unlock_bh(&sc->lock); exit:
spin_unlock_bh(&tn->nametbl_lock);
}
int tipc_nametbl_init(struct net *net)
{ struct tipc_net *tn = tipc_net(net); struct name_table *nt; int i;
nt = kzalloc(sizeof(*nt), GFP_KERNEL); if (!nt) return -ENOMEM;
for (i = 0; i < TIPC_NAMETBL_SIZE; i++)
INIT_HLIST_HEAD(&nt->services[i]);
/** * tipc_service_delete - purge all publications for a service and delete it * @net: the associated network namespace * @sc: tipc_service to delete
*/ staticvoid tipc_service_delete(struct net *net, struct tipc_service *sc)
{ struct service_range *sr, *tmpr; struct publication *p, *tmp;
/* Verify name table is empty and purge any lingering * publications, then release the name table
*/
spin_lock_bh(&tn->nametbl_lock); for (i = 0; i < TIPC_NAMETBL_SIZE; i++) { if (hlist_empty(&nt->services[i])) continue;
service_head = &nt->services[i];
hlist_for_each_entry_rcu(service, service_head, service_list) {
tipc_service_delete(net, service);
}
}
spin_unlock_bh(&tn->nametbl_lock);
rcu_read_lock();
err = tipc_nl_service_list(net, &msg, &last_type,
&last_lower, &last_key); if (!err) {
done = 1;
} elseif (err != -EMSGSIZE) { /* We never set seq or call nl_dump_check_consistent() this * means that setting prev_seq here will cause the consistence * check to fail in the netlink callback handler. Resulting in * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if * we got an error.
*/
cb->prev_seq = 1;
}
rcu_read_unlock();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.