-/**
- * bclink_accept_pkt - accept an incoming, in-sequence broadcast packet
- *
- * Called with both sending node's lock and bclink_lock taken.
- */
-static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
-{
- struct tipc_net *tn = net_generic(node->net, tipc_net_id);
-
- bclink_update_last_sent(node, seqno);
- node->bclink.last_in = seqno;
- node->bclink.oos_state = 0;
- tn->bcl->stats.recv_info++;
-
- /*
- * Unicast an ACK periodically, ensuring that
- * all nodes in the cluster don't ACK at the same time
- */
- if (((seqno - tn->own_addr) % TIPC_MIN_LINK_WIN) == 0) {
- tipc_link_proto_xmit(node_active_link(node, node->addr),
- STATE_MSG, 0, 0, 0, 0);
- tn->bcl->stats.sent_acks++;
- }
-}
-
-/**
- * tipc_bclink_rcv - receive a broadcast packet, and deliver upwards
- *
- * RCU is locked, no other locks set
- */
-void tipc_bclink_rcv(struct net *net, struct sk_buff *buf)
-{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *bcl = tn->bcl;
- struct tipc_msg *msg = buf_msg(buf);
- struct tipc_node *node;
- u32 next_in;
- u32 seqno;
- int deferred = 0;
- int pos = 0;
- struct sk_buff *iskb;
- struct sk_buff_head *arrvq, *inputq;
-
- /* Screen out unwanted broadcast messages */
- if (msg_mc_netid(msg) != tn->net_id)
- goto exit;
-
- node = tipc_node_find(net, msg_prevnode(msg));
- if (unlikely(!node))
- goto exit;
- tipc_node_lock(node);
- if (unlikely(!node->bclink.recv_permitted))
- goto unlock;
-
- /* Handle broadcast protocol message */
- if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) {
- if (msg_type(msg) != STATE_MSG)
- goto unlock;
- if (msg_destnode(msg) == tn->own_addr) {
- tipc_bclink_acknowledge(node, msg_bcast_ack(msg));
- tipc_bclink_lock(net);
- bcl->stats.recv_nacks++;
- tn->bcbase->retransmit_to = node;
- bclink_retransmit_pkt(tn, msg_bcgap_after(msg),
- msg_bcgap_to(msg));
- tipc_bclink_unlock(net);
- tipc_node_unlock(node);
- } else {
- tipc_node_unlock(node);
- bclink_peek_nack(net, msg);
- }
- tipc_node_put(node);
- goto exit;
- }
- /* Handle in-sequence broadcast message */
- seqno = msg_seqno(msg);
- next_in = mod(node->bclink.last_in + 1);
- arrvq = &tn->bcbase->arrvq;
- inputq = &tn->bcbase->inputq;
-
- if (likely(seqno == next_in)) {
-receive:
- /* Deliver message to destination */
- if (likely(msg_isdata(msg))) {
- tipc_bclink_lock(net);
- bclink_accept_pkt(node, seqno);
- spin_lock_bh(&inputq->lock);
- __skb_queue_tail(arrvq, buf);
- spin_unlock_bh(&inputq->lock);
- node->action_flags |= TIPC_BCAST_MSG_EVT;
- tipc_bclink_unlock(net);
- tipc_node_unlock(node);
- } else if (msg_user(msg) == MSG_BUNDLER) {
- tipc_bclink_lock(net);
- bclink_accept_pkt(node, seqno);
- bcl->stats.recv_bundles++;
- bcl->stats.recv_bundled += msg_msgcnt(msg);
- pos = 0;
- while (tipc_msg_extract(buf, &iskb, &pos)) {
- spin_lock_bh(&inputq->lock);
- __skb_queue_tail(arrvq, iskb);
- spin_unlock_bh(&inputq->lock);
- }
- node->action_flags |= TIPC_BCAST_MSG_EVT;
- tipc_bclink_unlock(net);
- tipc_node_unlock(node);
- } else if (msg_user(msg) == MSG_FRAGMENTER) {
- tipc_bclink_lock(net);
- bclink_accept_pkt(node, seqno);
- tipc_buf_append(&node->bclink.reasm_buf, &buf);
- if (unlikely(!buf && !node->bclink.reasm_buf)) {
- tipc_bclink_unlock(net);
- goto unlock;
- }
- bcl->stats.recv_fragments++;
- if (buf) {
- bcl->stats.recv_fragmented++;
- msg = buf_msg(buf);
- tipc_bclink_unlock(net);
- goto receive;
- }
- tipc_bclink_unlock(net);
- tipc_node_unlock(node);
- } else {
- tipc_bclink_lock(net);
- bclink_accept_pkt(node, seqno);
- tipc_bclink_unlock(net);
- tipc_node_unlock(node);
- kfree_skb(buf);
- }
- buf = NULL;
-
- /* Determine new synchronization state */
- tipc_node_lock(node);
- if (unlikely(!tipc_node_is_up(node)))
- goto unlock;
-
- if (node->bclink.last_in == node->bclink.last_sent)
- goto unlock;
-
- if (skb_queue_empty(&node->bclink.deferdq)) {
- node->bclink.oos_state = 1;
- goto unlock;
- }
-
- msg = buf_msg(skb_peek(&node->bclink.deferdq));
- seqno = msg_seqno(msg);
- next_in = mod(next_in + 1);
- if (seqno != next_in)
- goto unlock;
-
- /* Take in-sequence message from deferred queue & deliver it */
- buf = __skb_dequeue(&node->bclink.deferdq);
- goto receive;
- }
-
- /* Handle out-of-sequence broadcast message */
- if (less(next_in, seqno)) {
- deferred = tipc_link_defer_pkt(&node->bclink.deferdq,
- buf);
- bclink_update_last_sent(node, seqno);
- buf = NULL;
- }
-
- tipc_bclink_lock(net);
-
- if (deferred)
- bcl->stats.deferred_recv++;
- else
- bcl->stats.duplicates++;
-
- tipc_bclink_unlock(net);
-
-unlock:
- tipc_node_unlock(node);
- tipc_node_put(node);
-exit:
- kfree_skb(buf);
-}
-
-u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr)
-{
- return (n_ptr->bclink.recv_permitted &&
- (tipc_bclink_get_last_sent(n_ptr->net) != n_ptr->bclink.acked));
-}
-
-
-/**
- * tipc_bcbearer_send - send a packet through the broadcast pseudo-bearer
- *
- * Send packet over as many bearers as necessary to reach all nodes
- * that have joined the broadcast link.
- *
- * Returns 0 (packet sent successfully) under all circumstances,
- * since the broadcast link's pseudo-bearer never blocks
- */
-static int tipc_bcbearer_send(struct net *net, struct sk_buff *buf,
- struct tipc_bearer *unused1,
- struct tipc_media_addr *unused2)
-{
- int bp_index;
- struct tipc_msg *msg = buf_msg(buf);
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bcbearer *bcbearer = tn->bcbearer;
- struct tipc_bc_base *bclink = tn->bcbase;
-
- /* Prepare broadcast link message for reliable transmission,
- * if first time trying to send it;
- * preparation is skipped for broadcast link protocol messages
- * since they are sent in an unreliable manner and don't need it
- */
- if (likely(!msg_non_seq(buf_msg(buf)))) {
- bcbuf_set_acks(buf, bclink->bcast_nodes.count);
- msg_set_non_seq(msg, 1);
- msg_set_mc_netid(msg, tn->net_id);
- tn->bcl->stats.sent_info++;
- if (WARN_ON(!bclink->bcast_nodes.count)) {
- dump_stack();
- return 0;
- }
- }
- msg_set_mc_netid(msg, tn->net_id);
-
- /* Send buffer over bearers until all targets reached */
- bcbearer->remains = bclink->bcast_nodes;
-
- for (bp_index = 0; bp_index < MAX_BEARERS; bp_index++) {
- struct tipc_bearer *p = bcbearer->bpairs[bp_index].primary;
- struct tipc_bearer *s = bcbearer->bpairs[bp_index].secondary;
- struct tipc_bearer *bp[2] = {p, s};
- struct tipc_bearer *b = bp[msg_link_selector(msg)];
- struct sk_buff *tbuf;
-
- if (!p)
- break; /* No more bearers to try */
- if (!b)
- b = p;
- tipc_nmap_diff(&bcbearer->remains, &b->nodes,
- &bcbearer->remains_new);
- if (bcbearer->remains_new.count == bcbearer->remains.count)
- continue; /* Nothing added by bearer pair */
-
- if (bp_index == 0) {
- /* Use original buffer for first bearer */
- tipc_bearer_send(net, b->identity, buf, &b->bcast_addr);
- } else {
- /* Avoid concurrent buffer access */
- tbuf = pskb_copy_for_clone(buf, GFP_ATOMIC);
- if (!tbuf)
- break;
- tipc_bearer_send(net, b->identity, tbuf,
- &b->bcast_addr);
- kfree_skb(tbuf); /* Bearer keeps a clone */
- }
- if (bcbearer->remains_new.count == 0)
- break; /* All targets reached */
-
- bcbearer->remains = bcbearer->remains_new;
- }
-
- return 0;
-}
-
-/**
- * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer
- */
-void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr,
- u32 node, bool action)
-{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bcbearer *bcbearer = tn->bcbearer;
- struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp;
- struct tipc_bcbearer_pair *bp_curr;
- struct tipc_bearer *b;
- int b_index;
- int pri;
-
- tipc_bclink_lock(net);
-
- if (action)
- tipc_nmap_add(nm_ptr, node);
- else
- tipc_nmap_remove(nm_ptr, node);
-
- /* Group bearers by priority (can assume max of two per priority) */
- memset(bp_temp, 0, sizeof(bcbearer->bpairs_temp));
-
- rcu_read_lock();
- for (b_index = 0; b_index < MAX_BEARERS; b_index++) {
- b = rcu_dereference_rtnl(tn->bearer_list[b_index]);
- if (!b || !b->nodes.count)
- continue;
-
- if (!bp_temp[b->priority].primary)
- bp_temp[b->priority].primary = b;
- else
- bp_temp[b->priority].secondary = b;
- }
- rcu_read_unlock();
-
- /* Create array of bearer pairs for broadcasting */
- bp_curr = bcbearer->bpairs;
- memset(bcbearer->bpairs, 0, sizeof(bcbearer->bpairs));
-
- for (pri = TIPC_MAX_LINK_PRI; pri >= 0; pri--) {
-
- if (!bp_temp[pri].primary)
- continue;
-
- bp_curr->primary = bp_temp[pri].primary;
-
- if (bp_temp[pri].secondary) {
- if (tipc_nmap_equal(&bp_temp[pri].primary->nodes,
- &bp_temp[pri].secondary->nodes)) {
- bp_curr->secondary = bp_temp[pri].secondary;
- } else {
- bp_curr++;
- bp_curr->primary = bp_temp[pri].secondary;
- }
- }
-
- bp_curr++;
- }
-
- tipc_bclink_unlock(net);
-}
-