162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * net/tipc/name_table.c: TIPC name table code 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2000-2006, 2014-2018, Ericsson AB 562306a36Sopenharmony_ci * Copyright (c) 2004-2008, 2010-2014, Wind River Systems 662306a36Sopenharmony_ci * Copyright (c) 2020-2021, Red Hat Inc 762306a36Sopenharmony_ci * All rights reserved. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1062306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1362306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1462306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1562306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1662306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1762306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its 1862306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived from 1962306a36Sopenharmony_ci * this software without specific prior written permission. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 2262306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free 2362306a36Sopenharmony_ci * Software Foundation. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2662306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2762306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2862306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 2962306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3062306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3162306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3262306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3362306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3462306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3562306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <net/sock.h> 3962306a36Sopenharmony_ci#include <linux/list_sort.h> 4062306a36Sopenharmony_ci#include <linux/rbtree_augmented.h> 4162306a36Sopenharmony_ci#include "core.h" 4262306a36Sopenharmony_ci#include "netlink.h" 4362306a36Sopenharmony_ci#include "name_table.h" 4462306a36Sopenharmony_ci#include "name_distr.h" 4562306a36Sopenharmony_ci#include "subscr.h" 4662306a36Sopenharmony_ci#include "bcast.h" 4762306a36Sopenharmony_ci#include "addr.h" 4862306a36Sopenharmony_ci#include "node.h" 4962306a36Sopenharmony_ci#include "group.h" 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/** 5262306a36Sopenharmony_ci * struct service_range - container for all bindings of a service range 5362306a36Sopenharmony_ci * @lower: service range lower bound 5462306a36Sopenharmony_ci * @upper: service range upper bound 5562306a36Sopenharmony_ci * @tree_node: member of service range RB tree 5662306a36Sopenharmony_ci * @max: largest 'upper' in this node subtree 5762306a36Sopenharmony_ci * @local_publ: list of identical publications made from this node 5862306a36Sopenharmony_ci * Used by closest_first lookup and multicast lookup algorithm 5962306a36Sopenharmony_ci * @all_publ: all publications identical to this one, whatever node and scope 6062306a36Sopenharmony_ci * Used by round-robin lookup algorithm 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistruct service_range { 6362306a36Sopenharmony_ci u32 lower; 6462306a36Sopenharmony_ci u32 upper; 6562306a36Sopenharmony_ci struct rb_node tree_node; 6662306a36Sopenharmony_ci u32 max; 6762306a36Sopenharmony_ci struct list_head local_publ; 6862306a36Sopenharmony_ci struct list_head all_publ; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/** 7262306a36Sopenharmony_ci * struct tipc_service - container for all published instances of a service type 7362306a36Sopenharmony_ci * @type: 32 bit 'type' value for service 7462306a36Sopenharmony_ci * @publ_cnt: increasing counter for publications in this service 7562306a36Sopenharmony_ci * @ranges: rb tree containing all service ranges for this service 7662306a36Sopenharmony_ci * @service_list: links to adjacent name ranges in hash chain 7762306a36Sopenharmony_ci * @subscriptions: list of subscriptions for this service type 7862306a36Sopenharmony_ci * @lock: spinlock controlling access to pertaining service ranges/publications 7962306a36Sopenharmony_ci * @rcu: RCU callback head used for deferred freeing 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistruct tipc_service { 8262306a36Sopenharmony_ci u32 type; 8362306a36Sopenharmony_ci u32 publ_cnt; 8462306a36Sopenharmony_ci struct rb_root ranges; 8562306a36Sopenharmony_ci struct hlist_node service_list; 8662306a36Sopenharmony_ci struct list_head subscriptions; 8762306a36Sopenharmony_ci spinlock_t lock; /* Covers service range list */ 8862306a36Sopenharmony_ci struct rcu_head rcu; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define service_range_upper(sr) ((sr)->upper) 9262306a36Sopenharmony_ciRB_DECLARE_CALLBACKS_MAX(static, sr_callbacks, 9362306a36Sopenharmony_ci struct service_range, tree_node, u32, max, 9462306a36Sopenharmony_ci service_range_upper) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define service_range_entry(rbtree_node) \ 9762306a36Sopenharmony_ci (container_of(rbtree_node, struct service_range, tree_node)) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define service_range_overlap(sr, start, end) \ 10062306a36Sopenharmony_ci ((sr)->lower <= (end) && (sr)->upper >= (start)) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/** 10362306a36Sopenharmony_ci * service_range_foreach_match - iterate over tipc service rbtree for each 10462306a36Sopenharmony_ci * range match 10562306a36Sopenharmony_ci * @sr: the service range pointer as a loop cursor 10662306a36Sopenharmony_ci * @sc: the pointer to tipc service which holds the service range rbtree 10762306a36Sopenharmony_ci * @start: beginning of the search range (end >= start) for matching 10862306a36Sopenharmony_ci * @end: end of the search range (end >= start) for matching 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci#define service_range_foreach_match(sr, sc, start, end) \ 11162306a36Sopenharmony_ci for (sr = service_range_match_first((sc)->ranges.rb_node, \ 11262306a36Sopenharmony_ci start, \ 11362306a36Sopenharmony_ci end); \ 11462306a36Sopenharmony_ci sr; \ 11562306a36Sopenharmony_ci sr = service_range_match_next(&(sr)->tree_node, \ 11662306a36Sopenharmony_ci start, \ 11762306a36Sopenharmony_ci end)) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * service_range_match_first - find first service range matching a range 12162306a36Sopenharmony_ci * @n: the root node of service range rbtree for searching 12262306a36Sopenharmony_ci * @start: beginning of the search range (end >= start) for matching 12362306a36Sopenharmony_ci * @end: end of the search range (end >= start) for matching 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Return: the leftmost service range node in the rbtree that overlaps the 12662306a36Sopenharmony_ci * specific range if any. Otherwise, returns NULL. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic struct service_range *service_range_match_first(struct rb_node *n, 12962306a36Sopenharmony_ci u32 start, u32 end) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct service_range *sr; 13262306a36Sopenharmony_ci struct rb_node *l, *r; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Non overlaps in tree at all? */ 13562306a36Sopenharmony_ci if (!n || service_range_entry(n)->max < start) 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci while (n) { 13962306a36Sopenharmony_ci l = n->rb_left; 14062306a36Sopenharmony_ci if (l && service_range_entry(l)->max >= start) { 14162306a36Sopenharmony_ci /* A leftmost overlap range node must be one in the left 14262306a36Sopenharmony_ci * subtree. If not, it has lower > end, then nodes on 14362306a36Sopenharmony_ci * the right side cannot satisfy the condition either. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci n = l; 14662306a36Sopenharmony_ci continue; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* No one in the left subtree can match, return if this node is 15062306a36Sopenharmony_ci * an overlap i.e. leftmost. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci sr = service_range_entry(n); 15362306a36Sopenharmony_ci if (service_range_overlap(sr, start, end)) 15462306a36Sopenharmony_ci return sr; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Ok, try to lookup on the right side */ 15762306a36Sopenharmony_ci r = n->rb_right; 15862306a36Sopenharmony_ci if (sr->lower <= end && 15962306a36Sopenharmony_ci r && service_range_entry(r)->max >= start) { 16062306a36Sopenharmony_ci n = r; 16162306a36Sopenharmony_ci continue; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/** 17062306a36Sopenharmony_ci * service_range_match_next - find next service range matching a range 17162306a36Sopenharmony_ci * @n: a node in service range rbtree from which the searching starts 17262306a36Sopenharmony_ci * @start: beginning of the search range (end >= start) for matching 17362306a36Sopenharmony_ci * @end: end of the search range (end >= start) for matching 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * Return: the next service range node to the given node in the rbtree that 17662306a36Sopenharmony_ci * overlaps the specific range if any. Otherwise, returns NULL. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_cistatic struct service_range *service_range_match_next(struct rb_node *n, 17962306a36Sopenharmony_ci u32 start, u32 end) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct service_range *sr; 18262306a36Sopenharmony_ci struct rb_node *p, *r; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci while (n) { 18562306a36Sopenharmony_ci r = n->rb_right; 18662306a36Sopenharmony_ci if (r && service_range_entry(r)->max >= start) 18762306a36Sopenharmony_ci /* A next overlap range node must be one in the right 18862306a36Sopenharmony_ci * subtree. If not, it has lower > end, then any next 18962306a36Sopenharmony_ci * successor (- an ancestor) of this node cannot 19062306a36Sopenharmony_ci * satisfy the condition either. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci return service_range_match_first(r, start, end); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* No one in the right subtree can match, go up to find an 19562306a36Sopenharmony_ci * ancestor of this node which is parent of a left-hand child. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci while ((p = rb_parent(n)) && n == p->rb_right) 19862306a36Sopenharmony_ci n = p; 19962306a36Sopenharmony_ci if (!p) 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Return if this ancestor is an overlap */ 20362306a36Sopenharmony_ci sr = service_range_entry(p); 20462306a36Sopenharmony_ci if (service_range_overlap(sr, start, end)) 20562306a36Sopenharmony_ci return sr; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Ok, try to lookup more from this ancestor */ 20862306a36Sopenharmony_ci if (sr->lower <= end) { 20962306a36Sopenharmony_ci n = p; 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return NULL; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int hash(int x) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci return x & (TIPC_NAMETBL_SIZE - 1); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/** 22462306a36Sopenharmony_ci * tipc_publ_create - create a publication structure 22562306a36Sopenharmony_ci * @ua: the service range the user is binding to 22662306a36Sopenharmony_ci * @sk: the address of the socket that is bound 22762306a36Sopenharmony_ci * @key: publication key 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic struct publication *tipc_publ_create(struct tipc_uaddr *ua, 23062306a36Sopenharmony_ci struct tipc_socket_addr *sk, 23162306a36Sopenharmony_ci u32 key) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct publication *p = kzalloc(sizeof(*p), GFP_ATOMIC); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!p) 23662306a36Sopenharmony_ci return NULL; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci p->sr = ua->sr; 23962306a36Sopenharmony_ci p->sk = *sk; 24062306a36Sopenharmony_ci p->scope = ua->scope; 24162306a36Sopenharmony_ci p->key = key; 24262306a36Sopenharmony_ci INIT_LIST_HEAD(&p->binding_sock); 24362306a36Sopenharmony_ci INIT_LIST_HEAD(&p->binding_node); 24462306a36Sopenharmony_ci INIT_LIST_HEAD(&p->local_publ); 24562306a36Sopenharmony_ci INIT_LIST_HEAD(&p->all_publ); 24662306a36Sopenharmony_ci INIT_LIST_HEAD(&p->list); 24762306a36Sopenharmony_ci return p; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/** 25162306a36Sopenharmony_ci * tipc_service_create - create a service structure for the specified 'type' 25262306a36Sopenharmony_ci * @net: network namespace 25362306a36Sopenharmony_ci * @ua: address representing the service to be bound 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * Allocates a single range structure and sets it to all 0's. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_cistatic struct tipc_service *tipc_service_create(struct net *net, 25862306a36Sopenharmony_ci struct tipc_uaddr *ua) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct name_table *nt = tipc_name_table(net); 26162306a36Sopenharmony_ci struct tipc_service *service; 26262306a36Sopenharmony_ci struct hlist_head *hd; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci service = kzalloc(sizeof(*service), GFP_ATOMIC); 26562306a36Sopenharmony_ci if (!service) { 26662306a36Sopenharmony_ci pr_warn("Service creation failed, no memory\n"); 26762306a36Sopenharmony_ci return NULL; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci spin_lock_init(&service->lock); 27162306a36Sopenharmony_ci service->type = ua->sr.type; 27262306a36Sopenharmony_ci service->ranges = RB_ROOT; 27362306a36Sopenharmony_ci INIT_HLIST_NODE(&service->service_list); 27462306a36Sopenharmony_ci INIT_LIST_HEAD(&service->subscriptions); 27562306a36Sopenharmony_ci hd = &nt->services[hash(ua->sr.type)]; 27662306a36Sopenharmony_ci hlist_add_head_rcu(&service->service_list, hd); 27762306a36Sopenharmony_ci return service; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* tipc_service_find_range - find service range matching publication parameters 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_cistatic struct service_range *tipc_service_find_range(struct tipc_service *sc, 28362306a36Sopenharmony_ci struct tipc_uaddr *ua) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct service_range *sr; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci service_range_foreach_match(sr, sc, ua->sr.lower, ua->sr.upper) { 28862306a36Sopenharmony_ci /* Look for exact match */ 28962306a36Sopenharmony_ci if (sr->lower == ua->sr.lower && sr->upper == ua->sr.upper) 29062306a36Sopenharmony_ci return sr; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return NULL; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic struct service_range *tipc_service_create_range(struct tipc_service *sc, 29762306a36Sopenharmony_ci struct publication *p) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct rb_node **n, *parent = NULL; 30062306a36Sopenharmony_ci struct service_range *sr; 30162306a36Sopenharmony_ci u32 lower = p->sr.lower; 30262306a36Sopenharmony_ci u32 upper = p->sr.upper; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci n = &sc->ranges.rb_node; 30562306a36Sopenharmony_ci while (*n) { 30662306a36Sopenharmony_ci parent = *n; 30762306a36Sopenharmony_ci sr = service_range_entry(parent); 30862306a36Sopenharmony_ci if (lower == sr->lower && upper == sr->upper) 30962306a36Sopenharmony_ci return sr; 31062306a36Sopenharmony_ci if (sr->max < upper) 31162306a36Sopenharmony_ci sr->max = upper; 31262306a36Sopenharmony_ci if (lower <= sr->lower) 31362306a36Sopenharmony_ci n = &parent->rb_left; 31462306a36Sopenharmony_ci else 31562306a36Sopenharmony_ci n = &parent->rb_right; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci sr = kzalloc(sizeof(*sr), GFP_ATOMIC); 31862306a36Sopenharmony_ci if (!sr) 31962306a36Sopenharmony_ci return NULL; 32062306a36Sopenharmony_ci sr->lower = lower; 32162306a36Sopenharmony_ci sr->upper = upper; 32262306a36Sopenharmony_ci sr->max = upper; 32362306a36Sopenharmony_ci INIT_LIST_HEAD(&sr->local_publ); 32462306a36Sopenharmony_ci INIT_LIST_HEAD(&sr->all_publ); 32562306a36Sopenharmony_ci rb_link_node(&sr->tree_node, parent, n); 32662306a36Sopenharmony_ci rb_insert_augmented(&sr->tree_node, &sc->ranges, &sr_callbacks); 32762306a36Sopenharmony_ci return sr; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic bool tipc_service_insert_publ(struct net *net, 33162306a36Sopenharmony_ci struct tipc_service *sc, 33262306a36Sopenharmony_ci struct publication *p) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct tipc_subscription *sub, *tmp; 33562306a36Sopenharmony_ci struct service_range *sr; 33662306a36Sopenharmony_ci struct publication *_p; 33762306a36Sopenharmony_ci u32 node = p->sk.node; 33862306a36Sopenharmony_ci bool first = false; 33962306a36Sopenharmony_ci bool res = false; 34062306a36Sopenharmony_ci u32 key = p->key; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 34362306a36Sopenharmony_ci sr = tipc_service_create_range(sc, p); 34462306a36Sopenharmony_ci if (!sr) 34562306a36Sopenharmony_ci goto exit; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci first = list_empty(&sr->all_publ); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Return if the publication already exists */ 35062306a36Sopenharmony_ci list_for_each_entry(_p, &sr->all_publ, all_publ) { 35162306a36Sopenharmony_ci if (_p->key == key && (!_p->sk.node || _p->sk.node == node)) { 35262306a36Sopenharmony_ci pr_debug("Failed to bind duplicate %u,%u,%u/%u:%u/%u\n", 35362306a36Sopenharmony_ci p->sr.type, p->sr.lower, p->sr.upper, 35462306a36Sopenharmony_ci node, p->sk.ref, key); 35562306a36Sopenharmony_ci goto exit; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (in_own_node(net, p->sk.node)) 36062306a36Sopenharmony_ci list_add(&p->local_publ, &sr->local_publ); 36162306a36Sopenharmony_ci list_add(&p->all_publ, &sr->all_publ); 36262306a36Sopenharmony_ci p->id = sc->publ_cnt++; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Any subscriptions waiting for notification? */ 36562306a36Sopenharmony_ci list_for_each_entry_safe(sub, tmp, &sc->subscriptions, service_list) { 36662306a36Sopenharmony_ci tipc_sub_report_overlap(sub, p, TIPC_PUBLISHED, first); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci res = true; 36962306a36Sopenharmony_ciexit: 37062306a36Sopenharmony_ci if (!res) 37162306a36Sopenharmony_ci pr_warn("Failed to bind to %u,%u,%u\n", 37262306a36Sopenharmony_ci p->sr.type, p->sr.lower, p->sr.upper); 37362306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 37462306a36Sopenharmony_ci return res; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/** 37862306a36Sopenharmony_ci * tipc_service_remove_publ - remove a publication from a service 37962306a36Sopenharmony_ci * @r: service_range to remove publication from 38062306a36Sopenharmony_ci * @sk: address publishing socket 38162306a36Sopenharmony_ci * @key: target publication key 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_cistatic struct publication *tipc_service_remove_publ(struct service_range *r, 38462306a36Sopenharmony_ci struct tipc_socket_addr *sk, 38562306a36Sopenharmony_ci u32 key) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct publication *p; 38862306a36Sopenharmony_ci u32 node = sk->node; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci list_for_each_entry(p, &r->all_publ, all_publ) { 39162306a36Sopenharmony_ci if (p->key != key || (node && node != p->sk.node)) 39262306a36Sopenharmony_ci continue; 39362306a36Sopenharmony_ci list_del(&p->all_publ); 39462306a36Sopenharmony_ci list_del(&p->local_publ); 39562306a36Sopenharmony_ci return p; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci return NULL; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * Code reused: time_after32() for the same purpose 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci#define publication_after(pa, pb) time_after32((pa)->id, (pb)->id) 40462306a36Sopenharmony_cistatic int tipc_publ_sort(void *priv, const struct list_head *a, 40562306a36Sopenharmony_ci const struct list_head *b) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct publication *pa, *pb; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci pa = container_of(a, struct publication, list); 41062306a36Sopenharmony_ci pb = container_of(b, struct publication, list); 41162306a36Sopenharmony_ci return publication_after(pa, pb); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/** 41562306a36Sopenharmony_ci * tipc_service_subscribe - attach a subscription, and optionally 41662306a36Sopenharmony_ci * issue the prescribed number of events if there is any service 41762306a36Sopenharmony_ci * range overlapping with the requested range 41862306a36Sopenharmony_ci * @service: the tipc_service to attach the @sub to 41962306a36Sopenharmony_ci * @sub: the subscription to attach 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic void tipc_service_subscribe(struct tipc_service *service, 42262306a36Sopenharmony_ci struct tipc_subscription *sub) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct publication *p, *first, *tmp; 42562306a36Sopenharmony_ci struct list_head publ_list; 42662306a36Sopenharmony_ci struct service_range *sr; 42762306a36Sopenharmony_ci u32 filter, lower, upper; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci filter = sub->s.filter; 43062306a36Sopenharmony_ci lower = sub->s.seq.lower; 43162306a36Sopenharmony_ci upper = sub->s.seq.upper; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci tipc_sub_get(sub); 43462306a36Sopenharmony_ci list_add(&sub->service_list, &service->subscriptions); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (filter & TIPC_SUB_NO_STATUS) 43762306a36Sopenharmony_ci return; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci INIT_LIST_HEAD(&publ_list); 44062306a36Sopenharmony_ci service_range_foreach_match(sr, service, lower, upper) { 44162306a36Sopenharmony_ci first = NULL; 44262306a36Sopenharmony_ci list_for_each_entry(p, &sr->all_publ, all_publ) { 44362306a36Sopenharmony_ci if (filter & TIPC_SUB_PORTS) 44462306a36Sopenharmony_ci list_add_tail(&p->list, &publ_list); 44562306a36Sopenharmony_ci else if (!first || publication_after(first, p)) 44662306a36Sopenharmony_ci /* Pick this range's *first* publication */ 44762306a36Sopenharmony_ci first = p; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci if (first) 45062306a36Sopenharmony_ci list_add_tail(&first->list, &publ_list); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Sort the publications before reporting */ 45462306a36Sopenharmony_ci list_sort(NULL, &publ_list, tipc_publ_sort); 45562306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, &publ_list, list) { 45662306a36Sopenharmony_ci tipc_sub_report_overlap(sub, p, TIPC_PUBLISHED, true); 45762306a36Sopenharmony_ci list_del_init(&p->list); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic struct tipc_service *tipc_service_find(struct net *net, 46262306a36Sopenharmony_ci struct tipc_uaddr *ua) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct name_table *nt = tipc_name_table(net); 46562306a36Sopenharmony_ci struct hlist_head *service_head; 46662306a36Sopenharmony_ci struct tipc_service *service; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci service_head = &nt->services[hash(ua->sr.type)]; 46962306a36Sopenharmony_ci hlist_for_each_entry_rcu(service, service_head, service_list) { 47062306a36Sopenharmony_ci if (service->type == ua->sr.type) 47162306a36Sopenharmony_ci return service; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci return NULL; 47462306a36Sopenharmony_ci}; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistruct publication *tipc_nametbl_insert_publ(struct net *net, 47762306a36Sopenharmony_ci struct tipc_uaddr *ua, 47862306a36Sopenharmony_ci struct tipc_socket_addr *sk, 47962306a36Sopenharmony_ci u32 key) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct tipc_service *sc; 48262306a36Sopenharmony_ci struct publication *p; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci p = tipc_publ_create(ua, sk, key); 48562306a36Sopenharmony_ci if (!p) 48662306a36Sopenharmony_ci return NULL; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci sc = tipc_service_find(net, ua); 48962306a36Sopenharmony_ci if (!sc) 49062306a36Sopenharmony_ci sc = tipc_service_create(net, ua); 49162306a36Sopenharmony_ci if (sc && tipc_service_insert_publ(net, sc, p)) 49262306a36Sopenharmony_ci return p; 49362306a36Sopenharmony_ci kfree(p); 49462306a36Sopenharmony_ci return NULL; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistruct publication *tipc_nametbl_remove_publ(struct net *net, 49862306a36Sopenharmony_ci struct tipc_uaddr *ua, 49962306a36Sopenharmony_ci struct tipc_socket_addr *sk, 50062306a36Sopenharmony_ci u32 key) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct tipc_subscription *sub, *tmp; 50362306a36Sopenharmony_ci struct publication *p = NULL; 50462306a36Sopenharmony_ci struct service_range *sr; 50562306a36Sopenharmony_ci struct tipc_service *sc; 50662306a36Sopenharmony_ci bool last; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci sc = tipc_service_find(net, ua); 50962306a36Sopenharmony_ci if (!sc) 51062306a36Sopenharmony_ci goto exit; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 51362306a36Sopenharmony_ci sr = tipc_service_find_range(sc, ua); 51462306a36Sopenharmony_ci if (!sr) 51562306a36Sopenharmony_ci goto unlock; 51662306a36Sopenharmony_ci p = tipc_service_remove_publ(sr, sk, key); 51762306a36Sopenharmony_ci if (!p) 51862306a36Sopenharmony_ci goto unlock; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Notify any waiting subscriptions */ 52162306a36Sopenharmony_ci last = list_empty(&sr->all_publ); 52262306a36Sopenharmony_ci list_for_each_entry_safe(sub, tmp, &sc->subscriptions, service_list) { 52362306a36Sopenharmony_ci tipc_sub_report_overlap(sub, p, TIPC_WITHDRAWN, last); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Remove service range item if this was its last publication */ 52762306a36Sopenharmony_ci if (list_empty(&sr->all_publ)) { 52862306a36Sopenharmony_ci rb_erase_augmented(&sr->tree_node, &sc->ranges, &sr_callbacks); 52962306a36Sopenharmony_ci kfree(sr); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Delete service item if no more publications and subscriptions */ 53362306a36Sopenharmony_ci if (RB_EMPTY_ROOT(&sc->ranges) && list_empty(&sc->subscriptions)) { 53462306a36Sopenharmony_ci hlist_del_init_rcu(&sc->service_list); 53562306a36Sopenharmony_ci kfree_rcu(sc, rcu); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ciunlock: 53862306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 53962306a36Sopenharmony_ciexit: 54062306a36Sopenharmony_ci if (!p) { 54162306a36Sopenharmony_ci pr_err("Failed to remove unknown binding: %u,%u,%u/%u:%u/%u\n", 54262306a36Sopenharmony_ci ua->sr.type, ua->sr.lower, ua->sr.upper, 54362306a36Sopenharmony_ci sk->node, sk->ref, key); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci return p; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/** 54962306a36Sopenharmony_ci * tipc_nametbl_lookup_anycast - perform service instance to socket translation 55062306a36Sopenharmony_ci * @net: network namespace 55162306a36Sopenharmony_ci * @ua: service address to look up 55262306a36Sopenharmony_ci * @sk: address to socket we want to find 55362306a36Sopenharmony_ci * 55462306a36Sopenharmony_ci * On entry, a non-zero 'sk->node' indicates the node where we want lookup to be 55562306a36Sopenharmony_ci * performed, which may not be this one. 55662306a36Sopenharmony_ci * 55762306a36Sopenharmony_ci * On exit: 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * - If lookup is deferred to another node, leave 'sk->node' unchanged and 56062306a36Sopenharmony_ci * return 'true'. 56162306a36Sopenharmony_ci * - If lookup is successful, set the 'sk->node' and 'sk->ref' (== portid) which 56262306a36Sopenharmony_ci * represent the bound socket and return 'true'. 56362306a36Sopenharmony_ci * - If lookup fails, return 'false' 56462306a36Sopenharmony_ci * 56562306a36Sopenharmony_ci * Note that for legacy users (node configured with Z.C.N address format) the 56662306a36Sopenharmony_ci * 'closest-first' lookup algorithm must be maintained, i.e., if sk.node is 0 56762306a36Sopenharmony_ci * we must look in the local binding list first 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_cibool tipc_nametbl_lookup_anycast(struct net *net, 57062306a36Sopenharmony_ci struct tipc_uaddr *ua, 57162306a36Sopenharmony_ci struct tipc_socket_addr *sk) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 57462306a36Sopenharmony_ci bool legacy = tn->legacy_addr_format; 57562306a36Sopenharmony_ci u32 self = tipc_own_addr(net); 57662306a36Sopenharmony_ci u32 inst = ua->sa.instance; 57762306a36Sopenharmony_ci struct service_range *r; 57862306a36Sopenharmony_ci struct tipc_service *sc; 57962306a36Sopenharmony_ci struct publication *p; 58062306a36Sopenharmony_ci struct list_head *l; 58162306a36Sopenharmony_ci bool res = false; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (!tipc_in_scope(legacy, sk->node, self)) 58462306a36Sopenharmony_ci return true; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci rcu_read_lock(); 58762306a36Sopenharmony_ci sc = tipc_service_find(net, ua); 58862306a36Sopenharmony_ci if (unlikely(!sc)) 58962306a36Sopenharmony_ci goto exit; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 59262306a36Sopenharmony_ci service_range_foreach_match(r, sc, inst, inst) { 59362306a36Sopenharmony_ci /* Select lookup algo: local, closest-first or round-robin */ 59462306a36Sopenharmony_ci if (sk->node == self) { 59562306a36Sopenharmony_ci l = &r->local_publ; 59662306a36Sopenharmony_ci if (list_empty(l)) 59762306a36Sopenharmony_ci continue; 59862306a36Sopenharmony_ci p = list_first_entry(l, struct publication, local_publ); 59962306a36Sopenharmony_ci list_move_tail(&p->local_publ, &r->local_publ); 60062306a36Sopenharmony_ci } else if (legacy && !sk->node && !list_empty(&r->local_publ)) { 60162306a36Sopenharmony_ci l = &r->local_publ; 60262306a36Sopenharmony_ci p = list_first_entry(l, struct publication, local_publ); 60362306a36Sopenharmony_ci list_move_tail(&p->local_publ, &r->local_publ); 60462306a36Sopenharmony_ci } else { 60562306a36Sopenharmony_ci l = &r->all_publ; 60662306a36Sopenharmony_ci p = list_first_entry(l, struct publication, all_publ); 60762306a36Sopenharmony_ci list_move_tail(&p->all_publ, &r->all_publ); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci *sk = p->sk; 61062306a36Sopenharmony_ci res = true; 61162306a36Sopenharmony_ci /* Todo: as for legacy, pick the first matching range only, a 61262306a36Sopenharmony_ci * "true" round-robin will be performed as needed. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ciexit: 61962306a36Sopenharmony_ci rcu_read_unlock(); 62062306a36Sopenharmony_ci return res; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* tipc_nametbl_lookup_group(): lookup destinaton(s) in a communication group 62462306a36Sopenharmony_ci * Returns a list of one (== group anycast) or more (== group multicast) 62562306a36Sopenharmony_ci * destination socket/node pairs matching the given address. 62662306a36Sopenharmony_ci * The requester may or may not want to exclude himself from the list. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_cibool tipc_nametbl_lookup_group(struct net *net, struct tipc_uaddr *ua, 62962306a36Sopenharmony_ci struct list_head *dsts, int *dstcnt, 63062306a36Sopenharmony_ci u32 exclude, bool mcast) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci u32 self = tipc_own_addr(net); 63362306a36Sopenharmony_ci u32 inst = ua->sa.instance; 63462306a36Sopenharmony_ci struct service_range *sr; 63562306a36Sopenharmony_ci struct tipc_service *sc; 63662306a36Sopenharmony_ci struct publication *p; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci *dstcnt = 0; 63962306a36Sopenharmony_ci rcu_read_lock(); 64062306a36Sopenharmony_ci sc = tipc_service_find(net, ua); 64162306a36Sopenharmony_ci if (unlikely(!sc)) 64262306a36Sopenharmony_ci goto exit; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Todo: a full search i.e. service_range_foreach_match() instead? */ 64762306a36Sopenharmony_ci sr = service_range_match_first(sc->ranges.rb_node, inst, inst); 64862306a36Sopenharmony_ci if (!sr) 64962306a36Sopenharmony_ci goto no_match; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci list_for_each_entry(p, &sr->all_publ, all_publ) { 65262306a36Sopenharmony_ci if (p->scope != ua->scope) 65362306a36Sopenharmony_ci continue; 65462306a36Sopenharmony_ci if (p->sk.ref == exclude && p->sk.node == self) 65562306a36Sopenharmony_ci continue; 65662306a36Sopenharmony_ci tipc_dest_push(dsts, p->sk.node, p->sk.ref); 65762306a36Sopenharmony_ci (*dstcnt)++; 65862306a36Sopenharmony_ci if (mcast) 65962306a36Sopenharmony_ci continue; 66062306a36Sopenharmony_ci list_move_tail(&p->all_publ, &sr->all_publ); 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_cino_match: 66462306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 66562306a36Sopenharmony_ciexit: 66662306a36Sopenharmony_ci rcu_read_unlock(); 66762306a36Sopenharmony_ci return !list_empty(dsts); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* tipc_nametbl_lookup_mcast_sockets(): look up node local destinaton sockets 67162306a36Sopenharmony_ci * matching the given address 67262306a36Sopenharmony_ci * Used on nodes which have received a multicast/broadcast message 67362306a36Sopenharmony_ci * Returns a list of local sockets 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_civoid tipc_nametbl_lookup_mcast_sockets(struct net *net, struct tipc_uaddr *ua, 67662306a36Sopenharmony_ci struct list_head *dports) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct service_range *sr; 67962306a36Sopenharmony_ci struct tipc_service *sc; 68062306a36Sopenharmony_ci struct publication *p; 68162306a36Sopenharmony_ci u8 scope = ua->scope; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci rcu_read_lock(); 68462306a36Sopenharmony_ci sc = tipc_service_find(net, ua); 68562306a36Sopenharmony_ci if (!sc) 68662306a36Sopenharmony_ci goto exit; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 68962306a36Sopenharmony_ci service_range_foreach_match(sr, sc, ua->sr.lower, ua->sr.upper) { 69062306a36Sopenharmony_ci list_for_each_entry(p, &sr->local_publ, local_publ) { 69162306a36Sopenharmony_ci if (scope == p->scope || scope == TIPC_ANY_SCOPE) 69262306a36Sopenharmony_ci tipc_dest_push(dports, 0, p->sk.ref); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 69662306a36Sopenharmony_ciexit: 69762306a36Sopenharmony_ci rcu_read_unlock(); 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* tipc_nametbl_lookup_mcast_nodes(): look up all destination nodes matching 70162306a36Sopenharmony_ci * the given address. Used in sending node. 70262306a36Sopenharmony_ci * Used on nodes which are sending out a multicast/broadcast message 70362306a36Sopenharmony_ci * Returns a list of nodes, including own node if applicable 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_civoid tipc_nametbl_lookup_mcast_nodes(struct net *net, struct tipc_uaddr *ua, 70662306a36Sopenharmony_ci struct tipc_nlist *nodes) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct service_range *sr; 70962306a36Sopenharmony_ci struct tipc_service *sc; 71062306a36Sopenharmony_ci struct publication *p; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci rcu_read_lock(); 71362306a36Sopenharmony_ci sc = tipc_service_find(net, ua); 71462306a36Sopenharmony_ci if (!sc) 71562306a36Sopenharmony_ci goto exit; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 71862306a36Sopenharmony_ci service_range_foreach_match(sr, sc, ua->sr.lower, ua->sr.upper) { 71962306a36Sopenharmony_ci list_for_each_entry(p, &sr->all_publ, all_publ) { 72062306a36Sopenharmony_ci tipc_nlist_add(nodes, p->sk.node); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 72462306a36Sopenharmony_ciexit: 72562306a36Sopenharmony_ci rcu_read_unlock(); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* tipc_nametbl_build_group - build list of communication group members 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_civoid tipc_nametbl_build_group(struct net *net, struct tipc_group *grp, 73162306a36Sopenharmony_ci struct tipc_uaddr *ua) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct service_range *sr; 73462306a36Sopenharmony_ci struct tipc_service *sc; 73562306a36Sopenharmony_ci struct publication *p; 73662306a36Sopenharmony_ci struct rb_node *n; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci rcu_read_lock(); 73962306a36Sopenharmony_ci sc = tipc_service_find(net, ua); 74062306a36Sopenharmony_ci if (!sc) 74162306a36Sopenharmony_ci goto exit; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 74462306a36Sopenharmony_ci for (n = rb_first(&sc->ranges); n; n = rb_next(n)) { 74562306a36Sopenharmony_ci sr = container_of(n, struct service_range, tree_node); 74662306a36Sopenharmony_ci list_for_each_entry(p, &sr->all_publ, all_publ) { 74762306a36Sopenharmony_ci if (p->scope != ua->scope) 74862306a36Sopenharmony_ci continue; 74962306a36Sopenharmony_ci tipc_group_add_member(grp, p->sk.node, p->sk.ref, 75062306a36Sopenharmony_ci p->sr.lower); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 75462306a36Sopenharmony_ciexit: 75562306a36Sopenharmony_ci rcu_read_unlock(); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/* tipc_nametbl_publish - add service binding to name table 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_cistruct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua, 76162306a36Sopenharmony_ci struct tipc_socket_addr *sk, u32 key) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct name_table *nt = tipc_name_table(net); 76462306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 76562306a36Sopenharmony_ci struct publication *p = NULL; 76662306a36Sopenharmony_ci struct sk_buff *skb = NULL; 76762306a36Sopenharmony_ci u32 rc_dests; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci spin_lock_bh(&tn->nametbl_lock); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (nt->local_publ_count >= TIPC_MAX_PUBL) { 77262306a36Sopenharmony_ci pr_warn("Bind failed, max limit %u reached\n", TIPC_MAX_PUBL); 77362306a36Sopenharmony_ci goto exit; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci p = tipc_nametbl_insert_publ(net, ua, sk, key); 77762306a36Sopenharmony_ci if (p) { 77862306a36Sopenharmony_ci nt->local_publ_count++; 77962306a36Sopenharmony_ci skb = tipc_named_publish(net, p); 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci rc_dests = nt->rc_dests; 78262306a36Sopenharmony_ciexit: 78362306a36Sopenharmony_ci spin_unlock_bh(&tn->nametbl_lock); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (skb) 78662306a36Sopenharmony_ci tipc_node_broadcast(net, skb, rc_dests); 78762306a36Sopenharmony_ci return p; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/** 79262306a36Sopenharmony_ci * tipc_nametbl_withdraw - withdraw a service binding 79362306a36Sopenharmony_ci * @net: network namespace 79462306a36Sopenharmony_ci * @ua: service address/range being unbound 79562306a36Sopenharmony_ci * @sk: address of the socket being unbound from 79662306a36Sopenharmony_ci * @key: target publication key 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_civoid tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua, 79962306a36Sopenharmony_ci struct tipc_socket_addr *sk, u32 key) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct name_table *nt = tipc_name_table(net); 80262306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 80362306a36Sopenharmony_ci struct sk_buff *skb = NULL; 80462306a36Sopenharmony_ci struct publication *p; 80562306a36Sopenharmony_ci u32 rc_dests; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci spin_lock_bh(&tn->nametbl_lock); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci p = tipc_nametbl_remove_publ(net, ua, sk, key); 81062306a36Sopenharmony_ci if (p) { 81162306a36Sopenharmony_ci nt->local_publ_count--; 81262306a36Sopenharmony_ci skb = tipc_named_withdraw(net, p); 81362306a36Sopenharmony_ci list_del_init(&p->binding_sock); 81462306a36Sopenharmony_ci kfree_rcu(p, rcu); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci rc_dests = nt->rc_dests; 81762306a36Sopenharmony_ci spin_unlock_bh(&tn->nametbl_lock); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (skb) 82062306a36Sopenharmony_ci tipc_node_broadcast(net, skb, rc_dests); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/** 82462306a36Sopenharmony_ci * tipc_nametbl_subscribe - add a subscription object to the name table 82562306a36Sopenharmony_ci * @sub: subscription to add 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_cibool tipc_nametbl_subscribe(struct tipc_subscription *sub) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(sub->net); 83062306a36Sopenharmony_ci u32 type = sub->s.seq.type; 83162306a36Sopenharmony_ci struct tipc_service *sc; 83262306a36Sopenharmony_ci struct tipc_uaddr ua; 83362306a36Sopenharmony_ci bool res = true; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_NODE_SCOPE, type, 83662306a36Sopenharmony_ci sub->s.seq.lower, sub->s.seq.upper); 83762306a36Sopenharmony_ci spin_lock_bh(&tn->nametbl_lock); 83862306a36Sopenharmony_ci sc = tipc_service_find(sub->net, &ua); 83962306a36Sopenharmony_ci if (!sc) 84062306a36Sopenharmony_ci sc = tipc_service_create(sub->net, &ua); 84162306a36Sopenharmony_ci if (sc) { 84262306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 84362306a36Sopenharmony_ci tipc_service_subscribe(sc, sub); 84462306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 84562306a36Sopenharmony_ci } else { 84662306a36Sopenharmony_ci pr_warn("Failed to subscribe for {%u,%u,%u}\n", 84762306a36Sopenharmony_ci type, sub->s.seq.lower, sub->s.seq.upper); 84862306a36Sopenharmony_ci res = false; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci spin_unlock_bh(&tn->nametbl_lock); 85162306a36Sopenharmony_ci return res; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/** 85562306a36Sopenharmony_ci * tipc_nametbl_unsubscribe - remove a subscription object from name table 85662306a36Sopenharmony_ci * @sub: subscription to remove 85762306a36Sopenharmony_ci */ 85862306a36Sopenharmony_civoid tipc_nametbl_unsubscribe(struct tipc_subscription *sub) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(sub->net); 86162306a36Sopenharmony_ci struct tipc_service *sc; 86262306a36Sopenharmony_ci struct tipc_uaddr ua; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_NODE_SCOPE, 86562306a36Sopenharmony_ci sub->s.seq.type, sub->s.seq.lower, sub->s.seq.upper); 86662306a36Sopenharmony_ci spin_lock_bh(&tn->nametbl_lock); 86762306a36Sopenharmony_ci sc = tipc_service_find(sub->net, &ua); 86862306a36Sopenharmony_ci if (!sc) 86962306a36Sopenharmony_ci goto exit; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 87262306a36Sopenharmony_ci list_del_init(&sub->service_list); 87362306a36Sopenharmony_ci tipc_sub_put(sub); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Delete service item if no more publications and subscriptions */ 87662306a36Sopenharmony_ci if (RB_EMPTY_ROOT(&sc->ranges) && list_empty(&sc->subscriptions)) { 87762306a36Sopenharmony_ci hlist_del_init_rcu(&sc->service_list); 87862306a36Sopenharmony_ci kfree_rcu(sc, rcu); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 88162306a36Sopenharmony_ciexit: 88262306a36Sopenharmony_ci spin_unlock_bh(&tn->nametbl_lock); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ciint tipc_nametbl_init(struct net *net) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 88862306a36Sopenharmony_ci struct name_table *nt; 88962306a36Sopenharmony_ci int i; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci nt = kzalloc(sizeof(*nt), GFP_KERNEL); 89262306a36Sopenharmony_ci if (!nt) 89362306a36Sopenharmony_ci return -ENOMEM; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci for (i = 0; i < TIPC_NAMETBL_SIZE; i++) 89662306a36Sopenharmony_ci INIT_HLIST_HEAD(&nt->services[i]); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci INIT_LIST_HEAD(&nt->node_scope); 89962306a36Sopenharmony_ci INIT_LIST_HEAD(&nt->cluster_scope); 90062306a36Sopenharmony_ci rwlock_init(&nt->cluster_scope_lock); 90162306a36Sopenharmony_ci tn->nametbl = nt; 90262306a36Sopenharmony_ci spin_lock_init(&tn->nametbl_lock); 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci/** 90762306a36Sopenharmony_ci * tipc_service_delete - purge all publications for a service and delete it 90862306a36Sopenharmony_ci * @net: the associated network namespace 90962306a36Sopenharmony_ci * @sc: tipc_service to delete 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_cistatic void tipc_service_delete(struct net *net, struct tipc_service *sc) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct service_range *sr, *tmpr; 91462306a36Sopenharmony_ci struct publication *p, *tmp; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci spin_lock_bh(&sc->lock); 91762306a36Sopenharmony_ci rbtree_postorder_for_each_entry_safe(sr, tmpr, &sc->ranges, tree_node) { 91862306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, &sr->all_publ, all_publ) { 91962306a36Sopenharmony_ci tipc_service_remove_publ(sr, &p->sk, p->key); 92062306a36Sopenharmony_ci kfree_rcu(p, rcu); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci rb_erase_augmented(&sr->tree_node, &sc->ranges, &sr_callbacks); 92362306a36Sopenharmony_ci kfree(sr); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci hlist_del_init_rcu(&sc->service_list); 92662306a36Sopenharmony_ci spin_unlock_bh(&sc->lock); 92762306a36Sopenharmony_ci kfree_rcu(sc, rcu); 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_civoid tipc_nametbl_stop(struct net *net) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct name_table *nt = tipc_name_table(net); 93362306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 93462306a36Sopenharmony_ci struct hlist_head *service_head; 93562306a36Sopenharmony_ci struct tipc_service *service; 93662306a36Sopenharmony_ci u32 i; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* Verify name table is empty and purge any lingering 93962306a36Sopenharmony_ci * publications, then release the name table 94062306a36Sopenharmony_ci */ 94162306a36Sopenharmony_ci spin_lock_bh(&tn->nametbl_lock); 94262306a36Sopenharmony_ci for (i = 0; i < TIPC_NAMETBL_SIZE; i++) { 94362306a36Sopenharmony_ci if (hlist_empty(&nt->services[i])) 94462306a36Sopenharmony_ci continue; 94562306a36Sopenharmony_ci service_head = &nt->services[i]; 94662306a36Sopenharmony_ci hlist_for_each_entry_rcu(service, service_head, service_list) { 94762306a36Sopenharmony_ci tipc_service_delete(net, service); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci spin_unlock_bh(&tn->nametbl_lock); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci synchronize_net(); 95362306a36Sopenharmony_ci kfree(nt); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic int __tipc_nl_add_nametable_publ(struct tipc_nl_msg *msg, 95762306a36Sopenharmony_ci struct tipc_service *service, 95862306a36Sopenharmony_ci struct service_range *sr, 95962306a36Sopenharmony_ci u32 *last_key) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci struct publication *p; 96262306a36Sopenharmony_ci struct nlattr *attrs; 96362306a36Sopenharmony_ci struct nlattr *b; 96462306a36Sopenharmony_ci void *hdr; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (*last_key) { 96762306a36Sopenharmony_ci list_for_each_entry(p, &sr->all_publ, all_publ) 96862306a36Sopenharmony_ci if (p->key == *last_key) 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci if (list_entry_is_head(p, &sr->all_publ, all_publ)) 97162306a36Sopenharmony_ci return -EPIPE; 97262306a36Sopenharmony_ci } else { 97362306a36Sopenharmony_ci p = list_first_entry(&sr->all_publ, 97462306a36Sopenharmony_ci struct publication, 97562306a36Sopenharmony_ci all_publ); 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci list_for_each_entry_from(p, &sr->all_publ, all_publ) { 97962306a36Sopenharmony_ci *last_key = p->key; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, 98262306a36Sopenharmony_ci &tipc_genl_family, NLM_F_MULTI, 98362306a36Sopenharmony_ci TIPC_NL_NAME_TABLE_GET); 98462306a36Sopenharmony_ci if (!hdr) 98562306a36Sopenharmony_ci return -EMSGSIZE; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_NAME_TABLE); 98862306a36Sopenharmony_ci if (!attrs) 98962306a36Sopenharmony_ci goto msg_full; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci b = nla_nest_start_noflag(msg->skb, TIPC_NLA_NAME_TABLE_PUBL); 99262306a36Sopenharmony_ci if (!b) 99362306a36Sopenharmony_ci goto attr_msg_full; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_TYPE, service->type)) 99662306a36Sopenharmony_ci goto publ_msg_full; 99762306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_LOWER, sr->lower)) 99862306a36Sopenharmony_ci goto publ_msg_full; 99962306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_UPPER, sr->upper)) 100062306a36Sopenharmony_ci goto publ_msg_full; 100162306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_SCOPE, p->scope)) 100262306a36Sopenharmony_ci goto publ_msg_full; 100362306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_NODE, p->sk.node)) 100462306a36Sopenharmony_ci goto publ_msg_full; 100562306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_REF, p->sk.ref)) 100662306a36Sopenharmony_ci goto publ_msg_full; 100762306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_KEY, p->key)) 100862306a36Sopenharmony_ci goto publ_msg_full; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci nla_nest_end(msg->skb, b); 101162306a36Sopenharmony_ci nla_nest_end(msg->skb, attrs); 101262306a36Sopenharmony_ci genlmsg_end(msg->skb, hdr); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci *last_key = 0; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cipubl_msg_full: 101962306a36Sopenharmony_ci nla_nest_cancel(msg->skb, b); 102062306a36Sopenharmony_ciattr_msg_full: 102162306a36Sopenharmony_ci nla_nest_cancel(msg->skb, attrs); 102262306a36Sopenharmony_cimsg_full: 102362306a36Sopenharmony_ci genlmsg_cancel(msg->skb, hdr); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return -EMSGSIZE; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic int __tipc_nl_service_range_list(struct tipc_nl_msg *msg, 102962306a36Sopenharmony_ci struct tipc_service *sc, 103062306a36Sopenharmony_ci u32 *last_lower, u32 *last_key) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct service_range *sr; 103362306a36Sopenharmony_ci struct rb_node *n; 103462306a36Sopenharmony_ci int err; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci for (n = rb_first(&sc->ranges); n; n = rb_next(n)) { 103762306a36Sopenharmony_ci sr = container_of(n, struct service_range, tree_node); 103862306a36Sopenharmony_ci if (sr->lower < *last_lower) 103962306a36Sopenharmony_ci continue; 104062306a36Sopenharmony_ci err = __tipc_nl_add_nametable_publ(msg, sc, sr, last_key); 104162306a36Sopenharmony_ci if (err) { 104262306a36Sopenharmony_ci *last_lower = sr->lower; 104362306a36Sopenharmony_ci return err; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci *last_lower = 0; 104762306a36Sopenharmony_ci return 0; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic int tipc_nl_service_list(struct net *net, struct tipc_nl_msg *msg, 105162306a36Sopenharmony_ci u32 *last_type, u32 *last_lower, u32 *last_key) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 105462306a36Sopenharmony_ci struct tipc_service *service = NULL; 105562306a36Sopenharmony_ci struct hlist_head *head; 105662306a36Sopenharmony_ci struct tipc_uaddr ua; 105762306a36Sopenharmony_ci int err; 105862306a36Sopenharmony_ci int i; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (*last_type) 106162306a36Sopenharmony_ci i = hash(*last_type); 106262306a36Sopenharmony_ci else 106362306a36Sopenharmony_ci i = 0; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci for (; i < TIPC_NAMETBL_SIZE; i++) { 106662306a36Sopenharmony_ci head = &tn->nametbl->services[i]; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (*last_type || 106962306a36Sopenharmony_ci (!i && *last_key && (*last_lower == *last_key))) { 107062306a36Sopenharmony_ci tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_NODE_SCOPE, 107162306a36Sopenharmony_ci *last_type, *last_lower, *last_lower); 107262306a36Sopenharmony_ci service = tipc_service_find(net, &ua); 107362306a36Sopenharmony_ci if (!service) 107462306a36Sopenharmony_ci return -EPIPE; 107562306a36Sopenharmony_ci } else { 107662306a36Sopenharmony_ci hlist_for_each_entry_rcu(service, head, service_list) 107762306a36Sopenharmony_ci break; 107862306a36Sopenharmony_ci if (!service) 107962306a36Sopenharmony_ci continue; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci hlist_for_each_entry_from_rcu(service, service_list) { 108362306a36Sopenharmony_ci spin_lock_bh(&service->lock); 108462306a36Sopenharmony_ci err = __tipc_nl_service_range_list(msg, service, 108562306a36Sopenharmony_ci last_lower, 108662306a36Sopenharmony_ci last_key); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (err) { 108962306a36Sopenharmony_ci *last_type = service->type; 109062306a36Sopenharmony_ci spin_unlock_bh(&service->lock); 109162306a36Sopenharmony_ci return err; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci spin_unlock_bh(&service->lock); 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci *last_type = 0; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci return 0; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ciint tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 110362306a36Sopenharmony_ci u32 last_type = cb->args[0]; 110462306a36Sopenharmony_ci u32 last_lower = cb->args[1]; 110562306a36Sopenharmony_ci u32 last_key = cb->args[2]; 110662306a36Sopenharmony_ci int done = cb->args[3]; 110762306a36Sopenharmony_ci struct tipc_nl_msg msg; 110862306a36Sopenharmony_ci int err; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (done) 111162306a36Sopenharmony_ci return 0; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci msg.skb = skb; 111462306a36Sopenharmony_ci msg.portid = NETLINK_CB(cb->skb).portid; 111562306a36Sopenharmony_ci msg.seq = cb->nlh->nlmsg_seq; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci rcu_read_lock(); 111862306a36Sopenharmony_ci err = tipc_nl_service_list(net, &msg, &last_type, 111962306a36Sopenharmony_ci &last_lower, &last_key); 112062306a36Sopenharmony_ci if (!err) { 112162306a36Sopenharmony_ci done = 1; 112262306a36Sopenharmony_ci } else if (err != -EMSGSIZE) { 112362306a36Sopenharmony_ci /* We never set seq or call nl_dump_check_consistent() this 112462306a36Sopenharmony_ci * means that setting prev_seq here will cause the consistence 112562306a36Sopenharmony_ci * check to fail in the netlink callback handler. Resulting in 112662306a36Sopenharmony_ci * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if 112762306a36Sopenharmony_ci * we got an error. 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_ci cb->prev_seq = 1; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci rcu_read_unlock(); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci cb->args[0] = last_type; 113462306a36Sopenharmony_ci cb->args[1] = last_lower; 113562306a36Sopenharmony_ci cb->args[2] = last_key; 113662306a36Sopenharmony_ci cb->args[3] = done; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return skb->len; 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistruct tipc_dest *tipc_dest_find(struct list_head *l, u32 node, u32 port) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct tipc_dest *dst; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci list_for_each_entry(dst, l, list) { 114662306a36Sopenharmony_ci if (dst->node == node && dst->port == port) 114762306a36Sopenharmony_ci return dst; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci return NULL; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cibool tipc_dest_push(struct list_head *l, u32 node, u32 port) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct tipc_dest *dst; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (tipc_dest_find(l, node, port)) 115762306a36Sopenharmony_ci return false; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci dst = kmalloc(sizeof(*dst), GFP_ATOMIC); 116062306a36Sopenharmony_ci if (unlikely(!dst)) 116162306a36Sopenharmony_ci return false; 116262306a36Sopenharmony_ci dst->node = node; 116362306a36Sopenharmony_ci dst->port = port; 116462306a36Sopenharmony_ci list_add(&dst->list, l); 116562306a36Sopenharmony_ci return true; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cibool tipc_dest_pop(struct list_head *l, u32 *node, u32 *port) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct tipc_dest *dst; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (list_empty(l)) 117362306a36Sopenharmony_ci return false; 117462306a36Sopenharmony_ci dst = list_first_entry(l, typeof(*dst), list); 117562306a36Sopenharmony_ci if (port) 117662306a36Sopenharmony_ci *port = dst->port; 117762306a36Sopenharmony_ci if (node) 117862306a36Sopenharmony_ci *node = dst->node; 117962306a36Sopenharmony_ci list_del(&dst->list); 118062306a36Sopenharmony_ci kfree(dst); 118162306a36Sopenharmony_ci return true; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cibool tipc_dest_del(struct list_head *l, u32 node, u32 port) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct tipc_dest *dst; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci dst = tipc_dest_find(l, node, port); 118962306a36Sopenharmony_ci if (!dst) 119062306a36Sopenharmony_ci return false; 119162306a36Sopenharmony_ci list_del(&dst->list); 119262306a36Sopenharmony_ci kfree(dst); 119362306a36Sopenharmony_ci return true; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_civoid tipc_dest_list_purge(struct list_head *l) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci struct tipc_dest *dst, *tmp; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci list_for_each_entry_safe(dst, tmp, l, list) { 120162306a36Sopenharmony_ci list_del(&dst->list); 120262306a36Sopenharmony_ci kfree(dst); 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci} 1205