162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * net/tipc/group.c: TIPC group messaging code
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2017, Ericsson AB
562306a36Sopenharmony_ci * Copyright (c) 2020, Red Hat Inc
662306a36Sopenharmony_ci * All rights reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
962306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
1262306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
1362306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
1462306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
1562306a36Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
1662306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its
1762306a36Sopenharmony_ci *    contributors may be used to endorse or promote products derived from
1862306a36Sopenharmony_ci *    this software without specific prior written permission.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
2162306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
2262306a36Sopenharmony_ci * Software Foundation.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2562306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2662306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2762306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2862306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2962306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3062306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3162306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3262306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3362306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3462306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "core.h"
3862306a36Sopenharmony_ci#include "addr.h"
3962306a36Sopenharmony_ci#include "group.h"
4062306a36Sopenharmony_ci#include "bcast.h"
4162306a36Sopenharmony_ci#include "topsrv.h"
4262306a36Sopenharmony_ci#include "msg.h"
4362306a36Sopenharmony_ci#include "socket.h"
4462306a36Sopenharmony_ci#include "node.h"
4562306a36Sopenharmony_ci#include "name_table.h"
4662306a36Sopenharmony_ci#include "subscr.h"
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define ADV_UNIT (((MAX_MSG_SIZE + MAX_H_SIZE) / FLOWCTL_BLK_SZ) + 1)
4962306a36Sopenharmony_ci#define ADV_IDLE ADV_UNIT
5062306a36Sopenharmony_ci#define ADV_ACTIVE (ADV_UNIT * 12)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cienum mbr_state {
5362306a36Sopenharmony_ci	MBR_JOINING,
5462306a36Sopenharmony_ci	MBR_PUBLISHED,
5562306a36Sopenharmony_ci	MBR_JOINED,
5662306a36Sopenharmony_ci	MBR_PENDING,
5762306a36Sopenharmony_ci	MBR_ACTIVE,
5862306a36Sopenharmony_ci	MBR_RECLAIMING,
5962306a36Sopenharmony_ci	MBR_REMITTED,
6062306a36Sopenharmony_ci	MBR_LEAVING
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct tipc_member {
6462306a36Sopenharmony_ci	struct rb_node tree_node;
6562306a36Sopenharmony_ci	struct list_head list;
6662306a36Sopenharmony_ci	struct list_head small_win;
6762306a36Sopenharmony_ci	struct sk_buff_head deferredq;
6862306a36Sopenharmony_ci	struct tipc_group *group;
6962306a36Sopenharmony_ci	u32 node;
7062306a36Sopenharmony_ci	u32 port;
7162306a36Sopenharmony_ci	u32 instance;
7262306a36Sopenharmony_ci	enum mbr_state state;
7362306a36Sopenharmony_ci	u16 advertised;
7462306a36Sopenharmony_ci	u16 window;
7562306a36Sopenharmony_ci	u16 bc_rcv_nxt;
7662306a36Sopenharmony_ci	u16 bc_syncpt;
7762306a36Sopenharmony_ci	u16 bc_acked;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct tipc_group {
8162306a36Sopenharmony_ci	struct rb_root members;
8262306a36Sopenharmony_ci	struct list_head small_win;
8362306a36Sopenharmony_ci	struct list_head pending;
8462306a36Sopenharmony_ci	struct list_head active;
8562306a36Sopenharmony_ci	struct tipc_nlist dests;
8662306a36Sopenharmony_ci	struct net *net;
8762306a36Sopenharmony_ci	int subid;
8862306a36Sopenharmony_ci	u32 type;
8962306a36Sopenharmony_ci	u32 instance;
9062306a36Sopenharmony_ci	u32 scope;
9162306a36Sopenharmony_ci	u32 portid;
9262306a36Sopenharmony_ci	u16 member_cnt;
9362306a36Sopenharmony_ci	u16 active_cnt;
9462306a36Sopenharmony_ci	u16 max_active;
9562306a36Sopenharmony_ci	u16 bc_snd_nxt;
9662306a36Sopenharmony_ci	u16 bc_ackers;
9762306a36Sopenharmony_ci	bool *open;
9862306a36Sopenharmony_ci	bool loopback;
9962306a36Sopenharmony_ci	bool events;
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m,
10362306a36Sopenharmony_ci				  int mtyp, struct sk_buff_head *xmitq);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void tipc_group_open(struct tipc_member *m, bool *wakeup)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	*wakeup = false;
10862306a36Sopenharmony_ci	if (list_empty(&m->small_win))
10962306a36Sopenharmony_ci		return;
11062306a36Sopenharmony_ci	list_del_init(&m->small_win);
11162306a36Sopenharmony_ci	*m->group->open = true;
11262306a36Sopenharmony_ci	*wakeup = true;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void tipc_group_decr_active(struct tipc_group *grp,
11662306a36Sopenharmony_ci				   struct tipc_member *m)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	if (m->state == MBR_ACTIVE || m->state == MBR_RECLAIMING ||
11962306a36Sopenharmony_ci	    m->state == MBR_REMITTED)
12062306a36Sopenharmony_ci		grp->active_cnt--;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int tipc_group_rcvbuf_limit(struct tipc_group *grp)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int max_active, active_pool, idle_pool;
12662306a36Sopenharmony_ci	int mcnt = grp->member_cnt + 1;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Limit simultaneous reception from other members */
12962306a36Sopenharmony_ci	max_active = min(mcnt / 8, 64);
13062306a36Sopenharmony_ci	max_active = max(max_active, 16);
13162306a36Sopenharmony_ci	grp->max_active = max_active;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Reserve blocks for active and idle members */
13462306a36Sopenharmony_ci	active_pool = max_active * ADV_ACTIVE;
13562306a36Sopenharmony_ci	idle_pool = (mcnt - max_active) * ADV_IDLE;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Scale to bytes, considering worst-case truesize/msgsize ratio */
13862306a36Sopenharmony_ci	return (active_pool + idle_pool) * FLOWCTL_BLK_SZ * 4;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciu16 tipc_group_bc_snd_nxt(struct tipc_group *grp)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	return grp->bc_snd_nxt;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic bool tipc_group_is_receiver(struct tipc_member *m)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	return m && m->state != MBR_JOINING && m->state != MBR_LEAVING;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic bool tipc_group_is_sender(struct tipc_member *m)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	return m && m->state != MBR_JOINING && m->state != MBR_PUBLISHED;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciu32 tipc_group_exclude(struct tipc_group *grp)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	if (!grp->loopback)
15962306a36Sopenharmony_ci		return grp->portid;
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistruct tipc_group *tipc_group_create(struct net *net, u32 portid,
16462306a36Sopenharmony_ci				     struct tipc_group_req *mreq,
16562306a36Sopenharmony_ci				     bool *group_is_open)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	u32 filter = TIPC_SUB_PORTS | TIPC_SUB_NO_STATUS;
16862306a36Sopenharmony_ci	bool global = mreq->scope != TIPC_NODE_SCOPE;
16962306a36Sopenharmony_ci	struct tipc_group *grp;
17062306a36Sopenharmony_ci	u32 type = mreq->type;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	grp = kzalloc(sizeof(*grp), GFP_ATOMIC);
17362306a36Sopenharmony_ci	if (!grp)
17462306a36Sopenharmony_ci		return NULL;
17562306a36Sopenharmony_ci	tipc_nlist_init(&grp->dests, tipc_own_addr(net));
17662306a36Sopenharmony_ci	INIT_LIST_HEAD(&grp->small_win);
17762306a36Sopenharmony_ci	INIT_LIST_HEAD(&grp->active);
17862306a36Sopenharmony_ci	INIT_LIST_HEAD(&grp->pending);
17962306a36Sopenharmony_ci	grp->members = RB_ROOT;
18062306a36Sopenharmony_ci	grp->net = net;
18162306a36Sopenharmony_ci	grp->portid = portid;
18262306a36Sopenharmony_ci	grp->type = type;
18362306a36Sopenharmony_ci	grp->instance = mreq->instance;
18462306a36Sopenharmony_ci	grp->scope = mreq->scope;
18562306a36Sopenharmony_ci	grp->loopback = mreq->flags & TIPC_GROUP_LOOPBACK;
18662306a36Sopenharmony_ci	grp->events = mreq->flags & TIPC_GROUP_MEMBER_EVTS;
18762306a36Sopenharmony_ci	grp->open = group_is_open;
18862306a36Sopenharmony_ci	*grp->open = false;
18962306a36Sopenharmony_ci	filter |= global ? TIPC_SUB_CLUSTER_SCOPE : TIPC_SUB_NODE_SCOPE;
19062306a36Sopenharmony_ci	if (tipc_topsrv_kern_subscr(net, portid, type, 0, ~0,
19162306a36Sopenharmony_ci				    filter, &grp->subid))
19262306a36Sopenharmony_ci		return grp;
19362306a36Sopenharmony_ci	kfree(grp);
19462306a36Sopenharmony_ci	return NULL;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_civoid tipc_group_join(struct net *net, struct tipc_group *grp, int *sk_rcvbuf)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct rb_root *tree = &grp->members;
20062306a36Sopenharmony_ci	struct tipc_member *m, *tmp;
20162306a36Sopenharmony_ci	struct sk_buff_head xmitq;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
20462306a36Sopenharmony_ci	rbtree_postorder_for_each_entry_safe(m, tmp, tree, tree_node) {
20562306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, &xmitq);
20662306a36Sopenharmony_ci		tipc_group_update_member(m, 0);
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	tipc_node_distr_xmit(net, &xmitq);
20962306a36Sopenharmony_ci	*sk_rcvbuf = tipc_group_rcvbuf_limit(grp);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_civoid tipc_group_delete(struct net *net, struct tipc_group *grp)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct rb_root *tree = &grp->members;
21562306a36Sopenharmony_ci	struct tipc_member *m, *tmp;
21662306a36Sopenharmony_ci	struct sk_buff_head xmitq;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	rbtree_postorder_for_each_entry_safe(m, tmp, tree, tree_node) {
22162306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, m, GRP_LEAVE_MSG, &xmitq);
22262306a36Sopenharmony_ci		__skb_queue_purge(&m->deferredq);
22362306a36Sopenharmony_ci		list_del(&m->list);
22462306a36Sopenharmony_ci		kfree(m);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	tipc_node_distr_xmit(net, &xmitq);
22762306a36Sopenharmony_ci	tipc_nlist_purge(&grp->dests);
22862306a36Sopenharmony_ci	tipc_topsrv_kern_unsubscr(net, grp->subid);
22962306a36Sopenharmony_ci	kfree(grp);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic struct tipc_member *tipc_group_find_member(struct tipc_group *grp,
23362306a36Sopenharmony_ci						  u32 node, u32 port)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct rb_node *n = grp->members.rb_node;
23662306a36Sopenharmony_ci	u64 nkey, key = (u64)node << 32 | port;
23762306a36Sopenharmony_ci	struct tipc_member *m;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	while (n) {
24062306a36Sopenharmony_ci		m = container_of(n, struct tipc_member, tree_node);
24162306a36Sopenharmony_ci		nkey = (u64)m->node << 32 | m->port;
24262306a36Sopenharmony_ci		if (key < nkey)
24362306a36Sopenharmony_ci			n = n->rb_left;
24462306a36Sopenharmony_ci		else if (key > nkey)
24562306a36Sopenharmony_ci			n = n->rb_right;
24662306a36Sopenharmony_ci		else
24762306a36Sopenharmony_ci			return m;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	return NULL;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic struct tipc_member *tipc_group_find_dest(struct tipc_group *grp,
25362306a36Sopenharmony_ci						u32 node, u32 port)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct tipc_member *m;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	m = tipc_group_find_member(grp, node, port);
25862306a36Sopenharmony_ci	if (m && tipc_group_is_receiver(m))
25962306a36Sopenharmony_ci		return m;
26062306a36Sopenharmony_ci	return NULL;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic struct tipc_member *tipc_group_find_node(struct tipc_group *grp,
26462306a36Sopenharmony_ci						u32 node)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct tipc_member *m;
26762306a36Sopenharmony_ci	struct rb_node *n;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	for (n = rb_first(&grp->members); n; n = rb_next(n)) {
27062306a36Sopenharmony_ci		m = container_of(n, struct tipc_member, tree_node);
27162306a36Sopenharmony_ci		if (m->node == node)
27262306a36Sopenharmony_ci			return m;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	return NULL;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int tipc_group_add_to_tree(struct tipc_group *grp,
27862306a36Sopenharmony_ci				  struct tipc_member *m)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	u64 nkey, key = (u64)m->node << 32 | m->port;
28162306a36Sopenharmony_ci	struct rb_node **n, *parent = NULL;
28262306a36Sopenharmony_ci	struct tipc_member *tmp;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	n = &grp->members.rb_node;
28562306a36Sopenharmony_ci	while (*n) {
28662306a36Sopenharmony_ci		tmp = container_of(*n, struct tipc_member, tree_node);
28762306a36Sopenharmony_ci		parent = *n;
28862306a36Sopenharmony_ci		tmp = container_of(parent, struct tipc_member, tree_node);
28962306a36Sopenharmony_ci		nkey = (u64)tmp->node << 32 | tmp->port;
29062306a36Sopenharmony_ci		if (key < nkey)
29162306a36Sopenharmony_ci			n = &(*n)->rb_left;
29262306a36Sopenharmony_ci		else if (key > nkey)
29362306a36Sopenharmony_ci			n = &(*n)->rb_right;
29462306a36Sopenharmony_ci		else
29562306a36Sopenharmony_ci			return -EEXIST;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	rb_link_node(&m->tree_node, parent, n);
29862306a36Sopenharmony_ci	rb_insert_color(&m->tree_node, &grp->members);
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic struct tipc_member *tipc_group_create_member(struct tipc_group *grp,
30362306a36Sopenharmony_ci						    u32 node, u32 port,
30462306a36Sopenharmony_ci						    u32 instance, int state)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct tipc_member *m;
30762306a36Sopenharmony_ci	int ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	m = kzalloc(sizeof(*m), GFP_ATOMIC);
31062306a36Sopenharmony_ci	if (!m)
31162306a36Sopenharmony_ci		return NULL;
31262306a36Sopenharmony_ci	INIT_LIST_HEAD(&m->list);
31362306a36Sopenharmony_ci	INIT_LIST_HEAD(&m->small_win);
31462306a36Sopenharmony_ci	__skb_queue_head_init(&m->deferredq);
31562306a36Sopenharmony_ci	m->group = grp;
31662306a36Sopenharmony_ci	m->node = node;
31762306a36Sopenharmony_ci	m->port = port;
31862306a36Sopenharmony_ci	m->instance = instance;
31962306a36Sopenharmony_ci	m->bc_acked = grp->bc_snd_nxt - 1;
32062306a36Sopenharmony_ci	ret = tipc_group_add_to_tree(grp, m);
32162306a36Sopenharmony_ci	if (ret < 0) {
32262306a36Sopenharmony_ci		kfree(m);
32362306a36Sopenharmony_ci		return NULL;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	grp->member_cnt++;
32662306a36Sopenharmony_ci	tipc_nlist_add(&grp->dests, m->node);
32762306a36Sopenharmony_ci	m->state = state;
32862306a36Sopenharmony_ci	return m;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_civoid tipc_group_add_member(struct tipc_group *grp, u32 node,
33262306a36Sopenharmony_ci			   u32 port, u32 instance)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	tipc_group_create_member(grp, node, port, instance, MBR_PUBLISHED);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void tipc_group_delete_member(struct tipc_group *grp,
33862306a36Sopenharmony_ci				     struct tipc_member *m)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	rb_erase(&m->tree_node, &grp->members);
34162306a36Sopenharmony_ci	grp->member_cnt--;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* Check if we were waiting for replicast ack from this member */
34462306a36Sopenharmony_ci	if (grp->bc_ackers && less(m->bc_acked, grp->bc_snd_nxt - 1))
34562306a36Sopenharmony_ci		grp->bc_ackers--;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	list_del_init(&m->list);
34862306a36Sopenharmony_ci	list_del_init(&m->small_win);
34962306a36Sopenharmony_ci	tipc_group_decr_active(grp, m);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* If last member on a node, remove node from dest list */
35262306a36Sopenharmony_ci	if (!tipc_group_find_node(grp, m->node))
35362306a36Sopenharmony_ci		tipc_nlist_del(&grp->dests, m->node);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	kfree(m);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistruct tipc_nlist *tipc_group_dests(struct tipc_group *grp)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	return &grp->dests;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_civoid tipc_group_self(struct tipc_group *grp, struct tipc_service_range *seq,
36462306a36Sopenharmony_ci		     int *scope)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	seq->type = grp->type;
36762306a36Sopenharmony_ci	seq->lower = grp->instance;
36862306a36Sopenharmony_ci	seq->upper = grp->instance;
36962306a36Sopenharmony_ci	*scope = grp->scope;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_civoid tipc_group_update_member(struct tipc_member *m, int len)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct tipc_group *grp = m->group;
37562306a36Sopenharmony_ci	struct tipc_member *_m, *tmp;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (!tipc_group_is_receiver(m))
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	m->window -= len;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (m->window >= ADV_IDLE)
38362306a36Sopenharmony_ci		return;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	list_del_init(&m->small_win);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* Sort member into small_window members' list */
38862306a36Sopenharmony_ci	list_for_each_entry_safe(_m, tmp, &grp->small_win, small_win) {
38962306a36Sopenharmony_ci		if (_m->window > m->window)
39062306a36Sopenharmony_ci			break;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	list_add_tail(&m->small_win, &_m->small_win);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_civoid tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	u16 prev = grp->bc_snd_nxt - 1;
39862306a36Sopenharmony_ci	struct tipc_member *m;
39962306a36Sopenharmony_ci	struct rb_node *n;
40062306a36Sopenharmony_ci	u16 ackers = 0;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	for (n = rb_first(&grp->members); n; n = rb_next(n)) {
40362306a36Sopenharmony_ci		m = container_of(n, struct tipc_member, tree_node);
40462306a36Sopenharmony_ci		if (tipc_group_is_receiver(m)) {
40562306a36Sopenharmony_ci			tipc_group_update_member(m, len);
40662306a36Sopenharmony_ci			m->bc_acked = prev;
40762306a36Sopenharmony_ci			ackers++;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* Mark number of acknowledges to expect, if any */
41262306a36Sopenharmony_ci	if (ack)
41362306a36Sopenharmony_ci		grp->bc_ackers = ackers;
41462306a36Sopenharmony_ci	grp->bc_snd_nxt++;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cibool tipc_group_cong(struct tipc_group *grp, u32 dnode, u32 dport,
41862306a36Sopenharmony_ci		     int len, struct tipc_member **mbr)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct sk_buff_head xmitq;
42162306a36Sopenharmony_ci	struct tipc_member *m;
42262306a36Sopenharmony_ci	int adv, state;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	m = tipc_group_find_dest(grp, dnode, dport);
42562306a36Sopenharmony_ci	if (!tipc_group_is_receiver(m)) {
42662306a36Sopenharmony_ci		*mbr = NULL;
42762306a36Sopenharmony_ci		return false;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci	*mbr = m;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (m->window >= len)
43262306a36Sopenharmony_ci		return false;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	*grp->open = false;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/* If not fully advertised, do it now to prevent mutual blocking */
43762306a36Sopenharmony_ci	adv = m->advertised;
43862306a36Sopenharmony_ci	state = m->state;
43962306a36Sopenharmony_ci	if (state == MBR_JOINED && adv == ADV_IDLE)
44062306a36Sopenharmony_ci		return true;
44162306a36Sopenharmony_ci	if (state == MBR_ACTIVE && adv == ADV_ACTIVE)
44262306a36Sopenharmony_ci		return true;
44362306a36Sopenharmony_ci	if (state == MBR_PENDING && adv == ADV_IDLE)
44462306a36Sopenharmony_ci		return true;
44562306a36Sopenharmony_ci	__skb_queue_head_init(&xmitq);
44662306a36Sopenharmony_ci	tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, &xmitq);
44762306a36Sopenharmony_ci	tipc_node_distr_xmit(grp->net, &xmitq);
44862306a36Sopenharmony_ci	return true;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cibool tipc_group_bc_cong(struct tipc_group *grp, int len)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct tipc_member *m = NULL;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* If prev bcast was replicast, reject until all receivers have acked */
45662306a36Sopenharmony_ci	if (grp->bc_ackers) {
45762306a36Sopenharmony_ci		*grp->open = false;
45862306a36Sopenharmony_ci		return true;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	if (list_empty(&grp->small_win))
46162306a36Sopenharmony_ci		return false;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	m = list_first_entry(&grp->small_win, struct tipc_member, small_win);
46462306a36Sopenharmony_ci	if (m->window >= len)
46562306a36Sopenharmony_ci		return false;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return tipc_group_cong(grp, m->node, m->port, len, &m);
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci/* tipc_group_sort_msg() - sort msg into queue by bcast sequence number
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_cistatic void tipc_group_sort_msg(struct sk_buff *skb, struct sk_buff_head *defq)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct tipc_msg *_hdr, *hdr = buf_msg(skb);
47562306a36Sopenharmony_ci	u16 bc_seqno = msg_grp_bc_seqno(hdr);
47662306a36Sopenharmony_ci	struct sk_buff *_skb, *tmp;
47762306a36Sopenharmony_ci	int mtyp = msg_type(hdr);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Bcast/mcast may be bypassed by ucast or other bcast, - sort it in */
48062306a36Sopenharmony_ci	if (mtyp == TIPC_GRP_BCAST_MSG || mtyp == TIPC_GRP_MCAST_MSG) {
48162306a36Sopenharmony_ci		skb_queue_walk_safe(defq, _skb, tmp) {
48262306a36Sopenharmony_ci			_hdr = buf_msg(_skb);
48362306a36Sopenharmony_ci			if (!less(bc_seqno, msg_grp_bc_seqno(_hdr)))
48462306a36Sopenharmony_ci				continue;
48562306a36Sopenharmony_ci			__skb_queue_before(defq, _skb, skb);
48662306a36Sopenharmony_ci			return;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci		/* Bcast was not bypassed, - add to tail */
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci	/* Unicasts are never bypassed, - always add to tail */
49162306a36Sopenharmony_ci	__skb_queue_tail(defq, skb);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/* tipc_group_filter_msg() - determine if we should accept arriving message
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_civoid tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *inputq,
49762306a36Sopenharmony_ci			   struct sk_buff_head *xmitq)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct sk_buff *skb = __skb_dequeue(inputq);
50062306a36Sopenharmony_ci	bool ack, deliver, update, leave = false;
50162306a36Sopenharmony_ci	struct sk_buff_head *defq;
50262306a36Sopenharmony_ci	struct tipc_member *m;
50362306a36Sopenharmony_ci	struct tipc_msg *hdr;
50462306a36Sopenharmony_ci	u32 node, port;
50562306a36Sopenharmony_ci	int mtyp, blks;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (!skb)
50862306a36Sopenharmony_ci		return;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	hdr = buf_msg(skb);
51162306a36Sopenharmony_ci	node =  msg_orignode(hdr);
51262306a36Sopenharmony_ci	port = msg_origport(hdr);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (!msg_in_group(hdr))
51562306a36Sopenharmony_ci		goto drop;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	m = tipc_group_find_member(grp, node, port);
51862306a36Sopenharmony_ci	if (!tipc_group_is_sender(m))
51962306a36Sopenharmony_ci		goto drop;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (less(msg_grp_bc_seqno(hdr), m->bc_rcv_nxt))
52262306a36Sopenharmony_ci		goto drop;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	TIPC_SKB_CB(skb)->orig_member = m->instance;
52562306a36Sopenharmony_ci	defq = &m->deferredq;
52662306a36Sopenharmony_ci	tipc_group_sort_msg(skb, defq);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	while ((skb = skb_peek(defq))) {
52962306a36Sopenharmony_ci		hdr = buf_msg(skb);
53062306a36Sopenharmony_ci		mtyp = msg_type(hdr);
53162306a36Sopenharmony_ci		blks = msg_blocks(hdr);
53262306a36Sopenharmony_ci		deliver = true;
53362306a36Sopenharmony_ci		ack = false;
53462306a36Sopenharmony_ci		update = false;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		if (more(msg_grp_bc_seqno(hdr), m->bc_rcv_nxt))
53762306a36Sopenharmony_ci			break;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		/* Decide what to do with message */
54062306a36Sopenharmony_ci		switch (mtyp) {
54162306a36Sopenharmony_ci		case TIPC_GRP_MCAST_MSG:
54262306a36Sopenharmony_ci			if (msg_nameinst(hdr) != grp->instance) {
54362306a36Sopenharmony_ci				update = true;
54462306a36Sopenharmony_ci				deliver = false;
54562306a36Sopenharmony_ci			}
54662306a36Sopenharmony_ci			fallthrough;
54762306a36Sopenharmony_ci		case TIPC_GRP_BCAST_MSG:
54862306a36Sopenharmony_ci			m->bc_rcv_nxt++;
54962306a36Sopenharmony_ci			ack = msg_grp_bc_ack_req(hdr);
55062306a36Sopenharmony_ci			break;
55162306a36Sopenharmony_ci		case TIPC_GRP_UCAST_MSG:
55262306a36Sopenharmony_ci			break;
55362306a36Sopenharmony_ci		case TIPC_GRP_MEMBER_EVT:
55462306a36Sopenharmony_ci			if (m->state == MBR_LEAVING)
55562306a36Sopenharmony_ci				leave = true;
55662306a36Sopenharmony_ci			if (!grp->events)
55762306a36Sopenharmony_ci				deliver = false;
55862306a36Sopenharmony_ci			break;
55962306a36Sopenharmony_ci		default:
56062306a36Sopenharmony_ci			break;
56162306a36Sopenharmony_ci		}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		/* Execute decisions */
56462306a36Sopenharmony_ci		__skb_dequeue(defq);
56562306a36Sopenharmony_ci		if (deliver)
56662306a36Sopenharmony_ci			__skb_queue_tail(inputq, skb);
56762306a36Sopenharmony_ci		else
56862306a36Sopenharmony_ci			kfree_skb(skb);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		if (ack)
57162306a36Sopenharmony_ci			tipc_group_proto_xmit(grp, m, GRP_ACK_MSG, xmitq);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (leave) {
57462306a36Sopenharmony_ci			__skb_queue_purge(defq);
57562306a36Sopenharmony_ci			tipc_group_delete_member(grp, m);
57662306a36Sopenharmony_ci			break;
57762306a36Sopenharmony_ci		}
57862306a36Sopenharmony_ci		if (!update)
57962306a36Sopenharmony_ci			continue;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		tipc_group_update_rcv_win(grp, blks, node, port, xmitq);
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	return;
58462306a36Sopenharmony_cidrop:
58562306a36Sopenharmony_ci	kfree_skb(skb);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_civoid tipc_group_update_rcv_win(struct tipc_group *grp, int blks, u32 node,
58962306a36Sopenharmony_ci			       u32 port, struct sk_buff_head *xmitq)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct list_head *active = &grp->active;
59262306a36Sopenharmony_ci	int max_active = grp->max_active;
59362306a36Sopenharmony_ci	int reclaim_limit = max_active * 3 / 4;
59462306a36Sopenharmony_ci	int active_cnt = grp->active_cnt;
59562306a36Sopenharmony_ci	struct tipc_member *m, *rm, *pm;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	m = tipc_group_find_member(grp, node, port);
59862306a36Sopenharmony_ci	if (!m)
59962306a36Sopenharmony_ci		return;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	m->advertised -= blks;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	switch (m->state) {
60462306a36Sopenharmony_ci	case MBR_JOINED:
60562306a36Sopenharmony_ci		/* First, decide if member can go active */
60662306a36Sopenharmony_ci		if (active_cnt <= max_active) {
60762306a36Sopenharmony_ci			m->state = MBR_ACTIVE;
60862306a36Sopenharmony_ci			list_add_tail(&m->list, active);
60962306a36Sopenharmony_ci			grp->active_cnt++;
61062306a36Sopenharmony_ci			tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
61162306a36Sopenharmony_ci		} else {
61262306a36Sopenharmony_ci			m->state = MBR_PENDING;
61362306a36Sopenharmony_ci			list_add_tail(&m->list, &grp->pending);
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		if (active_cnt < reclaim_limit)
61762306a36Sopenharmony_ci			break;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		/* Reclaim from oldest active member, if possible */
62062306a36Sopenharmony_ci		if (!list_empty(active)) {
62162306a36Sopenharmony_ci			rm = list_first_entry(active, struct tipc_member, list);
62262306a36Sopenharmony_ci			rm->state = MBR_RECLAIMING;
62362306a36Sopenharmony_ci			list_del_init(&rm->list);
62462306a36Sopenharmony_ci			tipc_group_proto_xmit(grp, rm, GRP_RECLAIM_MSG, xmitq);
62562306a36Sopenharmony_ci			break;
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci		/* Nobody to reclaim from; - revert oldest pending to JOINED */
62862306a36Sopenharmony_ci		pm = list_first_entry(&grp->pending, struct tipc_member, list);
62962306a36Sopenharmony_ci		list_del_init(&pm->list);
63062306a36Sopenharmony_ci		pm->state = MBR_JOINED;
63162306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, pm, GRP_ADV_MSG, xmitq);
63262306a36Sopenharmony_ci		break;
63362306a36Sopenharmony_ci	case MBR_ACTIVE:
63462306a36Sopenharmony_ci		if (!list_is_last(&m->list, &grp->active))
63562306a36Sopenharmony_ci			list_move_tail(&m->list, &grp->active);
63662306a36Sopenharmony_ci		if (m->advertised > (ADV_ACTIVE * 3 / 4))
63762306a36Sopenharmony_ci			break;
63862306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
63962306a36Sopenharmony_ci		break;
64062306a36Sopenharmony_ci	case MBR_REMITTED:
64162306a36Sopenharmony_ci		if (m->advertised > ADV_IDLE)
64262306a36Sopenharmony_ci			break;
64362306a36Sopenharmony_ci		m->state = MBR_JOINED;
64462306a36Sopenharmony_ci		grp->active_cnt--;
64562306a36Sopenharmony_ci		if (m->advertised < ADV_IDLE) {
64662306a36Sopenharmony_ci			pr_warn_ratelimited("Rcv unexpected msg after REMIT\n");
64762306a36Sopenharmony_ci			tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		if (list_empty(&grp->pending))
65162306a36Sopenharmony_ci			return;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		/* Set oldest pending member to active and advertise */
65462306a36Sopenharmony_ci		pm = list_first_entry(&grp->pending, struct tipc_member, list);
65562306a36Sopenharmony_ci		pm->state = MBR_ACTIVE;
65662306a36Sopenharmony_ci		list_move_tail(&pm->list, &grp->active);
65762306a36Sopenharmony_ci		grp->active_cnt++;
65862306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, pm, GRP_ADV_MSG, xmitq);
65962306a36Sopenharmony_ci		break;
66062306a36Sopenharmony_ci	case MBR_RECLAIMING:
66162306a36Sopenharmony_ci	case MBR_JOINING:
66262306a36Sopenharmony_ci	case MBR_LEAVING:
66362306a36Sopenharmony_ci	default:
66462306a36Sopenharmony_ci		break;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic void tipc_group_create_event(struct tipc_group *grp,
66962306a36Sopenharmony_ci				    struct tipc_member *m,
67062306a36Sopenharmony_ci				    u32 event, u16 seqno,
67162306a36Sopenharmony_ci				    struct sk_buff_head *inputq)
67262306a36Sopenharmony_ci{	u32 dnode = tipc_own_addr(grp->net);
67362306a36Sopenharmony_ci	struct tipc_event evt;
67462306a36Sopenharmony_ci	struct sk_buff *skb;
67562306a36Sopenharmony_ci	struct tipc_msg *hdr;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	memset(&evt, 0, sizeof(evt));
67862306a36Sopenharmony_ci	evt.event = event;
67962306a36Sopenharmony_ci	evt.found_lower = m->instance;
68062306a36Sopenharmony_ci	evt.found_upper = m->instance;
68162306a36Sopenharmony_ci	evt.port.ref = m->port;
68262306a36Sopenharmony_ci	evt.port.node = m->node;
68362306a36Sopenharmony_ci	evt.s.seq.type = grp->type;
68462306a36Sopenharmony_ci	evt.s.seq.lower = m->instance;
68562306a36Sopenharmony_ci	evt.s.seq.upper = m->instance;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_GRP_MEMBER_EVT,
68862306a36Sopenharmony_ci			      GROUP_H_SIZE, sizeof(evt), dnode, m->node,
68962306a36Sopenharmony_ci			      grp->portid, m->port, 0);
69062306a36Sopenharmony_ci	if (!skb)
69162306a36Sopenharmony_ci		return;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	hdr = buf_msg(skb);
69462306a36Sopenharmony_ci	msg_set_nametype(hdr, grp->type);
69562306a36Sopenharmony_ci	msg_set_grp_evt(hdr, event);
69662306a36Sopenharmony_ci	msg_set_dest_droppable(hdr, true);
69762306a36Sopenharmony_ci	msg_set_grp_bc_seqno(hdr, seqno);
69862306a36Sopenharmony_ci	memcpy(msg_data(hdr), &evt, sizeof(evt));
69962306a36Sopenharmony_ci	TIPC_SKB_CB(skb)->orig_member = m->instance;
70062306a36Sopenharmony_ci	__skb_queue_tail(inputq, skb);
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m,
70462306a36Sopenharmony_ci				  int mtyp, struct sk_buff_head *xmitq)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct tipc_msg *hdr;
70762306a36Sopenharmony_ci	struct sk_buff *skb;
70862306a36Sopenharmony_ci	int adv = 0;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	skb = tipc_msg_create(GROUP_PROTOCOL, mtyp, INT_H_SIZE, 0,
71162306a36Sopenharmony_ci			      m->node, tipc_own_addr(grp->net),
71262306a36Sopenharmony_ci			      m->port, grp->portid, 0);
71362306a36Sopenharmony_ci	if (!skb)
71462306a36Sopenharmony_ci		return;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (m->state == MBR_ACTIVE)
71762306a36Sopenharmony_ci		adv = ADV_ACTIVE - m->advertised;
71862306a36Sopenharmony_ci	else if (m->state == MBR_JOINED || m->state == MBR_PENDING)
71962306a36Sopenharmony_ci		adv = ADV_IDLE - m->advertised;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	hdr = buf_msg(skb);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (mtyp == GRP_JOIN_MSG) {
72462306a36Sopenharmony_ci		msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt);
72562306a36Sopenharmony_ci		msg_set_adv_win(hdr, adv);
72662306a36Sopenharmony_ci		m->advertised += adv;
72762306a36Sopenharmony_ci	} else if (mtyp == GRP_LEAVE_MSG) {
72862306a36Sopenharmony_ci		msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt);
72962306a36Sopenharmony_ci	} else if (mtyp == GRP_ADV_MSG) {
73062306a36Sopenharmony_ci		msg_set_adv_win(hdr, adv);
73162306a36Sopenharmony_ci		m->advertised += adv;
73262306a36Sopenharmony_ci	} else if (mtyp == GRP_ACK_MSG) {
73362306a36Sopenharmony_ci		msg_set_grp_bc_acked(hdr, m->bc_rcv_nxt);
73462306a36Sopenharmony_ci	} else if (mtyp == GRP_REMIT_MSG) {
73562306a36Sopenharmony_ci		msg_set_grp_remitted(hdr, m->window);
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci	msg_set_dest_droppable(hdr, true);
73862306a36Sopenharmony_ci	__skb_queue_tail(xmitq, skb);
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_civoid tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup,
74262306a36Sopenharmony_ci			  struct tipc_msg *hdr, struct sk_buff_head *inputq,
74362306a36Sopenharmony_ci			  struct sk_buff_head *xmitq)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	u32 node = msg_orignode(hdr);
74662306a36Sopenharmony_ci	u32 port = msg_origport(hdr);
74762306a36Sopenharmony_ci	struct tipc_member *m, *pm;
74862306a36Sopenharmony_ci	u16 remitted, in_flight;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (!grp)
75162306a36Sopenharmony_ci		return;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	if (grp->scope == TIPC_NODE_SCOPE && node != tipc_own_addr(grp->net))
75462306a36Sopenharmony_ci		return;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	m = tipc_group_find_member(grp, node, port);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	switch (msg_type(hdr)) {
75962306a36Sopenharmony_ci	case GRP_JOIN_MSG:
76062306a36Sopenharmony_ci		if (!m)
76162306a36Sopenharmony_ci			m = tipc_group_create_member(grp, node, port,
76262306a36Sopenharmony_ci						     0, MBR_JOINING);
76362306a36Sopenharmony_ci		if (!m)
76462306a36Sopenharmony_ci			return;
76562306a36Sopenharmony_ci		m->bc_syncpt = msg_grp_bc_syncpt(hdr);
76662306a36Sopenharmony_ci		m->bc_rcv_nxt = m->bc_syncpt;
76762306a36Sopenharmony_ci		m->window += msg_adv_win(hdr);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		/* Wait until PUBLISH event is received if necessary */
77062306a36Sopenharmony_ci		if (m->state != MBR_PUBLISHED)
77162306a36Sopenharmony_ci			return;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		/* Member can be taken into service */
77462306a36Sopenharmony_ci		m->state = MBR_JOINED;
77562306a36Sopenharmony_ci		tipc_group_open(m, usr_wakeup);
77662306a36Sopenharmony_ci		tipc_group_update_member(m, 0);
77762306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
77862306a36Sopenharmony_ci		tipc_group_create_event(grp, m, TIPC_PUBLISHED,
77962306a36Sopenharmony_ci					m->bc_syncpt, inputq);
78062306a36Sopenharmony_ci		return;
78162306a36Sopenharmony_ci	case GRP_LEAVE_MSG:
78262306a36Sopenharmony_ci		if (!m)
78362306a36Sopenharmony_ci			return;
78462306a36Sopenharmony_ci		m->bc_syncpt = msg_grp_bc_syncpt(hdr);
78562306a36Sopenharmony_ci		list_del_init(&m->list);
78662306a36Sopenharmony_ci		tipc_group_open(m, usr_wakeup);
78762306a36Sopenharmony_ci		tipc_group_decr_active(grp, m);
78862306a36Sopenharmony_ci		m->state = MBR_LEAVING;
78962306a36Sopenharmony_ci		tipc_group_create_event(grp, m, TIPC_WITHDRAWN,
79062306a36Sopenharmony_ci					m->bc_syncpt, inputq);
79162306a36Sopenharmony_ci		return;
79262306a36Sopenharmony_ci	case GRP_ADV_MSG:
79362306a36Sopenharmony_ci		if (!m)
79462306a36Sopenharmony_ci			return;
79562306a36Sopenharmony_ci		m->window += msg_adv_win(hdr);
79662306a36Sopenharmony_ci		tipc_group_open(m, usr_wakeup);
79762306a36Sopenharmony_ci		return;
79862306a36Sopenharmony_ci	case GRP_ACK_MSG:
79962306a36Sopenharmony_ci		if (!m)
80062306a36Sopenharmony_ci			return;
80162306a36Sopenharmony_ci		m->bc_acked = msg_grp_bc_acked(hdr);
80262306a36Sopenharmony_ci		if (--grp->bc_ackers)
80362306a36Sopenharmony_ci			return;
80462306a36Sopenharmony_ci		list_del_init(&m->small_win);
80562306a36Sopenharmony_ci		*m->group->open = true;
80662306a36Sopenharmony_ci		*usr_wakeup = true;
80762306a36Sopenharmony_ci		tipc_group_update_member(m, 0);
80862306a36Sopenharmony_ci		return;
80962306a36Sopenharmony_ci	case GRP_RECLAIM_MSG:
81062306a36Sopenharmony_ci		if (!m)
81162306a36Sopenharmony_ci			return;
81262306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, m, GRP_REMIT_MSG, xmitq);
81362306a36Sopenharmony_ci		m->window = ADV_IDLE;
81462306a36Sopenharmony_ci		tipc_group_open(m, usr_wakeup);
81562306a36Sopenharmony_ci		return;
81662306a36Sopenharmony_ci	case GRP_REMIT_MSG:
81762306a36Sopenharmony_ci		if (!m || m->state != MBR_RECLAIMING)
81862306a36Sopenharmony_ci			return;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		remitted = msg_grp_remitted(hdr);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		/* Messages preceding the REMIT still in receive queue */
82362306a36Sopenharmony_ci		if (m->advertised > remitted) {
82462306a36Sopenharmony_ci			m->state = MBR_REMITTED;
82562306a36Sopenharmony_ci			in_flight = m->advertised - remitted;
82662306a36Sopenharmony_ci			m->advertised = ADV_IDLE + in_flight;
82762306a36Sopenharmony_ci			return;
82862306a36Sopenharmony_ci		}
82962306a36Sopenharmony_ci		/* This should never happen */
83062306a36Sopenharmony_ci		if (m->advertised < remitted)
83162306a36Sopenharmony_ci			pr_warn_ratelimited("Unexpected REMIT msg\n");
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		/* All messages preceding the REMIT have been read */
83462306a36Sopenharmony_ci		m->state = MBR_JOINED;
83562306a36Sopenharmony_ci		grp->active_cnt--;
83662306a36Sopenharmony_ci		m->advertised = ADV_IDLE;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci		/* Set oldest pending member to active and advertise */
83962306a36Sopenharmony_ci		if (list_empty(&grp->pending))
84062306a36Sopenharmony_ci			return;
84162306a36Sopenharmony_ci		pm = list_first_entry(&grp->pending, struct tipc_member, list);
84262306a36Sopenharmony_ci		pm->state = MBR_ACTIVE;
84362306a36Sopenharmony_ci		list_move_tail(&pm->list, &grp->active);
84462306a36Sopenharmony_ci		grp->active_cnt++;
84562306a36Sopenharmony_ci		if (pm->advertised <= (ADV_ACTIVE * 3 / 4))
84662306a36Sopenharmony_ci			tipc_group_proto_xmit(grp, pm, GRP_ADV_MSG, xmitq);
84762306a36Sopenharmony_ci		return;
84862306a36Sopenharmony_ci	default:
84962306a36Sopenharmony_ci		pr_warn("Received unknown GROUP_PROTO message\n");
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci/* tipc_group_member_evt() - receive and handle a member up/down event
85462306a36Sopenharmony_ci */
85562306a36Sopenharmony_civoid tipc_group_member_evt(struct tipc_group *grp,
85662306a36Sopenharmony_ci			   bool *usr_wakeup,
85762306a36Sopenharmony_ci			   int *sk_rcvbuf,
85862306a36Sopenharmony_ci			   struct tipc_msg *hdr,
85962306a36Sopenharmony_ci			   struct sk_buff_head *inputq,
86062306a36Sopenharmony_ci			   struct sk_buff_head *xmitq)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct tipc_event *evt = (void *)msg_data(hdr);
86362306a36Sopenharmony_ci	u32 instance = evt->found_lower;
86462306a36Sopenharmony_ci	u32 node = evt->port.node;
86562306a36Sopenharmony_ci	u32 port = evt->port.ref;
86662306a36Sopenharmony_ci	int event = evt->event;
86762306a36Sopenharmony_ci	struct tipc_member *m;
86862306a36Sopenharmony_ci	struct net *net;
86962306a36Sopenharmony_ci	u32 self;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (!grp)
87262306a36Sopenharmony_ci		return;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	net = grp->net;
87562306a36Sopenharmony_ci	self = tipc_own_addr(net);
87662306a36Sopenharmony_ci	if (!grp->loopback && node == self && port == grp->portid)
87762306a36Sopenharmony_ci		return;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	m = tipc_group_find_member(grp, node, port);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	switch (event) {
88262306a36Sopenharmony_ci	case TIPC_PUBLISHED:
88362306a36Sopenharmony_ci		/* Send and wait for arrival of JOIN message if necessary */
88462306a36Sopenharmony_ci		if (!m) {
88562306a36Sopenharmony_ci			m = tipc_group_create_member(grp, node, port, instance,
88662306a36Sopenharmony_ci						     MBR_PUBLISHED);
88762306a36Sopenharmony_ci			if (!m)
88862306a36Sopenharmony_ci				break;
88962306a36Sopenharmony_ci			tipc_group_update_member(m, 0);
89062306a36Sopenharmony_ci			tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq);
89162306a36Sopenharmony_ci			break;
89262306a36Sopenharmony_ci		}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		if (m->state != MBR_JOINING)
89562306a36Sopenharmony_ci			break;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		/* Member can be taken into service */
89862306a36Sopenharmony_ci		m->instance = instance;
89962306a36Sopenharmony_ci		m->state = MBR_JOINED;
90062306a36Sopenharmony_ci		tipc_group_open(m, usr_wakeup);
90162306a36Sopenharmony_ci		tipc_group_update_member(m, 0);
90262306a36Sopenharmony_ci		tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq);
90362306a36Sopenharmony_ci		tipc_group_create_event(grp, m, TIPC_PUBLISHED,
90462306a36Sopenharmony_ci					m->bc_syncpt, inputq);
90562306a36Sopenharmony_ci		break;
90662306a36Sopenharmony_ci	case TIPC_WITHDRAWN:
90762306a36Sopenharmony_ci		if (!m)
90862306a36Sopenharmony_ci			break;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		tipc_group_decr_active(grp, m);
91162306a36Sopenharmony_ci		m->state = MBR_LEAVING;
91262306a36Sopenharmony_ci		list_del_init(&m->list);
91362306a36Sopenharmony_ci		tipc_group_open(m, usr_wakeup);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		/* Only send event if no LEAVE message can be expected */
91662306a36Sopenharmony_ci		if (!tipc_node_is_up(net, node))
91762306a36Sopenharmony_ci			tipc_group_create_event(grp, m, TIPC_WITHDRAWN,
91862306a36Sopenharmony_ci						m->bc_rcv_nxt, inputq);
91962306a36Sopenharmony_ci		break;
92062306a36Sopenharmony_ci	default:
92162306a36Sopenharmony_ci		break;
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci	*sk_rcvbuf = tipc_group_rcvbuf_limit(grp);
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ciint tipc_group_fill_sock_diag(struct tipc_group *grp, struct sk_buff *skb)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct nlattr *group = nla_nest_start_noflag(skb, TIPC_NLA_SOCK_GROUP);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (!group)
93162306a36Sopenharmony_ci		return -EMSGSIZE;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (nla_put_u32(skb, TIPC_NLA_SOCK_GROUP_ID,
93462306a36Sopenharmony_ci			grp->type) ||
93562306a36Sopenharmony_ci	    nla_put_u32(skb, TIPC_NLA_SOCK_GROUP_INSTANCE,
93662306a36Sopenharmony_ci			grp->instance) ||
93762306a36Sopenharmony_ci	    nla_put_u32(skb, TIPC_NLA_SOCK_GROUP_BC_SEND_NEXT,
93862306a36Sopenharmony_ci			grp->bc_snd_nxt))
93962306a36Sopenharmony_ci		goto group_msg_cancel;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (grp->scope == TIPC_NODE_SCOPE)
94262306a36Sopenharmony_ci		if (nla_put_flag(skb, TIPC_NLA_SOCK_GROUP_NODE_SCOPE))
94362306a36Sopenharmony_ci			goto group_msg_cancel;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (grp->scope == TIPC_CLUSTER_SCOPE)
94662306a36Sopenharmony_ci		if (nla_put_flag(skb, TIPC_NLA_SOCK_GROUP_CLUSTER_SCOPE))
94762306a36Sopenharmony_ci			goto group_msg_cancel;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (*grp->open)
95062306a36Sopenharmony_ci		if (nla_put_flag(skb, TIPC_NLA_SOCK_GROUP_OPEN))
95162306a36Sopenharmony_ci			goto group_msg_cancel;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	nla_nest_end(skb, group);
95462306a36Sopenharmony_ci	return 0;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cigroup_msg_cancel:
95762306a36Sopenharmony_ci	nla_nest_cancel(skb, group);
95862306a36Sopenharmony_ci	return -1;
95962306a36Sopenharmony_ci}
960