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