OSDN Git Service

Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[uclinux-h8/linux.git] / net / tipc / link.c
index 43a515d..ca8b8e0 100644 (file)
@@ -86,7 +86,7 @@ static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
  */
 #define  STARTING_EVT    856384768     /* link processing trigger */
 #define  TRAFFIC_MSG_EVT 560815u       /* rx'd ??? */
-#define  TIMEOUT_EVT     560817u       /* link timer expired */
+#define  SILENCE_EVT     560817u       /* timer dicovered silence from peer */
 
 /*
  * State value stored in 'failover_pkts'
@@ -106,6 +106,7 @@ static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf);
 static void tipc_link_input(struct tipc_link *l, struct sk_buff *skb);
 static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb);
 static bool tipc_link_failover_rcv(struct tipc_link *l, struct sk_buff **skb);
+static void link_set_timer(struct tipc_link *link, unsigned long time);
 /*
  *  Simple link routines
  */
@@ -197,11 +198,12 @@ static void link_timeout(unsigned long data)
        }
 
        /* do all other link processing performed on a periodic basis */
-       link_state_event(l_ptr, TIMEOUT_EVT);
-
+       if (l_ptr->silent_intv_cnt || tipc_bclink_acks_missing(l_ptr->owner))
+               link_state_event(l_ptr, SILENCE_EVT);
+       l_ptr->silent_intv_cnt++;
        if (skb_queue_len(&l_ptr->backlogq))
                tipc_link_push_packets(l_ptr);
-
+       link_set_timer(l_ptr, l_ptr->keepalive_intv);
        tipc_node_unlock(l_ptr->owner);
        tipc_link_put(l_ptr);
 }
@@ -233,8 +235,8 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
 
        if (n_ptr->link_cnt >= MAX_BEARERS) {
                tipc_addr_string_fill(addr_string, n_ptr->addr);
-               pr_err("Attempt to establish %uth link to %s. Max %u allowed.\n",
-                       n_ptr->link_cnt, addr_string, MAX_BEARERS);
+               pr_err("Cannot establish %uth link to %s. Max %u allowed.\n",
+                      n_ptr->link_cnt, addr_string, MAX_BEARERS);
                return NULL;
        }
 
@@ -261,7 +263,6 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
                /* note: peer i/f name is updated by reset/activate message */
        memcpy(&l_ptr->media_addr, media_addr, sizeof(*media_addr));
        l_ptr->owner = n_ptr;
-       l_ptr->checkpoint = 1;
        l_ptr->peer_session = INVALID_SESSION;
        l_ptr->bearer_id = b_ptr->identity;
        link_set_supervision_props(l_ptr, b_ptr->tolerance);
@@ -280,7 +281,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
        l_ptr->mtu = l_ptr->advertised_mtu;
        l_ptr->priority = b_ptr->priority;
        tipc_link_set_queue_limits(l_ptr, b_ptr->window);
-       l_ptr->next_out_no = 1;
+       l_ptr->snd_nxt = 1;
        __skb_queue_head_init(&l_ptr->transmq);
        __skb_queue_head_init(&l_ptr->backlogq);
        __skb_queue_head_init(&l_ptr->deferdq);
@@ -311,8 +312,7 @@ void tipc_link_delete(struct tipc_link *l)
        tipc_link_put(l);
 }
 
