]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - net/tipc/link.c
tipc: eliminate remnants of hungarian notation
[mirror_ubuntu-zesty-kernel.git] / net / tipc / link.c
index 9efbdbde2b0863542a08c91c136fadc9aa0cc6d8..b11afe71dfc18b9e643e0f00672b1977424581ec 100644 (file)
 
 #include <linux/pkt_sched.h>
 
+struct tipc_stats {
+       u32 sent_info;          /* used in counting # sent packets */
+       u32 recv_info;          /* used in counting # recv'd packets */
+       u32 sent_states;
+       u32 recv_states;
+       u32 sent_probes;
+       u32 recv_probes;
+       u32 sent_nacks;
+       u32 recv_nacks;
+       u32 sent_acks;
+       u32 sent_bundled;
+       u32 sent_bundles;
+       u32 recv_bundled;
+       u32 recv_bundles;
+       u32 retransmitted;
+       u32 sent_fragmented;
+       u32 sent_fragments;
+       u32 recv_fragmented;
+       u32 recv_fragments;
+       u32 link_congs;         /* # port sends blocked by congestion */
+       u32 deferred_recv;
+       u32 duplicates;
+       u32 max_queue_sz;       /* send queue size high water mark */
+       u32 accu_queue_sz;      /* used for send queue size profiling */
+       u32 queue_sz_counts;    /* used for send queue size profiling */
+       u32 msg_length_counts;  /* used for message length profiling */
+       u32 msg_lengths_total;  /* used for message length profiling */
+       u32 msg_length_profile[7]; /* used for msg. length profiling */
+};
+
+/**
+ * struct tipc_link - TIPC link data structure
+ * @addr: network address of link's peer node
+ * @name: link name character string
+ * @media_addr: media address to use when sending messages over link
+ * @timer: link timer
+ * @net: pointer to namespace struct
+ * @refcnt: reference counter for permanent references (owner node & timer)
+ * @peer_session: link session # being used by peer end of link
+ * @peer_bearer_id: bearer id used by link's peer endpoint
+ * @bearer_id: local bearer id used by link
+ * @tolerance: minimum link continuity loss needed to reset link [in ms]
+ * @keepalive_intv: link keepalive timer interval
+ * @abort_limit: # of unacknowledged continuity probes needed to reset link
+ * @state: current state of link FSM
+ * @peer_caps: bitmap describing capabilities of peer node
+ * @silent_intv_cnt: # of timer intervals without any reception from peer
+ * @proto_msg: template for control messages generated by link
+ * @pmsg: convenience pointer to "proto_msg" field
+ * @priority: current link priority
+ * @net_plane: current link network plane ('A' through 'H')
+ * @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
+ * @mtu: current maximum packet size for this link
+ * @advertised_mtu: advertised own mtu when link is being established
+ * @transmitq: queue for sent, non-acked messages
+ * @backlogq: queue for messages waiting to be sent
+ * @snt_nxt: next sequence number to use for outbound messages
+ * @last_retransmitted: sequence number of most recently retransmitted message
+ * @stale_count: # of identical retransmit requests made by peer
+ * @ackers: # of peers that needs to ack each packet before it can be released
+ * @acked: # last packet acked by a certain peer. Used for broadcast.
+ * @rcv_nxt: next sequence number to expect for inbound messages
+ * @deferred_queue: deferred queue saved OOS b'cast message received from node
+ * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
+ * @inputq: buffer queue for messages to be delivered upwards
+ * @namedq: buffer queue for name table messages to be delivered upwards
+ * @next_out: ptr to first unsent outbound message in queue
+ * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
+ * @long_msg_seq_no: next identifier to use for outbound fragmented messages
+ * @reasm_buf: head of partially reassembled inbound message fragments
+ * @bc_rcvr: marks that this is a broadcast receiver link
+ * @stats: collects statistics regarding link activity
+ */
+struct tipc_link {
+       u32 addr;
+       char name[TIPC_MAX_LINK_NAME];
+       struct tipc_media_addr *media_addr;
+       struct net *net;
+
+       /* Management and link supervision data */
+       u32 peer_session;
+       u32 peer_bearer_id;
+       u32 bearer_id;
+       u32 tolerance;
+       unsigned long keepalive_intv;
+       u32 abort_limit;
+       u32 state;
+       u16 peer_caps;
+       bool active;
+       u32 silent_intv_cnt;
+       struct {
+               unchar hdr[INT_H_SIZE];
+               unchar body[TIPC_MAX_IF_NAME];
+       } proto_msg;
+       struct tipc_msg *pmsg;
+       u32 priority;
+       char net_plane;
+
+       /* Failover/synch */
+       u16 drop_point;
+       struct sk_buff *failover_reasm_skb;
+
+       /* Max packet negotiation */
+       u16 mtu;
+       u16 advertised_mtu;
+
+       /* Sending */
+       struct sk_buff_head transmq;
+       struct sk_buff_head backlogq;
+       struct {
+               u16 len;
+               u16 limit;
+       } backlog[5];
+       u16 snd_nxt;
+       u16 last_retransm;
+       u16 window;
+       u32 stale_count;
+
+       /* Reception */
+       u16 rcv_nxt;
+       u32 rcv_unacked;
+       struct sk_buff_head deferdq;
+       struct sk_buff_head *inputq;
+       struct sk_buff_head *namedq;
+
+       /* Congestion handling */
+       struct sk_buff_head wakeupq;
+
+       /* Fragmentation/reassembly */
+       struct sk_buff *reasm_buf;
+
+       /* Broadcast */
+       u16 ackers;
+       u16 acked;
+       struct tipc_link *bc_rcvlink;
+       struct tipc_link *bc_sndlink;
+       int nack_state;
+       bool bc_peer_is_up;
+
+       /* Statistics */
+       struct tipc_stats stats;
+};
+
 /*
  * Error message prefixes
  */
 static const char *link_co_err = "Link tunneling error, ";
 static const char *link_rst_msg = "Resetting link ";
