Commit beeecd42c3b41d17d0bf1d839db99274c287f514

Authored by Bjørn Mork
Committed by David S. Miller
1 parent 43e4c6dfc0

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);