xref: /openbmc/linux/net/tipc/monitor.c (revision 292a089d)
135c55c98SJon Paul Maloy /*
235c55c98SJon Paul Maloy  * net/tipc/monitor.c
335c55c98SJon Paul Maloy  *
435c55c98SJon Paul Maloy  * Copyright (c) 2016, Ericsson AB
535c55c98SJon Paul Maloy  * All rights reserved.
635c55c98SJon Paul Maloy  *
735c55c98SJon Paul Maloy  * Redistribution and use in source and binary forms, with or without
835c55c98SJon Paul Maloy  * modification, are permitted provided that the following conditions are met:
935c55c98SJon Paul Maloy  *
1035c55c98SJon Paul Maloy  * 1. Redistributions of source code must retain the above copyright
1135c55c98SJon Paul Maloy  *    notice, this list of conditions and the following disclaimer.
1235c55c98SJon Paul Maloy  * 2. Redistributions in binary form must reproduce the above copyright
1335c55c98SJon Paul Maloy  *    notice, this list of conditions and the following disclaimer in the
1435c55c98SJon Paul Maloy  *    documentation and/or other materials provided with the distribution.
1535c55c98SJon Paul Maloy  * 3. Neither the names of the copyright holders nor the names of its
1635c55c98SJon Paul Maloy  *    contributors may be used to endorse or promote products derived from
1735c55c98SJon Paul Maloy  *    this software without specific prior written permission.
1835c55c98SJon Paul Maloy  *
1935c55c98SJon Paul Maloy  * Alternatively, this software may be distributed under the terms of the
2035c55c98SJon Paul Maloy  * GNU General Public License ("GPL") version 2 as published by the Free
2135c55c98SJon Paul Maloy  * Software Foundation.
2235c55c98SJon Paul Maloy  *
2335c55c98SJon Paul Maloy  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2435c55c98SJon Paul Maloy  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2535c55c98SJon Paul Maloy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2635c55c98SJon Paul Maloy  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2735c55c98SJon Paul Maloy  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2835c55c98SJon Paul Maloy  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2935c55c98SJon Paul Maloy  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3035c55c98SJon Paul Maloy  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3135c55c98SJon Paul Maloy  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3235c55c98SJon Paul Maloy  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3335c55c98SJon Paul Maloy  * POSSIBILITY OF SUCH DAMAGE.
3435c55c98SJon Paul Maloy  */
3535c55c98SJon Paul Maloy 
36cf6f7e1dSParthasarathy Bhuvaragan #include <net/genetlink.h>
3735c55c98SJon Paul Maloy #include "core.h"
3835c55c98SJon Paul Maloy #include "addr.h"
3935c55c98SJon Paul Maloy #include "monitor.h"
40cf6f7e1dSParthasarathy Bhuvaragan #include "bearer.h"
4135c55c98SJon Paul Maloy 
4235c55c98SJon Paul Maloy #define MAX_MON_DOMAIN       64
4335c55c98SJon Paul Maloy #define MON_TIMEOUT          120000
4435c55c98SJon Paul Maloy #define MAX_PEER_DOWN_EVENTS 4
4535c55c98SJon Paul Maloy 
4635c55c98SJon Paul Maloy /* struct tipc_mon_domain: domain record to be transferred between peers
4735c55c98SJon Paul Maloy  * @len: actual size of domain record
4835c55c98SJon Paul Maloy  * @gen: current generation of sender's domain
4935c55c98SJon Paul Maloy  * @ack_gen: most recent generation of self's domain acked by peer
5035c55c98SJon Paul Maloy  * @member_cnt: number of domain member nodes described in this record
5135c55c98SJon Paul Maloy  * @up_map: bit map indicating which of the members the sender considers up
5235c55c98SJon Paul Maloy  * @members: identity of the domain members
5335c55c98SJon Paul Maloy  */
5435c55c98SJon Paul Maloy struct tipc_mon_domain {
5535c55c98SJon Paul Maloy 	u16 len;
5635c55c98SJon Paul Maloy 	u16 gen;
5735c55c98SJon Paul Maloy 	u16 ack_gen;
5835c55c98SJon Paul Maloy 	u16 member_cnt;
5935c55c98SJon Paul Maloy 	u64 up_map;
6035c55c98SJon Paul Maloy 	u32 members[MAX_MON_DOMAIN];
6135c55c98SJon Paul Maloy };
6235c55c98SJon Paul Maloy 
6335c55c98SJon Paul Maloy /* struct tipc_peer: state of a peer node and its domain
6435c55c98SJon Paul Maloy  * @addr: tipc node identity of peer
6535c55c98SJon Paul Maloy  * @head_map: shows which other nodes currently consider peer 'up'
6635c55c98SJon Paul Maloy  * @domain: most recent domain record from peer
6735c55c98SJon Paul Maloy  * @hash: position in hashed lookup list
6835c55c98SJon Paul Maloy  * @list: position in linked list, in circular ascending order by 'addr'
6935c55c98SJon Paul Maloy  * @applied: number of reported domain members applied on this monitor list
7035c55c98SJon Paul Maloy  * @is_up: peer is up as seen from this node
7135c55c98SJon Paul Maloy  * @is_head: peer is assigned domain head as seen from this node
7235c55c98SJon Paul Maloy  * @is_local: peer is in local domain and should be continuously monitored
7335c55c98SJon Paul Maloy  * @down_cnt: - numbers of other peers which have reported this on lost
7435c55c98SJon Paul Maloy  */
7535c55c98SJon Paul Maloy struct tipc_peer {
7635c55c98SJon Paul Maloy 	u32 addr;
7735c55c98SJon Paul Maloy 	struct tipc_mon_domain *domain;
7835c55c98SJon Paul Maloy 	struct hlist_node hash;
7935c55c98SJon Paul Maloy 	struct list_head list;
8035c55c98SJon Paul Maloy 	u8 applied;
8135c55c98SJon Paul Maloy 	u8 down_cnt;
8235c55c98SJon Paul Maloy 	bool is_up;
8335c55c98SJon Paul Maloy 	bool is_head;
8435c55c98SJon Paul Maloy 	bool is_local;
8535c55c98SJon Paul Maloy };
8635c55c98SJon Paul Maloy 
8735c55c98SJon Paul Maloy struct tipc_monitor {
8835c55c98SJon Paul Maloy 	struct hlist_head peers[NODE_HTABLE_SIZE];
8935c55c98SJon Paul Maloy 	int peer_cnt;
9035c55c98SJon Paul Maloy 	struct tipc_peer *self;
9135c55c98SJon Paul Maloy 	rwlock_t lock;
9235c55c98SJon Paul Maloy 	struct tipc_mon_domain cache;
9335c55c98SJon Paul Maloy 	u16 list_gen;
9435c55c98SJon Paul Maloy 	u16 dom_gen;
9535c55c98SJon Paul Maloy 	struct net *net;
9635c55c98SJon Paul Maloy 	struct timer_list timer;
9735c55c98SJon Paul Maloy 	unsigned long timer_intv;
9835c55c98SJon Paul Maloy };
9935c55c98SJon Paul Maloy 
tipc_monitor(struct net * net,int bearer_id)10035c55c98SJon Paul Maloy static struct tipc_monitor *tipc_monitor(struct net *net, int bearer_id)
10135c55c98SJon Paul Maloy {
10235c55c98SJon Paul Maloy 	return tipc_net(net)->monitors[bearer_id];
10335c55c98SJon Paul Maloy }
10435c55c98SJon Paul Maloy 
10535c55c98SJon Paul Maloy const int tipc_max_domain_size = sizeof(struct tipc_mon_domain);
10635c55c98SJon Paul Maloy 
mon_cpu_to_le16(u16 val)10797bc84bbSHoang Huu Le static inline u16 mon_cpu_to_le16(u16 val)
10897bc84bbSHoang Huu Le {
10997bc84bbSHoang Huu Le 	return (__force __u16)htons(val);
11097bc84bbSHoang Huu Le }
11197bc84bbSHoang Huu Le 
mon_cpu_to_le32(u32 val)11297bc84bbSHoang Huu Le static inline u32 mon_cpu_to_le32(u32 val)
11397bc84bbSHoang Huu Le {
11497bc84bbSHoang Huu Le 	return (__force __u32)htonl(val);
11597bc84bbSHoang Huu Le }
11697bc84bbSHoang Huu Le 
mon_cpu_to_le64(u64 val)11797bc84bbSHoang Huu Le static inline u64 mon_cpu_to_le64(u64 val)
11897bc84bbSHoang Huu Le {
11997bc84bbSHoang Huu Le 	return (__force __u64)cpu_to_be64(val);
12097bc84bbSHoang Huu Le }
12197bc84bbSHoang Huu Le 
mon_le16_to_cpu(u16 val)12297bc84bbSHoang Huu Le static inline u16 mon_le16_to_cpu(u16 val)
12397bc84bbSHoang Huu Le {
12497bc84bbSHoang Huu Le 	return ntohs((__force __be16)val);
12597bc84bbSHoang Huu Le }
12697bc84bbSHoang Huu Le 
mon_le32_to_cpu(u32 val)12797bc84bbSHoang Huu Le static inline u32 mon_le32_to_cpu(u32 val)
12897bc84bbSHoang Huu Le {
12997bc84bbSHoang Huu Le 	return ntohl((__force __be32)val);
13097bc84bbSHoang Huu Le }
13197bc84bbSHoang Huu Le 
mon_le64_to_cpu(u64 val)13297bc84bbSHoang Huu Le static inline u64 mon_le64_to_cpu(u64 val)
13397bc84bbSHoang Huu Le {
13497bc84bbSHoang Huu Le 	return be64_to_cpu((__force __be64)val);
13597bc84bbSHoang Huu Le }
13697bc84bbSHoang Huu Le 
13735c55c98SJon Paul Maloy /* dom_rec_len(): actual length of domain record for transport
13835c55c98SJon Paul Maloy  */
dom_rec_len(struct tipc_mon_domain * dom,u16 mcnt)13935c55c98SJon Paul Maloy static int dom_rec_len(struct tipc_mon_domain *dom, u16 mcnt)
14035c55c98SJon Paul Maloy {
141520ec343SZheng Yongjun 	return (offsetof(struct tipc_mon_domain, members)) + (mcnt * sizeof(u32));
14235c55c98SJon Paul Maloy }
14335c55c98SJon Paul Maloy 
14435c55c98SJon Paul Maloy /* dom_size() : calculate size of own domain based on number of peers
14535c55c98SJon Paul Maloy  */
dom_size(int peers)14635c55c98SJon Paul Maloy static int dom_size(int peers)
14735c55c98SJon Paul Maloy {
14835c55c98SJon Paul Maloy 	int i = 0;
14935c55c98SJon Paul Maloy 
15035c55c98SJon Paul Maloy 	while ((i * i) < peers)
15135c55c98SJon Paul Maloy 		i++;
15235c55c98SJon Paul Maloy 	return i < MAX_MON_DOMAIN ? i : MAX_MON_DOMAIN;
15335c55c98SJon Paul Maloy }
15435c55c98SJon Paul Maloy 
map_set(u64 * up_map,int i,unsigned int v)15535c55c98SJon Paul Maloy static void map_set(u64 *up_map, int i, unsigned int v)
15635c55c98SJon Paul Maloy {
1570350cb48SDan Carpenter 	*up_map &= ~(1ULL << i);
1580350cb48SDan Carpenter 	*up_map |= ((u64)v << i);
15935c55c98SJon Paul Maloy }
16035c55c98SJon Paul Maloy 
map_get(u64 up_map,int i)16135c55c98SJon Paul Maloy static int map_get(u64 up_map, int i)
16235c55c98SJon Paul Maloy {
163e2b224abSDan Carpenter 	return (up_map & (1ULL << i)) >> i;
16435c55c98SJon Paul Maloy }
16535c55c98SJon Paul Maloy 
peer_prev(struct tipc_peer * peer)16635c55c98SJon Paul Maloy static struct tipc_peer *peer_prev(struct tipc_peer *peer)
16735c55c98SJon Paul Maloy {
16835c55c98SJon Paul Maloy 	return list_last_entry(&peer->list, struct tipc_peer, list);
16935c55c98SJon Paul Maloy }
17035c55c98SJon Paul Maloy 
peer_nxt(struct tipc_peer * peer)17135c55c98SJon Paul Maloy static struct tipc_peer *peer_nxt(struct tipc_peer *peer)
17235c55c98SJon Paul Maloy {
17335c55c98SJon Paul Maloy 	return list_first_entry(&peer->list, struct tipc_peer, list);
17435c55c98SJon Paul Maloy }
17535c55c98SJon Paul Maloy 
peer_head(struct tipc_peer * peer)17635c55c98SJon Paul Maloy static struct tipc_peer *peer_head(struct tipc_peer *peer)
17735c55c98SJon Paul Maloy {
17835c55c98SJon Paul Maloy 	while (!peer->is_head)
17935c55c98SJon Paul Maloy 		peer = peer_prev(peer);
18035c55c98SJon Paul Maloy 	return peer;
18135c55c98SJon Paul Maloy }
18235c55c98SJon Paul Maloy 
get_peer(struct tipc_monitor * mon,u32 addr)18335c55c98SJon Paul Maloy static struct tipc_peer *get_peer(struct tipc_monitor *mon, u32 addr)
18435c55c98SJon Paul Maloy {
18535c55c98SJon Paul Maloy 	struct tipc_peer *peer;
18635c55c98SJon Paul Maloy 	unsigned int thash = tipc_hashfn(addr);
18735c55c98SJon Paul Maloy 
18835c55c98SJon Paul Maloy 	hlist_for_each_entry(peer, &mon->peers[thash], hash) {
18935c55c98SJon Paul Maloy 		if (peer->addr == addr)
19035c55c98SJon Paul Maloy 			return peer;
19135c55c98SJon Paul Maloy 	}
19235c55c98SJon Paul Maloy 	return NULL;
19335c55c98SJon Paul Maloy }
19435c55c98SJon Paul Maloy 
get_self(struct net * net,int bearer_id)19535c55c98SJon Paul Maloy static struct tipc_peer *get_self(struct net *net, int bearer_id)
19635c55c98SJon Paul Maloy {
19735c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
19835c55c98SJon Paul Maloy 
19935c55c98SJon Paul Maloy 	return mon->self;
20035c55c98SJon Paul Maloy }
20135c55c98SJon Paul Maloy 
tipc_mon_is_active(struct net * net,struct tipc_monitor * mon)20235c55c98SJon Paul Maloy static inline bool tipc_mon_is_active(struct net *net, struct tipc_monitor *mon)
20335c55c98SJon Paul Maloy {
20435c55c98SJon Paul Maloy 	struct tipc_net *tn = tipc_net(net);
20535c55c98SJon Paul Maloy 
20635c55c98SJon Paul Maloy 	return mon->peer_cnt > tn->mon_threshold;
20735c55c98SJon Paul Maloy }
20835c55c98SJon Paul Maloy 
20935c55c98SJon Paul Maloy /* mon_identify_lost_members() : - identify amd mark potentially lost members
21035c55c98SJon Paul Maloy  */
mon_identify_lost_members(struct tipc_peer * peer,struct tipc_mon_domain * dom_bef,int applied_bef)21135c55c98SJon Paul Maloy static void mon_identify_lost_members(struct tipc_peer *peer,
21235c55c98SJon Paul Maloy 				      struct tipc_mon_domain *dom_bef,
21335c55c98SJon Paul Maloy 				      int applied_bef)
21435c55c98SJon Paul Maloy {
21535c55c98SJon Paul Maloy 	struct tipc_peer *member = peer;
21635c55c98SJon Paul Maloy 	struct tipc_mon_domain *dom_aft = peer->domain;
21735c55c98SJon Paul Maloy 	int applied_aft = peer->applied;
21835c55c98SJon Paul Maloy 	int i;
21935c55c98SJon Paul Maloy 
22035c55c98SJon Paul Maloy 	for (i = 0; i < applied_bef; i++) {
22135c55c98SJon Paul Maloy 		member = peer_nxt(member);
22235c55c98SJon Paul Maloy 
22335c55c98SJon Paul Maloy 		/* Do nothing if self or peer already see member as down */
22435c55c98SJon Paul Maloy 		if (!member->is_up || !map_get(dom_bef->up_map, i))
22535c55c98SJon Paul Maloy 			continue;
22635c55c98SJon Paul Maloy 
22735c55c98SJon Paul Maloy 		/* Loss of local node must be detected by active probing */
22835c55c98SJon Paul Maloy 		if (member->is_local)
22935c55c98SJon Paul Maloy 			continue;
23035c55c98SJon Paul Maloy 
23135c55c98SJon Paul Maloy 		/* Start probing if member was removed from applied domain */
23235c55c98SJon Paul Maloy 		if (!applied_aft || (applied_aft < i)) {
23335c55c98SJon Paul Maloy 			member->down_cnt = 1;
23435c55c98SJon Paul Maloy 			continue;
23535c55c98SJon Paul Maloy 		}
23635c55c98SJon Paul Maloy 
23735c55c98SJon Paul Maloy 		/* Member loss is confirmed if it is still in applied domain */
23835c55c98SJon Paul Maloy 		if (!map_get(dom_aft->up_map, i))
23935c55c98SJon Paul Maloy 			member->down_cnt++;
24035c55c98SJon Paul Maloy 	}
24135c55c98SJon Paul Maloy }
24235c55c98SJon Paul Maloy 
24335c55c98SJon Paul Maloy /* mon_apply_domain() : match a peer's domain record against monitor list
24435c55c98SJon Paul Maloy  */
mon_apply_domain(struct tipc_monitor * mon,struct tipc_peer * peer)24535c55c98SJon Paul Maloy static void mon_apply_domain(struct tipc_monitor *mon,
24635c55c98SJon Paul Maloy 			     struct tipc_peer *peer)
24735c55c98SJon Paul Maloy {
24835c55c98SJon Paul Maloy 	struct tipc_mon_domain *dom = peer->domain;
24935c55c98SJon Paul Maloy 	struct tipc_peer *member;
25035c55c98SJon Paul Maloy 	u32 addr;
25135c55c98SJon Paul Maloy 	int i;
25235c55c98SJon Paul Maloy 
25335c55c98SJon Paul Maloy 	if (!dom || !peer->is_up)
25435c55c98SJon Paul Maloy 		return;
25535c55c98SJon Paul Maloy 
25635c55c98SJon Paul Maloy 	/* Scan across domain members and match against monitor list */
25735c55c98SJon Paul Maloy 	peer->applied = 0;
25835c55c98SJon Paul Maloy 	member = peer_nxt(peer);
25935c55c98SJon Paul Maloy 	for (i = 0; i < dom->member_cnt; i++) {
26035c55c98SJon Paul Maloy 		addr = dom->members[i];
26135c55c98SJon Paul Maloy 		if (addr != member->addr)
26235c55c98SJon Paul Maloy 			return;
26335c55c98SJon Paul Maloy 		peer->applied++;
26435c55c98SJon Paul Maloy 		member = peer_nxt(member);
26535c55c98SJon Paul Maloy 	}
26635c55c98SJon Paul Maloy }
26735c55c98SJon Paul Maloy 
26835c55c98SJon Paul Maloy /* mon_update_local_domain() : update after peer addition/removal/up/down
26935c55c98SJon Paul Maloy  */
mon_update_local_domain(struct tipc_monitor * mon)27035c55c98SJon Paul Maloy static void mon_update_local_domain(struct tipc_monitor *mon)
27135c55c98SJon Paul Maloy {
27235c55c98SJon Paul Maloy 	struct tipc_peer *self = mon->self;
27335c55c98SJon Paul Maloy 	struct tipc_mon_domain *cache = &mon->cache;
27435c55c98SJon Paul Maloy 	struct tipc_mon_domain *dom = self->domain;
27535c55c98SJon Paul Maloy 	struct tipc_peer *peer = self;
27635c55c98SJon Paul Maloy 	u64 prev_up_map = dom->up_map;
27735c55c98SJon Paul Maloy 	u16 member_cnt, i;
27835c55c98SJon Paul Maloy 	bool diff;
27935c55c98SJon Paul Maloy 
28035c55c98SJon Paul Maloy 	/* Update local domain size based on current size of cluster */
28135c55c98SJon Paul Maloy 	member_cnt = dom_size(mon->peer_cnt) - 1;
28235c55c98SJon Paul Maloy 	self->applied = member_cnt;
28335c55c98SJon Paul Maloy 
28435c55c98SJon Paul Maloy 	/* Update native and cached outgoing local domain records */
28535c55c98SJon Paul Maloy 	dom->len = dom_rec_len(dom, member_cnt);
28635c55c98SJon Paul Maloy 	diff = dom->member_cnt != member_cnt;
28735c55c98SJon Paul Maloy 	dom->member_cnt = member_cnt;
28835c55c98SJon Paul Maloy 	for (i = 0; i < member_cnt; i++) {
28935c55c98SJon Paul Maloy 		peer = peer_nxt(peer);
29035c55c98SJon Paul Maloy 		diff |= dom->members[i] != peer->addr;
29135c55c98SJon Paul Maloy 		dom->members[i] = peer->addr;
29235c55c98SJon Paul Maloy 		map_set(&dom->up_map, i, peer->is_up);
29397bc84bbSHoang Huu Le 		cache->members[i] = mon_cpu_to_le32(peer->addr);
29435c55c98SJon Paul Maloy 	}
29535c55c98SJon Paul Maloy 	diff |= dom->up_map != prev_up_map;
29635c55c98SJon Paul Maloy 	if (!diff)
29735c55c98SJon Paul Maloy 		return;
29835c55c98SJon Paul Maloy 	dom->gen = ++mon->dom_gen;
29997bc84bbSHoang Huu Le 	cache->len = mon_cpu_to_le16(dom->len);
30097bc84bbSHoang Huu Le 	cache->gen = mon_cpu_to_le16(dom->gen);
30197bc84bbSHoang Huu Le 	cache->member_cnt = mon_cpu_to_le16(member_cnt);
30297bc84bbSHoang Huu Le 	cache->up_map = mon_cpu_to_le64(dom->up_map);
30335c55c98SJon Paul Maloy 	mon_apply_domain(mon, self);
30435c55c98SJon Paul Maloy }
30535c55c98SJon Paul Maloy 
30635c55c98SJon Paul Maloy /* mon_update_neighbors() : update preceding neighbors of added/removed peer
30735c55c98SJon Paul Maloy  */
mon_update_neighbors(struct tipc_monitor * mon,struct tipc_peer * peer)30835c55c98SJon Paul Maloy static void mon_update_neighbors(struct tipc_monitor *mon,
30935c55c98SJon Paul Maloy 				 struct tipc_peer *peer)
31035c55c98SJon Paul Maloy {
31135c55c98SJon Paul Maloy 	int dz, i;
31235c55c98SJon Paul Maloy 
31335c55c98SJon Paul Maloy 	dz = dom_size(mon->peer_cnt);
31435c55c98SJon Paul Maloy 	for (i = 0; i < dz; i++) {
31535c55c98SJon Paul Maloy 		mon_apply_domain(mon, peer);
31635c55c98SJon Paul Maloy 		peer = peer_prev(peer);
31735c55c98SJon Paul Maloy 	}
31835c55c98SJon Paul Maloy }
31935c55c98SJon Paul Maloy 
32035c55c98SJon Paul Maloy /* mon_assign_roles() : reassign peer roles after a network change
32135c55c98SJon Paul Maloy  * The monitor list is consistent at this stage; i.e., each peer is monitoring
32235c55c98SJon Paul Maloy  * a set of domain members as matched between domain record and the monitor list
32335c55c98SJon Paul Maloy  */
mon_assign_roles(struct tipc_monitor * mon,struct tipc_peer * head)32435c55c98SJon Paul Maloy static void mon_assign_roles(struct tipc_monitor *mon, struct tipc_peer *head)
32535c55c98SJon Paul Maloy {
32635c55c98SJon Paul Maloy 	struct tipc_peer *peer = peer_nxt(head);
32735c55c98SJon Paul Maloy 	struct tipc_peer *self = mon->self;
32835c55c98SJon Paul Maloy 	int i = 0;
32935c55c98SJon Paul Maloy 
33035c55c98SJon Paul Maloy 	for (; peer != self; peer = peer_nxt(peer)) {
33135c55c98SJon Paul Maloy 		peer->is_local = false;
33235c55c98SJon Paul Maloy 
33335c55c98SJon Paul Maloy 		/* Update domain member */
33435c55c98SJon Paul Maloy 		if (i++ < head->applied) {
33535c55c98SJon Paul Maloy 			peer->is_head = false;
33635c55c98SJon Paul Maloy 			if (head == self)
33735c55c98SJon Paul Maloy 				peer->is_local = true;
33835c55c98SJon Paul Maloy 			continue;
33935c55c98SJon Paul Maloy 		}
34035c55c98SJon Paul Maloy 		/* Assign next domain head */
34135c55c98SJon Paul Maloy 		if (!peer->is_up)
34235c55c98SJon Paul Maloy 			continue;
34335c55c98SJon Paul Maloy 		if (peer->is_head)
34435c55c98SJon Paul Maloy 			break;
34535c55c98SJon Paul Maloy 		head = peer;
34635c55c98SJon Paul Maloy 		head->is_head = true;
34735c55c98SJon Paul Maloy 		i = 0;
34835c55c98SJon Paul Maloy 	}
34935c55c98SJon Paul Maloy 	mon->list_gen++;
35035c55c98SJon Paul Maloy }
35135c55c98SJon Paul Maloy 
tipc_mon_remove_peer(struct net * net,u32 addr,int bearer_id)35235c55c98SJon Paul Maloy void tipc_mon_remove_peer(struct net *net, u32 addr, int bearer_id)
35335c55c98SJon Paul Maloy {
35435c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
355746a1edaSHoang Le 	struct tipc_peer *self;
35635c55c98SJon Paul Maloy 	struct tipc_peer *peer, *prev, *head;
35735c55c98SJon Paul Maloy 
358746a1edaSHoang Le 	if (!mon)
359746a1edaSHoang Le 		return;
360746a1edaSHoang Le 
361746a1edaSHoang Le 	self = get_self(net, bearer_id);
36235c55c98SJon Paul Maloy 	write_lock_bh(&mon->lock);
36335c55c98SJon Paul Maloy 	peer = get_peer(mon, addr);
36435c55c98SJon Paul Maloy 	if (!peer)
36535c55c98SJon Paul Maloy 		goto exit;
36635c55c98SJon Paul Maloy 	prev = peer_prev(peer);
36735c55c98SJon Paul Maloy 	list_del(&peer->list);
36835c55c98SJon Paul Maloy 	hlist_del(&peer->hash);
36935c55c98SJon Paul Maloy 	kfree(peer->domain);
37035c55c98SJon Paul Maloy 	kfree(peer);
37135c55c98SJon Paul Maloy 	mon->peer_cnt--;
37235c55c98SJon Paul Maloy 	head = peer_head(prev);
37335c55c98SJon Paul Maloy 	if (head == self)
37435c55c98SJon Paul Maloy 		mon_update_local_domain(mon);
37535c55c98SJon Paul Maloy 	mon_update_neighbors(mon, prev);
37635c55c98SJon Paul Maloy 
37735c55c98SJon Paul Maloy 	/* Revert to full-mesh monitoring if we reach threshold */
37835c55c98SJon Paul Maloy 	if (!tipc_mon_is_active(net, mon)) {
37935c55c98SJon Paul Maloy 		list_for_each_entry(peer, &self->list, list) {
38035c55c98SJon Paul Maloy 			kfree(peer->domain);
38135c55c98SJon Paul Maloy 			peer->domain = NULL;
38235c55c98SJon Paul Maloy 			peer->applied = 0;
38335c55c98SJon Paul Maloy 		}
38435c55c98SJon Paul Maloy 	}
38535c55c98SJon Paul Maloy 	mon_assign_roles(mon, head);
38635c55c98SJon Paul Maloy exit:
38735c55c98SJon Paul Maloy 	write_unlock_bh(&mon->lock);
38835c55c98SJon Paul Maloy }
38935c55c98SJon Paul Maloy 
tipc_mon_add_peer(struct tipc_monitor * mon,u32 addr,struct tipc_peer ** peer)39035c55c98SJon Paul Maloy static bool tipc_mon_add_peer(struct tipc_monitor *mon, u32 addr,
39135c55c98SJon Paul Maloy 			      struct tipc_peer **peer)
39235c55c98SJon Paul Maloy {
39335c55c98SJon Paul Maloy 	struct tipc_peer *self = mon->self;
39435c55c98SJon Paul Maloy 	struct tipc_peer *cur, *prev, *p;
39535c55c98SJon Paul Maloy 
39635c55c98SJon Paul Maloy 	p = kzalloc(sizeof(*p), GFP_ATOMIC);
39735c55c98SJon Paul Maloy 	*peer = p;
39835c55c98SJon Paul Maloy 	if (!p)
39935c55c98SJon Paul Maloy 		return false;
40035c55c98SJon Paul Maloy 	p->addr = addr;
40135c55c98SJon Paul Maloy 
40235c55c98SJon Paul Maloy 	/* Add new peer to lookup list */
40335c55c98SJon Paul Maloy 	INIT_LIST_HEAD(&p->list);
40435c55c98SJon Paul Maloy 	hlist_add_head(&p->hash, &mon->peers[tipc_hashfn(addr)]);
40535c55c98SJon Paul Maloy 
40635c55c98SJon Paul Maloy 	/* Sort new peer into iterator list, in ascending circular order */
40735c55c98SJon Paul Maloy 	prev = self;
40835c55c98SJon Paul Maloy 	list_for_each_entry(cur, &self->list, list) {
40935c55c98SJon Paul Maloy 		if ((addr > prev->addr) && (addr < cur->addr))
41035c55c98SJon Paul Maloy 			break;
41135c55c98SJon Paul Maloy 		if (((addr < cur->addr) || (addr > prev->addr)) &&
41235c55c98SJon Paul Maloy 		    (prev->addr > cur->addr))
41335c55c98SJon Paul Maloy 			break;
41435c55c98SJon Paul Maloy 		prev = cur;
41535c55c98SJon Paul Maloy 	}
41635c55c98SJon Paul Maloy 	list_add_tail(&p->list, &cur->list);
41735c55c98SJon Paul Maloy 	mon->peer_cnt++;
41835c55c98SJon Paul Maloy 	mon_update_neighbors(mon, p);
41935c55c98SJon Paul Maloy 	return true;
42035c55c98SJon Paul Maloy }
42135c55c98SJon Paul Maloy 
tipc_mon_peer_up(struct net * net,u32 addr,int bearer_id)42235c55c98SJon Paul Maloy void tipc_mon_peer_up(struct net *net, u32 addr, int bearer_id)
42335c55c98SJon Paul Maloy {
42435c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
42535c55c98SJon Paul Maloy 	struct tipc_peer *self = get_self(net, bearer_id);
42635c55c98SJon Paul Maloy 	struct tipc_peer *peer, *head;
42735c55c98SJon Paul Maloy 
42835c55c98SJon Paul Maloy 	write_lock_bh(&mon->lock);
42935c55c98SJon Paul Maloy 	peer = get_peer(mon, addr);
43035c55c98SJon Paul Maloy 	if (!peer && !tipc_mon_add_peer(mon, addr, &peer))
43135c55c98SJon Paul Maloy 		goto exit;
43235c55c98SJon Paul Maloy 	peer->is_up = true;
43335c55c98SJon Paul Maloy 	head = peer_head(peer);
43435c55c98SJon Paul Maloy 	if (head == self)
43535c55c98SJon Paul Maloy 		mon_update_local_domain(mon);
43635c55c98SJon Paul Maloy 	mon_assign_roles(mon, head);
43735c55c98SJon Paul Maloy exit:
43835c55c98SJon Paul Maloy 	write_unlock_bh(&mon->lock);
43935c55c98SJon Paul Maloy }
44035c55c98SJon Paul Maloy 
tipc_mon_peer_down(struct net * net,u32 addr,int bearer_id)44135c55c98SJon Paul Maloy void tipc_mon_peer_down(struct net *net, u32 addr, int bearer_id)
44235c55c98SJon Paul Maloy {
44335c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
444746a1edaSHoang Le 	struct tipc_peer *self;
44535c55c98SJon Paul Maloy 	struct tipc_peer *peer, *head;
44635c55c98SJon Paul Maloy 	struct tipc_mon_domain *dom;
44735c55c98SJon Paul Maloy 	int applied;
44835c55c98SJon Paul Maloy 
449746a1edaSHoang Le 	if (!mon)
450746a1edaSHoang Le 		return;
451746a1edaSHoang Le 
452746a1edaSHoang Le 	self = get_self(net, bearer_id);
45335c55c98SJon Paul Maloy 	write_lock_bh(&mon->lock);
45435c55c98SJon Paul Maloy 	peer = get_peer(mon, addr);
45535c55c98SJon Paul Maloy 	if (!peer) {
45635c55c98SJon Paul Maloy 		pr_warn("Mon: unknown link %x/%u DOWN\n", addr, bearer_id);
45735c55c98SJon Paul Maloy 		goto exit;
45835c55c98SJon Paul Maloy 	}
45935c55c98SJon Paul Maloy 	applied = peer->applied;
46035c55c98SJon Paul Maloy 	peer->applied = 0;
46135c55c98SJon Paul Maloy 	dom = peer->domain;
46235c55c98SJon Paul Maloy 	peer->domain = NULL;
46335c55c98SJon Paul Maloy 	if (peer->is_head)
46435c55c98SJon Paul Maloy 		mon_identify_lost_members(peer, dom, applied);
46535c55c98SJon Paul Maloy 	kfree(dom);
46635c55c98SJon Paul Maloy 	peer->is_up = false;
46735c55c98SJon Paul Maloy 	peer->is_head = false;
46835c55c98SJon Paul Maloy 	peer->is_local = false;
46935c55c98SJon Paul Maloy 	peer->down_cnt = 0;
47035c55c98SJon Paul Maloy 	head = peer_head(peer);
47135c55c98SJon Paul Maloy 	if (head == self)
47235c55c98SJon Paul Maloy 		mon_update_local_domain(mon);
47335c55c98SJon Paul Maloy 	mon_assign_roles(mon, head);
47435c55c98SJon Paul Maloy exit:
47535c55c98SJon Paul Maloy 	write_unlock_bh(&mon->lock);
47635c55c98SJon Paul Maloy }
47735c55c98SJon Paul Maloy 
47835c55c98SJon Paul Maloy /* tipc_mon_rcv - process monitor domain event message
47935c55c98SJon Paul Maloy  */
tipc_mon_rcv(struct net * net,void * data,u16 dlen,u32 addr,struct tipc_mon_state * state,int bearer_id)48035c55c98SJon Paul Maloy void tipc_mon_rcv(struct net *net, void *data, u16 dlen, u32 addr,
48135c55c98SJon Paul Maloy 		  struct tipc_mon_state *state, int bearer_id)
48235c55c98SJon Paul Maloy {
48335c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
48435c55c98SJon Paul Maloy 	struct tipc_mon_domain *arrv_dom = data;
48535c55c98SJon Paul Maloy 	struct tipc_mon_domain dom_bef;
48635c55c98SJon Paul Maloy 	struct tipc_mon_domain *dom;
48735c55c98SJon Paul Maloy 	struct tipc_peer *peer;
48897bc84bbSHoang Huu Le 	u16 new_member_cnt = mon_le16_to_cpu(arrv_dom->member_cnt);
48935c55c98SJon Paul Maloy 	int new_dlen = dom_rec_len(arrv_dom, new_member_cnt);
49097bc84bbSHoang Huu Le 	u16 new_gen = mon_le16_to_cpu(arrv_dom->gen);
49197bc84bbSHoang Huu Le 	u16 acked_gen = mon_le16_to_cpu(arrv_dom->ack_gen);
49297bc84bbSHoang Huu Le 	u16 arrv_dlen = mon_le16_to_cpu(arrv_dom->len);
49335c55c98SJon Paul Maloy 	bool probing = state->probing;
49435c55c98SJon Paul Maloy 	int i, applied_bef;
49535c55c98SJon Paul Maloy 
49635c55c98SJon Paul Maloy 	state->probing = false;
49735c55c98SJon Paul Maloy 
49835c55c98SJon Paul Maloy 	/* Sanity check received domain record */
4999aa422adSJon Maloy 	if (new_member_cnt > MAX_MON_DOMAIN)
5009aa422adSJon Maloy 		return;
501d876a4d2SJon Paul Maloy 	if (dlen < dom_rec_len(arrv_dom, 0))
50235c55c98SJon Paul Maloy 		return;
503d876a4d2SJon Paul Maloy 	if (dlen != dom_rec_len(arrv_dom, new_member_cnt))
504d876a4d2SJon Paul Maloy 		return;
50597bc84bbSHoang Huu Le 	if (dlen < new_dlen || arrv_dlen != new_dlen)
506d876a4d2SJon Paul Maloy 		return;
50735c55c98SJon Paul Maloy 
50835c55c98SJon Paul Maloy 	/* Synch generation numbers with peer if link just came up */
50935c55c98SJon Paul Maloy 	if (!state->synched) {
51035c55c98SJon Paul Maloy 		state->peer_gen = new_gen - 1;
51135c55c98SJon Paul Maloy 		state->acked_gen = acked_gen;
51235c55c98SJon Paul Maloy 		state->synched = true;
51335c55c98SJon Paul Maloy 	}
51435c55c98SJon Paul Maloy 
51535c55c98SJon Paul Maloy 	if (more(acked_gen, state->acked_gen))
51635c55c98SJon Paul Maloy 		state->acked_gen = acked_gen;
51735c55c98SJon Paul Maloy 
51835c55c98SJon Paul Maloy 	/* Drop duplicate unless we are waiting for a probe response */
51935c55c98SJon Paul Maloy 	if (!more(new_gen, state->peer_gen) && !probing)
52035c55c98SJon Paul Maloy 		return;
52135c55c98SJon Paul Maloy 
52235c55c98SJon Paul Maloy 	write_lock_bh(&mon->lock);
52335c55c98SJon Paul Maloy 	peer = get_peer(mon, addr);
52435c55c98SJon Paul Maloy 	if (!peer || !peer->is_up)
52535c55c98SJon Paul Maloy 		goto exit;
52635c55c98SJon Paul Maloy 
52735c55c98SJon Paul Maloy 	/* Peer is confirmed, stop any ongoing probing */
52835c55c98SJon Paul Maloy 	peer->down_cnt = 0;
52935c55c98SJon Paul Maloy 
53035c55c98SJon Paul Maloy 	/* Task is done for duplicate record */
53135c55c98SJon Paul Maloy 	if (!more(new_gen, state->peer_gen))
53235c55c98SJon Paul Maloy 		goto exit;
53335c55c98SJon Paul Maloy 
53435c55c98SJon Paul Maloy 	state->peer_gen = new_gen;
53535c55c98SJon Paul Maloy 
53635c55c98SJon Paul Maloy 	/* Cache current domain record for later use */
53735c55c98SJon Paul Maloy 	dom_bef.member_cnt = 0;
53835c55c98SJon Paul Maloy 	dom = peer->domain;
53935c55c98SJon Paul Maloy 	if (dom)
54035c55c98SJon Paul Maloy 		memcpy(&dom_bef, dom, dom->len);
54135c55c98SJon Paul Maloy 
54235c55c98SJon Paul Maloy 	/* Transform and store received domain record */
54335c55c98SJon Paul Maloy 	if (!dom || (dom->len < new_dlen)) {
54435c55c98SJon Paul Maloy 		kfree(dom);
54535c55c98SJon Paul Maloy 		dom = kmalloc(new_dlen, GFP_ATOMIC);
54635c55c98SJon Paul Maloy 		peer->domain = dom;
54735c55c98SJon Paul Maloy 		if (!dom)
54835c55c98SJon Paul Maloy 			goto exit;
54935c55c98SJon Paul Maloy 	}
55035c55c98SJon Paul Maloy 	dom->len = new_dlen;
55135c55c98SJon Paul Maloy 	dom->gen = new_gen;
55235c55c98SJon Paul Maloy 	dom->member_cnt = new_member_cnt;
55397bc84bbSHoang Huu Le 	dom->up_map = mon_le64_to_cpu(arrv_dom->up_map);
55435c55c98SJon Paul Maloy 	for (i = 0; i < new_member_cnt; i++)
55597bc84bbSHoang Huu Le 		dom->members[i] = mon_le32_to_cpu(arrv_dom->members[i]);
55635c55c98SJon Paul Maloy 
55735c55c98SJon Paul Maloy 	/* Update peers affected by this domain record */
55835c55c98SJon Paul Maloy 	applied_bef = peer->applied;
55935c55c98SJon Paul Maloy 	mon_apply_domain(mon, peer);
56035c55c98SJon Paul Maloy 	mon_identify_lost_members(peer, &dom_bef, applied_bef);
56135c55c98SJon Paul Maloy 	mon_assign_roles(mon, peer_head(peer));
56235c55c98SJon Paul Maloy exit:
56335c55c98SJon Paul Maloy 	write_unlock_bh(&mon->lock);
56435c55c98SJon Paul Maloy }
56535c55c98SJon Paul Maloy 
tipc_mon_prep(struct net * net,void * data,int * dlen,struct tipc_mon_state * state,int bearer_id)56635c55c98SJon Paul Maloy void tipc_mon_prep(struct net *net, void *data, int *dlen,
56735c55c98SJon Paul Maloy 		   struct tipc_mon_state *state, int bearer_id)
56835c55c98SJon Paul Maloy {
56935c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
57035c55c98SJon Paul Maloy 	struct tipc_mon_domain *dom = data;
57135c55c98SJon Paul Maloy 	u16 gen = mon->dom_gen;
57235c55c98SJon Paul Maloy 	u16 len;
57335c55c98SJon Paul Maloy 
574fa368826SJon Maloy 	/* Send invalid record if not active */
575fa368826SJon Maloy 	if (!tipc_mon_is_active(net, mon)) {
576fa368826SJon Maloy 		dom->len = 0;
57735c55c98SJon Paul Maloy 		return;
578fa368826SJon Maloy 	}
57935c55c98SJon Paul Maloy 
58035c55c98SJon Paul Maloy 	/* Send only a dummy record with ack if peer has acked our last sent */
58135c55c98SJon Paul Maloy 	if (likely(state->acked_gen == gen)) {
58235c55c98SJon Paul Maloy 		len = dom_rec_len(dom, 0);
58335c55c98SJon Paul Maloy 		*dlen = len;
58497bc84bbSHoang Huu Le 		dom->len = mon_cpu_to_le16(len);
58597bc84bbSHoang Huu Le 		dom->gen = mon_cpu_to_le16(gen);
58697bc84bbSHoang Huu Le 		dom->ack_gen = mon_cpu_to_le16(state->peer_gen);
58735c55c98SJon Paul Maloy 		dom->member_cnt = 0;
58835c55c98SJon Paul Maloy 		return;
58935c55c98SJon Paul Maloy 	}
59035c55c98SJon Paul Maloy 	/* Send the full record */
59135c55c98SJon Paul Maloy 	read_lock_bh(&mon->lock);
59297bc84bbSHoang Huu Le 	len = mon_le16_to_cpu(mon->cache.len);
59335c55c98SJon Paul Maloy 	*dlen = len;
59435c55c98SJon Paul Maloy 	memcpy(data, &mon->cache, len);
59535c55c98SJon Paul Maloy 	read_unlock_bh(&mon->lock);
59697bc84bbSHoang Huu Le 	dom->ack_gen = mon_cpu_to_le16(state->peer_gen);
59735c55c98SJon Paul Maloy }
59835c55c98SJon Paul Maloy 
tipc_mon_get_state(struct net * net,u32 addr,struct tipc_mon_state * state,int bearer_id)59935c55c98SJon Paul Maloy void tipc_mon_get_state(struct net *net, u32 addr,
60035c55c98SJon Paul Maloy 			struct tipc_mon_state *state,
60135c55c98SJon Paul Maloy 			int bearer_id)
60235c55c98SJon Paul Maloy {
60335c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
60435c55c98SJon Paul Maloy 	struct tipc_peer *peer;
60535c55c98SJon Paul Maloy 
606fa368826SJon Maloy 	if (!tipc_mon_is_active(net, mon)) {
607fa368826SJon Maloy 		state->probing = false;
608fa368826SJon Maloy 		state->monitoring = true;
609fa368826SJon Maloy 		return;
610fa368826SJon Maloy 	}
611fa368826SJon Maloy 
61235c55c98SJon Paul Maloy 	/* Used cached state if table has not changed */
61335c55c98SJon Paul Maloy 	if (!state->probing &&
61435c55c98SJon Paul Maloy 	    (state->list_gen == mon->list_gen) &&
61535c55c98SJon Paul Maloy 	    (state->acked_gen == mon->dom_gen))
61635c55c98SJon Paul Maloy 		return;
61735c55c98SJon Paul Maloy 
61835c55c98SJon Paul Maloy 	read_lock_bh(&mon->lock);
61935c55c98SJon Paul Maloy 	peer = get_peer(mon, addr);
62035c55c98SJon Paul Maloy 	if (peer) {
62135c55c98SJon Paul Maloy 		state->probing = state->acked_gen != mon->dom_gen;
62235c55c98SJon Paul Maloy 		state->probing |= peer->down_cnt;
62335c55c98SJon Paul Maloy 		state->reset |= peer->down_cnt >= MAX_PEER_DOWN_EVENTS;
62435c55c98SJon Paul Maloy 		state->monitoring = peer->is_local;
62535c55c98SJon Paul Maloy 		state->monitoring |= peer->is_head;
62635c55c98SJon Paul Maloy 		state->list_gen = mon->list_gen;
62735c55c98SJon Paul Maloy 	}
62835c55c98SJon Paul Maloy 	read_unlock_bh(&mon->lock);
62935c55c98SJon Paul Maloy }
63035c55c98SJon Paul Maloy 
mon_timeout(struct timer_list * t)63131b102bbSKees Cook static void mon_timeout(struct timer_list *t)
63235c55c98SJon Paul Maloy {
63331b102bbSKees Cook 	struct tipc_monitor *mon = from_timer(mon, t, timer);
63435c55c98SJon Paul Maloy 	struct tipc_peer *self;
63535c55c98SJon Paul Maloy 	int best_member_cnt = dom_size(mon->peer_cnt) - 1;
63635c55c98SJon Paul Maloy 
63735c55c98SJon Paul Maloy 	write_lock_bh(&mon->lock);
63835c55c98SJon Paul Maloy 	self = mon->self;
63935c55c98SJon Paul Maloy 	if (self && (best_member_cnt != self->applied)) {
64035c55c98SJon Paul Maloy 		mon_update_local_domain(mon);
64135c55c98SJon Paul Maloy 		mon_assign_roles(mon, self);
64235c55c98SJon Paul Maloy 	}
64335c55c98SJon Paul Maloy 	write_unlock_bh(&mon->lock);
64435c55c98SJon Paul Maloy 	mod_timer(&mon->timer, jiffies + mon->timer_intv);
64535c55c98SJon Paul Maloy }
64635c55c98SJon Paul Maloy 
tipc_mon_create(struct net * net,int bearer_id)64735c55c98SJon Paul Maloy int tipc_mon_create(struct net *net, int bearer_id)
64835c55c98SJon Paul Maloy {
64935c55c98SJon Paul Maloy 	struct tipc_net *tn = tipc_net(net);
65035c55c98SJon Paul Maloy 	struct tipc_monitor *mon;
65135c55c98SJon Paul Maloy 	struct tipc_peer *self;
65235c55c98SJon Paul Maloy 	struct tipc_mon_domain *dom;
65335c55c98SJon Paul Maloy 
65435c55c98SJon Paul Maloy 	if (tn->monitors[bearer_id])
65535c55c98SJon Paul Maloy 		return 0;
65635c55c98SJon Paul Maloy 
65735c55c98SJon Paul Maloy 	mon = kzalloc(sizeof(*mon), GFP_ATOMIC);
65835c55c98SJon Paul Maloy 	self = kzalloc(sizeof(*self), GFP_ATOMIC);
65935c55c98SJon Paul Maloy 	dom = kzalloc(sizeof(*dom), GFP_ATOMIC);
66035c55c98SJon Paul Maloy 	if (!mon || !self || !dom) {
66135c55c98SJon Paul Maloy 		kfree(mon);
66235c55c98SJon Paul Maloy 		kfree(self);
66335c55c98SJon Paul Maloy 		kfree(dom);
66435c55c98SJon Paul Maloy 		return -ENOMEM;
66535c55c98SJon Paul Maloy 	}
66635c55c98SJon Paul Maloy 	tn->monitors[bearer_id] = mon;
66735c55c98SJon Paul Maloy 	rwlock_init(&mon->lock);
66835c55c98SJon Paul Maloy 	mon->net = net;
66935c55c98SJon Paul Maloy 	mon->peer_cnt = 1;
67035c55c98SJon Paul Maloy 	mon->self = self;
67135c55c98SJon Paul Maloy 	self->domain = dom;
67235c55c98SJon Paul Maloy 	self->addr = tipc_own_addr(net);
67335c55c98SJon Paul Maloy 	self->is_up = true;
67435c55c98SJon Paul Maloy 	self->is_head = true;
67535c55c98SJon Paul Maloy 	INIT_LIST_HEAD(&self->list);
67631b102bbSKees Cook 	timer_setup(&mon->timer, mon_timeout, 0);
67735c55c98SJon Paul Maloy 	mon->timer_intv = msecs_to_jiffies(MON_TIMEOUT + (tn->random & 0xffff));
67835c55c98SJon Paul Maloy 	mod_timer(&mon->timer, jiffies + mon->timer_intv);
67935c55c98SJon Paul Maloy 	return 0;
68035c55c98SJon Paul Maloy }
68135c55c98SJon Paul Maloy 
tipc_mon_delete(struct net * net,int bearer_id)68235c55c98SJon Paul Maloy void tipc_mon_delete(struct net *net, int bearer_id)
68335c55c98SJon Paul Maloy {
68435c55c98SJon Paul Maloy 	struct tipc_net *tn = tipc_net(net);
68535c55c98SJon Paul Maloy 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
686642a8439STommi Rantala 	struct tipc_peer *self;
68735c55c98SJon Paul Maloy 	struct tipc_peer *peer, *tmp;
68835c55c98SJon Paul Maloy 
689642a8439STommi Rantala 	if (!mon)
690642a8439STommi Rantala 		return;
691642a8439STommi Rantala 
692642a8439STommi Rantala 	self = get_self(net, bearer_id);
69335c55c98SJon Paul Maloy 	write_lock_bh(&mon->lock);
69435c55c98SJon Paul Maloy 	tn->monitors[bearer_id] = NULL;
69535c55c98SJon Paul Maloy 	list_for_each_entry_safe(peer, tmp, &self->list, list) {
69635c55c98SJon Paul Maloy 		list_del(&peer->list);
69735c55c98SJon Paul Maloy 		hlist_del(&peer->hash);
69835c55c98SJon Paul Maloy 		kfree(peer->domain);
69935c55c98SJon Paul Maloy 		kfree(peer);
70035c55c98SJon Paul Maloy 	}
70135c55c98SJon Paul Maloy 	mon->self = NULL;
70235c55c98SJon Paul Maloy 	write_unlock_bh(&mon->lock);
703*292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&mon->timer);
70435c55c98SJon Paul Maloy 	kfree(self->domain);
70535c55c98SJon Paul Maloy 	kfree(self);
70635c55c98SJon Paul Maloy 	kfree(mon);
70735c55c98SJon Paul Maloy }
7087b3f5229SParthasarathy Bhuvaragan 
tipc_mon_reinit_self(struct net * net)70946cb01eeSHoang Le void tipc_mon_reinit_self(struct net *net)
71046cb01eeSHoang Le {
71146cb01eeSHoang Le 	struct tipc_monitor *mon;
71246cb01eeSHoang Le 	int bearer_id;
71346cb01eeSHoang Le 
71446cb01eeSHoang Le 	for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {
71546cb01eeSHoang Le 		mon = tipc_monitor(net, bearer_id);
71646cb01eeSHoang Le 		if (!mon)
71746cb01eeSHoang Le 			continue;
71846cb01eeSHoang Le 		write_lock_bh(&mon->lock);
71946cb01eeSHoang Le 		mon->self->addr = tipc_own_addr(net);
72046cb01eeSHoang Le 		write_unlock_bh(&mon->lock);
72146cb01eeSHoang Le 	}
72246cb01eeSHoang Le }
72346cb01eeSHoang Le 
tipc_nl_monitor_set_threshold(struct net * net,u32 cluster_size)7247b3f5229SParthasarathy Bhuvaragan int tipc_nl_monitor_set_threshold(struct net *net, u32 cluster_size)
7257b3f5229SParthasarathy Bhuvaragan {
7267b3f5229SParthasarathy Bhuvaragan 	struct tipc_net *tn = tipc_net(net);
7277b3f5229SParthasarathy Bhuvaragan 
7287b3f5229SParthasarathy Bhuvaragan 	if (cluster_size > TIPC_CLUSTER_SIZE)
7297b3f5229SParthasarathy Bhuvaragan 		return -EINVAL;
7307b3f5229SParthasarathy Bhuvaragan 
7317b3f5229SParthasarathy Bhuvaragan 	tn->mon_threshold = cluster_size;
7327b3f5229SParthasarathy Bhuvaragan 
7337b3f5229SParthasarathy Bhuvaragan 	return 0;
7347b3f5229SParthasarathy Bhuvaragan }
735bf1035b2SParthasarathy Bhuvaragan 
tipc_nl_monitor_get_threshold(struct net * net)736bf1035b2SParthasarathy Bhuvaragan int tipc_nl_monitor_get_threshold(struct net *net)
737bf1035b2SParthasarathy Bhuvaragan {
738bf1035b2SParthasarathy Bhuvaragan 	struct tipc_net *tn = tipc_net(net);
739bf1035b2SParthasarathy Bhuvaragan 
740bf1035b2SParthasarathy Bhuvaragan 	return tn->mon_threshold;
741bf1035b2SParthasarathy Bhuvaragan }
742cf6f7e1dSParthasarathy Bhuvaragan 
__tipc_nl_add_monitor_peer(struct tipc_peer * peer,struct tipc_nl_msg * msg)743e064cce1SYueHaibing static int __tipc_nl_add_monitor_peer(struct tipc_peer *peer,
744e064cce1SYueHaibing 				      struct tipc_nl_msg *msg)
745cf6f7e1dSParthasarathy Bhuvaragan {
746cf6f7e1dSParthasarathy Bhuvaragan 	struct tipc_mon_domain *dom = peer->domain;
747cf6f7e1dSParthasarathy Bhuvaragan 	struct nlattr *attrs;
748cf6f7e1dSParthasarathy Bhuvaragan 	void *hdr;
749cf6f7e1dSParthasarathy Bhuvaragan 
750cf6f7e1dSParthasarathy Bhuvaragan 	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
751cf6f7e1dSParthasarathy Bhuvaragan 			  NLM_F_MULTI, TIPC_NL_MON_PEER_GET);
752cf6f7e1dSParthasarathy Bhuvaragan 	if (!hdr)
753cf6f7e1dSParthasarathy Bhuvaragan 		return -EMSGSIZE;
754cf6f7e1dSParthasarathy Bhuvaragan 
755ae0be8deSMichal Kubecek 	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON_PEER);
756cf6f7e1dSParthasarathy Bhuvaragan 	if (!attrs)
757cf6f7e1dSParthasarathy Bhuvaragan 		goto msg_full;
758cf6f7e1dSParthasarathy Bhuvaragan 
759cf6f7e1dSParthasarathy Bhuvaragan 	if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_ADDR, peer->addr))
760cf6f7e1dSParthasarathy Bhuvaragan 		goto attr_msg_full;
761cf6f7e1dSParthasarathy Bhuvaragan 	if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_APPLIED, peer->applied))
762cf6f7e1dSParthasarathy Bhuvaragan 		goto attr_msg_full;
763cf6f7e1dSParthasarathy Bhuvaragan 
764cf6f7e1dSParthasarathy Bhuvaragan 	if (peer->is_up)
765cf6f7e1dSParthasarathy Bhuvaragan 		if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_UP))
766cf6f7e1dSParthasarathy Bhuvaragan 			goto attr_msg_full;
767cf6f7e1dSParthasarathy Bhuvaragan 	if (peer->is_local)
768cf6f7e1dSParthasarathy Bhuvaragan 		if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_LOCAL))
769cf6f7e1dSParthasarathy Bhuvaragan 			goto attr_msg_full;
770cf6f7e1dSParthasarathy Bhuvaragan 	if (peer->is_head)
771cf6f7e1dSParthasarathy Bhuvaragan 		if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_HEAD))
772cf6f7e1dSParthasarathy Bhuvaragan 			goto attr_msg_full;
773cf6f7e1dSParthasarathy Bhuvaragan 
774cf6f7e1dSParthasarathy Bhuvaragan 	if (dom) {
775cf6f7e1dSParthasarathy Bhuvaragan 		if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_DOMGEN, dom->gen))
776cf6f7e1dSParthasarathy Bhuvaragan 			goto attr_msg_full;
777cf6f7e1dSParthasarathy Bhuvaragan 		if (nla_put_u64_64bit(msg->skb, TIPC_NLA_MON_PEER_UPMAP,
778cf6f7e1dSParthasarathy Bhuvaragan 				      dom->up_map, TIPC_NLA_MON_PEER_PAD))
779cf6f7e1dSParthasarathy Bhuvaragan 			goto attr_msg_full;
780cf6f7e1dSParthasarathy Bhuvaragan 		if (nla_put(msg->skb, TIPC_NLA_MON_PEER_MEMBERS,
781cf6f7e1dSParthasarathy Bhuvaragan 			    dom->member_cnt * sizeof(u32), &dom->members))
782cf6f7e1dSParthasarathy Bhuvaragan 			goto attr_msg_full;
783cf6f7e1dSParthasarathy Bhuvaragan 	}
784cf6f7e1dSParthasarathy Bhuvaragan 
785cf6f7e1dSParthasarathy Bhuvaragan 	nla_nest_end(msg->skb, attrs);
786cf6f7e1dSParthasarathy Bhuvaragan 	genlmsg_end(msg->skb, hdr);
787cf6f7e1dSParthasarathy Bhuvaragan 	return 0;
788cf6f7e1dSParthasarathy Bhuvaragan 
789cf6f7e1dSParthasarathy Bhuvaragan attr_msg_full:
790cf6f7e1dSParthasarathy Bhuvaragan 	nla_nest_cancel(msg->skb, attrs);
791cf6f7e1dSParthasarathy Bhuvaragan msg_full:
792cf6f7e1dSParthasarathy Bhuvaragan 	genlmsg_cancel(msg->skb, hdr);
793cf6f7e1dSParthasarathy Bhuvaragan 
794cf6f7e1dSParthasarathy Bhuvaragan 	return -EMSGSIZE;
795cf6f7e1dSParthasarathy Bhuvaragan }
796cf6f7e1dSParthasarathy Bhuvaragan 
tipc_nl_add_monitor_peer(struct net * net,struct tipc_nl_msg * msg,u32 bearer_id,u32 * prev_node)797cf6f7e1dSParthasarathy Bhuvaragan int tipc_nl_add_monitor_peer(struct net *net, struct tipc_nl_msg *msg,
798cf6f7e1dSParthasarathy Bhuvaragan 			     u32 bearer_id, u32 *prev_node)
799cf6f7e1dSParthasarathy Bhuvaragan {
800cf6f7e1dSParthasarathy Bhuvaragan 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
801672ca65dSParthasarathy Bhuvaragan 	struct tipc_peer *peer;
802cf6f7e1dSParthasarathy Bhuvaragan 
803cf6f7e1dSParthasarathy Bhuvaragan 	if (!mon)
804cf6f7e1dSParthasarathy Bhuvaragan 		return -EINVAL;
805cf6f7e1dSParthasarathy Bhuvaragan 
806cf6f7e1dSParthasarathy Bhuvaragan 	read_lock_bh(&mon->lock);
807672ca65dSParthasarathy Bhuvaragan 	peer = mon->self;
808cf6f7e1dSParthasarathy Bhuvaragan 	do {
809cf6f7e1dSParthasarathy Bhuvaragan 		if (*prev_node) {
810cf6f7e1dSParthasarathy Bhuvaragan 			if (peer->addr == *prev_node)
811cf6f7e1dSParthasarathy Bhuvaragan 				*prev_node = 0;
812cf6f7e1dSParthasarathy Bhuvaragan 			else
813cf6f7e1dSParthasarathy Bhuvaragan 				continue;
814cf6f7e1dSParthasarathy Bhuvaragan 		}
815cf6f7e1dSParthasarathy Bhuvaragan 		if (__tipc_nl_add_monitor_peer(peer, msg)) {
816cf6f7e1dSParthasarathy Bhuvaragan 			*prev_node = peer->addr;
817cf6f7e1dSParthasarathy Bhuvaragan 			read_unlock_bh(&mon->lock);
818cf6f7e1dSParthasarathy Bhuvaragan 			return -EMSGSIZE;
819cf6f7e1dSParthasarathy Bhuvaragan 		}
820cf6f7e1dSParthasarathy Bhuvaragan 	} while ((peer = peer_nxt(peer)) != mon->self);
821cf6f7e1dSParthasarathy Bhuvaragan 	read_unlock_bh(&mon->lock);
822cf6f7e1dSParthasarathy Bhuvaragan 
823cf6f7e1dSParthasarathy Bhuvaragan 	return 0;
824cf6f7e1dSParthasarathy Bhuvaragan }
825cf6f7e1dSParthasarathy Bhuvaragan 
__tipc_nl_add_monitor(struct net * net,struct tipc_nl_msg * msg,u32 bearer_id)826cf6f7e1dSParthasarathy Bhuvaragan int __tipc_nl_add_monitor(struct net *net, struct tipc_nl_msg *msg,
827cf6f7e1dSParthasarathy Bhuvaragan 			  u32 bearer_id)
828cf6f7e1dSParthasarathy Bhuvaragan {
829cf6f7e1dSParthasarathy Bhuvaragan 	struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
830cf6f7e1dSParthasarathy Bhuvaragan 	char bearer_name[TIPC_MAX_BEARER_NAME];
831cf6f7e1dSParthasarathy Bhuvaragan 	struct nlattr *attrs;
832cf6f7e1dSParthasarathy Bhuvaragan 	void *hdr;
833cf6f7e1dSParthasarathy Bhuvaragan 	int ret;
834cf6f7e1dSParthasarathy Bhuvaragan 
835cf6f7e1dSParthasarathy Bhuvaragan 	ret = tipc_bearer_get_name(net, bearer_name, bearer_id);
836cf6f7e1dSParthasarathy Bhuvaragan 	if (ret || !mon)
83736a50a98STung Nguyen 		return 0;
838cf6f7e1dSParthasarathy Bhuvaragan 
839cf6f7e1dSParthasarathy Bhuvaragan 	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
840cf6f7e1dSParthasarathy Bhuvaragan 			  NLM_F_MULTI, TIPC_NL_MON_GET);
841cf6f7e1dSParthasarathy Bhuvaragan 	if (!hdr)
842cf6f7e1dSParthasarathy Bhuvaragan 		return -EMSGSIZE;
843cf6f7e1dSParthasarathy Bhuvaragan 
844ae0be8deSMichal Kubecek 	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON);
845cf6f7e1dSParthasarathy Bhuvaragan 	if (!attrs)
846cf6f7e1dSParthasarathy Bhuvaragan 		goto msg_full;
847cf6f7e1dSParthasarathy Bhuvaragan 
848cf6f7e1dSParthasarathy Bhuvaragan 	read_lock_bh(&mon->lock);
849cf6f7e1dSParthasarathy Bhuvaragan 	if (nla_put_u32(msg->skb, TIPC_NLA_MON_REF, bearer_id))
850cf6f7e1dSParthasarathy Bhuvaragan 		goto attr_msg_full;
851cf6f7e1dSParthasarathy Bhuvaragan 	if (tipc_mon_is_active(net, mon))
852cf6f7e1dSParthasarathy Bhuvaragan 		if (nla_put_flag(msg->skb, TIPC_NLA_MON_ACTIVE))
853cf6f7e1dSParthasarathy Bhuvaragan 			goto attr_msg_full;
854cf6f7e1dSParthasarathy Bhuvaragan 	if (nla_put_string(msg->skb, TIPC_NLA_MON_BEARER_NAME, bearer_name))
855cf6f7e1dSParthasarathy Bhuvaragan 		goto attr_msg_full;
856cf6f7e1dSParthasarathy Bhuvaragan 	if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEERCNT, mon->peer_cnt))
857cf6f7e1dSParthasarathy Bhuvaragan 		goto attr_msg_full;
858cf6f7e1dSParthasarathy Bhuvaragan 	if (nla_put_u32(msg->skb, TIPC_NLA_MON_LISTGEN, mon->list_gen))
859cf6f7e1dSParthasarathy Bhuvaragan 		goto attr_msg_full;
860cf6f7e1dSParthasarathy Bhuvaragan 
861cf6f7e1dSParthasarathy Bhuvaragan 	read_unlock_bh(&mon->lock);
862cf6f7e1dSParthasarathy Bhuvaragan 	nla_nest_end(msg->skb, attrs);
863cf6f7e1dSParthasarathy Bhuvaragan 	genlmsg_end(msg->skb, hdr);
864cf6f7e1dSParthasarathy Bhuvaragan 
865cf6f7e1dSParthasarathy Bhuvaragan 	return 0;
866cf6f7e1dSParthasarathy Bhuvaragan 
867cf6f7e1dSParthasarathy Bhuvaragan attr_msg_full:
8686b65bc29SWei Yongjun 	read_unlock_bh(&mon->lock);
869cf6f7e1dSParthasarathy Bhuvaragan 	nla_nest_cancel(msg->skb, attrs);
870cf6f7e1dSParthasarathy Bhuvaragan msg_full:
871cf6f7e1dSParthasarathy Bhuvaragan 	genlmsg_cancel(msg->skb, hdr);
872cf6f7e1dSParthasarathy Bhuvaragan 
873cf6f7e1dSParthasarathy Bhuvaragan 	return -EMSGSIZE;
874cf6f7e1dSParthasarathy Bhuvaragan }
875