/** * tipc_msg_bundle(): Append contents of a buffer to tail of an existing one * @list: the buffer chain of the existing buffer ("bundle") * @skb: buffer to be appended * @mtu: max allowable size for the bundle buffer * Consumes buffer if successful * Returns true if bundling could be performed, otherwise false */ bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu) { struct sk_buff *bskb = skb_peek_tail(list); struct tipc_msg *bmsg = buf_msg(bskb); struct tipc_msg *msg = buf_msg(skb); unsigned int bsz = msg_size(bmsg); unsigned int msz = msg_size(msg); u32 start = align(bsz); u32 max = mtu - INT_H_SIZE; u32 pad = start - bsz; if (likely(msg_user(msg) == MSG_FRAGMENTER)) return false; if (unlikely(msg_user(msg) == CHANGEOVER_PROTOCOL)) return false; if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) return false; if (likely(msg_user(bmsg) != MSG_BUNDLER)) return false; if (likely(!TIPC_SKB_CB(bskb)->bundling)) return false; if (unlikely(skb_tailroom(bskb) < (pad + msz))) return false; if (unlikely(max < (start + msz))) return false; skb_put(bskb, pad + msz); skb_copy_to_linear_data_offset(bskb, start, skb->data, msz); msg_set_size(bmsg, start + msz); msg_set_msgcnt(bmsg, msg_msgcnt(bmsg) + 1); kfree_skb(skb); return true; }
/** * tipc_bclink_recv_pkt - receive a broadcast packet, and deliver upwards * * tipc_net_lock is read_locked, no other locks set */ void tipc_bclink_recv_pkt(struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); struct tipc_node *node; u32 next_in; u32 seqno; int deferred; /* Screen out unwanted broadcast messages */ if (msg_mc_netid(msg) != tipc_net_id) goto exit; node = tipc_node_find(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) == tipc_own_addr) { tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); spin_lock_bh(&bc_lock); bcl->stats.recv_nacks++; bclink->retransmit_to = node; bclink_retransmit_pkt(msg_bcgap_after(msg), msg_bcgap_to(msg)); spin_unlock_bh(&bc_lock); } else { tipc_node_unlock(node); bclink_peek_nack(msg); } goto exit; } /* Handle in-sequence broadcast message */ seqno = msg_seqno(msg); next_in = mod(node->bclink.last_in + 1); if (likely(seqno == next_in)) { receive: /* Deliver message to destination */ if (likely(msg_isdata(msg))) { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); spin_unlock_bh(&bc_lock); tipc_node_unlock(node); if (likely(msg_mcast(msg))) tipc_port_recv_mcast(buf, NULL); else kfree_skb(buf); } else if (msg_user(msg) == MSG_BUNDLER) { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); bcl->stats.recv_bundles++; bcl->stats.recv_bundled += msg_msgcnt(msg); spin_unlock_bh(&bc_lock); tipc_node_unlock(node); tipc_link_recv_bundle(buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { int ret = tipc_link_recv_fragment(&node->bclink.defragm, &buf, &msg); if (ret < 0) goto unlock; spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); bcl->stats.recv_fragments++; if (ret > 0) bcl->stats.recv_fragmented++; spin_unlock_bh(&bc_lock); tipc_node_unlock(node); tipc_net_route_msg(buf); } else if (msg_user(msg) == NAME_DISTRIBUTOR) { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); spin_unlock_bh(&bc_lock); tipc_node_unlock(node); tipc_named_recv(buf); } else { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); spin_unlock_bh(&bc_lock); 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 (!node->bclink.deferred_head) { node->bclink.oos_state = 1; goto unlock; } msg = buf_msg(node->bclink.deferred_head); 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 = node->bclink.deferred_head; node->bclink.deferred_head = buf->next; buf->next = NULL; node->bclink.deferred_size--; goto receive; } /* Handle out-of-sequence broadcast message */ if (less(next_in, seqno)) { deferred = tipc_link_defer_pkt(&node->bclink.deferred_head, &node->bclink.deferred_tail, buf); node->bclink.deferred_size += deferred; bclink_update_last_sent(node, seqno); buf = NULL; } else deferred = 0; spin_lock_bh(&bc_lock); if (deferred) bcl->stats.deferred_recv++; else bcl->stats.duplicates++; spin_unlock_bh(&bc_lock); unlock: tipc_node_unlock(node); exit: kfree_skb(buf); }
void tipc_msg_dbg(struct print_buf *buf, struct tipc_msg *msg, const char *str) { u32 usr = msg_user(msg); tipc_printf(buf, KERN_DEBUG); tipc_printf(buf, str); switch (usr) { case MSG_BUNDLER: tipc_printf(buf, "BNDL::"); tipc_printf(buf, "MSGS(%u):", msg_msgcnt(msg)); break; case BCAST_PROTOCOL: tipc_printf(buf, "BCASTP::"); break; case MSG_FRAGMENTER: tipc_printf(buf, "FRAGM::"); switch (msg_type(msg)) { case FIRST_FRAGMENT: tipc_printf(buf, "FIRST:"); break; case FRAGMENT: tipc_printf(buf, "BODY:"); break; case LAST_FRAGMENT: tipc_printf(buf, "LAST:"); break; default: tipc_printf(buf, "UNKNOWN:%x", msg_type(msg)); } tipc_printf(buf, "NO(%u/%u):", msg_long_msgno(msg), msg_fragm_no(msg)); break; case TIPC_LOW_IMPORTANCE: case TIPC_MEDIUM_IMPORTANCE: case TIPC_HIGH_IMPORTANCE: case TIPC_CRITICAL_IMPORTANCE: tipc_printf(buf, "DAT%u:", msg_user(msg)); if (msg_short(msg)) { tipc_printf(buf, "CON:"); break; } switch (msg_type(msg)) { case TIPC_CONN_MSG: tipc_printf(buf, "CON:"); break; case TIPC_MCAST_MSG: tipc_printf(buf, "MCST:"); break; case TIPC_NAMED_MSG: tipc_printf(buf, "NAM:"); break; case TIPC_DIRECT_MSG: tipc_printf(buf, "DIR:"); break; default: tipc_printf(buf, "UNKNOWN TYPE %u", msg_type(msg)); } if (msg_reroute_cnt(msg)) tipc_printf(buf, "REROUTED(%u):", msg_reroute_cnt(msg)); break; case NAME_DISTRIBUTOR: tipc_printf(buf, "NMD::"); switch (msg_type(msg)) { case PUBLICATION: tipc_printf(buf, "PUBL(%u):", (msg_size(msg) - msg_hdr_sz(msg)) / 20); /* Items */ break; case WITHDRAWAL: tipc_printf(buf, "WDRW:"); break; default: tipc_printf(buf, "UNKNOWN:%x", msg_type(msg)); } if (msg_reroute_cnt(msg)) tipc_printf(buf, "REROUTED(%u):", msg_reroute_cnt(msg)); break; case CONN_MANAGER: tipc_printf(buf, "CONN_MNG:"); switch (msg_type(msg)) { case CONN_PROBE: tipc_printf(buf, "PROBE:"); break; case CONN_PROBE_REPLY: tipc_printf(buf, "PROBE_REPLY:"); break; case CONN_ACK: tipc_printf(buf, "CONN_ACK:"); tipc_printf(buf, "ACK(%u):", msg_msgcnt(msg)); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x", msg_type(msg)); } if (msg_reroute_cnt(msg)) tipc_printf(buf, "REROUTED(%u):", msg_reroute_cnt(msg)); break; case LINK_PROTOCOL: switch (msg_type(msg)) { case STATE_MSG: tipc_printf(buf, "STATE:"); tipc_printf(buf, "%s:", msg_probe(msg) ? "PRB" : ""); tipc_printf(buf, "NXS(%u):", msg_next_sent(msg)); tipc_printf(buf, "GAP(%u):", msg_seq_gap(msg)); tipc_printf(buf, "LSTBC(%u):", msg_last_bcast(msg)); break; case RESET_MSG: tipc_printf(buf, "RESET:"); if (msg_size(msg) != msg_hdr_sz(msg)) tipc_printf(buf, "BEAR:%s:", msg_data(msg)); break; case ACTIVATE_MSG: tipc_printf(buf, "ACTIVATE:"); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x", msg_type(msg)); } tipc_printf(buf, "PLANE(%c):", msg_net_plane(msg)); tipc_printf(buf, "SESS(%u):", msg_session(msg)); break; case CHANGEOVER_PROTOCOL: tipc_printf(buf, "TUNL:"); switch (msg_type(msg)) { case DUPLICATE_MSG: tipc_printf(buf, "DUPL:"); break; case ORIGINAL_MSG: tipc_printf(buf, "ORIG:"); tipc_printf(buf, "EXP(%u)", msg_msgcnt(msg)); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x", msg_type(msg)); } break; case LINK_CONFIG: tipc_printf(buf, "CFG:"); switch (msg_type(msg)) { case DSC_REQ_MSG: tipc_printf(buf, "DSC_REQ:"); break; case DSC_RESP_MSG: tipc_printf(buf, "DSC_RESP:"); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x:", msg_type(msg)); break; } break; default: tipc_printf(buf, "UNKNOWN USER:"******"NO_NAME:"); break; case TIPC_ERR_NO_PORT: tipc_printf(buf, "NO_PORT:"); break; case TIPC_ERR_NO_NODE: tipc_printf(buf, "NO_PROC:"); break; case TIPC_ERR_OVERLOAD: tipc_printf(buf, "OVERLOAD:"); break; case TIPC_CONN_SHUTDOWN: tipc_printf(buf, "SHUTDOWN:"); break; default: tipc_printf(buf, "UNKNOWN ERROR(%x):", msg_errcode(msg)); } default: break; } tipc_printf(buf, "HZ(%u):", msg_hdr_sz(msg
void tipc_msg_print(struct print_buf *buf, struct tipc_msg *msg, const char *str) { u32 usr = msg_user(msg); tipc_printf(buf, str); switch (usr) { case MSG_BUNDLER: tipc_printf(buf, "BNDL::"); tipc_printf(buf, "MSGS(%u):", msg_msgcnt(msg)); break; case BCAST_PROTOCOL: tipc_printf(buf, "BCASTP::"); break; case MSG_FRAGMENTER: tipc_printf(buf, "FRAGM::"); switch (msg_type(msg)) { case FIRST_FRAGMENT: tipc_printf(buf, "FIRST:"); break; case FRAGMENT: tipc_printf(buf, "BODY:"); break; case LAST_FRAGMENT: tipc_printf(buf, "LAST:"); break; default: tipc_printf(buf, "UNKNOWN:%x",msg_type(msg)); } tipc_printf(buf, "NO(%u/%u):",msg_long_msgno(msg), msg_fragm_no(msg)); break; case TIPC_LOW_IMPORTANCE: case TIPC_MEDIUM_IMPORTANCE: case TIPC_HIGH_IMPORTANCE: case TIPC_CRITICAL_IMPORTANCE: tipc_printf(buf, "DAT%u:", msg_user(msg)); if (msg_short(msg)) { tipc_printf(buf, "CON:"); break; } switch (msg_type(msg)) { case TIPC_CONN_MSG: tipc_printf(buf, "CON:"); break; case TIPC_MCAST_MSG: tipc_printf(buf, "MCST:"); break; case TIPC_NAMED_MSG: tipc_printf(buf, "NAM:"); break; case TIPC_DIRECT_MSG: tipc_printf(buf, "DIR:"); break; default: tipc_printf(buf, "UNKNOWN TYPE %u",msg_type(msg)); } if (msg_routed(msg) && !msg_non_seq(msg)) tipc_printf(buf, "ROUT:"); if (msg_reroute_cnt(msg)) tipc_printf(buf, "REROUTED(%u):", msg_reroute_cnt(msg)); break; case NAME_DISTRIBUTOR: tipc_printf(buf, "NMD::"); switch (msg_type(msg)) { case PUBLICATION: tipc_printf(buf, "PUBL(%u):", (msg_size(msg) - msg_hdr_sz(msg)) / 20); /* Items */ break; case WITHDRAWAL: tipc_printf(buf, "WDRW:"); break; default: tipc_printf(buf, "UNKNOWN:%x",msg_type(msg)); } if (msg_routed(msg)) tipc_printf(buf, "ROUT:"); if (msg_reroute_cnt(msg)) tipc_printf(buf, "REROUTED(%u):", msg_reroute_cnt(msg)); break; case CONN_MANAGER: tipc_printf(buf, "CONN_MNG:"); switch (msg_type(msg)) { case CONN_PROBE: tipc_printf(buf, "PROBE:"); break; case CONN_PROBE_REPLY: tipc_printf(buf, "PROBE_REPLY:"); break; case CONN_ACK: tipc_printf(buf, "CONN_ACK:"); tipc_printf(buf, "ACK(%u):",msg_msgcnt(msg)); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x",msg_type(msg)); } if (msg_routed(msg)) tipc_printf(buf, "ROUT:"); if (msg_reroute_cnt(msg)) tipc_printf(buf, "REROUTED(%u):",msg_reroute_cnt(msg)); break; case LINK_PROTOCOL: tipc_printf(buf, "PROT:TIM(%u):",msg_timestamp(msg)); switch (msg_type(msg)) { case STATE_MSG: tipc_printf(buf, "STATE:"); tipc_printf(buf, "%s:",msg_probe(msg) ? "PRB" :""); tipc_printf(buf, "NXS(%u):",msg_next_sent(msg)); tipc_printf(buf, "GAP(%u):",msg_seq_gap(msg)); tipc_printf(buf, "LSTBC(%u):",msg_last_bcast(msg)); break; case RESET_MSG: tipc_printf(buf, "RESET:"); if (msg_size(msg) != msg_hdr_sz(msg)) tipc_printf(buf, "BEAR:%s:",msg_data(msg)); break; case ACTIVATE_MSG: tipc_printf(buf, "ACTIVATE:"); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x",msg_type(msg)); } tipc_printf(buf, "PLANE(%c):",msg_net_plane(msg)); tipc_printf(buf, "SESS(%u):",msg_session(msg)); break; case CHANGEOVER_PROTOCOL: tipc_printf(buf, "TUNL:"); switch (msg_type(msg)) { case DUPLICATE_MSG: tipc_printf(buf, "DUPL:"); break; case ORIGINAL_MSG: tipc_printf(buf, "ORIG:"); tipc_printf(buf, "EXP(%u)",msg_msgcnt(msg)); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x",msg_type(msg)); } break; case ROUTE_DISTRIBUTOR: tipc_printf(buf, "ROUTING_MNG:"); switch (msg_type(msg)) { case EXT_ROUTING_TABLE: tipc_printf(buf, "EXT_TBL:"); tipc_printf(buf, "TO:%x:",msg_remote_node(msg)); break; case LOCAL_ROUTING_TABLE: tipc_printf(buf, "LOCAL_TBL:"); tipc_printf(buf, "TO:%x:",msg_remote_node(msg)); break; case SLAVE_ROUTING_TABLE: tipc_printf(buf, "DP_TBL:"); tipc_printf(buf, "TO:%x:",msg_remote_node(msg)); break; case ROUTE_ADDITION: tipc_printf(buf, "ADD:"); tipc_printf(buf, "TO:%x:",msg_remote_node(msg)); break; case ROUTE_REMOVAL: tipc_printf(buf, "REMOVE:"); tipc_printf(buf, "TO:%x:",msg_remote_node(msg)); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x",msg_type(msg)); } break; case LINK_CONFIG: tipc_printf(buf, "CFG:"); switch (msg_type(msg)) { case DSC_REQ_MSG: tipc_printf(buf, "DSC_REQ:"); break; case DSC_RESP_MSG: tipc_printf(buf, "DSC_RESP:"); break; default: tipc_printf(buf, "UNKNOWN TYPE:%x:",msg_type(msg)); break; } break; default: tipc_printf(buf, "UNKNOWN USER:"******"NO_NAME:"); break; case TIPC_ERR_NO_PORT: tipc_printf(buf, "NO_PORT:"); break; case TIPC_ERR_NO_NODE: tipc_printf(buf, "NO_PROC:"); break; case TIPC_ERR_OVERLOAD: tipc_printf(buf, "OVERLOAD:"); break; case TIPC_CONN_SHUTDOWN: tipc_printf(buf, "SHUTDOWN:"); break; default: tipc_printf(buf, "UNKNOWN ERROR(%x):", msg_errcode(msg)); } default:{} } tipc_printf(buf, "HZ(%u):", msg_hdr_sz(msg)); tipc_printf(buf, "SZ(%u):", msg_size(msg)); tipc_printf(buf, "SQNO(%u):", msg_seqno(msg)); if (msg_non_seq(msg)) tipc_printf(buf, "NOSEQ:"); else { tipc_printf(buf, "ACK(%u):", msg_ack(msg)); } tipc_printf(buf, "BACK(%u):", msg_bcast_ack(msg)); tipc_printf(buf, "PRND(%x)", msg_prevnode(msg)); if (msg_isdata(msg)) { if (msg_named(msg)) { tipc_printf(buf, "NTYP(%u):", msg_nametype(msg)); tipc_printf(buf, "NINST(%u)", msg_nameinst(msg)); } } if ((usr != LINK_PROTOCOL) && (usr != LINK_CONFIG) && (usr != MSG_BUNDLER)) { if (!msg_short(msg)) { tipc_printf(buf, ":ORIG(%x:%u):", msg_orignode(msg), msg_origport(msg)); tipc_printf(buf, ":DEST(%x:%u):", msg_destnode(msg), msg_destport(msg)); } else { tipc_printf(buf, ":OPRT(%u):", msg_origport(msg)); tipc_printf(buf, ":DPRT(%u):", msg_destport(msg)); } if (msg_routed(msg) && !msg_non_seq(msg)) tipc_printf(buf, ":TSEQN(%u)", msg_transp_seqno(msg)); } if (msg_user(msg) == NAME_DISTRIBUTOR) { tipc_printf(buf, ":ONOD(%x):", msg_orignode(msg)); tipc_printf(buf, ":DNOD(%x):", msg_destnode(msg)); if (msg_routed(msg)) { tipc_printf(buf, ":CSEQN(%u)", msg_transp_seqno(msg)); } } if (msg_user(msg) == LINK_CONFIG) { u32* raw = (u32*)msg; struct tipc_media_addr* orig = (struct tipc_media_addr*)&raw[5]; tipc_printf(buf, ":REQL(%u):", msg_req_links(msg)); tipc_printf(buf, ":DDOM(%x):", msg_dest_domain(msg)); tipc_printf(buf, ":NETID(%u):", msg_bc_netid(msg)); tipc_media_addr_printf(buf, orig); } if (msg_user(msg) == BCAST_PROTOCOL) { tipc_printf(buf, "BCNACK:AFTER(%u):", msg_bcgap_after(msg)); tipc_printf(buf, "TO(%u):", msg_bcgap_to(msg)); } tipc_printf(buf, "\n"); if ((usr == CHANGEOVER_PROTOCOL) && (msg_msgcnt(msg))) { tipc_msg_print(buf,msg_get_wrapped(msg)," /"); } if ((usr == MSG_FRAGMENTER) && (msg_type(msg) == FIRST_FRAGMENT)) { tipc_msg_print(buf,msg_get_wrapped(msg)," /"); } }
void tipc_bclink_recv_pkt(struct sk_buff *buf) { #if (TIPC_BCAST_LOSS_RATE) static int rx_count = 0; #endif struct tipc_msg *msg = buf_msg(buf); struct tipc_node* node = tipc_node_find(msg_prevnode(msg)); u32 next_in; u32 seqno; struct sk_buff *deferred; msg_dbg(msg, "<BC<<<"); if (unlikely(!node || !tipc_node_is_up(node) || !node->bclink.supported || (msg_mc_netid(msg) != tipc_net_id))) { buf_discard(buf); return; } if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { msg_dbg(msg, "<BCNACK<<<"); if (msg_destnode(msg) == tipc_own_addr) { tipc_node_lock(node); tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); spin_lock_bh(&bc_lock); bcl->stats.recv_nacks++; bcl->owner->next = node; /* remember requestor */ bclink_retransmit_pkt(msg_bcgap_after(msg), msg_bcgap_to(msg)); bcl->owner->next = NULL; spin_unlock_bh(&bc_lock); } else { tipc_bclink_peek_nack(msg_destnode(msg), msg_bcast_tag(msg), msg_bcgap_after(msg), msg_bcgap_to(msg)); } buf_discard(buf); return; } #if (TIPC_BCAST_LOSS_RATE) if (++rx_count == TIPC_BCAST_LOSS_RATE) { rx_count = 0; buf_discard(buf); return; } #endif tipc_node_lock(node); receive: deferred = node->bclink.deferred_head; next_in = mod(node->bclink.last_in + 1); seqno = msg_seqno(msg); if (likely(seqno == next_in)) { bcl->stats.recv_info++; node->bclink.last_in++; bclink_set_gap(node); if (unlikely(bclink_ack_allowed(seqno))) { bclink_send_ack(node); bcl->stats.sent_acks++; } if (likely(msg_isdata(msg))) { tipc_node_unlock(node); tipc_port_recv_mcast(buf, NULL); } else if (msg_user(msg) == MSG_BUNDLER) { bcl->stats.recv_bundles++; bcl->stats.recv_bundled += msg_msgcnt(msg); tipc_node_unlock(node); tipc_link_recv_bundle(buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { bcl->stats.recv_fragments++; if (tipc_link_recv_fragment(&node->bclink.defragm, &buf, &msg)) bcl->stats.recv_fragmented++; tipc_node_unlock(node); tipc_net_route_msg(buf); } else { tipc_node_unlock(node); tipc_net_route_msg(buf); } if (deferred && (buf_seqno(deferred) == mod(next_in + 1))) { tipc_node_lock(node); buf = deferred; msg = buf_msg(buf); node->bclink.deferred_head = deferred->next; goto receive; } return; } else if (less(next_in, seqno)) { u32 gap_after = node->bclink.gap_after; u32 gap_to = node->bclink.gap_to; if (tipc_link_defer_pkt(&node->bclink.deferred_head, &node->bclink.deferred_tail, buf)) { node->bclink.nack_sync++; bcl->stats.deferred_recv++; if (seqno == mod(gap_after + 1)) node->bclink.gap_after = seqno; else if (less(gap_after, seqno) && less(seqno, gap_to)) node->bclink.gap_to = seqno; } if (bclink_ack_allowed(node->bclink.nack_sync)) { if (gap_to != gap_after) bclink_send_nack(node); bclink_set_gap(node); } } else { bcl->stats.duplicates++; buf_discard(buf); } tipc_node_unlock(node); }
void tipc_bclink_recv_pkt(struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); struct tipc_node *node; u32 next_in; u32 seqno; struct sk_buff *deferred; /* Screen out unwanted broadcast messages */ if (msg_mc_netid(msg) != tipc_net_id) goto exit; node = tipc_node_find(msg_prevnode(msg)); if (unlikely(!node)) goto exit; tipc_node_lock(node); if (unlikely(!node->bclink.supported)) goto unlock; if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { if (msg_type(msg) != STATE_MSG) goto unlock; if (msg_destnode(msg) == tipc_own_addr) { tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); spin_lock_bh(&bc_lock); bcl->stats.recv_nacks++; bclink->retransmit_to = node; bclink_retransmit_pkt(msg_bcgap_after(msg), msg_bcgap_to(msg)); spin_unlock_bh(&bc_lock); } else { tipc_node_unlock(node); tipc_bclink_peek_nack(msg_destnode(msg), msg_bcast_tag(msg), msg_bcgap_after(msg), msg_bcgap_to(msg)); } goto exit; } /* Handle in-sequence broadcast message */ receive: next_in = mod(node->bclink.last_in + 1); seqno = msg_seqno(msg); if (likely(seqno == next_in)) { bcl->stats.recv_info++; node->bclink.last_in++; bclink_set_gap(node); if (unlikely(bclink_ack_allowed(seqno))) { bclink_send_ack(node); bcl->stats.sent_acks++; } if (likely(msg_isdata(msg))) { tipc_node_unlock(node); if (likely(msg_mcast(msg))) tipc_port_recv_mcast(buf, NULL); else buf_discard(buf); } else if (msg_user(msg) == MSG_BUNDLER) { bcl->stats.recv_bundles++; bcl->stats.recv_bundled += msg_msgcnt(msg); tipc_node_unlock(node); tipc_link_recv_bundle(buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { bcl->stats.recv_fragments++; if (tipc_link_recv_fragment(&node->bclink.defragm, &buf, &msg)) bcl->stats.recv_fragmented++; tipc_node_unlock(node); tipc_net_route_msg(buf); } else if (msg_user(msg) == NAME_DISTRIBUTOR) { tipc_node_unlock(node); tipc_named_recv(buf); } else { tipc_node_unlock(node); buf_discard(buf); } buf = NULL; tipc_node_lock(node); deferred = node->bclink.deferred_head; if (deferred && (buf_seqno(deferred) == mod(next_in + 1))) { buf = deferred; msg = buf_msg(buf); node->bclink.deferred_head = deferred->next; goto receive; } } else if (less(next_in, seqno)) { u32 gap_after = node->bclink.gap_after; u32 gap_to = node->bclink.gap_to; if (tipc_link_defer_pkt(&node->bclink.deferred_head, &node->bclink.deferred_tail, buf)) { node->bclink.nack_sync++; bcl->stats.deferred_recv++; if (seqno == mod(gap_after + 1)) node->bclink.gap_after = seqno; else if (less(gap_after, seqno) && less(seqno, gap_to)) node->bclink.gap_to = seqno; } buf = NULL; if (bclink_ack_allowed(node->bclink.nack_sync)) { if (gap_to != gap_after) bclink_send_nack(node); bclink_set_gap(node); } } else { bcl->stats.duplicates++; } unlock: tipc_node_unlock(node); exit: buf_discard(buf); }