-void tipc_link_delete_list(struct net *net, unsigned int bearer_id,
-                          bool shutting_down)
+void tipc_link_delete_list(struct net *net, unsigned int bearer_id)
 {
        struct tipc_net *tn = net_generic(net, tipc_net_id);
        struct tipc_link *link;
@@ -451,9 +451,9 @@ void tipc_link_reset(struct tipc_link *l_ptr)
 
        if (was_active_link && tipc_node_is_up(l_ptr->owner) && (pl != l_ptr)) {
                l_ptr->flags |= LINK_FAILINGOVER;
-               l_ptr->failover_checkpt = l_ptr->next_in_no;
+               l_ptr->failover_checkpt = l_ptr->rcv_nxt;
                pl->failover_pkts = FIRST_FAILOVER;
-               pl->failover_checkpt = l_ptr->next_in_no;
+               pl->failover_checkpt = l_ptr->rcv_nxt;
                pl->failover_skb = l_ptr->reasm_buf;
        } else {
                kfree_skb(l_ptr->reasm_buf);
@@ -469,36 +469,19 @@ void tipc_link_reset(struct tipc_link *l_ptr)
        tipc_link_purge_backlog(l_ptr);
        l_ptr->reasm_buf = NULL;
        l_ptr->rcv_unacked = 0;
-       l_ptr->checkpoint = 1;
-       l_ptr->next_out_no = 1;
-       l_ptr->fsm_msg_cnt = 0;
+       l_ptr->snd_nxt = 1;
+       l_ptr->silent_intv_cnt = 0;
        l_ptr->stale_count = 0;
        link_reset_statistics(l_ptr);
 }
 
-void tipc_link_reset_list(struct net *net, unsigned int bearer_id)
-{
-       struct tipc_net *tn = net_generic(net, tipc_net_id);
-       struct tipc_link *l_ptr;
-       struct tipc_node *n_ptr;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
-               tipc_node_lock(n_ptr);
-               l_ptr = n_ptr->links[bearer_id];
-               if (l_ptr)
-                       tipc_link_reset(l_ptr);
-               tipc_node_unlock(n_ptr);
-       }
-       rcu_read_unlock();
-}
-
 static void link_activate(struct tipc_link *link)
 {
        struct tipc_node *node = link->owner;
 
-       link->next_in_no = 1;
+       link->rcv_nxt = 1;
        link->stats.recv_info = 1;
+       link->silent_intv_cnt = 0;
        tipc_node_link_up(node, link);
        tipc_bearer_add_dest(node->net, link->bearer_id, link->addr);
 }
