18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * net/tipc/monitor.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2016, Ericsson AB
58c2ecf20Sopenharmony_ci * All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
118c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
128c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
138c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
148c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
158c2ecf20Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its
168c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived from
178c2ecf20Sopenharmony_ci *    this software without specific prior written permission.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
208c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
218c2ecf20Sopenharmony_ci * Software Foundation.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
248c2ecf20Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
258c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
268c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
278c2ecf20Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
288c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
298c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
308c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
318c2ecf20Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
328c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
338c2ecf20Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <net/genetlink.h>
378c2ecf20Sopenharmony_ci#include "core.h"
388c2ecf20Sopenharmony_ci#include "addr.h"
398c2ecf20Sopenharmony_ci#include "monitor.h"
408c2ecf20Sopenharmony_ci#include "bearer.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define MAX_MON_DOMAIN       64
438c2ecf20Sopenharmony_ci#define MON_TIMEOUT          120000
448c2ecf20Sopenharmony_ci#define MAX_PEER_DOWN_EVENTS 4
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* struct tipc_mon_domain: domain record to be transferred between peers
478c2ecf20Sopenharmony_ci * @len: actual size of domain record
488c2ecf20Sopenharmony_ci * @gen: current generation of sender's domain
498c2ecf20Sopenharmony_ci * @ack_gen: most recent generation of self's domain acked by peer
508c2ecf20Sopenharmony_ci * @member_cnt: number of domain member nodes described in this record
518c2ecf20Sopenharmony_ci * @up_map: bit map indicating which of the members the sender considers up
528c2ecf20Sopenharmony_ci * @members: identity of the domain members
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistruct tipc_mon_domain {
558c2ecf20Sopenharmony_ci	u16 len;
568c2ecf20Sopenharmony_ci	u16 gen;
578c2ecf20Sopenharmony_ci	u16 ack_gen;
588c2ecf20Sopenharmony_ci	u16 member_cnt;
598c2ecf20Sopenharmony_ci	u64 up_map;
608c2ecf20Sopenharmony_ci	u32 members[MAX_MON_DOMAIN];
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* struct tipc_peer: state of a peer node and its domain
648c2ecf20Sopenharmony_ci * @addr: tipc node identity of peer
658c2ecf20Sopenharmony_ci * @head_map: shows which other nodes currently consider peer 'up'
668c2ecf20Sopenharmony_ci * @domain: most recent domain record from peer
678c2ecf20Sopenharmony_ci * @hash: position in hashed lookup list
688c2ecf20Sopenharmony_ci * @list: position in linked list, in circular ascending order by 'addr'
698c2ecf20Sopenharmony_ci * @applied: number of reported domain members applied on this monitor list
708c2ecf20Sopenharmony_ci * @is_up: peer is up as seen from this node
718c2ecf20Sopenharmony_ci * @is_head: peer is assigned domain head as seen from this node
728c2ecf20Sopenharmony_ci * @is_local: peer is in local domain and should be continuously monitored
738c2ecf20Sopenharmony_ci * @down_cnt: - numbers of other peers which have reported this on lost
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistruct tipc_peer {
768c2ecf20Sopenharmony_ci	u32 addr;
778c2ecf20Sopenharmony_ci	struct tipc_mon_domain *domain;
788c2ecf20Sopenharmony_ci	struct hlist_node hash;
798c2ecf20Sopenharmony_ci	struct list_head list;
808c2ecf20Sopenharmony_ci	u8 applied;
818c2ecf20Sopenharmony_ci	u8 down_cnt;
828c2ecf20Sopenharmony_ci	bool is_up;
838c2ecf20Sopenharmony_ci	bool is_head;
848c2ecf20Sopenharmony_ci	bool is_local;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistruct tipc_monitor {
888c2ecf20Sopenharmony_ci	struct hlist_head peers[NODE_HTABLE_SIZE];
898c2ecf20Sopenharmony_ci	int peer_cnt;
908c2ecf20Sopenharmony_ci	struct tipc_peer *self;
918c2ecf20Sopenharmony_ci	rwlock_t lock;
928c2ecf20Sopenharmony_ci	struct tipc_mon_domain cache;
938c2ecf20Sopenharmony_ci	u16 list_gen;
948c2ecf20Sopenharmony_ci	u16 dom_gen;
958c2ecf20Sopenharmony_ci	struct net *net;
968c2ecf20Sopenharmony_ci	struct timer_list timer;
978c2ecf20Sopenharmony_ci	unsigned long timer_intv;
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic struct tipc_monitor *tipc_monitor(struct net *net, int bearer_id)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return tipc_net(net)->monitors[bearer_id];
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ciconst int tipc_max_domain_size = sizeof(struct tipc_mon_domain);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* dom_rec_len(): actual length of domain record for transport
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_cistatic int dom_rec_len(struct tipc_mon_domain *dom, u16 mcnt)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	return ((void *)&dom->members - (void *)dom) + (mcnt * sizeof(u32));
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* dom_size() : calculate size of own domain based on number of peers
1158c2ecf20Sopenharmony_ci */
1168c2ecf20Sopenharmony_cistatic int dom_size(int peers)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int i = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	while ((i * i) < peers)
1218c2ecf20Sopenharmony_ci		i++;
1228c2ecf20Sopenharmony_ci	return i < MAX_MON_DOMAIN ? i : MAX_MON_DOMAIN;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void map_set(u64 *up_map, int i, unsigned int v)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	*up_map &= ~(1ULL << i);
1288c2ecf20Sopenharmony_ci	*up_map |= ((u64)v << i);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int map_get(u64 up_map, int i)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	return (up_map & (1ULL << i)) >> i;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic struct tipc_peer *peer_prev(struct tipc_peer *peer)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	return list_last_entry(&peer->list, struct tipc_peer, list);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic struct tipc_peer *peer_nxt(struct tipc_peer *peer)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	return list_first_entry(&peer->list, struct tipc_peer, list);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic struct tipc_peer *peer_head(struct tipc_peer *peer)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	while (!peer->is_head)
1498c2ecf20Sopenharmony_ci		peer = peer_prev(peer);
1508c2ecf20Sopenharmony_ci	return peer;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic struct tipc_peer *get_peer(struct tipc_monitor *mon, u32 addr)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct tipc_peer *peer;
1568c2ecf20Sopenharmony_ci	unsigned int thash = tipc_hashfn(addr);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	hlist_for_each_entry(peer, &mon->peers[thash], hash) {
1598c2ecf20Sopenharmony_ci		if (peer->addr == addr)
1608c2ecf20Sopenharmony_ci			return peer;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	return NULL;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic struct tipc_peer *get_self(struct net *net, int bearer_id)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return mon->self;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic inline bool tipc_mon_is_active(struct net *net, struct tipc_monitor *mon)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct tipc_net *tn = tipc_net(net);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return mon->peer_cnt > tn->mon_threshold;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/* mon_identify_lost_members() : - identify amd mark potentially lost members
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic void mon_identify_lost_members(struct tipc_peer *peer,
1828c2ecf20Sopenharmony_ci				      struct tipc_mon_domain *dom_bef,
1838c2ecf20Sopenharmony_ci				      int applied_bef)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct tipc_peer *member = peer;
1868c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom_aft = peer->domain;
1878c2ecf20Sopenharmony_ci	int applied_aft = peer->applied;
1888c2ecf20Sopenharmony_ci	int i;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	for (i = 0; i < applied_bef; i++) {
1918c2ecf20Sopenharmony_ci		member = peer_nxt(member);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		/* Do nothing if self or peer already see member as down */
1948c2ecf20Sopenharmony_ci		if (!member->is_up || !map_get(dom_bef->up_map, i))
1958c2ecf20Sopenharmony_ci			continue;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		/* Loss of local node must be detected by active probing */
1988c2ecf20Sopenharmony_ci		if (member->is_local)
1998c2ecf20Sopenharmony_ci			continue;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		/* Start probing if member was removed from applied domain */
2028c2ecf20Sopenharmony_ci		if (!applied_aft || (applied_aft < i)) {
2038c2ecf20Sopenharmony_ci			member->down_cnt = 1;
2048c2ecf20Sopenharmony_ci			continue;
2058c2ecf20Sopenharmony_ci		}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		/* Member loss is confirmed if it is still in applied domain */
2088c2ecf20Sopenharmony_ci		if (!map_get(dom_aft->up_map, i))
2098c2ecf20Sopenharmony_ci			member->down_cnt++;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/* mon_apply_domain() : match a peer's domain record against monitor list
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_cistatic void mon_apply_domain(struct tipc_monitor *mon,
2168c2ecf20Sopenharmony_ci			     struct tipc_peer *peer)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom = peer->domain;
2198c2ecf20Sopenharmony_ci	struct tipc_peer *member;
2208c2ecf20Sopenharmony_ci	u32 addr;
2218c2ecf20Sopenharmony_ci	int i;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (!dom || !peer->is_up)
2248c2ecf20Sopenharmony_ci		return;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Scan across domain members and match against monitor list */
2278c2ecf20Sopenharmony_ci	peer->applied = 0;
2288c2ecf20Sopenharmony_ci	member = peer_nxt(peer);
2298c2ecf20Sopenharmony_ci	for (i = 0; i < dom->member_cnt; i++) {
2308c2ecf20Sopenharmony_ci		addr = dom->members[i];
2318c2ecf20Sopenharmony_ci		if (addr != member->addr)
2328c2ecf20Sopenharmony_ci			return;
2338c2ecf20Sopenharmony_ci		peer->applied++;
2348c2ecf20Sopenharmony_ci		member = peer_nxt(member);
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/* mon_update_local_domain() : update after peer addition/removal/up/down
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_cistatic void mon_update_local_domain(struct tipc_monitor *mon)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct tipc_peer *self = mon->self;
2438c2ecf20Sopenharmony_ci	struct tipc_mon_domain *cache = &mon->cache;
2448c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom = self->domain;
2458c2ecf20Sopenharmony_ci	struct tipc_peer *peer = self;
2468c2ecf20Sopenharmony_ci	u64 prev_up_map = dom->up_map;
2478c2ecf20Sopenharmony_ci	u16 member_cnt, i;
2488c2ecf20Sopenharmony_ci	bool diff;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Update local domain size based on current size of cluster */
2518c2ecf20Sopenharmony_ci	member_cnt = dom_size(mon->peer_cnt) - 1;
2528c2ecf20Sopenharmony_ci	self->applied = member_cnt;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Update native and cached outgoing local domain records */
2558c2ecf20Sopenharmony_ci	dom->len = dom_rec_len(dom, member_cnt);
2568c2ecf20Sopenharmony_ci	diff = dom->member_cnt != member_cnt;
2578c2ecf20Sopenharmony_ci	dom->member_cnt = member_cnt;
2588c2ecf20Sopenharmony_ci	for (i = 0; i < member_cnt; i++) {
2598c2ecf20Sopenharmony_ci		peer = peer_nxt(peer);
2608c2ecf20Sopenharmony_ci		diff |= dom->members[i] != peer->addr;
2618c2ecf20Sopenharmony_ci		dom->members[i] = peer->addr;
2628c2ecf20Sopenharmony_ci		map_set(&dom->up_map, i, peer->is_up);
2638c2ecf20Sopenharmony_ci		cache->members[i] = htonl(peer->addr);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci	diff |= dom->up_map != prev_up_map;
2668c2ecf20Sopenharmony_ci	if (!diff)
2678c2ecf20Sopenharmony_ci		return;
2688c2ecf20Sopenharmony_ci	dom->gen = ++mon->dom_gen;
2698c2ecf20Sopenharmony_ci	cache->len = htons(dom->len);
2708c2ecf20Sopenharmony_ci	cache->gen = htons(dom->gen);
2718c2ecf20Sopenharmony_ci	cache->member_cnt = htons(member_cnt);
2728c2ecf20Sopenharmony_ci	cache->up_map = cpu_to_be64(dom->up_map);
2738c2ecf20Sopenharmony_ci	mon_apply_domain(mon, self);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/* mon_update_neighbors() : update preceding neighbors of added/removed peer
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_cistatic void mon_update_neighbors(struct tipc_monitor *mon,
2798c2ecf20Sopenharmony_ci				 struct tipc_peer *peer)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	int dz, i;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	dz = dom_size(mon->peer_cnt);
2848c2ecf20Sopenharmony_ci	for (i = 0; i < dz; i++) {
2858c2ecf20Sopenharmony_ci		mon_apply_domain(mon, peer);
2868c2ecf20Sopenharmony_ci		peer = peer_prev(peer);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/* mon_assign_roles() : reassign peer roles after a network change
2918c2ecf20Sopenharmony_ci * The monitor list is consistent at this stage; i.e., each peer is monitoring
2928c2ecf20Sopenharmony_ci * a set of domain members as matched between domain record and the monitor list
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_cistatic void mon_assign_roles(struct tipc_monitor *mon, struct tipc_peer *head)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct tipc_peer *peer = peer_nxt(head);
2978c2ecf20Sopenharmony_ci	struct tipc_peer *self = mon->self;
2988c2ecf20Sopenharmony_ci	int i = 0;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	for (; peer != self; peer = peer_nxt(peer)) {
3018c2ecf20Sopenharmony_ci		peer->is_local = false;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		/* Update domain member */
3048c2ecf20Sopenharmony_ci		if (i++ < head->applied) {
3058c2ecf20Sopenharmony_ci			peer->is_head = false;
3068c2ecf20Sopenharmony_ci			if (head == self)
3078c2ecf20Sopenharmony_ci				peer->is_local = true;
3088c2ecf20Sopenharmony_ci			continue;
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci		/* Assign next domain head */
3118c2ecf20Sopenharmony_ci		if (!peer->is_up)
3128c2ecf20Sopenharmony_ci			continue;
3138c2ecf20Sopenharmony_ci		if (peer->is_head)
3148c2ecf20Sopenharmony_ci			break;
3158c2ecf20Sopenharmony_ci		head = peer;
3168c2ecf20Sopenharmony_ci		head->is_head = true;
3178c2ecf20Sopenharmony_ci		i = 0;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	mon->list_gen++;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_civoid tipc_mon_remove_peer(struct net *net, u32 addr, int bearer_id)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
3258c2ecf20Sopenharmony_ci	struct tipc_peer *self;
3268c2ecf20Sopenharmony_ci	struct tipc_peer *peer, *prev, *head;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (!mon)
3298c2ecf20Sopenharmony_ci		return;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	self = get_self(net, bearer_id);
3328c2ecf20Sopenharmony_ci	write_lock_bh(&mon->lock);
3338c2ecf20Sopenharmony_ci	peer = get_peer(mon, addr);
3348c2ecf20Sopenharmony_ci	if (!peer)
3358c2ecf20Sopenharmony_ci		goto exit;
3368c2ecf20Sopenharmony_ci	prev = peer_prev(peer);
3378c2ecf20Sopenharmony_ci	list_del(&peer->list);
3388c2ecf20Sopenharmony_ci	hlist_del(&peer->hash);
3398c2ecf20Sopenharmony_ci	kfree(peer->domain);
3408c2ecf20Sopenharmony_ci	kfree(peer);
3418c2ecf20Sopenharmony_ci	mon->peer_cnt--;
3428c2ecf20Sopenharmony_ci	head = peer_head(prev);
3438c2ecf20Sopenharmony_ci	if (head == self)
3448c2ecf20Sopenharmony_ci		mon_update_local_domain(mon);
3458c2ecf20Sopenharmony_ci	mon_update_neighbors(mon, prev);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Revert to full-mesh monitoring if we reach threshold */
3488c2ecf20Sopenharmony_ci	if (!tipc_mon_is_active(net, mon)) {
3498c2ecf20Sopenharmony_ci		list_for_each_entry(peer, &self->list, list) {
3508c2ecf20Sopenharmony_ci			kfree(peer->domain);
3518c2ecf20Sopenharmony_ci			peer->domain = NULL;
3528c2ecf20Sopenharmony_ci			peer->applied = 0;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	mon_assign_roles(mon, head);
3568c2ecf20Sopenharmony_ciexit:
3578c2ecf20Sopenharmony_ci	write_unlock_bh(&mon->lock);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic bool tipc_mon_add_peer(struct tipc_monitor *mon, u32 addr,
3618c2ecf20Sopenharmony_ci			      struct tipc_peer **peer)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct tipc_peer *self = mon->self;
3648c2ecf20Sopenharmony_ci	struct tipc_peer *cur, *prev, *p;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	p = kzalloc(sizeof(*p), GFP_ATOMIC);
3678c2ecf20Sopenharmony_ci	*peer = p;
3688c2ecf20Sopenharmony_ci	if (!p)
3698c2ecf20Sopenharmony_ci		return false;
3708c2ecf20Sopenharmony_ci	p->addr = addr;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Add new peer to lookup list */
3738c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p->list);
3748c2ecf20Sopenharmony_ci	hlist_add_head(&p->hash, &mon->peers[tipc_hashfn(addr)]);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Sort new peer into iterator list, in ascending circular order */
3778c2ecf20Sopenharmony_ci	prev = self;
3788c2ecf20Sopenharmony_ci	list_for_each_entry(cur, &self->list, list) {
3798c2ecf20Sopenharmony_ci		if ((addr > prev->addr) && (addr < cur->addr))
3808c2ecf20Sopenharmony_ci			break;
3818c2ecf20Sopenharmony_ci		if (((addr < cur->addr) || (addr > prev->addr)) &&
3828c2ecf20Sopenharmony_ci		    (prev->addr > cur->addr))
3838c2ecf20Sopenharmony_ci			break;
3848c2ecf20Sopenharmony_ci		prev = cur;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci	list_add_tail(&p->list, &cur->list);
3878c2ecf20Sopenharmony_ci	mon->peer_cnt++;
3888c2ecf20Sopenharmony_ci	mon_update_neighbors(mon, p);
3898c2ecf20Sopenharmony_ci	return true;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_civoid tipc_mon_peer_up(struct net *net, u32 addr, int bearer_id)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
3958c2ecf20Sopenharmony_ci	struct tipc_peer *self = get_self(net, bearer_id);
3968c2ecf20Sopenharmony_ci	struct tipc_peer *peer, *head;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	write_lock_bh(&mon->lock);
3998c2ecf20Sopenharmony_ci	peer = get_peer(mon, addr);
4008c2ecf20Sopenharmony_ci	if (!peer && !tipc_mon_add_peer(mon, addr, &peer))
4018c2ecf20Sopenharmony_ci		goto exit;
4028c2ecf20Sopenharmony_ci	peer->is_up = true;
4038c2ecf20Sopenharmony_ci	head = peer_head(peer);
4048c2ecf20Sopenharmony_ci	if (head == self)
4058c2ecf20Sopenharmony_ci		mon_update_local_domain(mon);
4068c2ecf20Sopenharmony_ci	mon_assign_roles(mon, head);
4078c2ecf20Sopenharmony_ciexit:
4088c2ecf20Sopenharmony_ci	write_unlock_bh(&mon->lock);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_civoid tipc_mon_peer_down(struct net *net, u32 addr, int bearer_id)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
4148c2ecf20Sopenharmony_ci	struct tipc_peer *self;
4158c2ecf20Sopenharmony_ci	struct tipc_peer *peer, *head;
4168c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom;
4178c2ecf20Sopenharmony_ci	int applied;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (!mon)
4208c2ecf20Sopenharmony_ci		return;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	self = get_self(net, bearer_id);
4238c2ecf20Sopenharmony_ci	write_lock_bh(&mon->lock);
4248c2ecf20Sopenharmony_ci	peer = get_peer(mon, addr);
4258c2ecf20Sopenharmony_ci	if (!peer) {
4268c2ecf20Sopenharmony_ci		pr_warn("Mon: unknown link %x/%u DOWN\n", addr, bearer_id);
4278c2ecf20Sopenharmony_ci		goto exit;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci	applied = peer->applied;
4308c2ecf20Sopenharmony_ci	peer->applied = 0;
4318c2ecf20Sopenharmony_ci	dom = peer->domain;
4328c2ecf20Sopenharmony_ci	peer->domain = NULL;
4338c2ecf20Sopenharmony_ci	if (peer->is_head)
4348c2ecf20Sopenharmony_ci		mon_identify_lost_members(peer, dom, applied);
4358c2ecf20Sopenharmony_ci	kfree(dom);
4368c2ecf20Sopenharmony_ci	peer->is_up = false;
4378c2ecf20Sopenharmony_ci	peer->is_head = false;
4388c2ecf20Sopenharmony_ci	peer->is_local = false;
4398c2ecf20Sopenharmony_ci	peer->down_cnt = 0;
4408c2ecf20Sopenharmony_ci	head = peer_head(peer);
4418c2ecf20Sopenharmony_ci	if (head == self)
4428c2ecf20Sopenharmony_ci		mon_update_local_domain(mon);
4438c2ecf20Sopenharmony_ci	mon_assign_roles(mon, head);
4448c2ecf20Sopenharmony_ciexit:
4458c2ecf20Sopenharmony_ci	write_unlock_bh(&mon->lock);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci/* tipc_mon_rcv - process monitor domain event message
4498c2ecf20Sopenharmony_ci */
4508c2ecf20Sopenharmony_civoid tipc_mon_rcv(struct net *net, void *data, u16 dlen, u32 addr,
4518c2ecf20Sopenharmony_ci		  struct tipc_mon_state *state, int bearer_id)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
4548c2ecf20Sopenharmony_ci	struct tipc_mon_domain *arrv_dom = data;
4558c2ecf20Sopenharmony_ci	struct tipc_mon_domain dom_bef;
4568c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom;
4578c2ecf20Sopenharmony_ci	struct tipc_peer *peer;
4588c2ecf20Sopenharmony_ci	u16 new_member_cnt = ntohs(arrv_dom->member_cnt);
4598c2ecf20Sopenharmony_ci	int new_dlen = dom_rec_len(arrv_dom, new_member_cnt);
4608c2ecf20Sopenharmony_ci	u16 new_gen = ntohs(arrv_dom->gen);
4618c2ecf20Sopenharmony_ci	u16 acked_gen = ntohs(arrv_dom->ack_gen);
4628c2ecf20Sopenharmony_ci	bool probing = state->probing;
4638c2ecf20Sopenharmony_ci	int i, applied_bef;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	state->probing = false;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* Sanity check received domain record */
4688c2ecf20Sopenharmony_ci	if (new_member_cnt > MAX_MON_DOMAIN)
4698c2ecf20Sopenharmony_ci		return;
4708c2ecf20Sopenharmony_ci	if (dlen < dom_rec_len(arrv_dom, 0))
4718c2ecf20Sopenharmony_ci		return;
4728c2ecf20Sopenharmony_ci	if (dlen != dom_rec_len(arrv_dom, new_member_cnt))
4738c2ecf20Sopenharmony_ci		return;
4748c2ecf20Sopenharmony_ci	if ((dlen < new_dlen) || ntohs(arrv_dom->len) != new_dlen)
4758c2ecf20Sopenharmony_ci		return;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/* Synch generation numbers with peer if link just came up */
4788c2ecf20Sopenharmony_ci	if (!state->synched) {
4798c2ecf20Sopenharmony_ci		state->peer_gen = new_gen - 1;
4808c2ecf20Sopenharmony_ci		state->acked_gen = acked_gen;
4818c2ecf20Sopenharmony_ci		state->synched = true;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (more(acked_gen, state->acked_gen))
4858c2ecf20Sopenharmony_ci		state->acked_gen = acked_gen;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* Drop duplicate unless we are waiting for a probe response */
4888c2ecf20Sopenharmony_ci	if (!more(new_gen, state->peer_gen) && !probing)
4898c2ecf20Sopenharmony_ci		return;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	write_lock_bh(&mon->lock);
4928c2ecf20Sopenharmony_ci	peer = get_peer(mon, addr);
4938c2ecf20Sopenharmony_ci	if (!peer || !peer->is_up)
4948c2ecf20Sopenharmony_ci		goto exit;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* Peer is confirmed, stop any ongoing probing */
4978c2ecf20Sopenharmony_ci	peer->down_cnt = 0;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* Task is done for duplicate record */
5008c2ecf20Sopenharmony_ci	if (!more(new_gen, state->peer_gen))
5018c2ecf20Sopenharmony_ci		goto exit;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	state->peer_gen = new_gen;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/* Cache current domain record for later use */
5068c2ecf20Sopenharmony_ci	dom_bef.member_cnt = 0;
5078c2ecf20Sopenharmony_ci	dom = peer->domain;
5088c2ecf20Sopenharmony_ci	if (dom)
5098c2ecf20Sopenharmony_ci		memcpy(&dom_bef, dom, dom->len);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* Transform and store received domain record */
5128c2ecf20Sopenharmony_ci	if (!dom || (dom->len < new_dlen)) {
5138c2ecf20Sopenharmony_ci		kfree(dom);
5148c2ecf20Sopenharmony_ci		dom = kmalloc(new_dlen, GFP_ATOMIC);
5158c2ecf20Sopenharmony_ci		peer->domain = dom;
5168c2ecf20Sopenharmony_ci		if (!dom)
5178c2ecf20Sopenharmony_ci			goto exit;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci	dom->len = new_dlen;
5208c2ecf20Sopenharmony_ci	dom->gen = new_gen;
5218c2ecf20Sopenharmony_ci	dom->member_cnt = new_member_cnt;
5228c2ecf20Sopenharmony_ci	dom->up_map = be64_to_cpu(arrv_dom->up_map);
5238c2ecf20Sopenharmony_ci	for (i = 0; i < new_member_cnt; i++)
5248c2ecf20Sopenharmony_ci		dom->members[i] = ntohl(arrv_dom->members[i]);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* Update peers affected by this domain record */
5278c2ecf20Sopenharmony_ci	applied_bef = peer->applied;
5288c2ecf20Sopenharmony_ci	mon_apply_domain(mon, peer);
5298c2ecf20Sopenharmony_ci	mon_identify_lost_members(peer, &dom_bef, applied_bef);
5308c2ecf20Sopenharmony_ci	mon_assign_roles(mon, peer_head(peer));
5318c2ecf20Sopenharmony_ciexit:
5328c2ecf20Sopenharmony_ci	write_unlock_bh(&mon->lock);
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_civoid tipc_mon_prep(struct net *net, void *data, int *dlen,
5368c2ecf20Sopenharmony_ci		   struct tipc_mon_state *state, int bearer_id)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
5398c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom = data;
5408c2ecf20Sopenharmony_ci	u16 gen = mon->dom_gen;
5418c2ecf20Sopenharmony_ci	u16 len;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* Send invalid record if not active */
5448c2ecf20Sopenharmony_ci	if (!tipc_mon_is_active(net, mon)) {
5458c2ecf20Sopenharmony_ci		dom->len = 0;
5468c2ecf20Sopenharmony_ci		return;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/* Send only a dummy record with ack if peer has acked our last sent */
5508c2ecf20Sopenharmony_ci	if (likely(state->acked_gen == gen)) {
5518c2ecf20Sopenharmony_ci		len = dom_rec_len(dom, 0);
5528c2ecf20Sopenharmony_ci		*dlen = len;
5538c2ecf20Sopenharmony_ci		dom->len = htons(len);
5548c2ecf20Sopenharmony_ci		dom->gen = htons(gen);
5558c2ecf20Sopenharmony_ci		dom->ack_gen = htons(state->peer_gen);
5568c2ecf20Sopenharmony_ci		dom->member_cnt = 0;
5578c2ecf20Sopenharmony_ci		return;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci	/* Send the full record */
5608c2ecf20Sopenharmony_ci	read_lock_bh(&mon->lock);
5618c2ecf20Sopenharmony_ci	len = ntohs(mon->cache.len);
5628c2ecf20Sopenharmony_ci	*dlen = len;
5638c2ecf20Sopenharmony_ci	memcpy(data, &mon->cache, len);
5648c2ecf20Sopenharmony_ci	read_unlock_bh(&mon->lock);
5658c2ecf20Sopenharmony_ci	dom->ack_gen = htons(state->peer_gen);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_civoid tipc_mon_get_state(struct net *net, u32 addr,
5698c2ecf20Sopenharmony_ci			struct tipc_mon_state *state,
5708c2ecf20Sopenharmony_ci			int bearer_id)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
5738c2ecf20Sopenharmony_ci	struct tipc_peer *peer;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (!tipc_mon_is_active(net, mon)) {
5768c2ecf20Sopenharmony_ci		state->probing = false;
5778c2ecf20Sopenharmony_ci		state->monitoring = true;
5788c2ecf20Sopenharmony_ci		return;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* Used cached state if table has not changed */
5828c2ecf20Sopenharmony_ci	if (!state->probing &&
5838c2ecf20Sopenharmony_ci	    (state->list_gen == mon->list_gen) &&
5848c2ecf20Sopenharmony_ci	    (state->acked_gen == mon->dom_gen))
5858c2ecf20Sopenharmony_ci		return;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	read_lock_bh(&mon->lock);
5888c2ecf20Sopenharmony_ci	peer = get_peer(mon, addr);
5898c2ecf20Sopenharmony_ci	if (peer) {
5908c2ecf20Sopenharmony_ci		state->probing = state->acked_gen != mon->dom_gen;
5918c2ecf20Sopenharmony_ci		state->probing |= peer->down_cnt;
5928c2ecf20Sopenharmony_ci		state->reset |= peer->down_cnt >= MAX_PEER_DOWN_EVENTS;
5938c2ecf20Sopenharmony_ci		state->monitoring = peer->is_local;
5948c2ecf20Sopenharmony_ci		state->monitoring |= peer->is_head;
5958c2ecf20Sopenharmony_ci		state->list_gen = mon->list_gen;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci	read_unlock_bh(&mon->lock);
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic void mon_timeout(struct timer_list *t)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = from_timer(mon, t, timer);
6038c2ecf20Sopenharmony_ci	struct tipc_peer *self;
6048c2ecf20Sopenharmony_ci	int best_member_cnt = dom_size(mon->peer_cnt) - 1;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	write_lock_bh(&mon->lock);
6078c2ecf20Sopenharmony_ci	self = mon->self;
6088c2ecf20Sopenharmony_ci	if (self && (best_member_cnt != self->applied)) {
6098c2ecf20Sopenharmony_ci		mon_update_local_domain(mon);
6108c2ecf20Sopenharmony_ci		mon_assign_roles(mon, self);
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci	write_unlock_bh(&mon->lock);
6138c2ecf20Sopenharmony_ci	mod_timer(&mon->timer, jiffies + mon->timer_intv);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ciint tipc_mon_create(struct net *net, int bearer_id)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	struct tipc_net *tn = tipc_net(net);
6198c2ecf20Sopenharmony_ci	struct tipc_monitor *mon;
6208c2ecf20Sopenharmony_ci	struct tipc_peer *self;
6218c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (tn->monitors[bearer_id])
6248c2ecf20Sopenharmony_ci		return 0;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	mon = kzalloc(sizeof(*mon), GFP_ATOMIC);
6278c2ecf20Sopenharmony_ci	self = kzalloc(sizeof(*self), GFP_ATOMIC);
6288c2ecf20Sopenharmony_ci	dom = kzalloc(sizeof(*dom), GFP_ATOMIC);
6298c2ecf20Sopenharmony_ci	if (!mon || !self || !dom) {
6308c2ecf20Sopenharmony_ci		kfree(mon);
6318c2ecf20Sopenharmony_ci		kfree(self);
6328c2ecf20Sopenharmony_ci		kfree(dom);
6338c2ecf20Sopenharmony_ci		return -ENOMEM;
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci	tn->monitors[bearer_id] = mon;
6368c2ecf20Sopenharmony_ci	rwlock_init(&mon->lock);
6378c2ecf20Sopenharmony_ci	mon->net = net;
6388c2ecf20Sopenharmony_ci	mon->peer_cnt = 1;
6398c2ecf20Sopenharmony_ci	mon->self = self;
6408c2ecf20Sopenharmony_ci	self->domain = dom;
6418c2ecf20Sopenharmony_ci	self->addr = tipc_own_addr(net);
6428c2ecf20Sopenharmony_ci	self->is_up = true;
6438c2ecf20Sopenharmony_ci	self->is_head = true;
6448c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&self->list);
6458c2ecf20Sopenharmony_ci	timer_setup(&mon->timer, mon_timeout, 0);
6468c2ecf20Sopenharmony_ci	mon->timer_intv = msecs_to_jiffies(MON_TIMEOUT + (tn->random & 0xffff));
6478c2ecf20Sopenharmony_ci	mod_timer(&mon->timer, jiffies + mon->timer_intv);
6488c2ecf20Sopenharmony_ci	return 0;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_civoid tipc_mon_delete(struct net *net, int bearer_id)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct tipc_net *tn = tipc_net(net);
6548c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
6558c2ecf20Sopenharmony_ci	struct tipc_peer *self;
6568c2ecf20Sopenharmony_ci	struct tipc_peer *peer, *tmp;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (!mon)
6598c2ecf20Sopenharmony_ci		return;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	self = get_self(net, bearer_id);
6628c2ecf20Sopenharmony_ci	write_lock_bh(&mon->lock);
6638c2ecf20Sopenharmony_ci	tn->monitors[bearer_id] = NULL;
6648c2ecf20Sopenharmony_ci	list_for_each_entry_safe(peer, tmp, &self->list, list) {
6658c2ecf20Sopenharmony_ci		list_del(&peer->list);
6668c2ecf20Sopenharmony_ci		hlist_del(&peer->hash);
6678c2ecf20Sopenharmony_ci		kfree(peer->domain);
6688c2ecf20Sopenharmony_ci		kfree(peer);
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci	mon->self = NULL;
6718c2ecf20Sopenharmony_ci	write_unlock_bh(&mon->lock);
6728c2ecf20Sopenharmony_ci	del_timer_sync(&mon->timer);
6738c2ecf20Sopenharmony_ci	kfree(self->domain);
6748c2ecf20Sopenharmony_ci	kfree(self);
6758c2ecf20Sopenharmony_ci	kfree(mon);
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_civoid tipc_mon_reinit_self(struct net *net)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct tipc_monitor *mon;
6818c2ecf20Sopenharmony_ci	int bearer_id;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {
6848c2ecf20Sopenharmony_ci		mon = tipc_monitor(net, bearer_id);
6858c2ecf20Sopenharmony_ci		if (!mon)
6868c2ecf20Sopenharmony_ci			continue;
6878c2ecf20Sopenharmony_ci		write_lock_bh(&mon->lock);
6888c2ecf20Sopenharmony_ci		mon->self->addr = tipc_own_addr(net);
6898c2ecf20Sopenharmony_ci		write_unlock_bh(&mon->lock);
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ciint tipc_nl_monitor_set_threshold(struct net *net, u32 cluster_size)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct tipc_net *tn = tipc_net(net);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (cluster_size > TIPC_CLUSTER_SIZE)
6988c2ecf20Sopenharmony_ci		return -EINVAL;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	tn->mon_threshold = cluster_size;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	return 0;
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ciint tipc_nl_monitor_get_threshold(struct net *net)
7068c2ecf20Sopenharmony_ci{
7078c2ecf20Sopenharmony_ci	struct tipc_net *tn = tipc_net(net);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	return tn->mon_threshold;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int __tipc_nl_add_monitor_peer(struct tipc_peer *peer,
7138c2ecf20Sopenharmony_ci				      struct tipc_nl_msg *msg)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct tipc_mon_domain *dom = peer->domain;
7168c2ecf20Sopenharmony_ci	struct nlattr *attrs;
7178c2ecf20Sopenharmony_ci	void *hdr;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
7208c2ecf20Sopenharmony_ci			  NLM_F_MULTI, TIPC_NL_MON_PEER_GET);
7218c2ecf20Sopenharmony_ci	if (!hdr)
7228c2ecf20Sopenharmony_ci		return -EMSGSIZE;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON_PEER);
7258c2ecf20Sopenharmony_ci	if (!attrs)
7268c2ecf20Sopenharmony_ci		goto msg_full;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_ADDR, peer->addr))
7298c2ecf20Sopenharmony_ci		goto attr_msg_full;
7308c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_APPLIED, peer->applied))
7318c2ecf20Sopenharmony_ci		goto attr_msg_full;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (peer->is_up)
7348c2ecf20Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_UP))
7358c2ecf20Sopenharmony_ci			goto attr_msg_full;
7368c2ecf20Sopenharmony_ci	if (peer->is_local)
7378c2ecf20Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_LOCAL))
7388c2ecf20Sopenharmony_ci			goto attr_msg_full;
7398c2ecf20Sopenharmony_ci	if (peer->is_head)
7408c2ecf20Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_HEAD))
7418c2ecf20Sopenharmony_ci			goto attr_msg_full;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (dom) {
7448c2ecf20Sopenharmony_ci		if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_DOMGEN, dom->gen))
7458c2ecf20Sopenharmony_ci			goto attr_msg_full;
7468c2ecf20Sopenharmony_ci		if (nla_put_u64_64bit(msg->skb, TIPC_NLA_MON_PEER_UPMAP,
7478c2ecf20Sopenharmony_ci				      dom->up_map, TIPC_NLA_MON_PEER_PAD))
7488c2ecf20Sopenharmony_ci			goto attr_msg_full;
7498c2ecf20Sopenharmony_ci		if (nla_put(msg->skb, TIPC_NLA_MON_PEER_MEMBERS,
7508c2ecf20Sopenharmony_ci			    dom->member_cnt * sizeof(u32), &dom->members))
7518c2ecf20Sopenharmony_ci			goto attr_msg_full;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	nla_nest_end(msg->skb, attrs);
7558c2ecf20Sopenharmony_ci	genlmsg_end(msg->skb, hdr);
7568c2ecf20Sopenharmony_ci	return 0;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ciattr_msg_full:
7598c2ecf20Sopenharmony_ci	nla_nest_cancel(msg->skb, attrs);
7608c2ecf20Sopenharmony_cimsg_full:
7618c2ecf20Sopenharmony_ci	genlmsg_cancel(msg->skb, hdr);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	return -EMSGSIZE;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ciint tipc_nl_add_monitor_peer(struct net *net, struct tipc_nl_msg *msg,
7678c2ecf20Sopenharmony_ci			     u32 bearer_id, u32 *prev_node)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
7708c2ecf20Sopenharmony_ci	struct tipc_peer *peer;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (!mon)
7738c2ecf20Sopenharmony_ci		return -EINVAL;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	read_lock_bh(&mon->lock);
7768c2ecf20Sopenharmony_ci	peer = mon->self;
7778c2ecf20Sopenharmony_ci	do {
7788c2ecf20Sopenharmony_ci		if (*prev_node) {
7798c2ecf20Sopenharmony_ci			if (peer->addr == *prev_node)
7808c2ecf20Sopenharmony_ci				*prev_node = 0;
7818c2ecf20Sopenharmony_ci			else
7828c2ecf20Sopenharmony_ci				continue;
7838c2ecf20Sopenharmony_ci		}
7848c2ecf20Sopenharmony_ci		if (__tipc_nl_add_monitor_peer(peer, msg)) {
7858c2ecf20Sopenharmony_ci			*prev_node = peer->addr;
7868c2ecf20Sopenharmony_ci			read_unlock_bh(&mon->lock);
7878c2ecf20Sopenharmony_ci			return -EMSGSIZE;
7888c2ecf20Sopenharmony_ci		}
7898c2ecf20Sopenharmony_ci	} while ((peer = peer_nxt(peer)) != mon->self);
7908c2ecf20Sopenharmony_ci	read_unlock_bh(&mon->lock);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	return 0;
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ciint __tipc_nl_add_monitor(struct net *net, struct tipc_nl_msg *msg,
7968c2ecf20Sopenharmony_ci			  u32 bearer_id)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
7998c2ecf20Sopenharmony_ci	char bearer_name[TIPC_MAX_BEARER_NAME];
8008c2ecf20Sopenharmony_ci	struct nlattr *attrs;
8018c2ecf20Sopenharmony_ci	void *hdr;
8028c2ecf20Sopenharmony_ci	int ret;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	ret = tipc_bearer_get_name(net, bearer_name, bearer_id);
8058c2ecf20Sopenharmony_ci	if (ret || !mon)
8068c2ecf20Sopenharmony_ci		return 0;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
8098c2ecf20Sopenharmony_ci			  NLM_F_MULTI, TIPC_NL_MON_GET);
8108c2ecf20Sopenharmony_ci	if (!hdr)
8118c2ecf20Sopenharmony_ci		return -EMSGSIZE;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON);
8148c2ecf20Sopenharmony_ci	if (!attrs)
8158c2ecf20Sopenharmony_ci		goto msg_full;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	read_lock_bh(&mon->lock);
8188c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_MON_REF, bearer_id))
8198c2ecf20Sopenharmony_ci		goto attr_msg_full;
8208c2ecf20Sopenharmony_ci	if (tipc_mon_is_active(net, mon))
8218c2ecf20Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_MON_ACTIVE))
8228c2ecf20Sopenharmony_ci			goto attr_msg_full;
8238c2ecf20Sopenharmony_ci	if (nla_put_string(msg->skb, TIPC_NLA_MON_BEARER_NAME, bearer_name))
8248c2ecf20Sopenharmony_ci		goto attr_msg_full;
8258c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEERCNT, mon->peer_cnt))
8268c2ecf20Sopenharmony_ci		goto attr_msg_full;
8278c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_MON_LISTGEN, mon->list_gen))
8288c2ecf20Sopenharmony_ci		goto attr_msg_full;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	read_unlock_bh(&mon->lock);
8318c2ecf20Sopenharmony_ci	nla_nest_end(msg->skb, attrs);
8328c2ecf20Sopenharmony_ci	genlmsg_end(msg->skb, hdr);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	return 0;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ciattr_msg_full:
8378c2ecf20Sopenharmony_ci	read_unlock_bh(&mon->lock);
8388c2ecf20Sopenharmony_ci	nla_nest_cancel(msg->skb, attrs);
8398c2ecf20Sopenharmony_cimsg_full:
8408c2ecf20Sopenharmony_ci	genlmsg_cancel(msg->skb, hdr);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	return -EMSGSIZE;
8438c2ecf20Sopenharmony_ci}
844