Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tipc: add support for broadcast rcv stats dumping

This commit enables dumping the statistics of a broadcast-receiver link
like the traditional 'broadcast-link' one (which is for broadcast-
sender). The link dumping can be triggered via netlink (e.g. the
iproute2/tipc tool) by the link flag - 'TIPC_NLA_LINK_BROADCAST' as the
indicator.

The name of a broadcast-receiver link of a specific peer will be in the
format: 'broadcast-link:<peer-id>'.

For example:

Link <broadcast-link:1001002>
Window:50 packets
RX packets:7841 fragments:2408/440 bundles:0/0
TX packets:0 fragments:0/0 bundles:0/0
RX naks:0 defs:124 dups:0
TX naks:21 acks:0 retrans:0
Congestion link:0 Send queue max:0 avg:0

In addition, the broadcast-receiver link statistics can be reset in the
usual way via netlink by specifying that link name in command.

Note: the 'tipc_link_name_ext()' is removed because the link name can
now be retrieved simply via the 'l->name'.

Acked-by: Ying Xue <ying.xue@windriver.com>
Acked-by: Jon Maloy <jmaloy@redhat.com>
Signed-off-by: Tuong Lien <tuong.t.lien@dektech.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Tuong Lien and committed by
David S. Miller
03b6fefd a91d55d1