-static const char tipc_bclink_name[] = "broadcast-link";
-
-static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
-       [TIPC_NLA_LINK_UNSPEC]          = { .type = NLA_UNSPEC },
-       [TIPC_NLA_LINK_NAME] = {
-               .type = NLA_STRING,
-               .len = TIPC_MAX_LINK_NAME
-       },
-       [TIPC_NLA_LINK_MTU]             = { .type = NLA_U32 },
-       [TIPC_NLA_LINK_BROADCAST]       = { .type = NLA_FLAG },
-       [TIPC_NLA_LINK_UP]              = { .type = NLA_FLAG },
-       [TIPC_NLA_LINK_ACTIVE]          = { .type = NLA_FLAG },
-       [TIPC_NLA_LINK_PROP]            = { .type = NLA_NESTED },
-       [TIPC_NLA_LINK_STATS]           = { .type = NLA_NESTED },
-       [TIPC_NLA_LINK_RX]              = { .type = NLA_U32 },
-       [TIPC_NLA_LINK_TX]              = { .type = NLA_U32 }
-};
 
 /* Properties valid for media, bearar and link */
 static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
@@ -117,8 +245,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
 static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
                                      u16 rcvgap, int tolerance, int priority,
                                      struct sk_buff_head *xmitq);
-static void link_reset_statistics(struct tipc_link *l_ptr);
-static void link_print(struct tipc_link *l_ptr, const char *str);
+static void link_print(struct tipc_link *l, const char *str);
 static void tipc_link_build_nack_msg(struct tipc_link *l,
                                     struct sk_buff_head *xmitq);
 static void tipc_link_build_bc_init_msg(struct tipc_link *l,
@@ -183,6 +310,36 @@ void tipc_link_set_active(struct tipc_link *l, bool active)
        l->active = active;
 }
 
