I believe this one is ready for posting now. Please ack.

///jon


> -----Original Message-----
> From: Jon Maloy [mailto:[email protected]]
> Sent: Wednesday, 08 June, 2016 14:54
> To: [email protected]; Parthasarathy Bhuvaragan; Ying Xue;
> Richard Alpe; Jon Maloy
> Cc: [email protected]
> Subject: [PATCH net-next v8 1/1] tipc: add neighbor monitoring framework
> 
> TIPC based clusters are by default set up with full-mesh link
> connectivity between all nodes. Those links are expected to provide
> a short failure detection time, by default set to 1500 ms. Because
> of this, the background load for neighbor monitoring in an N-node
> cluster increases with a factor N on each node, while the overall
> monitoring traffic through the network infrastructure increases at
> a ~(N * (N - 1)) rate. Experience has shown that such clusters don't
> scale well beyond ~100 nodes unless we significantly increase failure
> discovery tolerance.
> 
> This commit introduces a framework and an algorithm that drastically
> reduces this background load, while basically maintaining the original
> failure detection times across the whole cluster. Using this algorithm,
> background load will now grow at a rate of ~(2 * sqrt(N)) per node, and
> at ~(2 * N * sqrt(N)) in traffic overhead. As an example, each node will
> now have to actively monitor 38 neighbors in a 400-node cluster, instead
> of as before 399.
> 
> This "Overlapping Ring Supervision Algorithm" is completely distributed
> and employs no centralized or coordinated state. It goes as follows:
> 
> - Each node makes up a linearly ascending, circular list of all its N
>   known neighbors, based on their TIPC node identity. This algorithm
>   must be the same on all nodes.
> 
> - The node then selects the next M = sqrt(N) - 1 nodes downstream from
>   itself in the list, and chooses to actively monitor those. This is
>   called its "local monitoring domain".
> 
> - It creates a domain record describing the monitoring domain, and
>   piggy-backs this in the data area of all neighbor monitoring messages
>   (LINK_PROTOCOL/STATE) leaving that node. This means that all nodes in
>   the cluster eventually (default within 400 ms) will learn about
>   its monitoring domain.
> 
> - Whenever a node discovers a change in its local domain, e.g., a node
>   has been added or has gone down, it creates and sends out a new
>   version of its node record to inform all neighbors about the change.
> 
> - A node receiving a domain record from anybody outside its local domain
>   matches this against its own list (which may not look the same), and
>   chooses to not actively monitor those members of the received domain
>   record that are also present in its own list. Instead, it relies on
>   indications from the direct monitoring nodes if an indirectly
>   monitored node has gone up or down. If a node is indicated lost, the
>   receiving node temporarily activates its own direct monitoring towards
>   that node in order to confirm, or not, that it is actually gone.
> 
> - Since each node is actively monitoring sqrt(N) downstream neighbors,
>   each node is also actively monitored by the same number of upstream
>   neighbors. This means that all non-direct monitoring nodes normally
>   will receive sqrt(N) indications that a node is gone.
> 
> - A major drawback with ring monitoring is how it handles failures that
>   cause massive network partitionings. If both a lost node and all its
>   direct monitoring neighbors are inside the lost partition, the nodes in
>   the remaining partition will never receive indications about the loss.
>   To overcome this, each node also chooses to actively monitor some
>   nodes outside its local domain. Those nodes are called remote domain
>   "heads", and are selected in such a way that no node in the cluster
>   will be more than two direct monitoring hops away. Because of this,
>   each node, apart from monitoring the member of its local domain, will
>   also typically monitor sqrt(N) remote head nodes.
> 
> - As an optimization, local list status, domain status and domain
>   records are marked with a generation number. This saves senders from
>   unnecessarily conveying  unaltered domain records, and receivers from
>   performing unneeded re-adaptations of their node monitoring list, such
>   as re-assigning domain heads.
> 
> v2: - Updated according to comments from Richard Alpe. His proposal
>       for the peer_xxx() functions didn't work out well, but for the
>       rest it was ok.
>     - Added a breakpoint (cluster size) where monitoring algo should
>       switch from "full mesh" to "overlapping ring". Default value
>       32, but I even tested to set it to 100 without any problems.
>       This should be the variable Partha should make configurable,
>       instead of just toggling as I suggested originally. By setting
>       the breakpoint to 0 ring monitoring is always on, by setting it
>       to some high number it is always off.
> 
> v3: - Renamed "mon_breakpoint" to "mon_threshold"
>     - Added code to revert to full-mesh monitoring when a node is
>       removed and we reach the monitor threshold from above.
> 
> v4: - Fixed typos in commit log and a couple of minor issues,
>       as per feedback from Ying.
>     - Removed unused function and flag for disabling the monitor.
>       This functionality was later superseded by commit 5b7066c3dd24
>       ("tipc: stricter filtering of packets in bearer layer"), I just
>       forgot to remove it.
> 
> v5: - Eliminated the 'head_map" field from struct tipc_peer. Instead,
>       we now identify lost domain members by comparing the peer's
>       old domain record with the newly arrived one. This is much simpler
>       and eliminates a source of potential table inconsistency.
>     - Replaced the 'confirm' bit in struct tipc_peer with a counter, so
>       that we can keep track of how many "peer down" event we have
>       received for a given peer since we last heard from it. When the
>       counter is >= 4, we reset the link without waiting for the own
>       confirmation probing to finish.
>     - Apply matching domains of remote peer's domains completely, even
>       when the latter comprise the own node. This has no functional
>       significance, but gives a more symmetric algorithm, and looks
>       better in the table printouts.
>     - Added a new condition for sending STATE messages on non-monitored
>       links. Those must be sent even when there are received/
>       unacknowledged broadcast messages, in order to acknowledge those.
>     - Added a slow (2 min) background timer which will check if the
>       current local domain size is optimal, and otherwise update the
>       local domain. This way, we smooth out the domain sizes, so that
>       all nodes eventually (in a full-mesh cluster) will have the same
>       size.
>     - Added a new check in update_local_domain(), so that we step
>       generation number only when something really has changed.
>     - Added an skb_trim() in build_proto_msg(), so that we send only
>       the actual size of the domain record, and nothing more.
>     - Moved skb_linearize() in link_proto_rcv() so that it now covers
>       all received messages. I found out the hard way that this is
>       necessary.
> 
> v6: - Updated some finction and varaible names, both according to
>       comments from Partha and own improvements.
>     - Introduced new flag 'check_gen' in struct mon_state to fixe a
>       problem with reception of domain records: when a peer has just
>       come up, it must accept any domain generation, without checking
>       if it is in a valid range.
>     - Tried to make the domain generation handling simpler and more
>       comprehensible.
> 
> v7: - Cosmetic refactoring of tipc_mon_rcv().
>     - Updated according to latest comments from Partha, including
>       the tipc_mon_is_active() function.
>     - After some pondering I decided to remove the one-bit boolean
>       fields after all. Such a design has some caveats, which is more
>       than we need in this rather complex code.
>     - Added correct data length and skb_trim() for all messages
>       created in link_build_proto_msg(), not only STATE messages.
>     - Added a random distribution to the monitor timer interval. The
>       interval will now be spread across the interval 2-3 minutes.
>     - Only consider tic_peer->down_cnt's value when the peer is not in
>       the local domain. I.e., loss of a local peer must always be
>       confirmed by direct probing. This eliminates the risk of avalanche
>       link losses because of false positives.
> 
> v8: - Added missing "peer->applied = 0" in tipc_mon_remove_peer() when
>       monitor moves back to full-mesh monitoring. This fixes a crash
>       reported by Partha.
> 
> Signed-off-by: Jon Maloy <[email protected]>
> ---
>  net/tipc/Makefile  |   2 +-
>  net/tipc/addr.h    |   1 +
>  net/tipc/bearer.c  |   8 +-
>  net/tipc/bearer.h  |   2 +-
>  net/tipc/core.c    |   1 +
>  net/tipc/core.h    |  15 +-
>  net/tipc/link.c    |  49 +++-
>  net/tipc/monitor.c | 651
> +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  net/tipc/monitor.h |  73 ++++++
>  net/tipc/node.c    |  26 +--
>  10 files changed, 797 insertions(+), 31 deletions(-)
>  create mode 100644 net/tipc/monitor.c
>  create mode 100644 net/tipc/monitor.h
> 
> diff --git a/net/tipc/Makefile b/net/tipc/Makefile
> index 57e460b..31b9f9c 100644
> --- a/net/tipc/Makefile
> +++ b/net/tipc/Makefile
> @@ -6,7 +6,7 @@ obj-$(CONFIG_TIPC) := tipc.o
> 
>  tipc-y       += addr.o bcast.o bearer.o \
>          core.o link.o discover.o msg.o  \
> -        name_distr.o  subscr.o name_table.o net.o  \
> +        name_distr.o  subscr.o monitor.o name_table.o net.o  \
>          netlink.o netlink_compat.o node.o socket.o eth_media.o \
>          server.o socket.o
> 
> diff --git a/net/tipc/addr.h b/net/tipc/addr.h
> index 93f7c98..64f4004 100644
> --- a/net/tipc/addr.h
> +++ b/net/tipc/addr.h
> @@ -73,4 +73,5 @@ int tipc_addr_node_valid(u32 addr);
>  int tipc_in_scope(u32 domain, u32 addr);
>  int tipc_addr_scope(u32 domain);
>  char *tipc_addr_string_fill(char *string, u32 addr);
> +
>  #endif
> diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
> index 6f11c62..9a70e1d 100644
> --- a/net/tipc/bearer.c
> +++ b/net/tipc/bearer.c
> @@ -1,7 +1,7 @@
>  /*
>   * net/tipc/bearer.c: TIPC bearer code
>   *
> - * Copyright (c) 1996-2006, 2013-2014, Ericsson AB
> + * Copyright (c) 1996-2006, 2013-2016, Ericsson AB
>   * Copyright (c) 2004-2006, 2010-2013, Wind River Systems
>   * All rights reserved.
>   *
> @@ -39,6 +39,7 @@
>  #include "bearer.h"
>  #include "link.h"
>  #include "discover.h"
> +#include "monitor.h"
>  #include "bcast.h"
>  #include "netlink.h"
> 
> @@ -313,6 +314,10 @@ restart:
>       rcu_assign_pointer(tn->bearer_list[bearer_id], b);
>       if (skb)
>               tipc_bearer_xmit_skb(net, bearer_id, skb, &b->bcast_addr);
> +
> +     if (tipc_mon_create(net, bearer_id))
> +             return -ENOMEM;
> +
>       pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
>               name,
>               tipc_addr_string_fill(addr_string, disc_domain), priority);
> @@ -348,6 +353,7 @@ static void bearer_disable(struct net *net, struct
> tipc_bearer *b)
>               tipc_disc_delete(b->link_req);
>       RCU_INIT_POINTER(tn->bearer_list[bearer_id], NULL);
>       kfree_rcu(b, rcu);
> +     tipc_mon_delete(net, bearer_id);
>  }
> 
>  int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b,
> diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
> index f686e41..0d337c7 100644
> --- a/net/tipc/bearer.h
> +++ b/net/tipc/bearer.h
> @@ -1,7 +1,7 @@
>  /*
>   * net/tipc/bearer.h: Include file for TIPC bearer code
>   *
> - * Copyright (c) 1996-2006, 2013-2014, Ericsson AB
> + * Copyright (c) 1996-2006, 2013-2016, Ericsson AB
>   * Copyright (c) 2005, 2010-2011, Wind River Systems
>   * All rights reserved.
>   *
> diff --git a/net/tipc/core.c b/net/tipc/core.c
> index fe1b062..236b043 100644
> --- a/net/tipc/core.c
> +++ b/net/tipc/core.c
> @@ -57,6 +57,7 @@ static int __net_init tipc_init_net(struct net *net)
> 
>       tn->net_id = 4711;
>       tn->own_addr = 0;
> +     tn->mon_threshold = TIPC_DEF_MON_THRESHOLD;
>       get_random_bytes(&tn->random, sizeof(int));
>       INIT_LIST_HEAD(&tn->node_list);
>       spin_lock_init(&tn->node_list_lock);
> diff --git a/net/tipc/core.h b/net/tipc/core.h
> index eff58dc..a1845fb 100644
> --- a/net/tipc/core.h
> +++ b/net/tipc/core.h
> @@ -66,11 +66,13 @@ struct tipc_bc_base;
>  struct tipc_link;
>  struct tipc_name_table;
>  struct tipc_server;
> +struct tipc_monitor;
> 
>  #define TIPC_MOD_VER "2.0.0"
> 
> -#define NODE_HTABLE_SIZE   512
> -#define MAX_BEARERS     3
> +#define NODE_HTABLE_SIZE       512
> +#define MAX_BEARERS           3
> +#define TIPC_DEF_MON_THRESHOLD  32
> 
>  extern int tipc_net_id __read_mostly;
>  extern int sysctl_tipc_rmem[3] __read_mostly;
> @@ -88,6 +90,10 @@ struct tipc_net {
>       u32 num_nodes;
>       u32 num_links;
> 
> +     /* Neighbor monitoring list */
> +     struct tipc_monitor *monitors[MAX_BEARERS];
> +     int mon_threshold;
> +
>       /* Bearer list */
>       struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1];
> 
> @@ -126,6 +132,11 @@ static inline struct list_head *tipc_nodes(struct net 
> *net)
>       return &tipc_net(net)->node_list;
>  }
> 
> +static inline unsigned int tipc_hashfn(u32 addr)
> +{
> +     return addr & (NODE_HTABLE_SIZE - 1);
> +}
> +
>  static inline u16 mod(u16 x)
>  {
>       return x & 0xffffu;
> diff --git a/net/tipc/link.c b/net/tipc/link.c
> index a904ccd..03f8bdf 100644
> --- a/net/tipc/link.c
> +++ b/net/tipc/link.c
> @@ -42,6 +42,7 @@
>  #include "name_distr.h"
>  #include "discover.h"
>  #include "netlink.h"
> +#include "monitor.h"
> 
>  #include <linux/pkt_sched.h>
> 
> @@ -95,6 +96,7 @@ struct tipc_stats {
>   * @pmsg: convenience pointer to "proto_msg" field
>   * @priority: current link priority
>   * @net_plane: current link network plane ('A' through 'H')
> + * @mon_state: cookie with information needed by link monitor
>   * @backlog_limit: backlog queue congestion thresholds (indexed by
> importance)
>   * @exp_msg_count: # of tunnelled messages expected during link changeover
>   * @reset_rcv_checkpt: seq # of last acknowledged message at time of link 
> reset
> @@ -138,6 +140,7 @@ struct tipc_link {
>       char if_name[TIPC_MAX_IF_NAME];
>       u32 priority;
>       char net_plane;
> +     struct tipc_mon_state mon_state;
>       u16 rst_cnt;
> 
>       /* Failover/synch */
> @@ -708,18 +711,25 @@ int tipc_link_timeout(struct tipc_link *l, struct
> sk_buff_head *xmitq)
>       bool setup = false;
>       u16 bc_snt = l->bc_sndlink->snd_nxt - 1;
>       u16 bc_acked = l->bc_rcvlink->acked;
> -
> -     link_profile_stats(l);
> +     struct tipc_mon_state *mstate = &l->mon_state;
> 
>       switch (l->state) {
>       case LINK_ESTABLISHED:
>       case LINK_SYNCHING:
> -             if (l->silent_intv_cnt > l->abort_limit)
> -                     return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
>               mtyp = STATE_MSG;
> +             link_profile_stats(l);
> +             tipc_mon_get_state(l->net, l->addr, mstate, l->bearer_id);
> +             if (mstate->reset || (l->silent_intv_cnt > l->abort_limit))
> +                     return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
>               state = bc_acked != bc_snt;
> -             probe = l->silent_intv_cnt;
> -             l->silent_intv_cnt++;
> +             state |= l->bc_rcvlink->rcv_unacked;
> +             state |= l->rcv_unacked;
> +             state |= !skb_queue_empty(&l->transmq);
> +             state |= !skb_queue_empty(&l->deferdq);
> +             probe = mstate->probing;
> +             probe |= l->silent_intv_cnt;
> +             if (probe || mstate->monitoring)
> +                     l->silent_intv_cnt++;
>               break;
>       case LINK_RESET:
>               setup = l->rst_cnt++ <= 4;
> @@ -830,6 +840,7 @@ void tipc_link_reset(struct tipc_link *l)
>       l->stats.recv_info = 0;
>       l->stale_count = 0;
>       l->bc_peer_is_up = false;
> +     memset(&l->mon_state, 0, sizeof(l->mon_state));
>       tipc_link_reset_stats(l);
>  }
> 
> @@ -1238,6 +1249,9 @@ static void tipc_link_build_proto_msg(struct tipc_link 
> *l,
> int mtyp, bool probe,
>       struct tipc_msg *hdr;
>       struct sk_buff_head *dfq = &l->deferdq;
>       bool node_up = link_is_up(l->bc_rcvlink);
> +     struct tipc_mon_state *mstate = &l->mon_state;
> +     int dlen = 0;
> +     void *data;
> 
>       /* Don't send protocol message during reset or link failover */
>       if (tipc_link_is_blocked(l))
> @@ -1250,12 +1264,13 @@ static void tipc_link_build_proto_msg(struct tipc_link
> *l, int mtyp, bool probe,
>               rcvgap = buf_seqno(skb_peek(dfq)) - l->rcv_nxt;
> 
>       skb = tipc_msg_create(LINK_PROTOCOL, mtyp, INT_H_SIZE,
> -                           TIPC_MAX_IF_NAME, l->addr,
> +                           tipc_max_domain_size, l->addr,
>                             tipc_own_addr(l->net), 0, 0, 0);
>       if (!skb)
>               return;
> 
>       hdr = buf_msg(skb);
> +     data = msg_data(hdr);
>       msg_set_session(hdr, l->session);
>       msg_set_bearer_id(hdr, l->bearer_id);
>       msg_set_net_plane(hdr, l->net_plane);
> @@ -1271,14 +1286,18 @@ static void tipc_link_build_proto_msg(struct tipc_link
> *l, int mtyp, bool probe,
> 
>       if (mtyp == STATE_MSG) {
>               msg_set_seq_gap(hdr, rcvgap);
> -             msg_set_size(hdr, INT_H_SIZE);
>               msg_set_probe(hdr, probe);
> +             tipc_mon_prep(l->net, data, &dlen, mstate, l->bearer_id);
> +             msg_set_size(hdr, INT_H_SIZE + dlen);
> +             skb_trim(skb, INT_H_SIZE + dlen);
>               l->stats.sent_states++;
>               l->rcv_unacked = 0;
>       } else {
>               /* RESET_MSG or ACTIVATE_MSG */
>               msg_set_max_pkt(hdr, l->advertised_mtu);
> -             strcpy(msg_data(hdr), l->if_name);
> +             strcpy(data, l->if_name);
> +             msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
> +             skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME);
>       }
>       if (probe)
>               l->stats.sent_probes++;
> @@ -1371,7 +1390,9 @@ static int tipc_link_proto_rcv(struct tipc_link *l, 
> struct
> sk_buff *skb,
>       u16 peers_tol = msg_link_tolerance(hdr);
>       u16 peers_prio = msg_linkprio(hdr);
>       u16 rcv_nxt = l->rcv_nxt;
> +     u16 dlen = msg_data_sz(hdr);
>       int mtyp = msg_type(hdr);
> +     void *data;
>       char *if_name;
>       int rc = 0;
> 
> @@ -1381,6 +1402,10 @@ static int tipc_link_proto_rcv(struct tipc_link *l, 
> struct
> sk_buff *skb,
>       if (tipc_own_addr(l->net) > msg_prevnode(hdr))
>               l->net_plane = msg_net_plane(hdr);
> 
> +     skb_linearize(skb);
> +     hdr = buf_msg(skb);
> +     data = msg_data(hdr);
> +
>       switch (mtyp) {
>       case RESET_MSG:
> 
> @@ -1391,8 +1416,6 @@ static int tipc_link_proto_rcv(struct tipc_link *l, 
> struct
> sk_buff *skb,
>               /* fall thru' */
> 
>       case ACTIVATE_MSG:
> -             skb_linearize(skb);
> -             hdr = buf_msg(skb);
> 
>               /* Complete own link name with peer's interface name */
>               if_name =  strrchr(l->name, ':') + 1;
> @@ -1400,7 +1423,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, 
> struct
> sk_buff *skb,
>                       break;
>               if (msg_data_sz(hdr) < TIPC_MAX_IF_NAME)
>                       break;
> -             strncpy(if_name, msg_data(hdr), TIPC_MAX_IF_NAME);
> +             strncpy(if_name, data, TIPC_MAX_IF_NAME);
> 
>               /* Update own tolerance if peer indicates a non-zero value */
>               if (in_range(peers_tol, TIPC_MIN_LINK_TOL,
> TIPC_MAX_LINK_TOL))
> @@ -1448,6 +1471,8 @@ static int tipc_link_proto_rcv(struct tipc_link *l, 
> struct
> sk_buff *skb,
>                               rc = TIPC_LINK_UP_EVT;
>                       break;
>               }
> +             tipc_mon_rcv(l->net, data, dlen, l->addr,
> +                          &l->mon_state, l->bearer_id);
> 
>               /* Send NACK if peer has sent pkts we haven't received yet */
>               if (more(peers_snd_nxt, rcv_nxt) && !tipc_link_is_synching(l))
> diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c
> new file mode 100644
> index 0000000..14d3021
> --- /dev/null
> +++ b/net/tipc/monitor.c
> @@ -0,0 +1,651 @@
> +/*
> + * net/tipc/monitor.c
> + *
> + * Copyright (c) 2016, Ericsson AB
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are 
> met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + * 3. Neither the names of the copyright holders nor the names of its
> + *    contributors may be used to endorse or promote products derived from
> + *    this software without specific prior written permission.
> + *
> + * Alternatively, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL") version 2 as published by the Free
> + * Software Foundation.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
> CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
> BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include "core.h"
> +#include "addr.h"
> +#include "monitor.h"
> +
> +#define MAX_MON_DOMAIN       64
> +#define MON_TIMEOUT          120000
> +#define MAX_PEER_DOWN_EVENTS 4
> +
> +/* struct tipc_mon_domain: domain record to be transferred between peers
> + * @len: actual size of domain record
> + * @gen: current generation of sender's domain
> + * @ack_gen: most recent generation of self's domain acked by peer
> + * @member_cnt: number of domain member nodes described in this record
> + * @up_map: bit map indicating which of the members the sender considers up
> + * @members: identity of the domain members
> + */
> +struct tipc_mon_domain {
> +     u16 len;
> +     u16 gen;
> +     u16 ack_gen;
> +     u16 member_cnt;
> +     u64 up_map;
> +     u32 members[MAX_MON_DOMAIN];
> +};
> +
> +/* struct tipc_peer: state of a peer node and its domain
> + * @addr: tipc node identity of peer
> + * @head_map: shows which other nodes currently consider peer 'up'
> + * @domain: most recent domain record from peer
> + * @hash: position in hashed lookup list
> + * @list: position in linked list, in circular ascending order by 'addr'
> + * @applied: number of reported domain members applied on this monitor list
> + * @is_up: peer is up as seen from this node
> + * @is_head: peer is assigned domain head as seen from this node
> + * @is_local: peer is in local domain and should be continuously monitored
> + * @down_cnt: - numbers of other peers which have reported this on lost
> + */
> +struct tipc_peer {
> +     u32 addr;
> +     struct tipc_mon_domain *domain;
> +     struct hlist_node hash;
> +     struct list_head list;
> +     u8 applied;
> +     u8 down_cnt;
> +     bool is_up;
> +     bool is_head;
> +     bool is_local;
> +};
> +
> +struct tipc_monitor {
> +     struct hlist_head peers[NODE_HTABLE_SIZE];
> +     int peer_cnt;
> +     struct tipc_peer *self;
> +     rwlock_t lock;
> +     struct tipc_mon_domain cache;
> +     u16 list_gen;
> +     u16 dom_gen;
> +     struct net *net;
> +     struct timer_list timer;
> +     unsigned long timer_intv;
> +};
> +
> +static struct tipc_monitor *tipc_monitor(struct net *net, int bearer_id)
> +{
> +     return tipc_net(net)->monitors[bearer_id];
> +}
> +
> +const int tipc_max_domain_size = sizeof(struct tipc_mon_domain);
> +
> +/* dom_rec_len(): actual length of domain record for transport
> + */
> +static int dom_rec_len(struct tipc_mon_domain *dom, u16 mcnt)
> +{
> +     return ((void *)&dom->members - (void *)dom) + (mcnt * sizeof(u32));
> +}
> +
> +/* dom_size() : calculate size of own domain based on number of peers
> + */
> +static int dom_size(int peers)
> +{
> +     int i = 0;
> +
> +     while ((i * i) < peers)
> +             i++;
> +     return i < MAX_MON_DOMAIN ? i : MAX_MON_DOMAIN;
> +}
> +
> +static void map_set(u64 *up_map, int i, unsigned int v)
> +{
> +     *up_map &= ~(1 << i);
> +     *up_map |= (v << i);
> +}
> +
> +static int map_get(u64 up_map, int i)
> +{
> +     return (up_map & (1 << i)) >> i;
> +}
> +
> +static struct tipc_peer *peer_prev(struct tipc_peer *peer)
> +{
> +     return list_last_entry(&peer->list, struct tipc_peer, list);
> +}
> +
> +static struct tipc_peer *peer_nxt(struct tipc_peer *peer)
> +{
> +     return list_first_entry(&peer->list, struct tipc_peer, list);
> +}
> +
> +static struct tipc_peer *peer_head(struct tipc_peer *peer)
> +{
> +     while (!peer->is_head)
> +             peer = peer_prev(peer);
> +     return peer;
> +}
> +
> +static struct tipc_peer *get_peer(struct tipc_monitor *mon, u32 addr)
> +{
> +     struct tipc_peer *peer;
> +     unsigned int thash = tipc_hashfn(addr);
> +
> +     hlist_for_each_entry(peer, &mon->peers[thash], hash) {
> +             if (peer->addr == addr)
> +                     return peer;
> +     }
> +     return NULL;
> +}
> +
> +static struct tipc_peer *get_self(struct net *net, int bearer_id)
> +{
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +
> +     return mon->self;
> +}
> +
> +static inline bool tipc_mon_is_active(struct net *net, struct tipc_monitor 
> *mon)
> +{
> +     struct tipc_net *tn = tipc_net(net);
> +
> +     return mon->peer_cnt > tn->mon_threshold;
> +}
> +
> +/* mon_identify_lost_members() : - identify amd mark potentially lost
> members
> + */
> +static void mon_identify_lost_members(struct tipc_peer *peer,
> +                                   struct tipc_mon_domain *dom_bef,
> +                                   int applied_bef)
> +{
> +     struct tipc_peer *member = peer;
> +     struct tipc_mon_domain *dom_aft = peer->domain;
> +     int applied_aft = peer->applied;
> +     int i;
> +
> +     for (i = 0; i < applied_bef; i++) {
> +             member = peer_nxt(member);
> +
> +             /* Do nothing if self or peer already see member as down */
> +             if (!member->is_up || !map_get(dom_bef->up_map, i))
> +                     continue;
> +
> +             /* Loss of local node must be detected by active probing */
> +             if (member->is_local)
> +                     continue;
> +
> +             /* Start probing if member was removed from applied domain */
> +             if (!applied_aft || (applied_aft < i)) {
> +                     member->down_cnt = 1;
> +                     continue;
> +             }
> +
> +             /* Member loss is confirmed if it is still in applied domain */
> +             if (!map_get(dom_aft->up_map, i))
> +                     member->down_cnt++;
> +     }
> +}
> +
> +/* mon_apply_domain() : match a peer's domain record against monitor list
> + */
> +static void mon_apply_domain(struct tipc_monitor *mon,
> +                          struct tipc_peer *peer)
> +{
> +     struct tipc_mon_domain *dom = peer->domain;
> +     struct tipc_peer *member;
> +     u32 addr;
> +     int i;
> +
> +     if (!dom || !peer->is_up)
> +             return;
> +
> +     /* Scan across domain members and match against monitor list */
> +     peer->applied = 0;
> +     member = peer_nxt(peer);
> +     for (i = 0; i < dom->member_cnt; i++) {
> +             addr = dom->members[i];
> +             if (addr != member->addr)
> +                     return;
> +             peer->applied++;
> +             member = peer_nxt(member);
> +     }
> +}
> +
> +/* mon_update_local_domain() : update after peer addition/removal/up/down
> + */
> +static void mon_update_local_domain(struct tipc_monitor *mon)
> +{
> +     struct tipc_peer *self = mon->self;
> +     struct tipc_mon_domain *cache = &mon->cache;
> +     struct tipc_mon_domain *dom = self->domain;
> +     struct tipc_peer *peer = self;
> +     u64 prev_up_map = dom->up_map;
> +     u16 member_cnt, i;
> +     bool diff;
> +
> +     /* Update local domain size based on current size of cluster */
> +     member_cnt = dom_size(mon->peer_cnt) - 1;
> +     self->applied = member_cnt;
> +
> +     /* Update native and cached outgoing local domain records */
> +     dom->len = dom_rec_len(dom, member_cnt);
> +     diff = dom->member_cnt != member_cnt;
> +     dom->member_cnt = member_cnt;
> +     for (i = 0; i < member_cnt; i++) {
> +             peer = peer_nxt(peer);
> +             diff |= dom->members[i] != peer->addr;
> +             dom->members[i] = peer->addr;
> +             map_set(&dom->up_map, i, peer->is_up);
> +             cache->members[i] = htonl(peer->addr);
> +     }
> +     diff |= dom->up_map != prev_up_map;
> +     if (!diff)
> +             return;
> +     dom->gen = ++mon->dom_gen;
> +     cache->len = htons(dom->len);
> +     cache->gen = htons(dom->gen);
> +     cache->member_cnt = htons(member_cnt);
> +     cache->up_map = cpu_to_be64(dom->up_map);
> +     mon_apply_domain(mon, self);
> +}
> +
> +/* mon_update_neighbors() : update preceding neighbors of added/removed
> peer
> + */
> +static void mon_update_neighbors(struct tipc_monitor *mon,
> +                              struct tipc_peer *peer)
> +{
> +     int dz, i;
> +
> +     dz = dom_size(mon->peer_cnt);
> +     for (i = 0; i < dz; i++) {
> +             mon_apply_domain(mon, peer);
> +             peer = peer_prev(peer);
> +     }
> +}
> +
> +/* mon_assign_roles() : reassign peer roles after a network change
> + * The monitor list is consistent at this stage; i.e., each peer is 
> monitoring
> + * a set of domain members as matched between domain record and the
> monitor list
> + */
> +static void mon_assign_roles(struct tipc_monitor *mon, struct tipc_peer 
> *head)
> +{
> +     struct tipc_peer *peer = peer_nxt(head);
> +     struct tipc_peer *self = mon->self;
> +     int i = 0;
> +
> +     for (; peer != self; peer = peer_nxt(peer)) {
> +             peer->is_local = false;
> +
> +             /* Update domain member */
> +             if (i++ < head->applied) {
> +                     peer->is_head = false;
> +                     if (head == self)
> +                             peer->is_local = true;
> +                     continue;
> +             }
> +             /* Assign next domain head */
> +             if (!peer->is_up)
> +                     continue;
> +             if (peer->is_head)
> +                     break;
> +             head = peer;
> +             head->is_head = true;
> +             i = 0;
> +     }
> +     mon->list_gen++;
> +}
> +
> +void tipc_mon_remove_peer(struct net *net, u32 addr, int bearer_id)
> +{
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +     struct tipc_peer *self = get_self(net, bearer_id);
> +     struct tipc_peer *peer, *prev, *head;
> +
> +     write_lock_bh(&mon->lock);
> +     peer = get_peer(mon, addr);
> +     if (!peer)
> +             goto exit;
> +     prev = peer_prev(peer);
> +     list_del(&peer->list);
> +     hlist_del(&peer->hash);
> +     kfree(peer->domain);
> +     kfree(peer);
> +     mon->peer_cnt--;
> +     head = peer_head(prev);
> +     if (head == self)
> +             mon_update_local_domain(mon);
> +     mon_update_neighbors(mon, prev);
> +
> +     /* Revert to full-mesh monitoring if we reach threshold */
> +     if (!tipc_mon_is_active(net, mon)) {
> +             list_for_each_entry(peer, &self->list, list) {
> +                     kfree(peer->domain);
> +                     peer->domain = NULL;
> +                     peer->applied = 0;
> +             }
> +     }
> +     mon_assign_roles(mon, head);
> +exit:
> +     write_unlock_bh(&mon->lock);
> +}
> +
> +static bool tipc_mon_add_peer(struct tipc_monitor *mon, u32 addr,
> +                           struct tipc_peer **peer)
> +{
> +     struct tipc_peer *self = mon->self;
> +     struct tipc_peer *cur, *prev, *p;
> +
> +     p = kzalloc(sizeof(*p), GFP_ATOMIC);
> +     *peer = p;
> +     if (!p)
> +             return false;
> +     p->addr = addr;
> +
> +     /* Add new peer to lookup list */
> +     INIT_LIST_HEAD(&p->list);
> +     hlist_add_head(&p->hash, &mon->peers[tipc_hashfn(addr)]);
> +
> +     /* Sort new peer into iterator list, in ascending circular order */
> +     prev = self;
> +     list_for_each_entry(cur, &self->list, list) {
> +             if ((addr > prev->addr) && (addr < cur->addr))
> +                     break;
> +             if (((addr < cur->addr) || (addr > prev->addr)) &&
> +                 (prev->addr > cur->addr))
> +                     break;
> +             prev = cur;
> +     }
> +     list_add_tail(&p->list, &cur->list);
> +     mon->peer_cnt++;
> +     mon_update_neighbors(mon, p);
> +     return true;
> +}
> +
> +void tipc_mon_peer_up(struct net *net, u32 addr, int bearer_id)
> +{
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +     struct tipc_peer *self = get_self(net, bearer_id);
> +     struct tipc_peer *peer, *head;
> +
> +     write_lock_bh(&mon->lock);
> +     peer = get_peer(mon, addr);
> +     if (!peer && !tipc_mon_add_peer(mon, addr, &peer))
> +             goto exit;
> +     peer->is_up = true;
> +     head = peer_head(peer);
> +     if (head == self)
> +             mon_update_local_domain(mon);
> +     mon_assign_roles(mon, head);
> +exit:
> +     write_unlock_bh(&mon->lock);
> +}
> +
> +void tipc_mon_peer_down(struct net *net, u32 addr, int bearer_id)
> +{
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +     struct tipc_peer *self = get_self(net, bearer_id);
> +     struct tipc_peer *peer, *head;
> +     struct tipc_mon_domain *dom;
> +     int applied;
> +
> +     write_lock_bh(&mon->lock);
> +     peer = get_peer(mon, addr);
> +     if (!peer) {
> +             pr_warn("Mon: unknown link %x/%u DOWN\n", addr,
> bearer_id);
> +             goto exit;
> +     }
> +     applied = peer->applied;
> +     peer->applied = 0;
> +     dom = peer->domain;
> +     peer->domain = NULL;
> +     if (peer->is_head)
> +             mon_identify_lost_members(peer, dom, applied);
> +     kfree(dom);
> +     peer->is_up = false;
> +     peer->is_head = false;
> +     peer->is_local = false;
> +     peer->down_cnt = 0;
> +     head = peer_head(peer);
> +     if (head == self)
> +             mon_update_local_domain(mon);
> +     mon_assign_roles(mon, head);
> +exit:
> +     write_unlock_bh(&mon->lock);
> +}
> +
> +/* tipc_mon_rcv - process monitor domain event message
> + */
> +void tipc_mon_rcv(struct net *net, void *data, u16 dlen, u32 addr,
> +               struct tipc_mon_state *state, int bearer_id)
> +{
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +     struct tipc_mon_domain *arrv_dom = data;
> +     struct tipc_mon_domain dom_bef;
> +     struct tipc_mon_domain *dom;
> +     struct tipc_peer *peer;
> +     u16 new_member_cnt = ntohs(arrv_dom->member_cnt);
> +     int new_dlen = dom_rec_len(arrv_dom, new_member_cnt);
> +     u16 new_gen = ntohs(arrv_dom->gen);
> +     u16 acked_gen = ntohs(arrv_dom->ack_gen);
> +     bool probing = state->probing;
> +     int i, applied_bef;
> +
> +     state->probing = false;
> +     if (!dlen)
> +             return;
> +
> +     /* Sanity check received domain record */
> +     if ((dlen != ntohs(arrv_dom->len)) || (dlen != new_dlen)) {
> +             pr_warn_ratelimited("Received illegal domain record\n");
> +             return;
> +     }
> +
> +     /* Synch generation numbers with peer if link just came up */
> +     if (!state->synched) {
> +             state->peer_gen = new_gen - 1;
> +             state->acked_gen = acked_gen;
> +             state->synched = true;
> +     }
> +
> +     if (more(acked_gen, state->acked_gen))
> +             state->acked_gen = acked_gen;
> +
> +     /* Drop duplicate unless we are waiting for a probe response */
> +     if (!more(new_gen, state->peer_gen) && !probing)
> +             return;
> +
> +     write_lock_bh(&mon->lock);
> +     peer = get_peer(mon, addr);
> +     if (!peer || !peer->is_up)
> +             goto exit;
> +
> +     /* Peer is confirmed, stop any ongoing probing */
> +     peer->down_cnt = 0;
> +
> +     /* Task is done for duplicate record */
> +     if (!more(new_gen, state->peer_gen))
> +             goto exit;
> +
> +     state->peer_gen = new_gen;
> +
> +     /* Cache current domain record for later use */
> +     dom_bef.member_cnt = 0;
> +     dom = peer->domain;
> +     if (dom)
> +             memcpy(&dom_bef, dom, dom->len);
> +
> +     /* Transform and store received domain record */
> +     if (!dom || (dom->len < new_dlen)) {
> +             kfree(dom);
> +             dom = kmalloc(new_dlen, GFP_ATOMIC);
> +             peer->domain = dom;
> +             if (!dom)
> +                     goto exit;
> +     }
> +     dom->len = new_dlen;
> +     dom->gen = new_gen;
> +     dom->member_cnt = new_member_cnt;
> +     dom->up_map = be64_to_cpu(arrv_dom->up_map);
> +     for (i = 0; i < new_member_cnt; i++)
> +             dom->members[i] = ntohl(arrv_dom->members[i]);
> +
> +     /* Update peers affected by this domain record */
> +     applied_bef = peer->applied;
> +     mon_apply_domain(mon, peer);
> +     mon_identify_lost_members(peer, &dom_bef, applied_bef);
> +     mon_assign_roles(mon, peer_head(peer));
> +exit:
> +     write_unlock_bh(&mon->lock);
> +}
> +
> +void tipc_mon_prep(struct net *net, void *data, int *dlen,
> +                struct tipc_mon_state *state, int bearer_id)
> +{
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +     struct tipc_mon_domain *dom = data;
> +     u16 gen = mon->dom_gen;
> +     u16 len;
> +
> +     if (!tipc_mon_is_active(net, mon))
> +             return;
> +
> +     /* Send only a dummy record with ack if peer has acked our last sent */
> +     if (likely(state->acked_gen == gen)) {
> +             len = dom_rec_len(dom, 0);
> +             *dlen = len;
> +             dom->len = htons(len);
> +             dom->gen = htons(gen);
> +             dom->ack_gen = htons(state->peer_gen);
> +             dom->member_cnt = 0;
> +             return;
> +     }
> +     /* Send the full record */
> +     read_lock_bh(&mon->lock);
> +     len = ntohs(mon->cache.len);
> +     *dlen = len;
> +     memcpy(data, &mon->cache, len);
> +     read_unlock_bh(&mon->lock);
> +     dom->ack_gen = htons(state->peer_gen);
> +}
> +
> +void tipc_mon_get_state(struct net *net, u32 addr,
> +                     struct tipc_mon_state *state,
> +                     int bearer_id)
> +{
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +     struct tipc_peer *peer;
> +
> +     /* Used cached state if table has not changed */
> +     if (!state->probing &&
> +         (state->list_gen == mon->list_gen) &&
> +         (state->acked_gen == mon->dom_gen))
> +             return;
> +
> +     read_lock_bh(&mon->lock);
> +     peer = get_peer(mon, addr);
> +     if (peer) {
> +             state->probing = state->acked_gen != mon->dom_gen;
> +             state->probing |= peer->down_cnt;
> +             state->reset |= peer->down_cnt >= MAX_PEER_DOWN_EVENTS;
> +             state->monitoring = peer->is_local;
> +             state->monitoring |= peer->is_head;
> +             state->list_gen = mon->list_gen;
> +     }
> +     read_unlock_bh(&mon->lock);
> +}
> +
> +static void mon_timeout(unsigned long m)
> +{
> +     struct tipc_monitor *mon = (void *)m;
> +     struct tipc_peer *self;
> +     int best_member_cnt = dom_size(mon->peer_cnt) - 1;
> +
> +     write_lock_bh(&mon->lock);
> +     self = mon->self;
> +     if (self && (best_member_cnt != self->applied)) {
> +             mon_update_local_domain(mon);
> +             mon_assign_roles(mon, self);
> +     }
> +     write_unlock_bh(&mon->lock);
> +     mod_timer(&mon->timer, jiffies + mon->timer_intv);
> +}
> +
> +int tipc_mon_create(struct net *net, int bearer_id)
> +{
> +     struct tipc_net *tn = tipc_net(net);
> +     struct tipc_monitor *mon;
> +     struct tipc_peer *self;
> +     struct tipc_mon_domain *dom;
> +
> +     if (tn->monitors[bearer_id])
> +             return 0;
> +
> +     mon = kzalloc(sizeof(*mon), GFP_ATOMIC);
> +     self = kzalloc(sizeof(*self), GFP_ATOMIC);
> +     dom = kzalloc(sizeof(*dom), GFP_ATOMIC);
> +     if (!mon || !self || !dom) {
> +             kfree(mon);
> +             kfree(self);
> +             kfree(dom);
> +             return -ENOMEM;
> +     }
> +     tn->monitors[bearer_id] = mon;
> +     rwlock_init(&mon->lock);
> +     mon->net = net;
> +     mon->peer_cnt = 1;
> +     mon->self = self;
> +     self->domain = dom;
> +     self->addr = tipc_own_addr(net);
> +     self->is_up = true;
> +     self->is_head = true;
> +     INIT_LIST_HEAD(&self->list);
> +     setup_timer(&mon->timer, mon_timeout, (unsigned long)mon);
> +     mon->timer_intv = msecs_to_jiffies(MON_TIMEOUT + (tn->random &
> 0xffff));
> +     mod_timer(&mon->timer, jiffies + mon->timer_intv);
> +     return 0;
> +}
> +
> +void tipc_mon_delete(struct net *net, int bearer_id)
> +{
> +     struct tipc_net *tn = tipc_net(net);
> +     struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> +     struct tipc_peer *self = get_self(net, bearer_id);
> +     struct tipc_peer *peer, *tmp;
> +
> +     write_lock_bh(&mon->lock);
> +     tn->monitors[bearer_id] = NULL;
> +     list_for_each_entry_safe(peer, tmp, &self->list, list) {
> +             list_del(&peer->list);
> +             hlist_del(&peer->hash);
> +             kfree(peer->domain);
> +             kfree(peer);
> +     }
> +     mon->self = NULL;
> +     write_unlock_bh(&mon->lock);
> +     del_timer_sync(&mon->timer);
> +     kfree(self->domain);
> +     kfree(self);
> +     kfree(mon);
> +}
> diff --git a/net/tipc/monitor.h b/net/tipc/monitor.h
> new file mode 100644
> index 0000000..598459c
> --- /dev/null
> +++ b/net/tipc/monitor.h
> @@ -0,0 +1,73 @@
> +/*
> + * net/tipc/monitor.h
> + *
> + * Copyright (c) 2015, Ericsson AB
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are 
> met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + * 3. Neither the names of the copyright holders nor the names of its
> + *    contributors may be used to endorse or promote products derived from
> + *    this software without specific prior written permission.
> + *
> + * Alternatively, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL") version 2 as published by the Free
> + * Software Foundation.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
> CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
> BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef _TIPC_MONITOR_H
> +#define _TIPC_MONITOR_H
> +
> +/* struct tipc_mon_state: link instance's cache of monitor list and domain 
> state
> + * @list_gen: current generation of this node's monitor list
> + * @gen: current generation of this node's local domain
> + * @peer_gen: most recent domain generation received from peer
> + * @acked_gen: most recent generation of self's domain acked by peer
> + * @monitoring: this peer endpoint should continuously monitored
> + * @probing: peer endpoint should be temporarily probed for potential loss
> + * @synched: domain record's generation has been synched with peer after
> reset
> + */
> +struct tipc_mon_state {
> +     u16 list_gen;
> +     u16 peer_gen;
> +     u16 acked_gen;
> +     bool monitoring :1;
> +     bool probing    :1;
> +     bool reset      :1;
> +     bool synched    :1;
> +};
> +
> +int tipc_mon_create(struct net *net, int bearer_id);
> +void tipc_mon_delete(struct net *net, int bearer_id);
> +
> +void tipc_mon_peer_up(struct net *net, u32 addr, int bearer_id);
> +void tipc_mon_peer_down(struct net *net, u32 addr, int bearer_id);
> +void tipc_mon_prep(struct net *net, void *data, int *dlen,
> +                struct tipc_mon_state *state, int bearer_id);
> +void tipc_mon_rcv(struct net *net, void *data, u16 dlen, u32 addr,
> +               struct tipc_mon_state *state, int bearer_id);
> +void tipc_mon_get_state(struct net *net, u32 addr,
> +                     struct tipc_mon_state *state,
> +                     int bearer_id);
> +void tipc_mon_remove_peer(struct net *net, u32 addr, int bearer_id);
> +
> +extern const int tipc_max_domain_size;
> +#endif
> diff --git a/net/tipc/node.c b/net/tipc/node.c
> index d6a490f..a3fc0a3 100644
> --- a/net/tipc/node.c
> +++ b/net/tipc/node.c
> @@ -40,6 +40,7 @@
>  #include "name_distr.h"
>  #include "socket.h"
>  #include "bcast.h"
> +#include "monitor.h"
>  #include "discover.h"
>  #include "netlink.h"
> 
> @@ -205,17 +206,6 @@ u16 tipc_node_get_capabilities(struct net *net, u32
> addr)
>       return caps;
>  }
> 
> -/*
> - * A trivial power-of-two bitmask technique is used for speed, since this
> - * operation is done for every incoming TIPC packet. The number of hash table
> - * entries has been chosen so that no hash chain exceeds 8 nodes and will
> - * usually be much smaller (typically only a single node).
> - */
> -static unsigned int tipc_hashfn(u32 addr)
> -{
> -     return addr & (NODE_HTABLE_SIZE - 1);
> -}
> -
>  static void tipc_node_kref_release(struct kref *kref)
>  {
>       struct tipc_node *n = container_of(kref, struct tipc_node, kref);
> @@ -279,6 +269,7 @@ static void tipc_node_write_unlock(struct tipc_node *n)
>       u32 addr = 0;
>       u32 flags = n->action_flags;
>       u32 link_id = 0;
> +     u32 bearer_id;
>       struct list_head *publ_list;
> 
>       if (likely(!flags)) {
> @@ -288,6 +279,7 @@ static void tipc_node_write_unlock(struct tipc_node *n)
> 
>       addr = n->addr;
>       link_id = n->link_id;
> +     bearer_id = link_id & 0xffff;
>       publ_list = &n->publ_list;
> 
>       n->action_flags &= ~(TIPC_NOTIFY_NODE_DOWN |
> TIPC_NOTIFY_NODE_UP |
> @@ -301,13 +293,16 @@ static void tipc_node_write_unlock(struct tipc_node
> *n)
>       if (flags & TIPC_NOTIFY_NODE_UP)
>               tipc_named_node_up(net, addr);
> 
> -     if (flags & TIPC_NOTIFY_LINK_UP)
> +     if (flags & TIPC_NOTIFY_LINK_UP) {
> +             tipc_mon_peer_up(net, addr, bearer_id);
>               tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,
>                                    TIPC_NODE_SCOPE, link_id, addr);
> -
> -     if (flags & TIPC_NOTIFY_LINK_DOWN)
> +     }
> +     if (flags & TIPC_NOTIFY_LINK_DOWN) {
> +             tipc_mon_peer_down(net, addr, bearer_id);
>               tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
>                                     link_id, addr);
> +     }
>  }
> 
>  struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 
> capabilities)
> @@ -691,6 +686,7 @@ static void tipc_node_link_down(struct tipc_node *n, int
> bearer_id, bool delete)
>       struct tipc_link *l = le->link;
>       struct tipc_media_addr *maddr;
>       struct sk_buff_head xmitq;
> +     int old_bearer_id = bearer_id;
> 
>       if (!l)
>               return;
> @@ -710,6 +706,8 @@ static void tipc_node_link_down(struct tipc_node *n, int
> bearer_id, bool delete)
>               tipc_link_fsm_evt(l, LINK_RESET_EVT);
>       }
>       tipc_node_write_unlock(n);
> +     if (delete)
> +             tipc_mon_remove_peer(n->net, n->addr, old_bearer_id);
>       tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr);
>       tipc_sk_rcv(n->net, &le->inputq);
>  }
> --
> 1.9.1


------------------------------------------------------------------------------
What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic
patterns at an interface-level. Reveals which users, apps, and protocols are 
consuming the most bandwidth. Provides multi-vendor support for NetFlow, 
J-Flow, sFlow and other flows. Make informed decisions using capacity 
planning reports. https://ad.doubleclick.net/ddm/clk/305295220;132659582;e
_______________________________________________
tipc-discussion mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tipc-discussion

Reply via email to