Commit beeecd42c3b41d17d0bf1d839db99274c287f514
Committed by
David S. Miller
1 parent
43e4c6dfc0
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
net: cdc_ncm/cdc_mbim: adding NCM protocol statistics
To have an idea of the effects of the protocol coalescing it's useful to have some counters showing the different aspects. Due to the asymmetrical usbnet interface the netdev rx_bytes counter has been counting real received payload, while the tx_bytes counter has included the NCM/MBIM framing overhead. This overhead can be many times the payload because of the aggressive padding strategy of this driver, and will vary a lot depending on device and traffic. With very few exceptions, users are only interested in the payload size. Having an somewhat accurate payload byte counter is particularly important for mobile broadband devices, which many NCM devices and of course all MBIM devices are. Users and userspace applications will use this counter to monitor account quotas. Having protocol specific counters for the overhead, we are now able to correct the tx_bytes netdev counter so that it shows the real payload Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 3 changed files with 108 additions and 0 deletions Side-by-side Diff
drivers/net/usb/cdc_mbim.c
... | ... | @@ -420,6 +420,7 @@ |
420 | 420 | struct usb_cdc_ncm_dpe16 *dpe16; |
421 | 421 | int ndpoffset; |
422 | 422 | int loopcount = 50; /* arbitrary max preventing infinite loop */ |
423 | + u32 payload = 0; | |
423 | 424 | u8 *c; |
424 | 425 | u16 tci; |
425 | 426 | |
... | ... | @@ -482,6 +483,7 @@ |
482 | 483 | if (!skb) |
483 | 484 | goto error; |
484 | 485 | usbnet_skb_return(dev, skb); |
486 | + payload += len; /* count payload bytes in this NTB */ | |
485 | 487 | } |
486 | 488 | } |
487 | 489 | err_ndp: |
... | ... | @@ -489,6 +491,10 @@ |
489 | 491 | ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); |
490 | 492 | if (ndpoffset && loopcount--) |
491 | 493 | goto next_ndp; |
494 | + | |
495 | + /* update stats */ | |
496 | + ctx->rx_overhead += skb_in->len - payload; | |
497 | + ctx->rx_ntbs++; | |
492 | 498 | |
493 | 499 | return 1; |
494 | 500 | error: |
drivers/net/usb/cdc_ncm.c
... | ... | @@ -65,6 +65,68 @@ |
65 | 65 | static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); |
66 | 66 | static struct usb_driver cdc_ncm_driver; |
67 | 67 | |
68 | +struct cdc_ncm_stats { | |
69 | + char stat_string[ETH_GSTRING_LEN]; | |
70 | + int sizeof_stat; | |
71 | + int stat_offset; | |
72 | +}; | |
73 | + | |
74 | +#define CDC_NCM_STAT(str, m) { \ | |
75 | + .stat_string = str, \ | |
76 | + .sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \ | |
77 | + .stat_offset = offsetof(struct cdc_ncm_ctx, m) } | |
78 | +#define CDC_NCM_SIMPLE_STAT(m) CDC_NCM_STAT(__stringify(m), m) | |
79 | + | |
80 | +static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = { | |
81 | + CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full), | |
82 | + CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full), | |
83 | + CDC_NCM_SIMPLE_STAT(tx_reason_timeout), | |
84 | + CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram), | |
85 | + CDC_NCM_SIMPLE_STAT(tx_overhead), | |
86 | + CDC_NCM_SIMPLE_STAT(tx_ntbs), | |
87 | + CDC_NCM_SIMPLE_STAT(rx_overhead), | |
88 | + CDC_NCM_SIMPLE_STAT(rx_ntbs), | |
89 | +}; | |
90 | + | |
91 | +static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset) | |
92 | +{ | |
93 | + switch (sset) { | |
94 | + case ETH_SS_STATS: | |
95 | + return ARRAY_SIZE(cdc_ncm_gstrings_stats); | |
96 | + default: | |
97 | + return -EOPNOTSUPP; | |
98 | + } | |
99 | +} | |
100 | + | |
101 | +static void cdc_ncm_get_ethtool_stats(struct net_device *netdev, | |
102 | + struct ethtool_stats __always_unused *stats, | |
103 | + u64 *data) | |
104 | +{ | |
105 | + struct usbnet *dev = netdev_priv(netdev); | |
106 | + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; | |
107 | + int i; | |
108 | + char *p = NULL; | |
109 | + | |
110 | + for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { | |
111 | + p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset; | |
112 | + data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; | |
113 | + } | |
114 | +} | |
115 | + | |
116 | +static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data) | |
117 | +{ | |
118 | + u8 *p = data; | |
119 | + int i; | |
120 | + | |
121 | + switch (stringset) { | |
122 | + case ETH_SS_STATS: | |
123 | + for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { | |
124 | + memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); | |
125 | + p += ETH_GSTRING_LEN; | |
126 | + } | |
127 | + } | |
128 | +} | |
129 | + | |
68 | 130 | static int cdc_ncm_get_coalesce(struct net_device *netdev, |
69 | 131 | struct ethtool_coalesce *ec) |
70 | 132 | { |
... | ... | @@ -122,6 +184,9 @@ |
122 | 184 | .get_msglevel = usbnet_get_msglevel, |
123 | 185 | .set_msglevel = usbnet_set_msglevel, |
124 | 186 | .get_ts_info = ethtool_op_get_ts_info, |
187 | + .get_sset_count = cdc_ncm_get_sset_count, | |
188 | + .get_strings = cdc_ncm_get_strings, | |
189 | + .get_ethtool_stats = cdc_ncm_get_ethtool_stats, | |
125 | 190 | .get_coalesce = cdc_ncm_get_coalesce, |
126 | 191 | .set_coalesce = cdc_ncm_set_coalesce, |
127 | 192 | }; |
... | ... | @@ -862,6 +927,9 @@ |
862 | 927 | |
863 | 928 | /* count total number of frames in this NTB */ |
864 | 929 | ctx->tx_curr_frame_num = 0; |
930 | + | |
931 | + /* recent payload counter for this skb_out */ | |
932 | + ctx->tx_curr_frame_payload = 0; | |
865 | 933 | } |
866 | 934 | |
867 | 935 | for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { |
... | ... | @@ -899,6 +967,7 @@ |
899 | 967 | ctx->tx_rem_sign = sign; |
900 | 968 | skb = NULL; |
901 | 969 | ready2send = 1; |
970 | + ctx->tx_reason_ntb_full++; /* count reason for transmitting */ | |
902 | 971 | } |
903 | 972 | break; |
904 | 973 | } |
905 | 974 | |
... | ... | @@ -912,12 +981,14 @@ |
912 | 981 | ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); |
913 | 982 | ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); |
914 | 983 | memcpy(skb_put(skb_out, skb->len), skb->data, skb->len); |
984 | + ctx->tx_curr_frame_payload += skb->len; /* count real tx payload data */ | |
915 | 985 | dev_kfree_skb_any(skb); |
916 | 986 | skb = NULL; |
917 | 987 | |
918 | 988 | /* send now if this NDP is full */ |
919 | 989 | if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) { |
920 | 990 | ready2send = 1; |
991 | + ctx->tx_reason_ndp_full++; /* count reason for transmitting */ | |
921 | 992 | break; |
922 | 993 | } |
923 | 994 | } |
... | ... | @@ -947,6 +1018,8 @@ |
947 | 1018 | goto exit_no_skb; |
948 | 1019 | |
949 | 1020 | } else { |
1021 | + if (n == ctx->tx_max_datagrams) | |
1022 | + ctx->tx_reason_max_datagram++; /* count reason for transmitting */ | |
950 | 1023 | /* frame goes out */ |
951 | 1024 | /* variables will be reset at next call */ |
952 | 1025 | } |
... | ... | @@ -974,6 +1047,17 @@ |
974 | 1047 | /* return skb */ |
975 | 1048 | ctx->tx_curr_skb = NULL; |
976 | 1049 | dev->net->stats.tx_packets += ctx->tx_curr_frame_num; |
1050 | + | |
1051 | + /* keep private stats: framing overhead and number of NTBs */ | |
1052 | + ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; | |
1053 | + ctx->tx_ntbs++; | |
1054 | + | |
1055 | + /* usbnet has already counted all the framing overhead. | |
1056 | + * Adjust the stats so that the tx_bytes counter show real | |
1057 | + * payload data instead. | |
1058 | + */ | |
1059 | + dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; | |
1060 | + | |
977 | 1061 | return skb_out; |
978 | 1062 | |
979 | 1063 | exit_no_skb: |
... | ... | @@ -1014,6 +1098,7 @@ |
1014 | 1098 | cdc_ncm_tx_timeout_start(ctx); |
1015 | 1099 | spin_unlock_bh(&ctx->mtx); |
1016 | 1100 | } else if (dev->net != NULL) { |
1101 | + ctx->tx_reason_timeout++; /* count reason for transmitting */ | |
1017 | 1102 | spin_unlock_bh(&ctx->mtx); |
1018 | 1103 | netif_tx_lock_bh(dev->net); |
1019 | 1104 | usbnet_start_xmit(NULL, dev->net); |
... | ... | @@ -1149,6 +1234,7 @@ |
1149 | 1234 | struct usb_cdc_ncm_dpe16 *dpe16; |
1150 | 1235 | int ndpoffset; |
1151 | 1236 | int loopcount = 50; /* arbitrary max preventing infinite loop */ |
1237 | + u32 payload = 0; | |
1152 | 1238 | |
1153 | 1239 | ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); |
1154 | 1240 | if (ndpoffset < 0) |
... | ... | @@ -1201,6 +1287,7 @@ |
1201 | 1287 | skb->data = ((u8 *)skb_in->data) + offset; |
1202 | 1288 | skb_set_tail_pointer(skb, len); |
1203 | 1289 | usbnet_skb_return(dev, skb); |
1290 | + payload += len; /* count payload bytes in this NTB */ | |
1204 | 1291 | } |
1205 | 1292 | } |
1206 | 1293 | err_ndp: |
... | ... | @@ -1208,6 +1295,10 @@ |
1208 | 1295 | ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); |
1209 | 1296 | if (ndpoffset && loopcount--) |
1210 | 1297 | goto next_ndp; |
1298 | + | |
1299 | + /* update stats */ | |
1300 | + ctx->rx_overhead += skb_in->len - payload; | |
1301 | + ctx->rx_ntbs++; | |
1211 | 1302 | |
1212 | 1303 | return 1; |
1213 | 1304 | error: |
include/linux/usb/cdc_ncm.h
... | ... | @@ -116,6 +116,17 @@ |
116 | 116 | u16 rx_seq; |
117 | 117 | u16 connected; |
118 | 118 | u16 min_tx_pkt; |
119 | + | |
120 | + /* statistics */ | |
121 | + u32 tx_curr_frame_payload; | |
122 | + u32 tx_reason_ntb_full; | |
123 | + u32 tx_reason_ndp_full; | |
124 | + u32 tx_reason_timeout; | |
125 | + u32 tx_reason_max_datagram; | |
126 | + u64 tx_overhead; | |
127 | + u64 tx_ntbs; | |
128 | + u64 rx_overhead; | |
129 | + u64 rx_ntbs; | |
119 | 130 | }; |
120 | 131 | |
121 | 132 | u8 cdc_ncm_select_altsetting(struct usb_interface *intf); |
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89
-
mentioned in commit 7a1e89