+u32 tipc_link_id(struct tipc_link *l)
+{
+       return l->peer_bearer_id << 16 | l->bearer_id;
+}
+
+int tipc_link_window(struct tipc_link *l)
+{
+       return l->window;
+}
+
+int tipc_link_prio(struct tipc_link *l)
+{
+       return l->priority;
+}
+
+unsigned long tipc_link_tolerance(struct tipc_link *l)
+{
+       return l->tolerance;
+}
+
+struct sk_buff_head *tipc_link_inputq(struct tipc_link *l)
+{
+       return l->inputq;
+}
+
+char tipc_link_plane(struct tipc_link *l)
+{
+       return l->net_plane;
+}
+
 void tipc_link_add_bc_peer(struct tipc_link *snd_l,
                           struct tipc_link *uc_l,
                           struct sk_buff_head *xmitq)
@@ -225,11 +382,31 @@ int tipc_link_mtu(struct tipc_link *l)
        return l->mtu;
 }
 
+u16 tipc_link_rcv_nxt(struct tipc_link *l)
+{
+       return l->rcv_nxt;
+}
+
+u16 tipc_link_acked(struct tipc_link *l)
+{
+       return l->acked;
+}
+
+char *tipc_link_name(struct tipc_link *l)
+{
+       return l->name;
+}
+
 static u32 link_own_addr(struct tipc_link *l)
 {
        return msg_prevnode(l->pmsg);
 }
 
+void tipc_link_reinit(struct tipc_link *l, u32 addr)
+{
+       msg_set_prevnode(l->pmsg, addr);
+}
+
 /**
  * tipc_link_create - create a new link
  * @n: pointer to associated node
@@ -692,7 +869,7 @@ void tipc_link_reset(struct tipc_link *l)
        l->stats.recv_info = 0;
        l->stale_count = 0;
        l->bc_peer_is_up = false;
-       link_reset_statistics(l);
+       tipc_link_reset_stats(l);
 }
 
 /**
@@ -1085,8 +1262,9 @@ drop:
 /*
  * Send protocol message to the other endpoint.
  */
-void tipc_link_proto_xmit(struct tipc_link *l, u32 msg_typ, int probe_msg,
-                         u32 gap, u32 tolerance, u32 priority)
+static void tipc_link_proto_xmit(struct tipc_link *l, u32 msg_typ,
+                                int probe_msg, u32 gap, u32 tolerance,
+                                u32 priority)
 {
        struct sk_buff *skb = NULL;
        struct sk_buff_head xmitq;
@@ -1260,6 +1438,8 @@ 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;
@@ -1525,53 +1705,17 @@ void tipc_link_set_queue_limits(struct tipc_link *l, u32 win)
        l->backlog[TIPC_SYSTEM_IMPORTANCE].limit   = max_bulk;
 }
 
-/* tipc_link_find_owner - locate owner node of link by link's name
- * @net: the applicable net namespace
- * @name: pointer to link name string
- * @bearer_id: pointer to index in 'node->links' array where the link was found.
- *
- * Returns pointer to node owning the link, or 0 if no matching link is found.
- */
-static struct tipc_node *tipc_link_find_owner(struct net *net,
-                                             const char *link_name,
-                                             unsigned int *bearer_id)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_link *l_ptr;
-       struct tipc_node *n_ptr;
-       struct tipc_node *found_node = NULL;
-       int i;
-
-       *bearer_id = 0;
-       rcu_read_lock();
-       list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
-               tipc_node_lock(n_ptr);
-               for (i = 0; i < MAX_BEARERS; i++) {
-                       l_ptr = n_ptr->links[i].link;
-                       if (l_ptr && !strcmp(l_ptr->name, link_name)) {
-                               *bearer_id = i;
-                               found_node = n_ptr;
-                               break;
-                       }
-               }
-               tipc_node_unlock(n_ptr);
-               if (found_node)
-                       break;
-       }
-       rcu_read_unlock();
-
-       return found_node;
-}
-
 /**
- * link_reset_statistics - reset link statistics
- * @l_ptr: pointer to link
+ * link_reset_stats - reset link statistics
+ * @l: pointer to link
  */
