xref: /openbmc/linux/net/tipc/group.c (revision b6f88d9c)
175da2163SJon Maloy /*
275da2163SJon Maloy  * net/tipc/group.c: TIPC group messaging code
375da2163SJon Maloy  *
475da2163SJon Maloy  * Copyright (c) 2017, Ericsson AB
5*b6f88d9cSJon Maloy  * Copyright (c) 2020, Red Hat Inc
675da2163SJon Maloy  * All rights reserved.
775da2163SJon Maloy  *
875da2163SJon Maloy  * Redistribution and use in source and binary forms, with or without
975da2163SJon Maloy  * modification, are permitted provided that the following conditions are met:
1075da2163SJon Maloy  *
1175da2163SJon Maloy  * 1. Redistributions of source code must retain the above copyright
1275da2163SJon Maloy  *    notice, this list of conditions and the following disclaimer.
1375da2163SJon Maloy  * 2. Redistributions in binary form must reproduce the above copyright
1475da2163SJon Maloy  *    notice, this list of conditions and the following disclaimer in the
1575da2163SJon Maloy  *    documentation and/or other materials provided with the distribution.
1675da2163SJon Maloy  * 3. Neither the names of the copyright holders nor the names of its
1775da2163SJon Maloy  *    contributors may be used to endorse or promote products derived from
1875da2163SJon Maloy  *    this software without specific prior written permission.
1975da2163SJon Maloy  *
2075da2163SJon Maloy  * Alternatively, this software may be distributed under the terms of the
2175da2163SJon Maloy  * GNU General Public License ("GPL") version 2 as published by the Free
2275da2163SJon Maloy  * Software Foundation.
2375da2163SJon Maloy  *
2475da2163SJon Maloy  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2575da2163SJon Maloy  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2675da2163SJon Maloy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2775da2163SJon Maloy  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2875da2163SJon Maloy  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2975da2163SJon Maloy  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3075da2163SJon Maloy  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3175da2163SJon Maloy  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3275da2163SJon Maloy  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3375da2163SJon Maloy  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3475da2163SJon Maloy  * POSSIBILITY OF SUCH DAMAGE.
3575da2163SJon Maloy  */
3675da2163SJon Maloy 
3775da2163SJon Maloy #include "core.h"
3875da2163SJon Maloy #include "addr.h"
3975da2163SJon Maloy #include "group.h"
4075da2163SJon Maloy #include "bcast.h"
41026321c6SJon Maloy #include "topsrv.h"
4275da2163SJon Maloy #include "msg.h"
4375da2163SJon Maloy #include "socket.h"
4475da2163SJon Maloy #include "node.h"
4575da2163SJon Maloy #include "name_table.h"
4675da2163SJon Maloy #include "subscr.h"
4775da2163SJon Maloy 
4875da2163SJon Maloy #define ADV_UNIT (((MAX_MSG_SIZE + MAX_H_SIZE) / FLOWCTL_BLK_SZ) + 1)
4975da2163SJon Maloy #define ADV_IDLE ADV_UNIT
50b7d42635SJon Maloy #define ADV_ACTIVE (ADV_UNIT * 12)
5175da2163SJon Maloy 
5275da2163SJon Maloy enum mbr_state {
5375da2163SJon Maloy 	MBR_JOINING,
5475da2163SJon Maloy 	MBR_PUBLISHED,
5575da2163SJon Maloy 	MBR_JOINED,
5604d7b574SJon Maloy 	MBR_PENDING,
5704d7b574SJon Maloy 	MBR_ACTIVE,
5804d7b574SJon Maloy 	MBR_RECLAIMING,
5904d7b574SJon Maloy 	MBR_REMITTED,
6075da2163SJon Maloy 	MBR_LEAVING
6175da2163SJon Maloy };
6275da2163SJon Maloy 
6375da2163SJon Maloy struct tipc_member {
6475da2163SJon Maloy 	struct rb_node tree_node;
6575da2163SJon Maloy 	struct list_head list;
6638266ca1SJon Maloy 	struct list_head small_win;
67b87a5ea3SJon Maloy 	struct sk_buff_head deferredq;
68b7d42635SJon Maloy 	struct tipc_group *group;
6975da2163SJon Maloy 	u32 node;
7075da2163SJon Maloy 	u32 port;
7131c82a2dSJon Maloy 	u32 instance;
7275da2163SJon Maloy 	enum mbr_state state;
73b7d42635SJon Maloy 	u16 advertised;
74b7d42635SJon Maloy 	u16 window;
7575da2163SJon Maloy 	u16 bc_rcv_nxt;
76a3bada70SJon Maloy 	u16 bc_syncpt;
772f487712SJon Maloy 	u16 bc_acked;
7875da2163SJon Maloy };
7975da2163SJon Maloy 
8075da2163SJon Maloy struct tipc_group {
8175da2163SJon Maloy 	struct rb_root members;
8238266ca1SJon Maloy 	struct list_head small_win;
8304d7b574SJon Maloy 	struct list_head pending;
8404d7b574SJon Maloy 	struct list_head active;
8575da2163SJon Maloy 	struct tipc_nlist dests;
8675da2163SJon Maloy 	struct net *net;
8775da2163SJon Maloy 	int subid;
8875da2163SJon Maloy 	u32 type;
8975da2163SJon Maloy 	u32 instance;
9075da2163SJon Maloy 	u32 scope;
9175da2163SJon Maloy 	u32 portid;
9275da2163SJon Maloy 	u16 member_cnt;
9304d7b574SJon Maloy 	u16 active_cnt;
9404d7b574SJon Maloy 	u16 max_active;
9575da2163SJon Maloy 	u16 bc_snd_nxt;
962f487712SJon Maloy 	u16 bc_ackers;
9760c25306SJon Maloy 	bool *open;
9875da2163SJon Maloy 	bool loopback;
99ae236fb2SJon Maloy 	bool events;
10075da2163SJon Maloy };
10175da2163SJon Maloy 
10275da2163SJon Maloy static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m,
10375da2163SJon Maloy 				  int mtyp, struct sk_buff_head *xmitq);
10475da2163SJon Maloy 
tipc_group_open(struct tipc_member * m,bool * wakeup)105eb929a91SJon Maloy static void tipc_group_open(struct tipc_member *m, bool *wakeup)
106eb929a91SJon Maloy {
107eb929a91SJon Maloy 	*wakeup = false;
108eb929a91SJon Maloy 	if (list_empty(&m->small_win))
109eb929a91SJon Maloy 		return;
110eb929a91SJon Maloy 	list_del_init(&m->small_win);
11160c25306SJon Maloy 	*m->group->open = true;
112eb929a91SJon Maloy 	*wakeup = true;
113eb929a91SJon Maloy }
114eb929a91SJon Maloy 
tipc_group_decr_active(struct tipc_group * grp,struct tipc_member * m)11504d7b574SJon Maloy static void tipc_group_decr_active(struct tipc_group *grp,
11604d7b574SJon Maloy 				   struct tipc_member *m)
11704d7b574SJon Maloy {
118f9c935dbSJon Maloy 	if (m->state == MBR_ACTIVE || m->state == MBR_RECLAIMING ||
119f9c935dbSJon Maloy 	    m->state == MBR_REMITTED)
12004d7b574SJon Maloy 		grp->active_cnt--;
12104d7b574SJon Maloy }
12204d7b574SJon Maloy 
tipc_group_rcvbuf_limit(struct tipc_group * grp)123b7d42635SJon Maloy static int tipc_group_rcvbuf_limit(struct tipc_group *grp)
124b7d42635SJon Maloy {
12504d7b574SJon Maloy 	int max_active, active_pool, idle_pool;
126b7d42635SJon Maloy 	int mcnt = grp->member_cnt + 1;
127b7d42635SJon Maloy 
12804d7b574SJon Maloy 	/* Limit simultaneous reception from other members */
12904d7b574SJon Maloy 	max_active = min(mcnt / 8, 64);
13004d7b574SJon Maloy 	max_active = max(max_active, 16);
13104d7b574SJon Maloy 	grp->max_active = max_active;
13204d7b574SJon Maloy 
13304d7b574SJon Maloy 	/* Reserve blocks for active and idle members */
13404d7b574SJon Maloy 	active_pool = max_active * ADV_ACTIVE;
13504d7b574SJon Maloy 	idle_pool = (mcnt - max_active) * ADV_IDLE;
13604d7b574SJon Maloy 
137b7d42635SJon Maloy 	/* Scale to bytes, considering worst-case truesize/msgsize ratio */
13804d7b574SJon Maloy 	return (active_pool + idle_pool) * FLOWCTL_BLK_SZ * 4;
139b7d42635SJon Maloy }
140b7d42635SJon Maloy 
tipc_group_bc_snd_nxt(struct tipc_group * grp)14175da2163SJon Maloy u16 tipc_group_bc_snd_nxt(struct tipc_group *grp)
14275da2163SJon Maloy {
14375da2163SJon Maloy 	return grp->bc_snd_nxt;
14475da2163SJon Maloy }
14575da2163SJon Maloy 
tipc_group_is_receiver(struct tipc_member * m)14638266ca1SJon Maloy static bool tipc_group_is_receiver(struct tipc_member *m)
147b7d42635SJon Maloy {
1480233493aSJon Maloy 	return m && m->state != MBR_JOINING && m->state != MBR_LEAVING;
149b7d42635SJon Maloy }
150b7d42635SJon Maloy 
tipc_group_is_sender(struct tipc_member * m)15138266ca1SJon Maloy static bool tipc_group_is_sender(struct tipc_member *m)
15275da2163SJon Maloy {
153d12d2e12SJon Maloy 	return m && m->state != MBR_JOINING && m->state != MBR_PUBLISHED;
15475da2163SJon Maloy }
15575da2163SJon Maloy 
tipc_group_exclude(struct tipc_group * grp)156ee106d7fSJon Maloy u32 tipc_group_exclude(struct tipc_group *grp)
157ee106d7fSJon Maloy {
158ee106d7fSJon Maloy 	if (!grp->loopback)
159ee106d7fSJon Maloy 		return grp->portid;
160ee106d7fSJon Maloy 	return 0;
161ee106d7fSJon Maloy }
162ee106d7fSJon Maloy 
tipc_group_create(struct net * net,u32 portid,struct tipc_group_req * mreq,bool * group_is_open)16375da2163SJon Maloy struct tipc_group *tipc_group_create(struct net *net, u32 portid,
16460c25306SJon Maloy 				     struct tipc_group_req *mreq,
16560c25306SJon Maloy 				     bool *group_is_open)
16675da2163SJon Maloy {
167232d07b7SJon Maloy 	u32 filter = TIPC_SUB_PORTS | TIPC_SUB_NO_STATUS;
168232d07b7SJon Maloy 	bool global = mreq->scope != TIPC_NODE_SCOPE;
16975da2163SJon Maloy 	struct tipc_group *grp;
17075da2163SJon Maloy 	u32 type = mreq->type;
17175da2163SJon Maloy 
17275da2163SJon Maloy 	grp = kzalloc(sizeof(*grp), GFP_ATOMIC);
17375da2163SJon Maloy 	if (!grp)
17475da2163SJon Maloy 		return NULL;
17575da2163SJon Maloy 	tipc_nlist_init(&grp->dests, tipc_own_addr(net));
17638266ca1SJon Maloy 	INIT_LIST_HEAD(&grp->small_win);
17704d7b574SJon Maloy 	INIT_LIST_HEAD(&grp->active);
17804d7b574SJon Maloy 	INIT_LIST_HEAD(&grp->pending);
17975da2163SJon Maloy 	grp->members = RB_ROOT;
18075da2163SJon Maloy 	grp->net = net;
18175da2163SJon Maloy 	grp->portid = portid;
18275da2163SJon Maloy 	grp->type = type;
18375da2163SJon Maloy 	grp->instance = mreq->instance;
18475da2163SJon Maloy 	grp->scope = mreq->scope;
18575da2163SJon Maloy 	grp->loopback = mreq->flags & TIPC_GROUP_LOOPBACK;
186ae236fb2SJon Maloy 	grp->events = mreq->flags & TIPC_GROUP_MEMBER_EVTS;
18760c25306SJon Maloy 	grp->open = group_is_open;
1881b22bcadSJon Maloy 	*grp->open = false;
189232d07b7SJon Maloy 	filter |= global ? TIPC_SUB_CLUSTER_SCOPE : TIPC_SUB_NODE_SCOPE;
190232d07b7SJon Maloy 	if (tipc_topsrv_kern_subscr(net, portid, type, 0, ~0,
191232d07b7SJon Maloy 				    filter, &grp->subid))
19275da2163SJon Maloy 		return grp;
19375da2163SJon Maloy 	kfree(grp);
19475da2163SJon Maloy 	return NULL;
19575da2163SJon Maloy }
19675da2163SJon Maloy 
tipc_group_join(struct net * net,struct tipc_group * grp,int * sk_rcvbuf)197d12d2e12SJon Maloy void tipc_group_join(struct net *net, struct tipc_group *grp, int *sk_rcvbuf)
198d12d2e12SJon Maloy {
199d12d2e12SJon Maloy 	struct rb_root *tree = &grp->members;
200d12d2e12SJon Maloy 	struct tipc_member *m, *tmp;
201d12d2e12SJon Maloy 	struct sk_buff_head xmitq;
202d12d2e12SJon Maloy 
203e654f9f5SJon Maloy 	__skb_queue_head_init(&xmitq);
204d12d2e12SJon Maloy 	rbtree_postorder_for_each_entry_safe(m, tmp, tree, tree_node) {
205d12d2e12SJon Maloy 		tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, &xmitq);
206d12d2e12SJon Maloy 		tipc_group_update_member(m, 0);
207d12d2e12SJon Maloy 	}
208d12d2e12SJon Maloy 	tipc_node_distr_xmit(net, &xmitq);
209d12d2e12SJon Maloy 	*sk_rcvbuf = tipc_group_rcvbuf_limit(grp);
210d12d2e12SJon Maloy }
211d12d2e12SJon Maloy 
tipc_group_delete(struct net * net,struct tipc_group * grp)21275da2163SJon Maloy void tipc_group_delete(struct net *net, struct tipc_group *grp)
21375da2163SJon Maloy {
21475da2163SJon Maloy 	struct rb_root *tree = &grp->members;
21575da2163SJon Maloy 	struct tipc_member *m, *tmp;
21675da2163SJon Maloy 	struct sk_buff_head xmitq;
21775da2163SJon Maloy 
21875da2163SJon Maloy 	__skb_queue_head_init(&xmitq);
21975da2163SJon Maloy 
22075da2163SJon Maloy 	rbtree_postorder_for_each_entry_safe(m, tmp, tree, tree_node) {
22175da2163SJon Maloy 		tipc_group_proto_xmit(grp, m, GRP_LEAVE_MSG, &xmitq);
2225cf02612SXin Long 		__skb_queue_purge(&m->deferredq);
22375da2163SJon Maloy 		list_del(&m->list);
22475da2163SJon Maloy 		kfree(m);
22575da2163SJon Maloy 	}
22675da2163SJon Maloy 	tipc_node_distr_xmit(net, &xmitq);
22775da2163SJon Maloy 	tipc_nlist_purge(&grp->dests);
22875da2163SJon Maloy 	tipc_topsrv_kern_unsubscr(net, grp->subid);
22975da2163SJon Maloy 	kfree(grp);
23075da2163SJon Maloy }
23175da2163SJon Maloy 
tipc_group_find_member(struct tipc_group * grp,u32 node,u32 port)232e064cce1SYueHaibing static struct tipc_member *tipc_group_find_member(struct tipc_group *grp,
23375da2163SJon Maloy 						  u32 node, u32 port)
23475da2163SJon Maloy {
23575da2163SJon Maloy 	struct rb_node *n = grp->members.rb_node;
23675da2163SJon Maloy 	u64 nkey, key = (u64)node << 32 | port;
23775da2163SJon Maloy 	struct tipc_member *m;
23875da2163SJon Maloy 
23975da2163SJon Maloy 	while (n) {
24075da2163SJon Maloy 		m = container_of(n, struct tipc_member, tree_node);
24175da2163SJon Maloy 		nkey = (u64)m->node << 32 | m->port;
24275da2163SJon Maloy 		if (key < nkey)
24375da2163SJon Maloy 			n = n->rb_left;
24475da2163SJon Maloy 		else if (key > nkey)
24575da2163SJon Maloy 			n = n->rb_right;
24675da2163SJon Maloy 		else
24775da2163SJon Maloy 			return m;
24875da2163SJon Maloy 	}
24975da2163SJon Maloy 	return NULL;
25075da2163SJon Maloy }
25175da2163SJon Maloy 
tipc_group_find_dest(struct tipc_group * grp,u32 node,u32 port)25227bd9ec0SJon Maloy static struct tipc_member *tipc_group_find_dest(struct tipc_group *grp,
25327bd9ec0SJon Maloy 						u32 node, u32 port)
25427bd9ec0SJon Maloy {
25527bd9ec0SJon Maloy 	struct tipc_member *m;
25627bd9ec0SJon Maloy 
25727bd9ec0SJon Maloy 	m = tipc_group_find_member(grp, node, port);
25838266ca1SJon Maloy 	if (m && tipc_group_is_receiver(m))
25927bd9ec0SJon Maloy 		return m;
26027bd9ec0SJon Maloy 	return NULL;
26127bd9ec0SJon Maloy }
26227bd9ec0SJon Maloy 
tipc_group_find_node(struct tipc_group * grp,u32 node)26375da2163SJon Maloy static struct tipc_member *tipc_group_find_node(struct tipc_group *grp,
26475da2163SJon Maloy 						u32 node)
26575da2163SJon Maloy {
26675da2163SJon Maloy 	struct tipc_member *m;
26775da2163SJon Maloy 	struct rb_node *n;
26875da2163SJon Maloy 
26975da2163SJon Maloy 	for (n = rb_first(&grp->members); n; n = rb_next(n)) {
27075da2163SJon Maloy 		m = container_of(n, struct tipc_member, tree_node);
27175da2163SJon Maloy 		if (m->node == node)
27275da2163SJon Maloy 			return m;
27375da2163SJon Maloy 	}
27475da2163SJon Maloy 	return NULL;
27575da2163SJon Maloy }
27675da2163SJon Maloy 
tipc_group_add_to_tree(struct tipc_group * grp,struct tipc_member * m)277bb3a420dSPeilin Ye static int tipc_group_add_to_tree(struct tipc_group *grp,
27875da2163SJon Maloy 				  struct tipc_member *m)
27975da2163SJon Maloy {
28075da2163SJon Maloy 	u64 nkey, key = (u64)m->node << 32 | m->port;
28175da2163SJon Maloy 	struct rb_node **n, *parent = NULL;
28275da2163SJon Maloy 	struct tipc_member *tmp;
28375da2163SJon Maloy 
28475da2163SJon Maloy 	n = &grp->members.rb_node;
28575da2163SJon Maloy 	while (*n) {
28675da2163SJon Maloy 		tmp = container_of(*n, struct tipc_member, tree_node);
28775da2163SJon Maloy 		parent = *n;
28875da2163SJon Maloy 		tmp = container_of(parent, struct tipc_member, tree_node);
28975da2163SJon Maloy 		nkey = (u64)tmp->node << 32 | tmp->port;
29075da2163SJon Maloy 		if (key < nkey)
29175da2163SJon Maloy 			n = &(*n)->rb_left;
29275da2163SJon Maloy 		else if (key > nkey)
29375da2163SJon Maloy 			n = &(*n)->rb_right;
29475da2163SJon Maloy 		else
295bb3a420dSPeilin Ye 			return -EEXIST;
29675da2163SJon Maloy 	}
29775da2163SJon Maloy 	rb_link_node(&m->tree_node, parent, n);
29875da2163SJon Maloy 	rb_insert_color(&m->tree_node, &grp->members);
299bb3a420dSPeilin Ye 	return 0;
30075da2163SJon Maloy }
30175da2163SJon Maloy 
tipc_group_create_member(struct tipc_group * grp,u32 node,u32 port,u32 instance,int state)30275da2163SJon Maloy static struct tipc_member *tipc_group_create_member(struct tipc_group *grp,
30375da2163SJon Maloy 						    u32 node, u32 port,
304d12d2e12SJon Maloy 						    u32 instance, int state)
30575da2163SJon Maloy {
30675da2163SJon Maloy 	struct tipc_member *m;
307bb3a420dSPeilin Ye 	int ret;
30875da2163SJon Maloy 
30975da2163SJon Maloy 	m = kzalloc(sizeof(*m), GFP_ATOMIC);
31075da2163SJon Maloy 	if (!m)
31175da2163SJon Maloy 		return NULL;
31275da2163SJon Maloy 	INIT_LIST_HEAD(&m->list);
31338266ca1SJon Maloy 	INIT_LIST_HEAD(&m->small_win);
314b87a5ea3SJon Maloy 	__skb_queue_head_init(&m->deferredq);
315b7d42635SJon Maloy 	m->group = grp;
31675da2163SJon Maloy 	m->node = node;
31775da2163SJon Maloy 	m->port = port;
318d12d2e12SJon Maloy 	m->instance = instance;
3192f487712SJon Maloy 	m->bc_acked = grp->bc_snd_nxt - 1;
320bb3a420dSPeilin Ye 	ret = tipc_group_add_to_tree(grp, m);
321bb3a420dSPeilin Ye 	if (ret < 0) {
322bb3a420dSPeilin Ye 		kfree(m);
323bb3a420dSPeilin Ye 		return NULL;
324bb3a420dSPeilin Ye 	}
32575da2163SJon Maloy 	grp->member_cnt++;
32675da2163SJon Maloy 	tipc_nlist_add(&grp->dests, m->node);
32775da2163SJon Maloy 	m->state = state;
32875da2163SJon Maloy 	return m;
32975da2163SJon Maloy }
33075da2163SJon Maloy 
tipc_group_add_member(struct tipc_group * grp,u32 node,u32 port,u32 instance)331d12d2e12SJon Maloy void tipc_group_add_member(struct tipc_group *grp, u32 node,
332d12d2e12SJon Maloy 			   u32 port, u32 instance)
33375da2163SJon Maloy {
334d12d2e12SJon Maloy 	tipc_group_create_member(grp, node, port, instance, MBR_PUBLISHED);
33575da2163SJon Maloy }
33675da2163SJon Maloy 
tipc_group_delete_member(struct tipc_group * grp,struct tipc_member * m)33775da2163SJon Maloy static void tipc_group_delete_member(struct tipc_group *grp,
33875da2163SJon Maloy 				     struct tipc_member *m)
33975da2163SJon Maloy {
34075da2163SJon Maloy 	rb_erase(&m->tree_node, &grp->members);
34175da2163SJon Maloy 	grp->member_cnt--;
3422f487712SJon Maloy 
3432f487712SJon Maloy 	/* Check if we were waiting for replicast ack from this member */
3442f487712SJon Maloy 	if (grp->bc_ackers && less(m->bc_acked, grp->bc_snd_nxt - 1))
3452f487712SJon Maloy 		grp->bc_ackers--;
3462f487712SJon Maloy 
34775da2163SJon Maloy 	list_del_init(&m->list);
34838266ca1SJon Maloy 	list_del_init(&m->small_win);
34904d7b574SJon Maloy 	tipc_group_decr_active(grp, m);
35075da2163SJon Maloy 
35175da2163SJon Maloy 	/* If last member on a node, remove node from dest list */
35275da2163SJon Maloy 	if (!tipc_group_find_node(grp, m->node))
35375da2163SJon Maloy 		tipc_nlist_del(&grp->dests, m->node);
35475da2163SJon Maloy 
35575da2163SJon Maloy 	kfree(m);
35675da2163SJon Maloy }
35775da2163SJon Maloy 
tipc_group_dests(struct tipc_group * grp)35875da2163SJon Maloy struct tipc_nlist *tipc_group_dests(struct tipc_group *grp)
35975da2163SJon Maloy {
36075da2163SJon Maloy 	return &grp->dests;
36175da2163SJon Maloy }
36275da2163SJon Maloy 
tipc_group_self(struct tipc_group * grp,struct tipc_service_range * seq,int * scope)363*b6f88d9cSJon Maloy void tipc_group_self(struct tipc_group *grp, struct tipc_service_range *seq,
36475da2163SJon Maloy 		     int *scope)
36575da2163SJon Maloy {
36675da2163SJon Maloy 	seq->type = grp->type;
36775da2163SJon Maloy 	seq->lower = grp->instance;
36875da2163SJon Maloy 	seq->upper = grp->instance;
36975da2163SJon Maloy 	*scope = grp->scope;
37075da2163SJon Maloy }
37175da2163SJon Maloy 
tipc_group_update_member(struct tipc_member * m,int len)372b7d42635SJon Maloy void tipc_group_update_member(struct tipc_member *m, int len)
37375da2163SJon Maloy {
374b7d42635SJon Maloy 	struct tipc_group *grp = m->group;
375b7d42635SJon Maloy 	struct tipc_member *_m, *tmp;
376b7d42635SJon Maloy 
37738266ca1SJon Maloy 	if (!tipc_group_is_receiver(m))
378b7d42635SJon Maloy 		return;
379b7d42635SJon Maloy 
380b7d42635SJon Maloy 	m->window -= len;
381b7d42635SJon Maloy 
382b7d42635SJon Maloy 	if (m->window >= ADV_IDLE)
383b7d42635SJon Maloy 		return;
384b7d42635SJon Maloy 
38538266ca1SJon Maloy 	list_del_init(&m->small_win);
386b7d42635SJon Maloy 
38738266ca1SJon Maloy 	/* Sort member into small_window members' list */
38838266ca1SJon Maloy 	list_for_each_entry_safe(_m, tmp, &grp->small_win, small_win) {
389d84d1b3bSJon Maloy 		if (_m->window > m->window)
390d84d1b3bSJon Maloy 			break;
391b7d42635SJon Maloy 	}
392d84d1b3bSJon Maloy 	list_add_tail(&m->small_win, &_m->small_win);
393b7d42635SJon Maloy }
394b7d42635SJon Maloy 
tipc_group_update_bc_members(struct tipc_group * grp,int len,bool ack)3952f487712SJon Maloy void tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack)
396b7d42635SJon Maloy {
3972f487712SJon Maloy 	u16 prev = grp->bc_snd_nxt - 1;
398b7d42635SJon Maloy 	struct tipc_member *m;
399b7d42635SJon Maloy 	struct rb_node *n;
4000a3d805cSJon Maloy 	u16 ackers = 0;
401b7d42635SJon Maloy 
402b7d42635SJon Maloy 	for (n = rb_first(&grp->members); n; n = rb_next(n)) {
403b7d42635SJon Maloy 		m = container_of(n, struct tipc_member, tree_node);
40438266ca1SJon Maloy 		if (tipc_group_is_receiver(m)) {
405b7d42635SJon Maloy 			tipc_group_update_member(m, len);
4062f487712SJon Maloy 			m->bc_acked = prev;
4070a3d805cSJon Maloy 			ackers++;
408b7d42635SJon Maloy 		}
4092f487712SJon Maloy 	}
4102f487712SJon Maloy 
4112f487712SJon Maloy 	/* Mark number of acknowledges to expect, if any */
4122f487712SJon Maloy 	if (ack)
4130a3d805cSJon Maloy 		grp->bc_ackers = ackers;
41475da2163SJon Maloy 	grp->bc_snd_nxt++;
41575da2163SJon Maloy }
41675da2163SJon Maloy 
tipc_group_cong(struct tipc_group * grp,u32 dnode,u32 dport,int len,struct tipc_member ** mbr)41727bd9ec0SJon Maloy bool tipc_group_cong(struct tipc_group *grp, u32 dnode, u32 dport,
41827bd9ec0SJon Maloy 		     int len, struct tipc_member **mbr)
41927bd9ec0SJon Maloy {
42027bd9ec0SJon Maloy 	struct sk_buff_head xmitq;
42127bd9ec0SJon Maloy 	struct tipc_member *m;
42227bd9ec0SJon Maloy 	int adv, state;
42327bd9ec0SJon Maloy 
42427bd9ec0SJon Maloy 	m = tipc_group_find_dest(grp, dnode, dport);
425eb929a91SJon Maloy 	if (!tipc_group_is_receiver(m)) {
426eb929a91SJon Maloy 		*mbr = NULL;
42727bd9ec0SJon Maloy 		return false;
428eb929a91SJon Maloy 	}
429eb929a91SJon Maloy 	*mbr = m;
430eb929a91SJon Maloy 
43127bd9ec0SJon Maloy 	if (m->window >= len)
43227bd9ec0SJon Maloy 		return false;
433eb929a91SJon Maloy 
43460c25306SJon Maloy 	*grp->open = false;
43527bd9ec0SJon Maloy 
43627bd9ec0SJon Maloy 	/* If not fully advertised, do it now to prevent mutual blocking */
43727bd9ec0SJon Maloy 	adv = m->advertised;
43827bd9ec0SJon Maloy 	state = m->state;
43927bd9ec0SJon Maloy 	if (state == MBR_JOINED && adv == ADV_IDLE)
44027bd9ec0SJon Maloy 		return true;
44104d7b574SJon Maloy 	if (state == MBR_ACTIVE && adv == ADV_ACTIVE)
44204d7b574SJon Maloy 		return true;
44304d7b574SJon Maloy 	if (state == MBR_PENDING && adv == ADV_IDLE)
44404d7b574SJon Maloy 		return true;
445e654f9f5SJon Maloy 	__skb_queue_head_init(&xmitq);
44627bd9ec0SJon Maloy 	tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, &xmitq);
44727bd9ec0SJon Maloy 	tipc_node_distr_xmit(grp->net, &xmitq);
44827bd9ec0SJon Maloy 	return true;
44927bd9ec0SJon Maloy }
45027bd9ec0SJon Maloy 
tipc_group_bc_cong(struct tipc_group * grp,int len)451b7d42635SJon Maloy bool tipc_group_bc_cong(struct tipc_group *grp, int len)
452b7d42635SJon Maloy {
45327bd9ec0SJon Maloy 	struct tipc_member *m = NULL;
454b7d42635SJon Maloy 
4552f487712SJon Maloy 	/* If prev bcast was replicast, reject until all receivers have acked */
456eb929a91SJon Maloy 	if (grp->bc_ackers) {
45760c25306SJon Maloy 		*grp->open = false;
4582f487712SJon Maloy 		return true;
459eb929a91SJon Maloy 	}
46038266ca1SJon Maloy 	if (list_empty(&grp->small_win))
461b7d42635SJon Maloy 		return false;
462b7d42635SJon Maloy 
46338266ca1SJon Maloy 	m = list_first_entry(&grp->small_win, struct tipc_member, small_win);
464b7d42635SJon Maloy 	if (m->window >= len)
465b7d42635SJon Maloy 		return false;
466b7d42635SJon Maloy 
46727bd9ec0SJon Maloy 	return tipc_group_cong(grp, m->node, m->port, len, &m);
468b7d42635SJon Maloy }
469b7d42635SJon Maloy 
470b87a5ea3SJon Maloy /* tipc_group_sort_msg() - sort msg into queue by bcast sequence number
471b87a5ea3SJon Maloy  */
tipc_group_sort_msg(struct sk_buff * skb,struct sk_buff_head * defq)472b87a5ea3SJon Maloy static void tipc_group_sort_msg(struct sk_buff *skb, struct sk_buff_head *defq)
473b87a5ea3SJon Maloy {
474b87a5ea3SJon Maloy 	struct tipc_msg *_hdr, *hdr = buf_msg(skb);
475b87a5ea3SJon Maloy 	u16 bc_seqno = msg_grp_bc_seqno(hdr);
476b87a5ea3SJon Maloy 	struct sk_buff *_skb, *tmp;
477b87a5ea3SJon Maloy 	int mtyp = msg_type(hdr);
478b87a5ea3SJon Maloy 
479a3bada70SJon Maloy 	/* Bcast/mcast may be bypassed by ucast or other bcast, - sort it in */
480b87a5ea3SJon Maloy 	if (mtyp == TIPC_GRP_BCAST_MSG || mtyp == TIPC_GRP_MCAST_MSG) {
481b87a5ea3SJon Maloy 		skb_queue_walk_safe(defq, _skb, tmp) {
482b87a5ea3SJon Maloy 			_hdr = buf_msg(_skb);
483b87a5ea3SJon Maloy 			if (!less(bc_seqno, msg_grp_bc_seqno(_hdr)))
484b87a5ea3SJon Maloy 				continue;
485b87a5ea3SJon Maloy 			__skb_queue_before(defq, _skb, skb);
486b87a5ea3SJon Maloy 			return;
487b87a5ea3SJon Maloy 		}
488b87a5ea3SJon Maloy 		/* Bcast was not bypassed, - add to tail */
489b87a5ea3SJon Maloy 	}
490b87a5ea3SJon Maloy 	/* Unicasts are never bypassed, - always add to tail */
491b87a5ea3SJon Maloy 	__skb_queue_tail(defq, skb);
492b87a5ea3SJon Maloy }
493b87a5ea3SJon Maloy 
49475da2163SJon Maloy /* tipc_group_filter_msg() - determine if we should accept arriving message
49575da2163SJon Maloy  */
tipc_group_filter_msg(struct tipc_group * grp,struct sk_buff_head * inputq,struct sk_buff_head * xmitq)49675da2163SJon Maloy void tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *inputq,
49775da2163SJon Maloy 			   struct sk_buff_head *xmitq)
49875da2163SJon Maloy {
49975da2163SJon Maloy 	struct sk_buff *skb = __skb_dequeue(inputq);
500a3bada70SJon Maloy 	bool ack, deliver, update, leave = false;
501b87a5ea3SJon Maloy 	struct sk_buff_head *defq;
50275da2163SJon Maloy 	struct tipc_member *m;
50375da2163SJon Maloy 	struct tipc_msg *hdr;
50475da2163SJon Maloy 	u32 node, port;
505b87a5ea3SJon Maloy 	int mtyp, blks;
50675da2163SJon Maloy 
50775da2163SJon Maloy 	if (!skb)
50875da2163SJon Maloy 		return;
50975da2163SJon Maloy 
51075da2163SJon Maloy 	hdr = buf_msg(skb);
51175da2163SJon Maloy 	node =  msg_orignode(hdr);
51275da2163SJon Maloy 	port = msg_origport(hdr);
51375da2163SJon Maloy 
51475da2163SJon Maloy 	if (!msg_in_group(hdr))
51575da2163SJon Maloy 		goto drop;
51675da2163SJon Maloy 
51775da2163SJon Maloy 	m = tipc_group_find_member(grp, node, port);
51838266ca1SJon Maloy 	if (!tipc_group_is_sender(m))
51975da2163SJon Maloy 		goto drop;
52075da2163SJon Maloy 
521b87a5ea3SJon Maloy 	if (less(msg_grp_bc_seqno(hdr), m->bc_rcv_nxt))
522b87a5ea3SJon Maloy 		goto drop;
5235b8dddb6SJon Maloy 
52431c82a2dSJon Maloy 	TIPC_SKB_CB(skb)->orig_member = m->instance;
525b87a5ea3SJon Maloy 	defq = &m->deferredq;
526b87a5ea3SJon Maloy 	tipc_group_sort_msg(skb, defq);
52775da2163SJon Maloy 
528b87a5ea3SJon Maloy 	while ((skb = skb_peek(defq))) {
529b87a5ea3SJon Maloy 		hdr = buf_msg(skb);
530b87a5ea3SJon Maloy 		mtyp = msg_type(hdr);
5312e724dcaSJon Maloy 		blks = msg_blocks(hdr);
532b87a5ea3SJon Maloy 		deliver = true;
5332f487712SJon Maloy 		ack = false;
534b87a5ea3SJon Maloy 		update = false;
535b87a5ea3SJon Maloy 
536b87a5ea3SJon Maloy 		if (more(msg_grp_bc_seqno(hdr), m->bc_rcv_nxt))
537b87a5ea3SJon Maloy 			break;
538b87a5ea3SJon Maloy 
539b87a5ea3SJon Maloy 		/* Decide what to do with message */
540b87a5ea3SJon Maloy 		switch (mtyp) {
541b87a5ea3SJon Maloy 		case TIPC_GRP_MCAST_MSG:
542b87a5ea3SJon Maloy 			if (msg_nameinst(hdr) != grp->instance) {
543b87a5ea3SJon Maloy 				update = true;
544b87a5ea3SJon Maloy 				deliver = false;
545b87a5ea3SJon Maloy 			}
546df561f66SGustavo A. R. Silva 			fallthrough;
547b87a5ea3SJon Maloy 		case TIPC_GRP_BCAST_MSG:
548b87a5ea3SJon Maloy 			m->bc_rcv_nxt++;
5492f487712SJon Maloy 			ack = msg_grp_bc_ack_req(hdr);
550b87a5ea3SJon Maloy 			break;
551b87a5ea3SJon Maloy 		case TIPC_GRP_UCAST_MSG:
552b87a5ea3SJon Maloy 			break;
553a3bada70SJon Maloy 		case TIPC_GRP_MEMBER_EVT:
554a3bada70SJon Maloy 			if (m->state == MBR_LEAVING)
555a3bada70SJon Maloy 				leave = true;
556a3bada70SJon Maloy 			if (!grp->events)
557a3bada70SJon Maloy 				deliver = false;
558a3bada70SJon Maloy 			break;
559b87a5ea3SJon Maloy 		default:
560b87a5ea3SJon Maloy 			break;
561b87a5ea3SJon Maloy 		}
562b87a5ea3SJon Maloy 
563b87a5ea3SJon Maloy 		/* Execute decisions */
564b87a5ea3SJon Maloy 		__skb_dequeue(defq);
565b87a5ea3SJon Maloy 		if (deliver)
566b87a5ea3SJon Maloy 			__skb_queue_tail(inputq, skb);
567b87a5ea3SJon Maloy 		else
568b87a5ea3SJon Maloy 			kfree_skb(skb);
569b87a5ea3SJon Maloy 
5702f487712SJon Maloy 		if (ack)
5712f487712SJon Maloy 			tipc_group_proto_xmit(grp, m, GRP_ACK_MSG, xmitq);
5722f487712SJon Maloy 
573a3bada70SJon Maloy 		if (leave) {
574a3bada70SJon Maloy 			__skb_queue_purge(defq);
575e0e853acSJon Maloy 			tipc_group_delete_member(grp, m);
576a3bada70SJon Maloy 			break;
577a3bada70SJon Maloy 		}
578b87a5ea3SJon Maloy 		if (!update)
579b87a5ea3SJon Maloy 			continue;
580b87a5ea3SJon Maloy 
581b87a5ea3SJon Maloy 		tipc_group_update_rcv_win(grp, blks, node, port, xmitq);
582b87a5ea3SJon Maloy 	}
58375da2163SJon Maloy 	return;
58475da2163SJon Maloy drop:
58575da2163SJon Maloy 	kfree_skb(skb);
58675da2163SJon Maloy }
58775da2163SJon Maloy 
tipc_group_update_rcv_win(struct tipc_group * grp,int blks,u32 node,u32 port,struct sk_buff_head * xmitq)588b7d42635SJon Maloy void tipc_group_update_rcv_win(struct tipc_group *grp, int blks, u32 node,
589b7d42635SJon Maloy 			       u32 port, struct sk_buff_head *xmitq)
590b7d42635SJon Maloy {
59104d7b574SJon Maloy 	struct list_head *active = &grp->active;
59204d7b574SJon Maloy 	int max_active = grp->max_active;
59304d7b574SJon Maloy 	int reclaim_limit = max_active * 3 / 4;
59404d7b574SJon Maloy 	int active_cnt = grp->active_cnt;
595f9c935dbSJon Maloy 	struct tipc_member *m, *rm, *pm;
596b7d42635SJon Maloy 
597b7d42635SJon Maloy 	m = tipc_group_find_member(grp, node, port);
598b7d42635SJon Maloy 	if (!m)
599b7d42635SJon Maloy 		return;
600b7d42635SJon Maloy 
601b7d42635SJon Maloy 	m->advertised -= blks;
602b7d42635SJon Maloy 
603b7d42635SJon Maloy 	switch (m->state) {
604b7d42635SJon Maloy 	case MBR_JOINED:
6054ea5dab5SJon Maloy 		/* First, decide if member can go active */
6064ea5dab5SJon Maloy 		if (active_cnt <= max_active) {
6074ea5dab5SJon Maloy 			m->state = MBR_ACTIVE;
6084ea5dab5SJon Maloy 			list_add_tail(&m->list, active);
6094ea5dab5SJon Maloy 			grp->active_cnt++;
6104ea5dab5SJon Maloy 			tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
6114ea5dab5SJon Maloy 		} else {
6124ea5dab5SJon Maloy 			m->state = MBR_PENDING;
6134ea5dab5SJon Maloy 			list_add_tail(&m->list, &grp->pending);
6144ea5dab5SJon Maloy 		}
6154ea5dab5SJon Maloy 
6164ea5dab5SJon Maloy 		if (active_cnt < reclaim_limit)
6174ea5dab5SJon Maloy 			break;
6184ea5dab5SJon Maloy 
6194ea5dab5SJon Maloy 		/* Reclaim from oldest active member, if possible */
6204ea5dab5SJon Maloy 		if (!list_empty(active)) {
62104d7b574SJon Maloy 			rm = list_first_entry(active, struct tipc_member, list);
62204d7b574SJon Maloy 			rm->state = MBR_RECLAIMING;
6238d5dee21SJon Maloy 			list_del_init(&rm->list);
62404d7b574SJon Maloy 			tipc_group_proto_xmit(grp, rm, GRP_RECLAIM_MSG, xmitq);
62504d7b574SJon Maloy 			break;
62604d7b574SJon Maloy 		}
6274ea5dab5SJon Maloy 		/* Nobody to reclaim from; - revert oldest pending to JOINED */
6284ea5dab5SJon Maloy 		pm = list_first_entry(&grp->pending, struct tipc_member, list);
6294ea5dab5SJon Maloy 		list_del_init(&pm->list);
6304ea5dab5SJon Maloy 		pm->state = MBR_JOINED;
6314ea5dab5SJon Maloy 		tipc_group_proto_xmit(grp, pm, GRP_ADV_MSG, xmitq);
6324ea5dab5SJon Maloy 		break;
63304d7b574SJon Maloy 	case MBR_ACTIVE:
63404d7b574SJon Maloy 		if (!list_is_last(&m->list, &grp->active))
63504d7b574SJon Maloy 			list_move_tail(&m->list, &grp->active);
63604d7b574SJon Maloy 		if (m->advertised > (ADV_ACTIVE * 3 / 4))
63704d7b574SJon Maloy 			break;
638b7d42635SJon Maloy 		tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
639b7d42635SJon Maloy 		break;
64004d7b574SJon Maloy 	case MBR_REMITTED:
64104d7b574SJon Maloy 		if (m->advertised > ADV_IDLE)
64204d7b574SJon Maloy 			break;
64304d7b574SJon Maloy 		m->state = MBR_JOINED;
6448d5dee21SJon Maloy 		grp->active_cnt--;
64504d7b574SJon Maloy 		if (m->advertised < ADV_IDLE) {
64604d7b574SJon Maloy 			pr_warn_ratelimited("Rcv unexpected msg after REMIT\n");
64704d7b574SJon Maloy 			tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
64804d7b574SJon Maloy 		}
6498d5dee21SJon Maloy 
650f9c935dbSJon Maloy 		if (list_empty(&grp->pending))
651f9c935dbSJon Maloy 			return;
652f9c935dbSJon Maloy 
653f9c935dbSJon Maloy 		/* Set oldest pending member to active and advertise */
654f9c935dbSJon Maloy 		pm = list_first_entry(&grp->pending, struct tipc_member, list);
655f9c935dbSJon Maloy 		pm->state = MBR_ACTIVE;
656f9c935dbSJon Maloy 		list_move_tail(&pm->list, &grp->active);
657f9c935dbSJon Maloy 		grp->active_cnt++;
658f9c935dbSJon Maloy 		tipc_group_proto_xmit(grp, pm, GRP_ADV_MSG, xmitq);
65904d7b574SJon Maloy 		break;
66004d7b574SJon Maloy 	case MBR_RECLAIMING:
661b7d42635SJon Maloy 	case MBR_JOINING:
662b7d42635SJon Maloy 	case MBR_LEAVING:
663b7d42635SJon Maloy 	default:
664b7d42635SJon Maloy 		break;
665b7d42635SJon Maloy 	}
666b7d42635SJon Maloy }
667b7d42635SJon Maloy 
tipc_group_create_event(struct tipc_group * grp,struct tipc_member * m,u32 event,u16 seqno,struct sk_buff_head * inputq)6687ad32bcbSJon Maloy static void tipc_group_create_event(struct tipc_group *grp,
6697ad32bcbSJon Maloy 				    struct tipc_member *m,
6707ad32bcbSJon Maloy 				    u32 event, u16 seqno,
6717ad32bcbSJon Maloy 				    struct sk_buff_head *inputq)
6727ad32bcbSJon Maloy {	u32 dnode = tipc_own_addr(grp->net);
6737ad32bcbSJon Maloy 	struct tipc_event evt;
6747ad32bcbSJon Maloy 	struct sk_buff *skb;
6757ad32bcbSJon Maloy 	struct tipc_msg *hdr;
6767ad32bcbSJon Maloy 
677b06f9d9fSJon Maloy 	memset(&evt, 0, sizeof(evt));
6787ad32bcbSJon Maloy 	evt.event = event;
6797ad32bcbSJon Maloy 	evt.found_lower = m->instance;
6807ad32bcbSJon Maloy 	evt.found_upper = m->instance;
6817ad32bcbSJon Maloy 	evt.port.ref = m->port;
6827ad32bcbSJon Maloy 	evt.port.node = m->node;
6837ad32bcbSJon Maloy 	evt.s.seq.type = grp->type;
6847ad32bcbSJon Maloy 	evt.s.seq.lower = m->instance;
6857ad32bcbSJon Maloy 	evt.s.seq.upper = m->instance;
6867ad32bcbSJon Maloy 
6877ad32bcbSJon Maloy 	skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_GRP_MEMBER_EVT,
6887ad32bcbSJon Maloy 			      GROUP_H_SIZE, sizeof(evt), dnode, m->node,
6897ad32bcbSJon Maloy 			      grp->portid, m->port, 0);
6907ad32bcbSJon Maloy 	if (!skb)
6917ad32bcbSJon Maloy 		return;
6927ad32bcbSJon Maloy 
6937ad32bcbSJon Maloy 	hdr = buf_msg(skb);
6947ad32bcbSJon Maloy 	msg_set_nametype(hdr, grp->type);
6957ad32bcbSJon Maloy 	msg_set_grp_evt(hdr, event);
6967ad32bcbSJon Maloy 	msg_set_dest_droppable(hdr, true);
6977ad32bcbSJon Maloy 	msg_set_grp_bc_seqno(hdr, seqno);
6987ad32bcbSJon Maloy 	memcpy(msg_data(hdr), &evt, sizeof(evt));
6997ad32bcbSJon Maloy 	TIPC_SKB_CB(skb)->orig_member = m->instance;
7007ad32bcbSJon Maloy 	__skb_queue_tail(inputq, skb);
7017ad32bcbSJon Maloy }
7027ad32bcbSJon Maloy 
tipc_group_proto_xmit(struct tipc_group * grp,struct tipc_member * m,int mtyp,struct sk_buff_head * xmitq)70375da2163SJon Maloy static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m,
70475da2163SJon Maloy 				  int mtyp, struct sk_buff_head *xmitq)
70575da2163SJon Maloy {
70675da2163SJon Maloy 	struct tipc_msg *hdr;
70775da2163SJon Maloy 	struct sk_buff *skb;
708b7d42635SJon Maloy 	int adv = 0;
70975da2163SJon Maloy 
71075da2163SJon Maloy 	skb = tipc_msg_create(GROUP_PROTOCOL, mtyp, INT_H_SIZE, 0,
71175da2163SJon Maloy 			      m->node, tipc_own_addr(grp->net),
71275da2163SJon Maloy 			      m->port, grp->portid, 0);
71375da2163SJon Maloy 	if (!skb)
71475da2163SJon Maloy 		return;
71575da2163SJon Maloy 
71604d7b574SJon Maloy 	if (m->state == MBR_ACTIVE)
717b7d42635SJon Maloy 		adv = ADV_ACTIVE - m->advertised;
71804d7b574SJon Maloy 	else if (m->state == MBR_JOINED || m->state == MBR_PENDING)
71904d7b574SJon Maloy 		adv = ADV_IDLE - m->advertised;
720b7d42635SJon Maloy 
72175da2163SJon Maloy 	hdr = buf_msg(skb);
722b7d42635SJon Maloy 
723b7d42635SJon Maloy 	if (mtyp == GRP_JOIN_MSG) {
72475da2163SJon Maloy 		msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt);
725b7d42635SJon Maloy 		msg_set_adv_win(hdr, adv);
726b7d42635SJon Maloy 		m->advertised += adv;
727a3bada70SJon Maloy 	} else if (mtyp == GRP_LEAVE_MSG) {
728a3bada70SJon Maloy 		msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt);
729b7d42635SJon Maloy 	} else if (mtyp == GRP_ADV_MSG) {
730b7d42635SJon Maloy 		msg_set_adv_win(hdr, adv);
731b7d42635SJon Maloy 		m->advertised += adv;
7322f487712SJon Maloy 	} else if (mtyp == GRP_ACK_MSG) {
7332f487712SJon Maloy 		msg_set_grp_bc_acked(hdr, m->bc_rcv_nxt);
73404d7b574SJon Maloy 	} else if (mtyp == GRP_REMIT_MSG) {
73504d7b574SJon Maloy 		msg_set_grp_remitted(hdr, m->window);
736b7d42635SJon Maloy 	}
73723483399SJon Maloy 	msg_set_dest_droppable(hdr, true);
73875da2163SJon Maloy 	__skb_queue_tail(xmitq, skb);
73975da2163SJon Maloy }
74075da2163SJon Maloy 
tipc_group_proto_rcv(struct tipc_group * grp,bool * usr_wakeup,struct tipc_msg * hdr,struct sk_buff_head * inputq,struct sk_buff_head * xmitq)741b7d42635SJon Maloy void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup,
742b7d42635SJon Maloy 			  struct tipc_msg *hdr, struct sk_buff_head *inputq,
74375da2163SJon Maloy 			  struct sk_buff_head *xmitq)
74475da2163SJon Maloy {
74575da2163SJon Maloy 	u32 node = msg_orignode(hdr);
74675da2163SJon Maloy 	u32 port = msg_origport(hdr);
74704d7b574SJon Maloy 	struct tipc_member *m, *pm;
74804d7b574SJon Maloy 	u16 remitted, in_flight;
74975da2163SJon Maloy 
75075da2163SJon Maloy 	if (!grp)
75175da2163SJon Maloy 		return;
75275da2163SJon Maloy 
753232d07b7SJon Maloy 	if (grp->scope == TIPC_NODE_SCOPE && node != tipc_own_addr(grp->net))
754232d07b7SJon Maloy 		return;
755232d07b7SJon Maloy 
75675da2163SJon Maloy 	m = tipc_group_find_member(grp, node, port);
75775da2163SJon Maloy 
75875da2163SJon Maloy 	switch (msg_type(hdr)) {
75975da2163SJon Maloy 	case GRP_JOIN_MSG:
76075da2163SJon Maloy 		if (!m)
76175da2163SJon Maloy 			m = tipc_group_create_member(grp, node, port,
762d12d2e12SJon Maloy 						     0, MBR_JOINING);
76375da2163SJon Maloy 		if (!m)
76475da2163SJon Maloy 			return;
765a3bada70SJon Maloy 		m->bc_syncpt = msg_grp_bc_syncpt(hdr);
766a3bada70SJon Maloy 		m->bc_rcv_nxt = m->bc_syncpt;
767b7d42635SJon Maloy 		m->window += msg_adv_win(hdr);
76875da2163SJon Maloy 
769d12d2e12SJon Maloy 		/* Wait until PUBLISH event is received if necessary */
770d12d2e12SJon Maloy 		if (m->state != MBR_PUBLISHED)
771d12d2e12SJon Maloy 			return;
772d12d2e12SJon Maloy 
773d12d2e12SJon Maloy 		/* Member can be taken into service */
77475da2163SJon Maloy 		m->state = MBR_JOINED;
775eb929a91SJon Maloy 		tipc_group_open(m, usr_wakeup);
776d12d2e12SJon Maloy 		tipc_group_update_member(m, 0);
777b7d42635SJon Maloy 		tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
7787ad32bcbSJon Maloy 		tipc_group_create_event(grp, m, TIPC_PUBLISHED,
7797ad32bcbSJon Maloy 					m->bc_syncpt, inputq);
78075da2163SJon Maloy 		return;
78175da2163SJon Maloy 	case GRP_LEAVE_MSG:
78275da2163SJon Maloy 		if (!m)
78375da2163SJon Maloy 			return;
784a3bada70SJon Maloy 		m->bc_syncpt = msg_grp_bc_syncpt(hdr);
7853f42f5feSJon Maloy 		list_del_init(&m->list);
786eb929a91SJon Maloy 		tipc_group_open(m, usr_wakeup);
78704d7b574SJon Maloy 		tipc_group_decr_active(grp, m);
78875da2163SJon Maloy 		m->state = MBR_LEAVING;
7897ad32bcbSJon Maloy 		tipc_group_create_event(grp, m, TIPC_WITHDRAWN,
7907ad32bcbSJon Maloy 					m->bc_syncpt, inputq);
791b7d42635SJon Maloy 		return;
792b7d42635SJon Maloy 	case GRP_ADV_MSG:
793b7d42635SJon Maloy 		if (!m)
794b7d42635SJon Maloy 			return;
795b7d42635SJon Maloy 		m->window += msg_adv_win(hdr);
796eb929a91SJon Maloy 		tipc_group_open(m, usr_wakeup);
79775da2163SJon Maloy 		return;
7982f487712SJon Maloy 	case GRP_ACK_MSG:
7992f487712SJon Maloy 		if (!m)
8002f487712SJon Maloy 			return;
8012f487712SJon Maloy 		m->bc_acked = msg_grp_bc_acked(hdr);
8022f487712SJon Maloy 		if (--grp->bc_ackers)
803eb929a91SJon Maloy 			return;
804eb929a91SJon Maloy 		list_del_init(&m->small_win);
80560c25306SJon Maloy 		*m->group->open = true;
8062f487712SJon Maloy 		*usr_wakeup = true;
807eb929a91SJon Maloy 		tipc_group_update_member(m, 0);
8082f487712SJon Maloy 		return;
80904d7b574SJon Maloy 	case GRP_RECLAIM_MSG:
81004d7b574SJon Maloy 		if (!m)
81104d7b574SJon Maloy 			return;
81204d7b574SJon Maloy 		tipc_group_proto_xmit(grp, m, GRP_REMIT_MSG, xmitq);
81304d7b574SJon Maloy 		m->window = ADV_IDLE;
814eb929a91SJon Maloy 		tipc_group_open(m, usr_wakeup);
81504d7b574SJon Maloy 		return;
81604d7b574SJon Maloy 	case GRP_REMIT_MSG:
81704d7b574SJon Maloy 		if (!m || m->state != MBR_RECLAIMING)
81804d7b574SJon Maloy 			return;
81904d7b574SJon Maloy 
82004d7b574SJon Maloy 		remitted = msg_grp_remitted(hdr);
82104d7b574SJon Maloy 
82204d7b574SJon Maloy 		/* Messages preceding the REMIT still in receive queue */
82304d7b574SJon Maloy 		if (m->advertised > remitted) {
82404d7b574SJon Maloy 			m->state = MBR_REMITTED;
82504d7b574SJon Maloy 			in_flight = m->advertised - remitted;
826f9c935dbSJon Maloy 			m->advertised = ADV_IDLE + in_flight;
827f9c935dbSJon Maloy 			return;
82804d7b574SJon Maloy 		}
8298d5dee21SJon Maloy 		/* This should never happen */
83004d7b574SJon Maloy 		if (m->advertised < remitted)
8318d5dee21SJon Maloy 			pr_warn_ratelimited("Unexpected REMIT msg\n");
83204d7b574SJon Maloy 
8338d5dee21SJon Maloy 		/* All messages preceding the REMIT have been read */
8348d5dee21SJon Maloy 		m->state = MBR_JOINED;
835f9c935dbSJon Maloy 		grp->active_cnt--;
8368d5dee21SJon Maloy 		m->advertised = ADV_IDLE;
83704d7b574SJon Maloy 
83804d7b574SJon Maloy 		/* Set oldest pending member to active and advertise */
83904d7b574SJon Maloy 		if (list_empty(&grp->pending))
84004d7b574SJon Maloy 			return;
84104d7b574SJon Maloy 		pm = list_first_entry(&grp->pending, struct tipc_member, list);
84204d7b574SJon Maloy 		pm->state = MBR_ACTIVE;
84304d7b574SJon Maloy 		list_move_tail(&pm->list, &grp->active);
84404d7b574SJon Maloy 		grp->active_cnt++;
84504d7b574SJon Maloy 		if (pm->advertised <= (ADV_ACTIVE * 3 / 4))
84604d7b574SJon Maloy 			tipc_group_proto_xmit(grp, pm, GRP_ADV_MSG, xmitq);
84704d7b574SJon Maloy 		return;
84875da2163SJon Maloy 	default:
84975da2163SJon Maloy 		pr_warn("Received unknown GROUP_PROTO message\n");
85075da2163SJon Maloy 	}
85175da2163SJon Maloy }
85275da2163SJon Maloy 
853b7d42635SJon Maloy /* tipc_group_member_evt() - receive and handle a member up/down event
854b7d42635SJon Maloy  */
tipc_group_member_evt(struct tipc_group * grp,bool * usr_wakeup,int * sk_rcvbuf,struct tipc_msg * hdr,struct sk_buff_head * inputq,struct sk_buff_head * xmitq)85575da2163SJon Maloy void tipc_group_member_evt(struct tipc_group *grp,
856b7d42635SJon Maloy 			   bool *usr_wakeup,
857b7d42635SJon Maloy 			   int *sk_rcvbuf,
8587ad32bcbSJon Maloy 			   struct tipc_msg *hdr,
859ae236fb2SJon Maloy 			   struct sk_buff_head *inputq,
86075da2163SJon Maloy 			   struct sk_buff_head *xmitq)
86175da2163SJon Maloy {
86275da2163SJon Maloy 	struct tipc_event *evt = (void *)msg_data(hdr);
863ae236fb2SJon Maloy 	u32 instance = evt->found_lower;
86475da2163SJon Maloy 	u32 node = evt->port.node;
86575da2163SJon Maloy 	u32 port = evt->port.ref;
866ae236fb2SJon Maloy 	int event = evt->event;
86775da2163SJon Maloy 	struct tipc_member *m;
86875da2163SJon Maloy 	struct net *net;
86975da2163SJon Maloy 	u32 self;
87075da2163SJon Maloy 
87175da2163SJon Maloy 	if (!grp)
8727ad32bcbSJon Maloy 		return;
87375da2163SJon Maloy 
87475da2163SJon Maloy 	net = grp->net;
87575da2163SJon Maloy 	self = tipc_own_addr(net);
87675da2163SJon Maloy 	if (!grp->loopback && node == self && port == grp->portid)
8777ad32bcbSJon Maloy 		return;
878ae236fb2SJon Maloy 
87975da2163SJon Maloy 	m = tipc_group_find_member(grp, node, port);
88075da2163SJon Maloy 
881d12d2e12SJon Maloy 	switch (event) {
882d12d2e12SJon Maloy 	case TIPC_PUBLISHED:
883d12d2e12SJon Maloy 		/* Send and wait for arrival of JOIN message if necessary */
884d12d2e12SJon Maloy 		if (!m) {
885d12d2e12SJon Maloy 			m = tipc_group_create_member(grp, node, port, instance,
886d12d2e12SJon Maloy 						     MBR_PUBLISHED);
88775da2163SJon Maloy 			if (!m)
888d12d2e12SJon Maloy 				break;
889d12d2e12SJon Maloy 			tipc_group_update_member(m, 0);
890d12d2e12SJon Maloy 			tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq);
891d12d2e12SJon Maloy 			break;
892d12d2e12SJon Maloy 		}
8937ad32bcbSJon Maloy 
894d12d2e12SJon Maloy 		if (m->state != MBR_JOINING)
895d12d2e12SJon Maloy 			break;
896d12d2e12SJon Maloy 
897d12d2e12SJon Maloy 		/* Member can be taken into service */
8987ad32bcbSJon Maloy 		m->instance = instance;
89975da2163SJon Maloy 		m->state = MBR_JOINED;
900eb929a91SJon Maloy 		tipc_group_open(m, usr_wakeup);
901b7d42635SJon Maloy 		tipc_group_update_member(m, 0);
902d12d2e12SJon Maloy 		tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq);
903d12d2e12SJon Maloy 		tipc_group_create_event(grp, m, TIPC_PUBLISHED,
904d12d2e12SJon Maloy 					m->bc_syncpt, inputq);
905d12d2e12SJon Maloy 		break;
906d12d2e12SJon Maloy 	case TIPC_WITHDRAWN:
90775da2163SJon Maloy 		if (!m)
908d12d2e12SJon Maloy 			break;
909ae236fb2SJon Maloy 
91004d7b574SJon Maloy 		tipc_group_decr_active(grp, m);
91175da2163SJon Maloy 		m->state = MBR_LEAVING;
9123f42f5feSJon Maloy 		list_del_init(&m->list);
913eb929a91SJon Maloy 		tipc_group_open(m, usr_wakeup);
914c2b22bcfSJon Maloy 
915c2b22bcfSJon Maloy 		/* Only send event if no LEAVE message can be expected */
916c2b22bcfSJon Maloy 		if (!tipc_node_is_up(net, node))
917c2b22bcfSJon Maloy 			tipc_group_create_event(grp, m, TIPC_WITHDRAWN,
918c2b22bcfSJon Maloy 						m->bc_rcv_nxt, inputq);
919d12d2e12SJon Maloy 		break;
920d12d2e12SJon Maloy 	default:
921d12d2e12SJon Maloy 		break;
922ae236fb2SJon Maloy 	}
923b7d42635SJon Maloy 	*sk_rcvbuf = tipc_group_rcvbuf_limit(grp);
92475da2163SJon Maloy }
925a1be5a20SGhantaKrishnamurthy MohanKrishna 
tipc_group_fill_sock_diag(struct tipc_group * grp,struct sk_buff * skb)926a1be5a20SGhantaKrishnamurthy MohanKrishna int tipc_group_fill_sock_diag(struct tipc_group *grp, struct sk_buff *skb)
927a1be5a20SGhantaKrishnamurthy MohanKrishna {
928ae0be8deSMichal Kubecek 	struct nlattr *group = nla_nest_start_noflag(skb, TIPC_NLA_SOCK_GROUP);
929a1be5a20SGhantaKrishnamurthy MohanKrishna 
9304589e28dSKangjie Lu 	if (!group)
9314589e28dSKangjie Lu 		return -EMSGSIZE;
9324589e28dSKangjie Lu 
933a1be5a20SGhantaKrishnamurthy MohanKrishna 	if (nla_put_u32(skb, TIPC_NLA_SOCK_GROUP_ID,
934a1be5a20SGhantaKrishnamurthy MohanKrishna 			grp->type) ||
935a1be5a20SGhantaKrishnamurthy MohanKrishna 	    nla_put_u32(skb, TIPC_NLA_SOCK_GROUP_INSTANCE,
936a1be5a20SGhantaKrishnamurthy MohanKrishna 			grp->instance) ||
937a1be5a20SGhantaKrishnamurthy MohanKrishna 	    nla_put_u32(skb, TIPC_NLA_SOCK_GROUP_BC_SEND_NEXT,
938a1be5a20SGhantaKrishnamurthy MohanKrishna 			grp->bc_snd_nxt))
939a1be5a20SGhantaKrishnamurthy MohanKrishna 		goto group_msg_cancel;
940a1be5a20SGhantaKrishnamurthy MohanKrishna 
941a1be5a20SGhantaKrishnamurthy MohanKrishna 	if (grp->scope == TIPC_NODE_SCOPE)
942a1be5a20SGhantaKrishnamurthy MohanKrishna 		if (nla_put_flag(skb, TIPC_NLA_SOCK_GROUP_NODE_SCOPE))
943a1be5a20SGhantaKrishnamurthy MohanKrishna 			goto group_msg_cancel;
944a1be5a20SGhantaKrishnamurthy MohanKrishna 
945a1be5a20SGhantaKrishnamurthy MohanKrishna 	if (grp->scope == TIPC_CLUSTER_SCOPE)
946a1be5a20SGhantaKrishnamurthy MohanKrishna 		if (nla_put_flag(skb, TIPC_NLA_SOCK_GROUP_CLUSTER_SCOPE))
947a1be5a20SGhantaKrishnamurthy MohanKrishna 			goto group_msg_cancel;
948a1be5a20SGhantaKrishnamurthy MohanKrishna 
949a1be5a20SGhantaKrishnamurthy MohanKrishna 	if (*grp->open)
950a1be5a20SGhantaKrishnamurthy MohanKrishna 		if (nla_put_flag(skb, TIPC_NLA_SOCK_GROUP_OPEN))
951a1be5a20SGhantaKrishnamurthy MohanKrishna 			goto group_msg_cancel;
952a1be5a20SGhantaKrishnamurthy MohanKrishna 
953a1be5a20SGhantaKrishnamurthy MohanKrishna 	nla_nest_end(skb, group);
954a1be5a20SGhantaKrishnamurthy MohanKrishna 	return 0;
955a1be5a20SGhantaKrishnamurthy MohanKrishna 
956a1be5a20SGhantaKrishnamurthy MohanKrishna group_msg_cancel:
957a1be5a20SGhantaKrishnamurthy MohanKrishna 	nla_nest_cancel(skb, group);
958a1be5a20SGhantaKrishnamurthy MohanKrishna 	return -1;
959a1be5a20SGhantaKrishnamurthy MohanKrishna }
960