+103 -58
+2 -4
net/tipc/bcast.c
··· 563 563 tipc_sk_rcv(net, inputq); 564 564 } 565 565 566 - int tipc_bclink_reset_stats(struct net *net) 566 + int tipc_bclink_reset_stats(struct net *net, struct tipc_link *l) 567 567 { 568 - struct tipc_link *l = tipc_bc_sndlink(net); 569 - 570 568 if (!l) 571 569 return -ENOPROTOOPT; 572 570 ··· 692 694 tn->bcbase = bb; 693 695 spin_lock_init(&tipc_net(net)->bclock); 694 696 695 - if (!tipc_link_bc_create(net, 0, 0, 697 + if (!tipc_link_bc_create(net, 0, 0, NULL, 696 698 FB_MTU, 697 699 BCLINK_WIN_DEFAULT, 698 700 BCLINK_WIN_DEFAULT,
+3 -2
net/tipc/bcast.h
··· 96 96 int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, 97 97 struct tipc_msg *hdr, 98 98 struct sk_buff_head *retrq); 99 - int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg); 99 + int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, 100 + struct tipc_link *bcl); 100 101 int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]); 101 - int tipc_bclink_reset_stats(struct net *net); 102 + int tipc_bclink_reset_stats(struct net *net, struct tipc_link *l); 102 103 103 104 u32 tipc_bcast_get_broadcast_mode(struct net *net); 104 105 u32 tipc_bcast_get_broadcast_ratio(struct net *net);
+35 -34
net/tipc/link.c
··· 539 539 * 540 540 * Returns true if link was created, otherwise false 541 541 */ 542 - bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, 542 + bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id, 543 543 int mtu, u32 min_win, u32 max_win, u16 peer_caps, 544 544 struct sk_buff_head *inputq, 545 545 struct sk_buff_head *namedq, ··· 554 554 return false; 555 555 556 556 l = *link; 557 - strcpy(l->name, tipc_bclink_name); 557 + if (peer_id) { 558 + char peer_str[NODE_ID_STR_LEN] = {0,}; 559 + 560 + tipc_nodeid2string(peer_str, peer_id); 561 + if (strlen(peer_str) > 16) 562 + sprintf(peer_str, "%x", peer); 563 + /* Broadcast receiver link name: "broadcast-link:<peer>" */ 564 + snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name, 565 + peer_str); 566 + } else { 567 + strcpy(l->name, tipc_bclink_name); 568 + } 558 569 trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!"); 559 570 tipc_link_reset(l); 560 571 l->state = LINK_RESET; ··· 1423 1412 gacks[n].ack = htons(expect - 1); 1424 1413 gacks[n].gap = htons(seqno - expect); 1425 1414 if (++n >= MAX_GAP_ACK_BLKS / 2) { 1426 - char buf[TIPC_MAX_LINK_NAME]; 1427 - 1428 1415 pr_info_ratelimited("Gacks on %s: %d, ql: %d!\n", 1429 - tipc_link_name_ext(l, buf), 1430 - n, 1416 + l->name, n, 1431 1417 skb_queue_len(&l->deferdq)); 1432 1418 return n; 1433 1419 } ··· 1595 1587 _skb->priority = TC_PRIO_CONTROL; 1596 1588 __skb_queue_tail(xmitq, _skb); 1597 1589 l->stats.retransmitted++; 1590 + if (!is_uc) 1591 + r->stats.retransmitted++; 1598 1592 *retransmitted = true; 1599 1593 /* Increase actual retrans counter & mark first time */ 1600 1594 if (!TIPC_SKB_CB(skb)->retr_cnt++) ··· 1763 1753 1764 1754 /* Defer delivery if sequence gap */ 1765 1755 if (unlikely(seqno != rcv_nxt)) { 1766 - __tipc_skb_queue_sorted(defq, seqno, skb); 1756 + if (!__tipc_skb_queue_sorted(defq, seqno, skb)) 1757 + l->stats.duplicates++; 1767 1758 rc |= tipc_link_build_nack_msg(l, xmitq); 1768 1759 break; 1769 1760 } ··· 1798 1787 int tolerance, int priority, 1799 1788 struct sk_buff_head *xmitq) 1800 1789 { 1801 - struct tipc_link *bcl = l->bc_rcvlink; 1802 - struct sk_buff *skb; 1803 - struct tipc_msg *hdr; 1804 - struct sk_buff_head *dfq = &l->deferdq; 1805 - bool node_up = link_is_up(bcl); 1806 1790 struct tipc_mon_state *mstate = &l->mon_state; 1791 + struct sk_buff_head *dfq = &l->deferdq; 1792 + struct tipc_link *bcl = l->bc_rcvlink; 1793 + struct tipc_msg *hdr; 1794 + struct sk_buff *skb; 1795 + bool node_up = link_is_up(bcl); 1796 + u16 glen = 0, bc_rcvgap = 0; 1807 1797 int dlen = 0; 1808 1798 void *data; 1809 - u16 glen = 0; 1810 1799 1811 1800 /* Don't send protocol message during reset or link failover */ 1812 1801 if (tipc_link_is_blocked(l)) ··· 1844 1833 if (l->peer_caps & TIPC_LINK_PROTO_SEQNO) 1845 1834 msg_set_seqno(hdr, l->snd_nxt_state++); 1846 1835 msg_set_seq_gap(hdr, rcvgap); 1847 - msg_set_bc_gap(hdr, link_bc_rcv_gap(bcl)); 1836 + bc_rcvgap = link_bc_rcv_gap(bcl); 1837 + msg_set_bc_gap(hdr, bc_rcvgap); 1848 1838 msg_set_probe(hdr, probe); 1849 1839 msg_set_is_keepalive(hdr, probe || probe_reply); 1850 1840 if (l->peer_caps & TIPC_GAP_ACK_BLOCK) ··· 1870 1858 l->stats.sent_probes++; 1871 1859 if (rcvgap) 1872 1860 l->stats.sent_nacks++; 1861 + if (bc_rcvgap) 1862 + bcl->stats.sent_nacks++; 1873 1863 skb->priority = TC_PRIO_CONTROL; 1874 1864 __skb_queue_tail(xmitq, skb); 1875 1865 trace_tipc_proto_build(skb, false, l->name); ··· 2372 2358 if (!l->bc_peer_is_up) 2373 2359 return rc; 2374 2360 2375 - l->stats.recv_nacks++; 2376 - 2377 2361 /* Ignore if peers_snd_nxt goes beyond receive window */ 2378 2362 if (more(peers_snd_nxt, l->rcv_nxt + l->window)) 2379 2363 return rc; ··· 2421 2409 2422 2410 if (!link_is_up(r) || !r->bc_peer_is_up) 2423 2411 return 0; 2412 + 2413 + if (gap) { 2414 + l->stats.recv_nacks++; 2415 + r->stats.recv_nacks++; 2416 + } 2424 2417 2425 2418 if (less(acked, r->acked) || (acked == r->acked && !gap && !ga)) 2426 2419 return 0; ··· 2738 2721 return -EMSGSIZE; 2739 2722 } 2740 2723 2741 - int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg) 2724 + int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, 2725 + struct tipc_link *bcl) 2742 2726 { 2743 2727 int err; 2744 2728 void *hdr; 2745 2729 struct nlattr *attrs; 2746 2730 struct nlattr *prop; 2747 - struct tipc_net *tn = net_generic(net, tipc_net_id); 2748 2731 u32 bc_mode = tipc_bcast_get_broadcast_mode(net); 2749 2732 u32 bc_ratio = tipc_bcast_get_broadcast_ratio(net); 2750 - struct tipc_link *bcl = tn->bcl; 2751 2733 2752 2734 if (!bcl) 2753 2735 return 0; ··· 2831 2815 void tipc_link_set_abort_limit(struct tipc_link *l, u32 limit) 2832 2816 { 2833 2817 l->abort_limit = limit; 2834 - } 2835 - 2836 - char *tipc_link_name_ext(struct tipc_link *l, char *buf) 2837 - { 2838 - if (!l) 2839 - scnprintf(buf, TIPC_MAX_LINK_NAME, "null"); 2840 - else if (link_is_bc_sndlink(l)) 2841 - scnprintf(buf, TIPC_MAX_LINK_NAME, "broadcast-sender"); 2842 - else if (link_is_bc_rcvlink(l)) 2843 - scnprintf(buf, TIPC_MAX_LINK_NAME, 2844 - "broadcast-receiver, peer %x", l->addr); 2845 - else 2846 - memcpy(buf, l->name, TIPC_MAX_LINK_NAME); 2847 - 2848 - return buf; 2849 2818 } 2850 2819 2851 2820 /**
+1 -2
net/tipc/link.h
··· 80 80 struct sk_buff_head *inputq, 81 81 struct sk_buff_head *namedq, 82 82 struct tipc_link **link); 83 - bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, 83 + bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id, 84 84 int mtu, u32 min_win, u32 max_win, u16 peer_caps, 85 85 struct sk_buff_head *inputq, 86 86 struct sk_buff_head *namedq, ··· 111 111 u16 tipc_link_acked(struct tipc_link *l); 112 112 u32 tipc_link_id(struct tipc_link *l); 113 113 char *tipc_link_name(struct tipc_link *l); 114 - char *tipc_link_name_ext(struct tipc_link *l, char *buf); 115 114 u32 tipc_link_state(struct tipc_link *l); 116 115 char tipc_link_plane(struct tipc_link *l); 117 116 int tipc_link_prio(struct tipc_link *l);
+5 -4
net/tipc/msg.c
··· 825 825 * @seqno: sequence number of buffer to add 826 826 * @skb: buffer to add 827 827 */ 828 - void __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, 828 + bool __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, 829 829 struct sk_buff *skb) 830 830 { 831 831 struct sk_buff *_skb, *tmp; 832 832 833 833 if (skb_queue_empty(list) || less(seqno, buf_seqno(skb_peek(list)))) { 834 834 __skb_queue_head(list, skb); 835 - return; 835 + return true; 836 836 } 837 837 838 838 if (more(seqno, buf_seqno(skb_peek_tail(list)))) { 839 839 __skb_queue_tail(list, skb); 840 - return; 840 + return true; 841 841 } 842 842 843 843 skb_queue_walk_safe(list, _skb, tmp) { ··· 846 846 if (seqno == buf_seqno(_skb)) 847 847 break; 848 848 __skb_queue_before(list, _skb, skb); 849 - return; 849 + return true; 850 850 } 851 851 kfree_skb(skb); 852 + return false; 852 853 } 853 854 854 855 void tipc_skb_reject(struct net *net, int err, struct sk_buff *skb,
+1 -1
net/tipc/msg.h
··· 1145 1145 bool tipc_msg_reassemble(struct sk_buff_head *list, struct sk_buff_head *rcvq); 1146 1146 bool tipc_msg_pskb_copy(u32 dst, struct sk_buff_head *msg, 1147 1147 struct sk_buff_head *cpy); 1148 - void __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, 1148 + bool __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, 1149 1149 struct sk_buff *skb); 1150 1150 bool tipc_msg_skb_clone(struct sk_buff_head *msg, struct sk_buff_head *cpy); 1151 1151
+1 -1
net/tipc/netlink.c
··· 188 188 }, 189 189 { 190 190 .cmd = TIPC_NL_LINK_GET, 191 - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 191 + .validate = GENL_DONT_VALIDATE_STRICT, 192 192 .doit = tipc_nl_node_get_link, 193 193 .dumpit = tipc_nl_node_dump_link, 194 194 },
+53 -8
net/tipc/node.c
··· 1138 1138 if (unlikely(!n->bc_entry.link)) { 1139 1139 snd_l = tipc_bc_sndlink(net); 1140 1140 if (!tipc_link_bc_create(net, tipc_own_addr(net), 1141 - addr, U16_MAX, 1141 + addr, peer_id, U16_MAX, 1142 1142 tipc_link_min_win(snd_l), 1143 1143 tipc_link_max_win(snd_l), 1144 1144 n->capabilities, ··· 2435 2435 return -ENOMEM; 2436 2436 2437 2437 if (strcmp(name, tipc_bclink_name) == 0) { 2438 - err = tipc_nl_add_bc_link(net, &msg); 2438 + err = tipc_nl_add_bc_link(net, &msg, tipc_net(net)->bcl); 2439 2439 if (err) 2440 2440 goto err_free; 2441 2441 } else { ··· 2479 2479 struct tipc_node *node; 2480 2480 struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1]; 2481 2481 struct net *net = sock_net(skb->sk); 2482 + struct tipc_net *tn = tipc_net(net); 2482 2483 struct tipc_link_entry *le; 2483 2484 2484 2485 if (!info->attrs[TIPC_NLA_LINK]) ··· 2496 2495 2497 2496 link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]); 2498 2497 2499 - if (strcmp(link_name, tipc_bclink_name) == 0) { 2500 - err = tipc_bclink_reset_stats(net); 2498 + err = -EINVAL; 2499 + if (!strcmp(link_name, tipc_bclink_name)) { 2500 + err = tipc_bclink_reset_stats(net, tipc_bc_sndlink(net)); 2501 2501 if (err) 2502 2502 return err; 2503 2503 return 0; 2504 + } else if (strstr(link_name, tipc_bclink_name)) { 2505 + rcu_read_lock(); 2506 + list_for_each_entry_rcu(node, &tn->node_list, list) { 2507 + tipc_node_read_lock(node); 2508 + link = node->bc_entry.link; 2509 + if (link && !strcmp(link_name, tipc_link_name(link))) { 2510 + err = tipc_bclink_reset_stats(net, link); 2511 + tipc_node_read_unlock(node); 2512 + break; 2513 + } 2514 + tipc_node_read_unlock(node); 2515 + } 2516 + rcu_read_unlock(); 2517 + return err; 2504 2518 } 2505 2519 2506 2520 node = tipc_node_find_by_name(net, link_name, &bearer_id); ··· 2539 2523 2540 2524 /* Caller should hold node lock */ 2541 2525 static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg, 2542 - struct tipc_node *node, u32 *prev_link) 2526 + struct tipc_node *node, u32 *prev_link, 2527 + bool bc_link) 2543 2528 { 2544 2529 u32 i; 2545 2530 int err; ··· 2556 2539 if (err) 2557 2540 return err; 2558 2541 } 2542 + 2543 + if (bc_link) { 2544 + *prev_link = i; 2545 + err = tipc_nl_add_bc_link(net, msg, node->bc_entry.link); 2546 + if (err) 2547 + return err; 2548 + } 2549 + 2559 2550 *prev_link = 0; 2560 2551 2561 2552 return 0; ··· 2572 2547 int tipc_nl_node_dump_link(struct sk_buff *skb, struct netlink_callback *cb) 2573 2548 { 2574 2549 struct net *net = sock_net(skb->sk); 2550 + struct nlattr **attrs = genl_dumpit_info(cb)->attrs; 2551 + struct nlattr *link[TIPC_NLA_LINK_MAX + 1]; 2575 2552 struct tipc_net *tn = net_generic(net, tipc_net_id); 2576 2553 struct tipc_node *node; 2577 2554 struct tipc_nl_msg msg; 2578 2555 u32 prev_node = cb->args[0]; 2579 2556 u32 prev_link = cb->args[1]; 2580 2557 int done = cb->args[2]; 2558 + bool bc_link = cb->args[3]; 2581 2559 int err; 2582 2560 2583 2561 if (done) 2584 2562 return 0; 2563 + 2564 + if (!prev_node) { 2565 + /* Check if broadcast-receiver links dumping is needed */ 2566 + if (attrs && attrs[TIPC_NLA_LINK]) { 2567 + err = nla_parse_nested_deprecated(link, 2568 + TIPC_NLA_LINK_MAX, 2569 + attrs[TIPC_NLA_LINK], 2570 + tipc_nl_link_policy, 2571 + NULL); 2572 + if (unlikely(err)) 2573 + return err; 2574 + if (unlikely(!link[TIPC_NLA_LINK_BROADCAST])) 2575 + return -EINVAL; 2576 + bc_link = true; 2577 + } 2578 + } 2585 2579 2586 2580 msg.skb = skb; 2587 2581 msg.portid = NETLINK_CB(cb->skb).portid; ··· 2625 2581 list) { 2626 2582 tipc_node_read_lock(node); 2627 2583 err = __tipc_nl_add_node_links(net, &msg, node, 2628 - &prev_link); 2584 + &prev_link, bc_link); 2629 2585 tipc_node_read_unlock(node); 2630 2586 if (err) 2631 2587 goto out; ··· 2633 2589 prev_node = node->addr; 2634 2590 } 2635 2591 } else { 2636 - err = tipc_nl_add_bc_link(net, &msg); 2592 + err = tipc_nl_add_bc_link(net, &msg, tn->bcl); 2637 2593 if (err) 2638 2594 goto out; 2639 2595 2640 2596 list_for_each_entry_rcu(node, &tn->node_list, list) { 2641 2597 tipc_node_read_lock(node); 2642 2598 err = __tipc_nl_add_node_links(net, &msg, node, 2643 - &prev_link); 2599 + &prev_link, bc_link); 2644 2600 tipc_node_read_unlock(node); 2645 2601 if (err) 2646 2602 goto out; ··· 2655 2611 cb->args[0] = prev_node; 2656 2612 cb->args[1] = prev_link; 2657 2613 cb->args[2] = done; 2614 + cb->args[3] = bc_link; 2658 2615 2659 2616 return skb->len; 2660 2617 }
+2 -2
net/tipc/trace.h
··· 255 255 256 256 TP_fast_assign( 257 257 __assign_str(header, header); 258 - tipc_link_name_ext(l, __entry->name); 258 + memcpy(__entry->name, tipc_link_name(l), TIPC_MAX_LINK_NAME); 259 259 tipc_link_dump(l, dqueues, __get_str(buf)); 260 260 ), 261 261 ··· 295 295 ), 296 296 297 297 TP_fast_assign( 298 - tipc_link_name_ext(r, __entry->name); 298 + memcpy(__entry->name, tipc_link_name(r), TIPC_MAX_LINK_NAME); 299 299 __entry->from = f; 300 300 __entry->to = t; 301 301 __entry->len = skb_queue_len(tq);