162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * net/tipc/monitor.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2016, Ericsson AB 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 862306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1162306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1262306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1362306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1462306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1562306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its 1662306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived from 1762306a36Sopenharmony_ci * this software without specific prior written permission. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 2062306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free 2162306a36Sopenharmony_ci * Software Foundation. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2462306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2562306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2662306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 2762306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2862306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2962306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3062306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3162306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3262306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3362306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <net/genetlink.h> 3762306a36Sopenharmony_ci#include "core.h" 3862306a36Sopenharmony_ci#include "addr.h" 3962306a36Sopenharmony_ci#include "monitor.h" 4062306a36Sopenharmony_ci#include "bearer.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define MAX_MON_DOMAIN 64 4362306a36Sopenharmony_ci#define MON_TIMEOUT 120000 4462306a36Sopenharmony_ci#define MAX_PEER_DOWN_EVENTS 4 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* struct tipc_mon_domain: domain record to be transferred between peers 4762306a36Sopenharmony_ci * @len: actual size of domain record 4862306a36Sopenharmony_ci * @gen: current generation of sender's domain 4962306a36Sopenharmony_ci * @ack_gen: most recent generation of self's domain acked by peer 5062306a36Sopenharmony_ci * @member_cnt: number of domain member nodes described in this record 5162306a36Sopenharmony_ci * @up_map: bit map indicating which of the members the sender considers up 5262306a36Sopenharmony_ci * @members: identity of the domain members 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistruct tipc_mon_domain { 5562306a36Sopenharmony_ci u16 len; 5662306a36Sopenharmony_ci u16 gen; 5762306a36Sopenharmony_ci u16 ack_gen; 5862306a36Sopenharmony_ci u16 member_cnt; 5962306a36Sopenharmony_ci u64 up_map; 6062306a36Sopenharmony_ci u32 members[MAX_MON_DOMAIN]; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* struct tipc_peer: state of a peer node and its domain 6462306a36Sopenharmony_ci * @addr: tipc node identity of peer 6562306a36Sopenharmony_ci * @head_map: shows which other nodes currently consider peer 'up' 6662306a36Sopenharmony_ci * @domain: most recent domain record from peer 6762306a36Sopenharmony_ci * @hash: position in hashed lookup list 6862306a36Sopenharmony_ci * @list: position in linked list, in circular ascending order by 'addr' 6962306a36Sopenharmony_ci * @applied: number of reported domain members applied on this monitor list 7062306a36Sopenharmony_ci * @is_up: peer is up as seen from this node 7162306a36Sopenharmony_ci * @is_head: peer is assigned domain head as seen from this node 7262306a36Sopenharmony_ci * @is_local: peer is in local domain and should be continuously monitored 7362306a36Sopenharmony_ci * @down_cnt: - numbers of other peers which have reported this on lost 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistruct tipc_peer { 7662306a36Sopenharmony_ci u32 addr; 7762306a36Sopenharmony_ci struct tipc_mon_domain *domain; 7862306a36Sopenharmony_ci struct hlist_node hash; 7962306a36Sopenharmony_ci struct list_head list; 8062306a36Sopenharmony_ci u8 applied; 8162306a36Sopenharmony_ci u8 down_cnt; 8262306a36Sopenharmony_ci bool is_up; 8362306a36Sopenharmony_ci bool is_head; 8462306a36Sopenharmony_ci bool is_local; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct tipc_monitor { 8862306a36Sopenharmony_ci struct hlist_head peers[NODE_HTABLE_SIZE]; 8962306a36Sopenharmony_ci int peer_cnt; 9062306a36Sopenharmony_ci struct tipc_peer *self; 9162306a36Sopenharmony_ci rwlock_t lock; 9262306a36Sopenharmony_ci struct tipc_mon_domain cache; 9362306a36Sopenharmony_ci u16 list_gen; 9462306a36Sopenharmony_ci u16 dom_gen; 9562306a36Sopenharmony_ci struct net *net; 9662306a36Sopenharmony_ci struct timer_list timer; 9762306a36Sopenharmony_ci unsigned long timer_intv; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct tipc_monitor *tipc_monitor(struct net *net, int bearer_id) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci return tipc_net(net)->monitors[bearer_id]; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciconst int tipc_max_domain_size = sizeof(struct tipc_mon_domain); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic inline u16 mon_cpu_to_le16(u16 val) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return (__force __u16)htons(val); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline u32 mon_cpu_to_le32(u32 val) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return (__force __u32)htonl(val); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic inline u64 mon_cpu_to_le64(u64 val) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return (__force __u64)cpu_to_be64(val); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline u16 mon_le16_to_cpu(u16 val) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci return ntohs((__force __be16)val); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic inline u32 mon_le32_to_cpu(u32 val) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci return ntohl((__force __be32)val); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline u64 mon_le64_to_cpu(u64 val) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci return be64_to_cpu((__force __be64)val); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* dom_rec_len(): actual length of domain record for transport 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic int dom_rec_len(struct tipc_mon_domain *dom, u16 mcnt) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return (offsetof(struct tipc_mon_domain, members)) + (mcnt * sizeof(u32)); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* dom_size() : calculate size of own domain based on number of peers 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic int dom_size(int peers) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int i = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci while ((i * i) < peers) 15162306a36Sopenharmony_ci i++; 15262306a36Sopenharmony_ci return i < MAX_MON_DOMAIN ? i : MAX_MON_DOMAIN; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void map_set(u64 *up_map, int i, unsigned int v) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci *up_map &= ~(1ULL << i); 15862306a36Sopenharmony_ci *up_map |= ((u64)v << i); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int map_get(u64 up_map, int i) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci return (up_map & (1ULL << i)) >> i; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct tipc_peer *peer_prev(struct tipc_peer *peer) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return list_last_entry(&peer->list, struct tipc_peer, list); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct tipc_peer *peer_nxt(struct tipc_peer *peer) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci return list_first_entry(&peer->list, struct tipc_peer, list); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct tipc_peer *peer_head(struct tipc_peer *peer) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci while (!peer->is_head) 17962306a36Sopenharmony_ci peer = peer_prev(peer); 18062306a36Sopenharmony_ci return peer; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic struct tipc_peer *get_peer(struct tipc_monitor *mon, u32 addr) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct tipc_peer *peer; 18662306a36Sopenharmony_ci unsigned int thash = tipc_hashfn(addr); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci hlist_for_each_entry(peer, &mon->peers[thash], hash) { 18962306a36Sopenharmony_ci if (peer->addr == addr) 19062306a36Sopenharmony_ci return peer; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci return NULL; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic struct tipc_peer *get_self(struct net *net, int bearer_id) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return mon->self; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic inline bool tipc_mon_is_active(struct net *net, struct tipc_monitor *mon) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return mon->peer_cnt > tn->mon_threshold; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* mon_identify_lost_members() : - identify amd mark potentially lost members 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic void mon_identify_lost_members(struct tipc_peer *peer, 21262306a36Sopenharmony_ci struct tipc_mon_domain *dom_bef, 21362306a36Sopenharmony_ci int applied_bef) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct tipc_peer *member = peer; 21662306a36Sopenharmony_ci struct tipc_mon_domain *dom_aft = peer->domain; 21762306a36Sopenharmony_ci int applied_aft = peer->applied; 21862306a36Sopenharmony_ci int i; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci for (i = 0; i < applied_bef; i++) { 22162306a36Sopenharmony_ci member = peer_nxt(member); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Do nothing if self or peer already see member as down */ 22462306a36Sopenharmony_ci if (!member->is_up || !map_get(dom_bef->up_map, i)) 22562306a36Sopenharmony_ci continue; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Loss of local node must be detected by active probing */ 22862306a36Sopenharmony_ci if (member->is_local) 22962306a36Sopenharmony_ci continue; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Start probing if member was removed from applied domain */ 23262306a36Sopenharmony_ci if (!applied_aft || (applied_aft < i)) { 23362306a36Sopenharmony_ci member->down_cnt = 1; 23462306a36Sopenharmony_ci continue; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Member loss is confirmed if it is still in applied domain */ 23862306a36Sopenharmony_ci if (!map_get(dom_aft->up_map, i)) 23962306a36Sopenharmony_ci member->down_cnt++; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* mon_apply_domain() : match a peer's domain record against monitor list 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_cistatic void mon_apply_domain(struct tipc_monitor *mon, 24662306a36Sopenharmony_ci struct tipc_peer *peer) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct tipc_mon_domain *dom = peer->domain; 24962306a36Sopenharmony_ci struct tipc_peer *member; 25062306a36Sopenharmony_ci u32 addr; 25162306a36Sopenharmony_ci int i; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!dom || !peer->is_up) 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Scan across domain members and match against monitor list */ 25762306a36Sopenharmony_ci peer->applied = 0; 25862306a36Sopenharmony_ci member = peer_nxt(peer); 25962306a36Sopenharmony_ci for (i = 0; i < dom->member_cnt; i++) { 26062306a36Sopenharmony_ci addr = dom->members[i]; 26162306a36Sopenharmony_ci if (addr != member->addr) 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci peer->applied++; 26462306a36Sopenharmony_ci member = peer_nxt(member); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* mon_update_local_domain() : update after peer addition/removal/up/down 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cistatic void mon_update_local_domain(struct tipc_monitor *mon) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct tipc_peer *self = mon->self; 27362306a36Sopenharmony_ci struct tipc_mon_domain *cache = &mon->cache; 27462306a36Sopenharmony_ci struct tipc_mon_domain *dom = self->domain; 27562306a36Sopenharmony_ci struct tipc_peer *peer = self; 27662306a36Sopenharmony_ci u64 prev_up_map = dom->up_map; 27762306a36Sopenharmony_ci u16 member_cnt, i; 27862306a36Sopenharmony_ci bool diff; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Update local domain size based on current size of cluster */ 28162306a36Sopenharmony_ci member_cnt = dom_size(mon->peer_cnt) - 1; 28262306a36Sopenharmony_ci self->applied = member_cnt; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Update native and cached outgoing local domain records */ 28562306a36Sopenharmony_ci dom->len = dom_rec_len(dom, member_cnt); 28662306a36Sopenharmony_ci diff = dom->member_cnt != member_cnt; 28762306a36Sopenharmony_ci dom->member_cnt = member_cnt; 28862306a36Sopenharmony_ci for (i = 0; i < member_cnt; i++) { 28962306a36Sopenharmony_ci peer = peer_nxt(peer); 29062306a36Sopenharmony_ci diff |= dom->members[i] != peer->addr; 29162306a36Sopenharmony_ci dom->members[i] = peer->addr; 29262306a36Sopenharmony_ci map_set(&dom->up_map, i, peer->is_up); 29362306a36Sopenharmony_ci cache->members[i] = mon_cpu_to_le32(peer->addr); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci diff |= dom->up_map != prev_up_map; 29662306a36Sopenharmony_ci if (!diff) 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci dom->gen = ++mon->dom_gen; 29962306a36Sopenharmony_ci cache->len = mon_cpu_to_le16(dom->len); 30062306a36Sopenharmony_ci cache->gen = mon_cpu_to_le16(dom->gen); 30162306a36Sopenharmony_ci cache->member_cnt = mon_cpu_to_le16(member_cnt); 30262306a36Sopenharmony_ci cache->up_map = mon_cpu_to_le64(dom->up_map); 30362306a36Sopenharmony_ci mon_apply_domain(mon, self); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* mon_update_neighbors() : update preceding neighbors of added/removed peer 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistatic void mon_update_neighbors(struct tipc_monitor *mon, 30962306a36Sopenharmony_ci struct tipc_peer *peer) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int dz, i; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci dz = dom_size(mon->peer_cnt); 31462306a36Sopenharmony_ci for (i = 0; i < dz; i++) { 31562306a36Sopenharmony_ci mon_apply_domain(mon, peer); 31662306a36Sopenharmony_ci peer = peer_prev(peer); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* mon_assign_roles() : reassign peer roles after a network change 32162306a36Sopenharmony_ci * The monitor list is consistent at this stage; i.e., each peer is monitoring 32262306a36Sopenharmony_ci * a set of domain members as matched between domain record and the monitor list 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_cistatic void mon_assign_roles(struct tipc_monitor *mon, struct tipc_peer *head) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct tipc_peer *peer = peer_nxt(head); 32762306a36Sopenharmony_ci struct tipc_peer *self = mon->self; 32862306a36Sopenharmony_ci int i = 0; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci for (; peer != self; peer = peer_nxt(peer)) { 33162306a36Sopenharmony_ci peer->is_local = false; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Update domain member */ 33462306a36Sopenharmony_ci if (i++ < head->applied) { 33562306a36Sopenharmony_ci peer->is_head = false; 33662306a36Sopenharmony_ci if (head == self) 33762306a36Sopenharmony_ci peer->is_local = true; 33862306a36Sopenharmony_ci continue; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci /* Assign next domain head */ 34162306a36Sopenharmony_ci if (!peer->is_up) 34262306a36Sopenharmony_ci continue; 34362306a36Sopenharmony_ci if (peer->is_head) 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci head = peer; 34662306a36Sopenharmony_ci head->is_head = true; 34762306a36Sopenharmony_ci i = 0; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci mon->list_gen++; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_civoid tipc_mon_remove_peer(struct net *net, u32 addr, int bearer_id) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 35562306a36Sopenharmony_ci struct tipc_peer *self; 35662306a36Sopenharmony_ci struct tipc_peer *peer, *prev, *head; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!mon) 35962306a36Sopenharmony_ci return; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci self = get_self(net, bearer_id); 36262306a36Sopenharmony_ci write_lock_bh(&mon->lock); 36362306a36Sopenharmony_ci peer = get_peer(mon, addr); 36462306a36Sopenharmony_ci if (!peer) 36562306a36Sopenharmony_ci goto exit; 36662306a36Sopenharmony_ci prev = peer_prev(peer); 36762306a36Sopenharmony_ci list_del(&peer->list); 36862306a36Sopenharmony_ci hlist_del(&peer->hash); 36962306a36Sopenharmony_ci kfree(peer->domain); 37062306a36Sopenharmony_ci kfree(peer); 37162306a36Sopenharmony_ci mon->peer_cnt--; 37262306a36Sopenharmony_ci head = peer_head(prev); 37362306a36Sopenharmony_ci if (head == self) 37462306a36Sopenharmony_ci mon_update_local_domain(mon); 37562306a36Sopenharmony_ci mon_update_neighbors(mon, prev); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Revert to full-mesh monitoring if we reach threshold */ 37862306a36Sopenharmony_ci if (!tipc_mon_is_active(net, mon)) { 37962306a36Sopenharmony_ci list_for_each_entry(peer, &self->list, list) { 38062306a36Sopenharmony_ci kfree(peer->domain); 38162306a36Sopenharmony_ci peer->domain = NULL; 38262306a36Sopenharmony_ci peer->applied = 0; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci mon_assign_roles(mon, head); 38662306a36Sopenharmony_ciexit: 38762306a36Sopenharmony_ci write_unlock_bh(&mon->lock); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic bool tipc_mon_add_peer(struct tipc_monitor *mon, u32 addr, 39162306a36Sopenharmony_ci struct tipc_peer **peer) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct tipc_peer *self = mon->self; 39462306a36Sopenharmony_ci struct tipc_peer *cur, *prev, *p; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_ATOMIC); 39762306a36Sopenharmony_ci *peer = p; 39862306a36Sopenharmony_ci if (!p) 39962306a36Sopenharmony_ci return false; 40062306a36Sopenharmony_ci p->addr = addr; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Add new peer to lookup list */ 40362306a36Sopenharmony_ci INIT_LIST_HEAD(&p->list); 40462306a36Sopenharmony_ci hlist_add_head(&p->hash, &mon->peers[tipc_hashfn(addr)]); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Sort new peer into iterator list, in ascending circular order */ 40762306a36Sopenharmony_ci prev = self; 40862306a36Sopenharmony_ci list_for_each_entry(cur, &self->list, list) { 40962306a36Sopenharmony_ci if ((addr > prev->addr) && (addr < cur->addr)) 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci if (((addr < cur->addr) || (addr > prev->addr)) && 41262306a36Sopenharmony_ci (prev->addr > cur->addr)) 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci prev = cur; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci list_add_tail(&p->list, &cur->list); 41762306a36Sopenharmony_ci mon->peer_cnt++; 41862306a36Sopenharmony_ci mon_update_neighbors(mon, p); 41962306a36Sopenharmony_ci return true; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_civoid tipc_mon_peer_up(struct net *net, u32 addr, int bearer_id) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 42562306a36Sopenharmony_ci struct tipc_peer *self = get_self(net, bearer_id); 42662306a36Sopenharmony_ci struct tipc_peer *peer, *head; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci write_lock_bh(&mon->lock); 42962306a36Sopenharmony_ci peer = get_peer(mon, addr); 43062306a36Sopenharmony_ci if (!peer && !tipc_mon_add_peer(mon, addr, &peer)) 43162306a36Sopenharmony_ci goto exit; 43262306a36Sopenharmony_ci peer->is_up = true; 43362306a36Sopenharmony_ci head = peer_head(peer); 43462306a36Sopenharmony_ci if (head == self) 43562306a36Sopenharmony_ci mon_update_local_domain(mon); 43662306a36Sopenharmony_ci mon_assign_roles(mon, head); 43762306a36Sopenharmony_ciexit: 43862306a36Sopenharmony_ci write_unlock_bh(&mon->lock); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_civoid tipc_mon_peer_down(struct net *net, u32 addr, int bearer_id) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 44462306a36Sopenharmony_ci struct tipc_peer *self; 44562306a36Sopenharmony_ci struct tipc_peer *peer, *head; 44662306a36Sopenharmony_ci struct tipc_mon_domain *dom; 44762306a36Sopenharmony_ci int applied; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!mon) 45062306a36Sopenharmony_ci return; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci self = get_self(net, bearer_id); 45362306a36Sopenharmony_ci write_lock_bh(&mon->lock); 45462306a36Sopenharmony_ci peer = get_peer(mon, addr); 45562306a36Sopenharmony_ci if (!peer) { 45662306a36Sopenharmony_ci pr_warn("Mon: unknown link %x/%u DOWN\n", addr, bearer_id); 45762306a36Sopenharmony_ci goto exit; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci applied = peer->applied; 46062306a36Sopenharmony_ci peer->applied = 0; 46162306a36Sopenharmony_ci dom = peer->domain; 46262306a36Sopenharmony_ci peer->domain = NULL; 46362306a36Sopenharmony_ci if (peer->is_head) 46462306a36Sopenharmony_ci mon_identify_lost_members(peer, dom, applied); 46562306a36Sopenharmony_ci kfree(dom); 46662306a36Sopenharmony_ci peer->is_up = false; 46762306a36Sopenharmony_ci peer->is_head = false; 46862306a36Sopenharmony_ci peer->is_local = false; 46962306a36Sopenharmony_ci peer->down_cnt = 0; 47062306a36Sopenharmony_ci head = peer_head(peer); 47162306a36Sopenharmony_ci if (head == self) 47262306a36Sopenharmony_ci mon_update_local_domain(mon); 47362306a36Sopenharmony_ci mon_assign_roles(mon, head); 47462306a36Sopenharmony_ciexit: 47562306a36Sopenharmony_ci write_unlock_bh(&mon->lock); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* tipc_mon_rcv - process monitor domain event message 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_civoid tipc_mon_rcv(struct net *net, void *data, u16 dlen, u32 addr, 48162306a36Sopenharmony_ci struct tipc_mon_state *state, int bearer_id) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 48462306a36Sopenharmony_ci struct tipc_mon_domain *arrv_dom = data; 48562306a36Sopenharmony_ci struct tipc_mon_domain dom_bef; 48662306a36Sopenharmony_ci struct tipc_mon_domain *dom; 48762306a36Sopenharmony_ci struct tipc_peer *peer; 48862306a36Sopenharmony_ci u16 new_member_cnt = mon_le16_to_cpu(arrv_dom->member_cnt); 48962306a36Sopenharmony_ci int new_dlen = dom_rec_len(arrv_dom, new_member_cnt); 49062306a36Sopenharmony_ci u16 new_gen = mon_le16_to_cpu(arrv_dom->gen); 49162306a36Sopenharmony_ci u16 acked_gen = mon_le16_to_cpu(arrv_dom->ack_gen); 49262306a36Sopenharmony_ci u16 arrv_dlen = mon_le16_to_cpu(arrv_dom->len); 49362306a36Sopenharmony_ci bool probing = state->probing; 49462306a36Sopenharmony_ci int i, applied_bef; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci state->probing = false; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Sanity check received domain record */ 49962306a36Sopenharmony_ci if (new_member_cnt > MAX_MON_DOMAIN) 50062306a36Sopenharmony_ci return; 50162306a36Sopenharmony_ci if (dlen < dom_rec_len(arrv_dom, 0)) 50262306a36Sopenharmony_ci return; 50362306a36Sopenharmony_ci if (dlen != dom_rec_len(arrv_dom, new_member_cnt)) 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci if (dlen < new_dlen || arrv_dlen != new_dlen) 50662306a36Sopenharmony_ci return; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Synch generation numbers with peer if link just came up */ 50962306a36Sopenharmony_ci if (!state->synched) { 51062306a36Sopenharmony_ci state->peer_gen = new_gen - 1; 51162306a36Sopenharmony_ci state->acked_gen = acked_gen; 51262306a36Sopenharmony_ci state->synched = true; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (more(acked_gen, state->acked_gen)) 51662306a36Sopenharmony_ci state->acked_gen = acked_gen; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* Drop duplicate unless we are waiting for a probe response */ 51962306a36Sopenharmony_ci if (!more(new_gen, state->peer_gen) && !probing) 52062306a36Sopenharmony_ci return; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci write_lock_bh(&mon->lock); 52362306a36Sopenharmony_ci peer = get_peer(mon, addr); 52462306a36Sopenharmony_ci if (!peer || !peer->is_up) 52562306a36Sopenharmony_ci goto exit; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Peer is confirmed, stop any ongoing probing */ 52862306a36Sopenharmony_ci peer->down_cnt = 0; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Task is done for duplicate record */ 53162306a36Sopenharmony_ci if (!more(new_gen, state->peer_gen)) 53262306a36Sopenharmony_ci goto exit; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci state->peer_gen = new_gen; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Cache current domain record for later use */ 53762306a36Sopenharmony_ci dom_bef.member_cnt = 0; 53862306a36Sopenharmony_ci dom = peer->domain; 53962306a36Sopenharmony_ci if (dom) 54062306a36Sopenharmony_ci memcpy(&dom_bef, dom, dom->len); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Transform and store received domain record */ 54362306a36Sopenharmony_ci if (!dom || (dom->len < new_dlen)) { 54462306a36Sopenharmony_ci kfree(dom); 54562306a36Sopenharmony_ci dom = kmalloc(new_dlen, GFP_ATOMIC); 54662306a36Sopenharmony_ci peer->domain = dom; 54762306a36Sopenharmony_ci if (!dom) 54862306a36Sopenharmony_ci goto exit; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci dom->len = new_dlen; 55162306a36Sopenharmony_ci dom->gen = new_gen; 55262306a36Sopenharmony_ci dom->member_cnt = new_member_cnt; 55362306a36Sopenharmony_ci dom->up_map = mon_le64_to_cpu(arrv_dom->up_map); 55462306a36Sopenharmony_ci for (i = 0; i < new_member_cnt; i++) 55562306a36Sopenharmony_ci dom->members[i] = mon_le32_to_cpu(arrv_dom->members[i]); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Update peers affected by this domain record */ 55862306a36Sopenharmony_ci applied_bef = peer->applied; 55962306a36Sopenharmony_ci mon_apply_domain(mon, peer); 56062306a36Sopenharmony_ci mon_identify_lost_members(peer, &dom_bef, applied_bef); 56162306a36Sopenharmony_ci mon_assign_roles(mon, peer_head(peer)); 56262306a36Sopenharmony_ciexit: 56362306a36Sopenharmony_ci write_unlock_bh(&mon->lock); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_civoid tipc_mon_prep(struct net *net, void *data, int *dlen, 56762306a36Sopenharmony_ci struct tipc_mon_state *state, int bearer_id) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 57062306a36Sopenharmony_ci struct tipc_mon_domain *dom = data; 57162306a36Sopenharmony_ci u16 gen = mon->dom_gen; 57262306a36Sopenharmony_ci u16 len; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Send invalid record if not active */ 57562306a36Sopenharmony_ci if (!tipc_mon_is_active(net, mon)) { 57662306a36Sopenharmony_ci dom->len = 0; 57762306a36Sopenharmony_ci return; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Send only a dummy record with ack if peer has acked our last sent */ 58162306a36Sopenharmony_ci if (likely(state->acked_gen == gen)) { 58262306a36Sopenharmony_ci len = dom_rec_len(dom, 0); 58362306a36Sopenharmony_ci *dlen = len; 58462306a36Sopenharmony_ci dom->len = mon_cpu_to_le16(len); 58562306a36Sopenharmony_ci dom->gen = mon_cpu_to_le16(gen); 58662306a36Sopenharmony_ci dom->ack_gen = mon_cpu_to_le16(state->peer_gen); 58762306a36Sopenharmony_ci dom->member_cnt = 0; 58862306a36Sopenharmony_ci return; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci /* Send the full record */ 59162306a36Sopenharmony_ci read_lock_bh(&mon->lock); 59262306a36Sopenharmony_ci len = mon_le16_to_cpu(mon->cache.len); 59362306a36Sopenharmony_ci *dlen = len; 59462306a36Sopenharmony_ci memcpy(data, &mon->cache, len); 59562306a36Sopenharmony_ci read_unlock_bh(&mon->lock); 59662306a36Sopenharmony_ci dom->ack_gen = mon_cpu_to_le16(state->peer_gen); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_civoid tipc_mon_get_state(struct net *net, u32 addr, 60062306a36Sopenharmony_ci struct tipc_mon_state *state, 60162306a36Sopenharmony_ci int bearer_id) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 60462306a36Sopenharmony_ci struct tipc_peer *peer; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (!tipc_mon_is_active(net, mon)) { 60762306a36Sopenharmony_ci state->probing = false; 60862306a36Sopenharmony_ci state->monitoring = true; 60962306a36Sopenharmony_ci return; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Used cached state if table has not changed */ 61362306a36Sopenharmony_ci if (!state->probing && 61462306a36Sopenharmony_ci (state->list_gen == mon->list_gen) && 61562306a36Sopenharmony_ci (state->acked_gen == mon->dom_gen)) 61662306a36Sopenharmony_ci return; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci read_lock_bh(&mon->lock); 61962306a36Sopenharmony_ci peer = get_peer(mon, addr); 62062306a36Sopenharmony_ci if (peer) { 62162306a36Sopenharmony_ci state->probing = state->acked_gen != mon->dom_gen; 62262306a36Sopenharmony_ci state->probing |= peer->down_cnt; 62362306a36Sopenharmony_ci state->reset |= peer->down_cnt >= MAX_PEER_DOWN_EVENTS; 62462306a36Sopenharmony_ci state->monitoring = peer->is_local; 62562306a36Sopenharmony_ci state->monitoring |= peer->is_head; 62662306a36Sopenharmony_ci state->list_gen = mon->list_gen; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci read_unlock_bh(&mon->lock); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void mon_timeout(struct timer_list *t) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct tipc_monitor *mon = from_timer(mon, t, timer); 63462306a36Sopenharmony_ci struct tipc_peer *self; 63562306a36Sopenharmony_ci int best_member_cnt = dom_size(mon->peer_cnt) - 1; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci write_lock_bh(&mon->lock); 63862306a36Sopenharmony_ci self = mon->self; 63962306a36Sopenharmony_ci if (self && (best_member_cnt != self->applied)) { 64062306a36Sopenharmony_ci mon_update_local_domain(mon); 64162306a36Sopenharmony_ci mon_assign_roles(mon, self); 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci write_unlock_bh(&mon->lock); 64462306a36Sopenharmony_ci mod_timer(&mon->timer, jiffies + mon->timer_intv); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ciint tipc_mon_create(struct net *net, int bearer_id) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 65062306a36Sopenharmony_ci struct tipc_monitor *mon; 65162306a36Sopenharmony_ci struct tipc_peer *self; 65262306a36Sopenharmony_ci struct tipc_mon_domain *dom; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (tn->monitors[bearer_id]) 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci mon = kzalloc(sizeof(*mon), GFP_ATOMIC); 65862306a36Sopenharmony_ci self = kzalloc(sizeof(*self), GFP_ATOMIC); 65962306a36Sopenharmony_ci dom = kzalloc(sizeof(*dom), GFP_ATOMIC); 66062306a36Sopenharmony_ci if (!mon || !self || !dom) { 66162306a36Sopenharmony_ci kfree(mon); 66262306a36Sopenharmony_ci kfree(self); 66362306a36Sopenharmony_ci kfree(dom); 66462306a36Sopenharmony_ci return -ENOMEM; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci tn->monitors[bearer_id] = mon; 66762306a36Sopenharmony_ci rwlock_init(&mon->lock); 66862306a36Sopenharmony_ci mon->net = net; 66962306a36Sopenharmony_ci mon->peer_cnt = 1; 67062306a36Sopenharmony_ci mon->self = self; 67162306a36Sopenharmony_ci self->domain = dom; 67262306a36Sopenharmony_ci self->addr = tipc_own_addr(net); 67362306a36Sopenharmony_ci self->is_up = true; 67462306a36Sopenharmony_ci self->is_head = true; 67562306a36Sopenharmony_ci INIT_LIST_HEAD(&self->list); 67662306a36Sopenharmony_ci timer_setup(&mon->timer, mon_timeout, 0); 67762306a36Sopenharmony_ci mon->timer_intv = msecs_to_jiffies(MON_TIMEOUT + (tn->random & 0xffff)); 67862306a36Sopenharmony_ci mod_timer(&mon->timer, jiffies + mon->timer_intv); 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_civoid tipc_mon_delete(struct net *net, int bearer_id) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 68562306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 68662306a36Sopenharmony_ci struct tipc_peer *self; 68762306a36Sopenharmony_ci struct tipc_peer *peer, *tmp; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!mon) 69062306a36Sopenharmony_ci return; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci self = get_self(net, bearer_id); 69362306a36Sopenharmony_ci write_lock_bh(&mon->lock); 69462306a36Sopenharmony_ci tn->monitors[bearer_id] = NULL; 69562306a36Sopenharmony_ci list_for_each_entry_safe(peer, tmp, &self->list, list) { 69662306a36Sopenharmony_ci list_del(&peer->list); 69762306a36Sopenharmony_ci hlist_del(&peer->hash); 69862306a36Sopenharmony_ci kfree(peer->domain); 69962306a36Sopenharmony_ci kfree(peer); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci mon->self = NULL; 70262306a36Sopenharmony_ci write_unlock_bh(&mon->lock); 70362306a36Sopenharmony_ci timer_shutdown_sync(&mon->timer); 70462306a36Sopenharmony_ci kfree(self->domain); 70562306a36Sopenharmony_ci kfree(self); 70662306a36Sopenharmony_ci kfree(mon); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_civoid tipc_mon_reinit_self(struct net *net) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct tipc_monitor *mon; 71262306a36Sopenharmony_ci int bearer_id; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) { 71562306a36Sopenharmony_ci mon = tipc_monitor(net, bearer_id); 71662306a36Sopenharmony_ci if (!mon) 71762306a36Sopenharmony_ci continue; 71862306a36Sopenharmony_ci write_lock_bh(&mon->lock); 71962306a36Sopenharmony_ci mon->self->addr = tipc_own_addr(net); 72062306a36Sopenharmony_ci write_unlock_bh(&mon->lock); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ciint tipc_nl_monitor_set_threshold(struct net *net, u32 cluster_size) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (cluster_size > TIPC_CLUSTER_SIZE) 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci tn->mon_threshold = cluster_size; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ciint tipc_nl_monitor_get_threshold(struct net *net) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct tipc_net *tn = tipc_net(net); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return tn->mon_threshold; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int __tipc_nl_add_monitor_peer(struct tipc_peer *peer, 74462306a36Sopenharmony_ci struct tipc_nl_msg *msg) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct tipc_mon_domain *dom = peer->domain; 74762306a36Sopenharmony_ci struct nlattr *attrs; 74862306a36Sopenharmony_ci void *hdr; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family, 75162306a36Sopenharmony_ci NLM_F_MULTI, TIPC_NL_MON_PEER_GET); 75262306a36Sopenharmony_ci if (!hdr) 75362306a36Sopenharmony_ci return -EMSGSIZE; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON_PEER); 75662306a36Sopenharmony_ci if (!attrs) 75762306a36Sopenharmony_ci goto msg_full; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_ADDR, peer->addr)) 76062306a36Sopenharmony_ci goto attr_msg_full; 76162306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_APPLIED, peer->applied)) 76262306a36Sopenharmony_ci goto attr_msg_full; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (peer->is_up) 76562306a36Sopenharmony_ci if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_UP)) 76662306a36Sopenharmony_ci goto attr_msg_full; 76762306a36Sopenharmony_ci if (peer->is_local) 76862306a36Sopenharmony_ci if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_LOCAL)) 76962306a36Sopenharmony_ci goto attr_msg_full; 77062306a36Sopenharmony_ci if (peer->is_head) 77162306a36Sopenharmony_ci if (nla_put_flag(msg->skb, TIPC_NLA_MON_PEER_HEAD)) 77262306a36Sopenharmony_ci goto attr_msg_full; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (dom) { 77562306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEER_DOMGEN, dom->gen)) 77662306a36Sopenharmony_ci goto attr_msg_full; 77762306a36Sopenharmony_ci if (nla_put_u64_64bit(msg->skb, TIPC_NLA_MON_PEER_UPMAP, 77862306a36Sopenharmony_ci dom->up_map, TIPC_NLA_MON_PEER_PAD)) 77962306a36Sopenharmony_ci goto attr_msg_full; 78062306a36Sopenharmony_ci if (nla_put(msg->skb, TIPC_NLA_MON_PEER_MEMBERS, 78162306a36Sopenharmony_ci dom->member_cnt * sizeof(u32), &dom->members)) 78262306a36Sopenharmony_ci goto attr_msg_full; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci nla_nest_end(msg->skb, attrs); 78662306a36Sopenharmony_ci genlmsg_end(msg->skb, hdr); 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ciattr_msg_full: 79062306a36Sopenharmony_ci nla_nest_cancel(msg->skb, attrs); 79162306a36Sopenharmony_cimsg_full: 79262306a36Sopenharmony_ci genlmsg_cancel(msg->skb, hdr); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return -EMSGSIZE; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ciint tipc_nl_add_monitor_peer(struct net *net, struct tipc_nl_msg *msg, 79862306a36Sopenharmony_ci u32 bearer_id, u32 *prev_node) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 80162306a36Sopenharmony_ci struct tipc_peer *peer; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (!mon) 80462306a36Sopenharmony_ci return -EINVAL; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci read_lock_bh(&mon->lock); 80762306a36Sopenharmony_ci peer = mon->self; 80862306a36Sopenharmony_ci do { 80962306a36Sopenharmony_ci if (*prev_node) { 81062306a36Sopenharmony_ci if (peer->addr == *prev_node) 81162306a36Sopenharmony_ci *prev_node = 0; 81262306a36Sopenharmony_ci else 81362306a36Sopenharmony_ci continue; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci if (__tipc_nl_add_monitor_peer(peer, msg)) { 81662306a36Sopenharmony_ci *prev_node = peer->addr; 81762306a36Sopenharmony_ci read_unlock_bh(&mon->lock); 81862306a36Sopenharmony_ci return -EMSGSIZE; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci } while ((peer = peer_nxt(peer)) != mon->self); 82162306a36Sopenharmony_ci read_unlock_bh(&mon->lock); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ciint __tipc_nl_add_monitor(struct net *net, struct tipc_nl_msg *msg, 82762306a36Sopenharmony_ci u32 bearer_id) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct tipc_monitor *mon = tipc_monitor(net, bearer_id); 83062306a36Sopenharmony_ci char bearer_name[TIPC_MAX_BEARER_NAME]; 83162306a36Sopenharmony_ci struct nlattr *attrs; 83262306a36Sopenharmony_ci void *hdr; 83362306a36Sopenharmony_ci int ret; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci ret = tipc_bearer_get_name(net, bearer_name, bearer_id); 83662306a36Sopenharmony_ci if (ret || !mon) 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family, 84062306a36Sopenharmony_ci NLM_F_MULTI, TIPC_NL_MON_GET); 84162306a36Sopenharmony_ci if (!hdr) 84262306a36Sopenharmony_ci return -EMSGSIZE; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_MON); 84562306a36Sopenharmony_ci if (!attrs) 84662306a36Sopenharmony_ci goto msg_full; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci read_lock_bh(&mon->lock); 84962306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_MON_REF, bearer_id)) 85062306a36Sopenharmony_ci goto attr_msg_full; 85162306a36Sopenharmony_ci if (tipc_mon_is_active(net, mon)) 85262306a36Sopenharmony_ci if (nla_put_flag(msg->skb, TIPC_NLA_MON_ACTIVE)) 85362306a36Sopenharmony_ci goto attr_msg_full; 85462306a36Sopenharmony_ci if (nla_put_string(msg->skb, TIPC_NLA_MON_BEARER_NAME, bearer_name)) 85562306a36Sopenharmony_ci goto attr_msg_full; 85662306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_MON_PEERCNT, mon->peer_cnt)) 85762306a36Sopenharmony_ci goto attr_msg_full; 85862306a36Sopenharmony_ci if (nla_put_u32(msg->skb, TIPC_NLA_MON_LISTGEN, mon->list_gen)) 85962306a36Sopenharmony_ci goto attr_msg_full; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci read_unlock_bh(&mon->lock); 86262306a36Sopenharmony_ci nla_nest_end(msg->skb, attrs); 86362306a36Sopenharmony_ci genlmsg_end(msg->skb, hdr); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ciattr_msg_full: 86862306a36Sopenharmony_ci read_unlock_bh(&mon->lock); 86962306a36Sopenharmony_ci nla_nest_cancel(msg->skb, attrs); 87062306a36Sopenharmony_cimsg_full: 87162306a36Sopenharmony_ci genlmsg_cancel(msg->skb, hdr); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return -EMSGSIZE; 87462306a36Sopenharmony_ci} 875