@@ -511,7 +494,7 @@ static void link_activate(struct tipc_link *link)
 static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
 {
        struct tipc_link *other;
-       unsigned long cont_intv = l_ptr->cont_intv;
+       unsigned long timer_intv = l_ptr->keepalive_intv;
 
        if (l_ptr->flags & LINK_STOPPED)
                return;
@@ -519,45 +502,33 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
        if (!(l_ptr->flags & LINK_STARTED) && (event != STARTING_EVT))
                return;         /* Not yet. */
 
-       if (l_ptr->flags & LINK_FAILINGOVER) {
-               if (event == TIMEOUT_EVT)
-                       link_set_timer(l_ptr, cont_intv);
+       if (l_ptr->flags & LINK_FAILINGOVER)
                return;
-       }
 
        switch (l_ptr->state) {
        case WORKING_WORKING:
                switch (event) {
                case TRAFFIC_MSG_EVT:
                case ACTIVATE_MSG:
+                       l_ptr->silent_intv_cnt = 0;
                        break;
-               case TIMEOUT_EVT:
-                       if (l_ptr->next_in_no != l_ptr->checkpoint) {
-                               l_ptr->checkpoint = l_ptr->next_in_no;
-                               if (tipc_bclink_acks_missing(l_ptr->owner)) {
+               case SILENCE_EVT:
+                       if (!l_ptr->silent_intv_cnt) {
+                               if (tipc_bclink_acks_missing(l_ptr->owner))
                                        tipc_link_proto_xmit(l_ptr, STATE_MSG,
                                                             0, 0, 0, 0);
-                                       l_ptr->fsm_msg_cnt++;
-                               }
-                               link_set_timer(l_ptr, cont_intv);
                                break;
                        }
                        l_ptr->state = WORKING_UNKNOWN;
-                       l_ptr->fsm_msg_cnt = 0;
                        tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
-                       link_set_timer(l_ptr, cont_intv / 4);
                        break;
                case RESET_MSG:
                        pr_debug("%s<%s>, requested by peer\n",
                                 link_rst_msg, l_ptr->name);
                        tipc_link_reset(l_ptr);
                        l_ptr->state = RESET_RESET;
-                       l_ptr->fsm_msg_cnt = 0;
                        tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
                                             0, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
-                       link_set_timer(l_ptr, cont_intv);
                        break;
                default:
                        pr_debug("%s%u in WW state\n", link_unk_evt, event);
@@ -568,46 +539,33 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
                case TRAFFIC_MSG_EVT:
                case ACTIVATE_MSG:
                        l_ptr->state = WORKING_WORKING;
-                       l_ptr->fsm_msg_cnt = 0;
-                       link_set_timer(l_ptr, cont_intv);
+                       l_ptr->silent_intv_cnt = 0;
                        break;
                case RESET_MSG:
                        pr_debug("%s<%s>, requested by peer while probing\n",
                                 link_rst_msg, l_ptr->name);
                        tipc_link_reset(l_ptr);
                        l_ptr->state = RESET_RESET;
-                       l_ptr->fsm_msg_cnt = 0;
                        tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
                                             0, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
-                       link_set_timer(l_ptr, cont_intv);
                        break;
-               case TIMEOUT_EVT:
-                       if (l_ptr->next_in_no != l_ptr->checkpoint) {
+               case SILENCE_EVT:
+                       if (!l_ptr->silent_intv_cnt) {
                                l_ptr->state = WORKING_WORKING;
-                               l_ptr->fsm_msg_cnt = 0;
-                               l_ptr->checkpoint = l_ptr->next_in_no;
-                               if (tipc_bclink_acks_missing(l_ptr->owner)) {
+                               if (tipc_bclink_acks_missing(l_ptr->owner))
                                        tipc_link_proto_xmit(l_ptr, STATE_MSG,
                                                             0, 0, 0, 0);
-                                       l_ptr->fsm_msg_cnt++;
-                               }
-                               link_set_timer(l_ptr, cont_intv);
-                       } else if (l_ptr->fsm_msg_cnt < l_ptr->abort_limit) {
+                       } else if (l_ptr->silent_intv_cnt <
+                                  l_ptr->abort_limit) {
                                tipc_link_proto_xmit(l_ptr, STATE_MSG,
                                                     1, 0, 0, 0);
-                               l_ptr->fsm_msg_cnt++;
-                               link_set_timer(l_ptr, cont_intv / 4);
                        } else {        /* Link has failed */
                                pr_debug("%s<%s>, peer not responding\n",
                                         link_rst_msg, l_ptr->name);
                                tipc_link_reset(l_ptr);
                                l_ptr->state = RESET_UNKNOWN;
-                               l_ptr->fsm_msg_cnt = 0;
                                tipc_link_proto_xmit(l_ptr, RESET_MSG,
                                                     0, 0, 0, 0);
-                               l_ptr->fsm_msg_cnt++;
-                               link_set_timer(l_ptr, cont_intv);
                        }
                        break;
                default:
@@ -623,31 +581,22 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
                        if (other && link_working_unknown(other))
                                break;
                        l_ptr->state = WORKING_WORKING;
-                       l_ptr->fsm_msg_cnt = 0;
                        link_activate(l_ptr);
                        tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
                        if (l_ptr->owner->working_links == 1)
                                tipc_link_sync_xmit(l_ptr);
-                       link_set_timer(l_ptr, cont_intv);
                        break;
                case RESET_MSG:
                        l_ptr->state = RESET_RESET;
-                       l_ptr->fsm_msg_cnt = 0;
                        tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
                                             1, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
-                       link_set_timer(l_ptr, cont_intv);
                        break;
                case STARTING_EVT:
                        l_ptr->flags |= LINK_STARTED;
-                       l_ptr->fsm_msg_cnt++;
-                       link_set_timer(l_ptr, cont_intv);
+                       link_set_timer(l_ptr, timer_intv);
                        break;
-               case TIMEOUT_EVT:
+               case SILENCE_EVT:
                        tipc_link_proto_xmit(l_ptr, RESET_MSG, 0, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
-                       link_set_timer(l_ptr, cont_intv);
                        break;
                default:
                        pr_err("%s%u in RU state\n", link_unk_evt, event);
@@ -661,21 +610,16 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
                        if (other && link_working_unknown(other))
                                break;
                        l_ptr->state = WORKING_WORKING;
-                       l_ptr->fsm_msg_cnt = 0;
                        link_activate(l_ptr);
                        tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
                        if (l_ptr->owner->working_links == 1)
                                tipc_link_sync_xmit(l_ptr);
-                       link_set_timer(l_ptr, cont_intv);
                        break;
                case RESET_MSG:
                        break;
-               case TIMEOUT_EVT:
+               case SILENCE_EVT:
                        tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
                                             0, 0, 0, 0);
-                       l_ptr->fsm_msg_cnt++;
-                       link_set_timer(l_ptr, cont_intv);
                        break;
                default:
                        pr_err("%s%u in RR state\n", link_unk_evt, event);
@@ -701,53 +645,58 @@ int __tipc_link_xmit(struct net *net, struct tipc_link *link,
 {
        struct tipc_msg *msg = buf_msg(skb_peek(list));
        unsigned int maxwin = link->window;
-       unsigned int imp = msg_importance(msg);
+       unsigned int i, imp = msg_importance(msg);
        uint mtu = link->mtu;
-       uint ack = mod(link->next_in_no - 1);
-       uint seqno = link->next_out_no;
-       uint bc_last_in = link->owner->bclink.last_in;
+       u16 ack = mod(link->rcv_nxt - 1);
+       u16 seqno = link->snd_nxt;
+       u16 bc_last_in = link->owner->bclink.last_in;
        struct tipc_media_addr *addr = &link->media_addr;
        struct sk_buff_head *transmq = &link->transmq;
        struct sk_buff_head *backlogq = &link->backlogq;
-       struct sk_buff *skb, *tmp;
-
-       /* Match backlog limit against msg importance: */
-       if (unlikely(link->backlog[imp].len >= link->backlog[imp].limit))
-               return link_schedule_user(link, list);
+       struct sk_buff *skb, *bskb;
 
+       /* Match msg importance against this and all higher backlog limits: */
+       for (i = imp; i <= TIPC_SYSTEM_IMPORTANCE; i++) {
+               if (unlikely(link->backlog[i].len >= link->backlog[i].limit))
+                       return link_schedule_user(link, list);
+       }
        if (unlikely(msg_size(msg) > mtu)) {
                __skb_queue_purge(list);
                return -EMSGSIZE;
        }
        /* Prepare each packet for sending, and add to relevant queue: */
-       skb_queue_walk_safe(list, skb, tmp) {
-               __skb_unlink(skb, list);
+       while (skb_queue_len(list)) {
+               skb = skb_peek(list);
                msg = buf_msg(skb);
                msg_set_seqno(msg, seqno);
                msg_set_ack(msg, ack);
                msg_set_bcast_ack(msg, bc_last_in);
 
                if (likely(skb_queue_len(transmq) < maxwin)) {
+                       __skb_dequeue(list);
                        __skb_queue_tail(transmq, skb);
                        tipc_bearer_send(net, link->bearer_id, skb, addr);
                        link->rcv_unacked = 0;
                        seqno++;
                        continue;
                }
-               if (tipc_msg_bundle(skb_peek_tail(backlogq), skb, mtu)) {
+               if (tipc_msg_bundle(skb_peek_tail(backlogq), msg, mtu)) {
+                       kfree_skb(__skb_dequeue(list));
                        link->stats.sent_bundled++;
                        continue;
                }
-               if (tipc_msg_make_bundle(&skb, mtu, link->addr)) {
+               if (tipc_msg_make_bundle(&bskb, msg, mtu, link->addr)) {
+                       kfree_skb(__skb_dequeue(list));
+                       __skb_queue_tail(backlogq, bskb);
+                       link->backlog[msg_importance(buf_msg(bskb))].len++;
                        link->stats.sent_bundled++;
                        link->stats.sent_bundles++;
-                       imp = msg_importance(buf_msg(skb));
+                       continue;
                }
-               __skb_queue_tail(backlogq, skb);
-               link->backlog[imp].len++;
-               seqno++;
+               link->backlog[imp].len += skb_queue_len(list);
+               skb_queue_splice_tail_init(list, backlogq);
        }
-       link->next_out_no = seqno;
+       link->snd_nxt = seqno;
        return 0;
 }
 
@@ -877,7 +826,8 @@ void tipc_link_push_packets(struct tipc_link *link)
 {
        struct sk_buff *skb;
        struct tipc_msg *msg;
-       unsigned int ack = mod(link->next_in_no - 1);
+       u16 seqno = link->snd_nxt;
+       u16 ack = mod(link->rcv_nxt - 1);
 
        while (skb_queue_len(&link->transmq) < link->window) {
                skb = __skb_dequeue(&link->backlogq);
@@ -886,12 +836,15 @@ void tipc_link_push_packets(struct tipc_link *link)
                msg = buf_msg(skb);
                link->backlog[msg_importance(msg)].len--;
                msg_set_ack(msg, ack);
+               msg_set_seqno(msg, seqno);
+               seqno = mod(seqno + 1);
                msg_set_bcast_ack(msg, link->owner->bclink.last_in);
                link->rcv_unacked = 0;
                __skb_queue_tail(&link->transmq, skb);
                tipc_bearer_send(link->owner->net, link->bearer_id,
                                 skb, &link->media_addr);
        }
+       link->snd_nxt = seqno;
 }
 
 void tipc_link_reset_all(struct tipc_node *node)
@@ -964,13 +917,13 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *skb,
        msg = buf_msg(skb);
 
        /* Detect repeated retransmit failures */
-       if (l_ptr->last_retransmitted == msg_seqno(msg)) {
+       if (l_ptr->last_retransm == msg_seqno(msg)) {
                if (++l_ptr->stale_count > 100) {
                        link_retransmit_failure(l_ptr, skb);
                        return;
                }
        } else {
-               l_ptr->last_retransmitted = msg_seqno(msg);
+               l_ptr->last_retransm = msg_seqno(msg);
                l_ptr->stale_count = 1;
        }
 
@@ -978,7 +931,7 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *skb,
                if (!retransmits)
                        break;
                msg = buf_msg(skb);
-               msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
+               msg_set_ack(msg, mod(l_ptr->rcv_nxt - 1));
                msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
                tipc_bearer_send(l_ptr->owner->net, l_ptr->bearer_id, skb,
                                 &l_ptr->media_addr);
@@ -1001,11 +954,11 @@ static bool link_synch(struct tipc_link *l)
                goto synched;
 
        /* Was last pre-synch packet added to input queue ? */
-       if (less_eq(pl->next_in_no, l->synch_point))
+       if (less_eq(pl->rcv_nxt, l->synch_point))
                return false;
 
        /* Is it still in the input queue ? */
-       post_synch = mod(pl->next_in_no - l->synch_point) - 1;
+       post_synch = mod(pl->rcv_nxt - l->synch_point) - 1;
        if (skb_queue_len(&pl->inputq) > post_synch)
                return false;
 synched:
@@ -1016,13 +969,13 @@ synched:
 static void link_retrieve_defq(struct tipc_link *link,
                               struct sk_buff_head *list)
 {
-       u32 seq_no;
+       u16 seq_no;
 
        if (skb_queue_empty(&link->deferdq))
                return;
 
        seq_no = buf_seqno(skb_peek(&link->deferdq));
-       if (seq_no == mod(link->next_in_no))
+       if (seq_no == link->rcv_nxt)
                skb_queue_splice_tail_init(&link->deferdq, list);
 }
 
@@ -1043,8 +996,8 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr)
        struct tipc_link *l_ptr;
        struct sk_buff *skb1, *tmp;
        struct tipc_msg *msg;
-       u32 seq_no;
-       u32 ackd;
+       u16 seq_no;
+       u16 ackd;
        u32 released;
 
        skb2list(skb, &head);
@@ -1137,18 +1090,20 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr)
                }
 
                /* Link is now in state WORKING_WORKING */
-               if (unlikely(seq_no != mod(l_ptr->next_in_no))) {
+               if (unlikely(seq_no != l_ptr->rcv_nxt)) {
                        link_handle_out_of_seq_msg(l_ptr, skb);
                        link_retrieve_defq(l_ptr, &head);
                        skb = NULL;
                        goto unlock;
                }
+               l_ptr->silent_intv_cnt = 0;
+
                /* Synchronize with parallel link if applicable */
                if (unlikely((l_ptr->flags & LINK_SYNCHING) && !msg_dup(msg))) {
                        if (!link_synch(l_ptr))
                                goto unlock;
                }
-               l_ptr->next_in_no++;
+               l_ptr->rcv_nxt++;
                if (unlikely(!skb_queue_empty(&l_ptr->deferdq)))
                        link_retrieve_defq(l_ptr, &head);
                if (unlikely(++l_ptr->rcv_unacked >= TIPC_MIN_LINK_WIN)) {
@@ -1268,7 +1223,7 @@ static void tipc_link_input(struct tipc_link *link, struct sk_buff *skb)
 u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *skb)
 {
        struct sk_buff *skb1;
-       u32 seq_no = buf_seqno(skb);
+       u16 seq_no = buf_seqno(skb);
 
        /* Empty queue ? */
        if (skb_queue_empty(list)) {
@@ -1284,7 +1239,7 @@ u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *skb)
 
        /* Locate insertion point in queue, then insert; discard if duplicate */
        skb_queue_walk(list, skb1) {
-               u32 curr_seqno = buf_seqno(skb1);
+               u16 curr_seqno = buf_seqno(skb1);
 
                if (seq_no == curr_seqno) {
                        kfree_skb(skb);
@@ -1312,14 +1267,14 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
                return;
        }
 
-       /* Record OOS packet arrival (force mismatch on next timeout) */
-       l_ptr->checkpoint--;
+       /* Record OOS packet arrival */
+       l_ptr->silent_intv_cnt = 0;
 
        /*
         * Discard packet if a duplicate; otherwise add it to deferred queue
         * and notify peer of gap as per protocol specification
         */
-       if (less(seq_no, mod(l_ptr->next_in_no))) {
+       if (less(seq_no, l_ptr->rcv_nxt)) {
                l_ptr->stats.duplicates++;
                kfree_skb(buf);
                return;
@@ -1344,6 +1299,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
        struct tipc_msg *msg = l_ptr->pmsg;
        u32 msg_size = sizeof(l_ptr->proto_msg);
        int r_flag;
+       u16 last_rcv;
 
        /* Don't send protocol message during link failover */
        if (l_ptr->flags & LINK_FAILINGOVER)
@@ -1360,16 +1316,14 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
        msg_set_last_bcast(msg, tipc_bclink_get_last_sent(l_ptr->owner->net));
 
        if (msg_typ == STATE_MSG) {
-               u32 next_sent = mod(l_ptr->next_out_no);
+               u16 next_sent = l_ptr->snd_nxt;
 
                if (!tipc_link_is_up(l_ptr))
                        return;
-               if (skb_queue_len(&l_ptr->backlogq))
-                       next_sent = buf_seqno(skb_peek(&l_ptr->backlogq));
                msg_set_next_sent(msg, next_sent);
                if (!skb_queue_empty(&l_ptr->deferdq)) {
-                       u32 rec = buf_seqno(skb_peek(&l_ptr->deferdq));
-                       gap = mod(rec - mod(l_ptr->next_in_no));
+                       last_rcv = buf_seqno(skb_peek(&l_ptr->deferdq));
+                       gap = mod(last_rcv - l_ptr->rcv_nxt);
                }
                msg_set_seq_gap(msg, gap);
                if (gap)
@@ -1377,7 +1331,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
                msg_set_link_tolerance(msg, tolerance);
                msg_set_linkprio(msg, priority);
                msg_set_max_pkt(msg, l_ptr->mtu);
-               msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
+               msg_set_ack(msg, mod(l_ptr->rcv_nxt - 1));
                msg_set_probe(msg, probe_msg != 0);
                if (probe_msg)
                        l_ptr->stats.sent_probes++;
@@ -1397,7 +1351,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
        msg_set_linkprio(msg, l_ptr->priority);
        msg_set_size(msg, msg_size);
 
-       msg_set_seqno(msg, mod(l_ptr->next_out_no + (0xffff/2)));
+       msg_set_seqno(msg, mod(l_ptr->snd_nxt + (0xffff / 2)));
 
        buf = tipc_buf_acquire(msg_size);
        if (!buf)
@@ -1496,17 +1450,15 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr,
                }
 
                /* Record reception; force mismatch at next timeout: */
-               l_ptr->checkpoint--;
+               l_ptr->silent_intv_cnt = 0;
 
                link_state_event(l_ptr, TRAFFIC_MSG_EVT);
                l_ptr->stats.recv_states++;
                if (link_reset_unknown(l_ptr))
                        break;
 
-               if (less_eq(mod(l_ptr->next_in_no), msg_next_sent(msg))) {
-                       rec_gap = mod(msg_next_sent(msg) -
-                                     mod(l_ptr->next_in_no));
-               }
+               if (less_eq(l_ptr->rcv_nxt, msg_next_sent(msg)))
+                       rec_gap = mod(msg_next_sent(msg) - l_ptr->rcv_nxt);
 
                if (msg_probe(msg))
                        l_ptr->stats.recv_probes++;
@@ -1580,6 +1532,11 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
 
        tipc_msg_init(link_own_addr(l_ptr), &tunnel_hdr, TUNNEL_PROTOCOL,
                      FAILOVER_MSG, INT_H_SIZE, l_ptr->addr);
+
+       skb_queue_walk(&l_ptr->backlogq, skb) {
+               msg_set_seqno(buf_msg(skb), l_ptr->snd_nxt);
+               l_ptr->snd_nxt = mod(l_ptr->snd_nxt + 1);
+       }
        skb_queue_splice_tail_init(&l_ptr->backlogq, &l_ptr->transmq);
        tipc_link_purge_backlog(l_ptr);
        msgcount = skb_queue_len(&l_ptr->transmq);
@@ -1640,6 +1597,7 @@ void tipc_link_dup_queue_xmit(struct tipc_link *link,
        struct tipc_msg tnl_hdr;
        struct sk_buff_head *queue = &link->transmq;
        int mcnt;
+       u16 seqno;
 
        tipc_msg_init(link_own_addr(link), &tnl_hdr, TUNNEL_PROTOCOL,
                      SYNCH_MSG, INT_H_SIZE, link->addr);
@@ -1653,7 +1611,7 @@ tunnel_queue:
                struct tipc_msg *msg = buf_msg(skb);
                u32 len = msg_size(msg);
 
-               msg_set_ack(msg, mod(link->next_in_no - 1));
+               msg_set_ack(msg, mod(link->rcv_nxt - 1));
                msg_set_bcast_ack(msg, link->owner->bclink.last_in);
                msg_set_size(&tnl_hdr, len + INT_H_SIZE);
                outskb = tipc_buf_acquire(len + INT_H_SIZE);
@@ -1671,6 +1629,11 @@ tunnel_queue:
        }
        if (queue == &link->backlogq)
                return;
+       seqno = link->snd_nxt;
+       skb_queue_walk(&link->backlogq, skb) {
+               msg_set_seqno(buf_msg(skb), seqno);
+               seqno = mod(seqno + 1);
+       }
        queue = &link->backlogq;
        goto tunnel_queue;
 }
@@ -1742,8 +1705,8 @@ static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol)
                return;
 
        l_ptr->tolerance = tol;
-       l_ptr->cont_intv = msecs_to_jiffies(intv);
-       l_ptr->abort_limit = tol / (jiffies_to_msecs(l_ptr->cont_intv) / 4);
+       l_ptr->keepalive_intv = msecs_to_jiffies(intv);
+       l_ptr->abort_limit = tol / (jiffies_to_msecs(l_ptr->keepalive_intv));
 }
 
 void tipc_link_set_queue_limits(struct tipc_link *l, u32 win)
@@ -1803,8 +1766,8 @@ static struct tipc_node *tipc_link_find_owner(struct net *net,
 static void link_reset_statistics(struct tipc_link *l_ptr)
 {
        memset(&l_ptr->stats, 0, sizeof(l_ptr->stats));
-       l_ptr->stats.sent_info = l_ptr->next_out_no;
-       l_ptr->stats.recv_info = l_ptr->next_in_no;
+       l_ptr->stats.sent_info = l_ptr->snd_nxt;
+       l_ptr->stats.recv_info = l_ptr->rcv_nxt;
 }
 
 static void link_print(struct tipc_link *l_ptr, const char *str)
@@ -1893,6 +1856,9 @@ int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)
 
        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;
@@ -2034,9 +2000,9 @@ static int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
                goto attr_msg_full;
        if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->mtu))
                goto attr_msg_full;
-       if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->next_in_no))
+       if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->rcv_nxt))
                goto attr_msg_full;
-       if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->next_out_no))
+       if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->snd_nxt))
                goto attr_msg_full;
 
        if (tipc_link_is_up(link))