-static void link_reset_statistics(struct tipc_link *l_ptr)
+void tipc_link_reset_stats(struct tipc_link *l)
 {
-       memset(&l_ptr->stats, 0, sizeof(l_ptr->stats));
-       l_ptr->stats.sent_info = l_ptr->snd_nxt;
-       l_ptr->stats.recv_info = l_ptr->rcv_nxt;
+       memset(&l->stats, 0, sizeof(l->stats));
+       if (!link_is_bc_sndlink(l)) {
+               l->stats.sent_info = l->snd_nxt;
+               l->stats.recv_info = l->rcv_nxt;
+       }
 }
 
 static void link_print(struct tipc_link *l, const char *str)
@@ -1624,84 +1768,6 @@ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])
        return 0;
 }
 
-int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)
-{
-       int err;
-       int res = 0;
-       int bearer_id;
-       char *name;
-       struct tipc_link *link;
-       struct tipc_node *node;
-       struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
-       struct net *net = sock_net(skb->sk);
-
-       if (!info->attrs[TIPC_NLA_LINK])
-               return -EINVAL;
-
-       err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
-                              info->attrs[TIPC_NLA_LINK],
-                              tipc_nl_link_policy);
-       if (err)
-               return err;
-
-       if (!attrs[TIPC_NLA_LINK_NAME])
-               return -EINVAL;
-
-       name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
-
-       if (strcmp(name, tipc_bclink_name) == 0)
-               return tipc_nl_bc_link_set(net, attrs);
-
-       node = tipc_link_find_owner(net, name, &bearer_id);
-       if (!node)
-               return -EINVAL;
-
-       tipc_node_lock(node);
-
-       link = node->links[bearer_id].link;
-       if (!link) {
-               res = -EINVAL;
-               goto out;
-       }
-
-       if (attrs[TIPC_NLA_LINK_PROP]) {
-               struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
-
-               err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP],
-                                             props);
-               if (err) {
-                       res = err;
-                       goto out;
-               }
-
-               if (props[TIPC_NLA_PROP_TOL]) {
-                       u32 tol;
-
-                       tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
-                       link->tolerance = tol;
-                       tipc_link_proto_xmit(link, STATE_MSG, 0, 0, tol, 0);
-               }
-               if (props[TIPC_NLA_PROP_PRIO]) {
-                       u32 prio;
-
-                       prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
-                       link->priority = prio;
-                       tipc_link_proto_xmit(link, STATE_MSG, 0, 0, 0, prio);
-               }
-               if (props[TIPC_NLA_PROP_WIN]) {
-                       u32 win;
-
-                       win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
-                       tipc_link_set_queue_limits(link, win);
-               }
-       }
-
-out:
-       tipc_node_unlock(node);
-
-       return res;
-}
-
 static int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s)
 {
        int i;
@@ -1768,8 +1834,8 @@ msg_full:
 }
 
 /* Caller should hold appropriate locks to protect the link */
-static int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
-                             struct tipc_link *link, int nlflags)
+int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
+                      struct tipc_link *link, int nlflags)
 {
        int err;
        void *hdr;
@@ -1838,198 +1904,134 @@ msg_full:
        return -EMSGSIZE;
 }
 
