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