@@ -2175,50 +2141,53 @@ out:
 int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)
 {
        struct net *net = genl_info_net(info);
-       struct sk_buff *ans_skb;
        struct tipc_nl_msg msg;
-       struct tipc_link *link;
-       struct tipc_node *node;
        char *name;
-       int bearer_id;
        int err;
 
+       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]);
-       node = tipc_link_find_owner(net, name, &bearer_id);
-       if (!node)
-               return -EINVAL;
 
-       ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (!ans_skb)
+       msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg.skb)
                return -ENOMEM;
 
-       msg.skb = ans_skb;
-       msg.portid = info->snd_portid;
-       msg.seq = info->snd_seq;
-
-       tipc_node_lock(node);
-       link = node->links[bearer_id];
-       if (!link) {
-               err = -EINVAL;
-               goto err_out;
-       }
-
-       err = __tipc_nl_add_link(net, &msg, link, 0);
-       if (err)
-               goto err_out;
+       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;
 
-       tipc_node_unlock(node);
+               node = tipc_link_find_owner(net, name, &bearer_id);
+               if (!node)
+                       return -EINVAL;
 
-       return genlmsg_reply(ans_skb, info);
+               tipc_node_lock(node);
+               link = node->links[bearer_id];
+               if (!link) {
+                       tipc_node_unlock(node);
+                       nlmsg_free(msg.skb);
+                       return -EINVAL;
+               }
 
-err_out:
-       tipc_node_unlock(node);
-       nlmsg_free(ans_skb);
+               err = __tipc_nl_add_link(net, &msg, link, 0);
+               tipc_node_unlock(node);
+               if (err) {
+                       nlmsg_free(msg.skb);
+                       return err;
+               }
+       }
 
-       return err;
+       return genlmsg_reply(msg.skb, info);
 }
 
 int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)