-/* Caller should hold node lock  */
-static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg,
-                                   struct tipc_node *node, u32 *prev_link)
+static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
+                                     struct tipc_stats *stats)
 {
-       u32 i;
-       int err;
-
-       for (i = *prev_link; i < MAX_BEARERS; i++) {
-               *prev_link = i;
-
-               if (!node->links[i].link)
-                       continue;
+       int i;
+       struct nlattr *nest;
 
-               err = __tipc_nl_add_link(net, msg,
-                                        node->links[i].link, NLM_F_MULTI);
-               if (err)
-                       return err;
-       }
-       *prev_link = 0;
+       struct nla_map {
+               __u32 key;
+               __u32 val;
+       };
 
-       return 0;
-}
+       struct nla_map map[] = {
+               {TIPC_NLA_STATS_RX_INFO, stats->recv_info},
+               {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
+               {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
+               {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
+               {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
+               {TIPC_NLA_STATS_TX_INFO, stats->sent_info},
+               {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
+               {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
+               {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
+               {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
+               {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
+               {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
+               {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
+               {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
+               {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
+               {TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
+               {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
+               {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
+               {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
+                       (stats->accu_queue_sz / stats->queue_sz_counts) : 0}
+       };
 
-int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
-       struct net *net = sock_net(skb->sk);
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_node *node;
-       struct tipc_nl_msg msg;
-       u32 prev_node = cb->args[0];
-       u32 prev_link = cb->args[1];
-       int done = cb->args[2];
-       int err;
+       nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS);
+       if (!nest)
+               return -EMSGSIZE;
 
-       if (done)
-               return 0;
+       for (i = 0; i <  ARRAY_SIZE(map); i++)
+               if (nla_put_u32(skb, map[i].key, map[i].val))
+                       goto msg_full;
 
-       msg.skb = skb;
-       msg.portid = NETLINK_CB(cb->skb).portid;
-       msg.seq = cb->nlh->nlmsg_seq;
-
-       rcu_read_lock();
-       if (prev_node) {
-               node = tipc_node_find(net, prev_node);
-               if (!node) {
-                       /* We never set seq or call nl_dump_check_consistent()
-                        * this means that setting prev_seq here will cause the
-                        * consistence check to fail in the netlink callback
-                        * handler. Resulting in the last NLMSG_DONE message
-                        * having the NLM_F_DUMP_INTR flag set.
-                        */
-                       cb->prev_seq = 1;
-                       goto out;
-               }
-               tipc_node_put(node);
-
-               list_for_each_entry_continue_rcu(node, &tn->node_list,
-                                                list) {
-                       tipc_node_lock(node);
-                       err = __tipc_nl_add_node_links(net, &msg, node,
-                                                      &prev_link);
-                       tipc_node_unlock(node);
-                       if (err)
-                               goto out;
-
-                       prev_node = node->addr;
-               }
-       } else {
-               err = tipc_nl_add_bc_link(net, &msg);
-               if (err)
-                       goto out;
-
-               list_for_each_entry_rcu(node, &tn->node_list, list) {
-                       tipc_node_lock(node);
-                       err = __tipc_nl_add_node_links(net, &msg, node,
-                                                      &prev_link);
-                       tipc_node_unlock(node);
-                       if (err)
-                               goto out;
-
-                       prev_node = node->addr;
-               }
-       }
-       done = 1;
-out:
-       rcu_read_unlock();
+       nla_nest_end(skb, nest);
 
-       cb->args[0] = prev_node;
-       cb->args[1] = prev_link;
-       cb->args[2] = done;
+       return 0;
+msg_full:
+       nla_nest_cancel(skb, nest);
 
-       return skb->len;
+       return -EMSGSIZE;
 }
 
-int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)
+int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)
 {
-       struct net *net = genl_info_net(info);
-       struct tipc_nl_msg msg;
-       char *name;
        int err;
+       void *hdr;
+       struct nlattr *attrs;
+       struct nlattr *prop;
+       struct tipc_net *tn = net_generic(net, tipc_net_id);
+       struct tipc_link *bcl = tn->bcl;
 
-       msg.portid = info->snd_portid;
-       msg.seq = info->snd_seq;
-
-       if (!info->attrs[TIPC_NLA_LINK_NAME])
-               return -EINVAL;
-       name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]);
+       if (!bcl)
+               return 0;
 
-       msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (!msg.skb)
-               return -ENOMEM;
+       tipc_bcast_lock(net);
 
-       if (strcmp(name, tipc_bclink_name) == 0) {
-               err = tipc_nl_add_bc_link(net, &msg);
-               if (err) {
-                       nlmsg_free(msg.skb);
-                       return err;
-               }
-       } else {
-               int bearer_id;
-               struct tipc_node *node;
-               struct tipc_link *link;
+       hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
+                         NLM_F_MULTI, TIPC_NL_LINK_GET);
+       if (!hdr)
+               return -EMSGSIZE;
 
-               node = tipc_link_find_owner(net, name, &bearer_id);
-               if (!node)
-                       return -EINVAL;
+       attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK);
+       if (!attrs)
+               goto msg_full;
 
-               tipc_node_lock(node);
-               link = node->links[bearer_id].link;
-               if (!link) {
-                       tipc_node_unlock(node);
-                       nlmsg_free(msg.skb);
-                       return -EINVAL;
-               }
+       /* The broadcast link is always up */
+       if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
+               goto attr_msg_full;
 
-               err = __tipc_nl_add_link(net, &msg, link, 0);
-               tipc_node_unlock(node);
-               if (err) {
-                       nlmsg_free(msg.skb);
-                       return err;
-               }
-       }
+       if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
+               goto attr_msg_full;
+       if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
+               goto attr_msg_full;
+       if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->rcv_nxt))
+               goto attr_msg_full;
+       if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->snd_nxt))
+               goto attr_msg_full;
 
-       return genlmsg_reply(msg.skb, info);
-}
+       prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP);
+       if (!prop)
+               goto attr_msg_full;
+       if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->window))
+               goto prop_msg_full;
+       nla_nest_end(msg->skb, prop);
 
-int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)
-{
-       int err;
-       char *link_name;
-       unsigned int bearer_id;
-       struct tipc_link *link;
-       struct tipc_node *node;
-       struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
-       struct net *net = sock_net(skb->sk);
-
-       if (!info->attrs[TIPC_NLA_LINK])
-               return -EINVAL;
-
-       err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
-                              info->attrs[TIPC_NLA_LINK],
-                              tipc_nl_link_policy);
+       err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
        if (err)
-               return err;
-
-       if (!attrs[TIPC_NLA_LINK_NAME])
-               return -EINVAL;
-
-       link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+               goto attr_msg_full;
 
-       if (strcmp(link_name, tipc_bclink_name) == 0) {
-               err = tipc_bclink_reset_stats(net);
-               if (err)
-                       return err;
-               return 0;
-       }
+       tipc_bcast_unlock(net);
+       nla_nest_end(msg->skb, attrs);
+       genlmsg_end(msg->skb, hdr);
 
-       node = tipc_link_find_owner(net, link_name, &bearer_id);
-       if (!node)
-               return -EINVAL;
+       return 0;
 
-       tipc_node_lock(node);
+prop_msg_full:
+       nla_nest_cancel(msg->skb, prop);
+attr_msg_full:
+       nla_nest_cancel(msg->skb, attrs);
+msg_full:
+       tipc_bcast_unlock(net);
+       genlmsg_cancel(msg->skb, hdr);
 
-       link = node->links[bearer_id].link;
-       if (!link) {
-               tipc_node_unlock(node);
-               return -EINVAL;
-       }
+       return -EMSGSIZE;
+}
 
-       link_reset_statistics(link);
+void tipc_link_set_tolerance(struct tipc_link *l, u32 tol)
+{
+       l->tolerance = tol;
+       tipc_link_proto_xmit(l, STATE_MSG, 0, 0, tol, 0);
+}
 
-       tipc_node_unlock(node);
+void tipc_link_set_prio(struct tipc_link *l, u32 prio)
+{
+       l->priority = prio;
+       tipc_link_proto_xmit(l, STATE_MSG, 0, 0, 0, prio);
+}
 
-       return 0;
+void tipc_link_set_abort_limit(struct tipc_link *l, u32 limit)
+{
+       l->abort_limit = limit;
 }