aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2014-05-16 15:48:25 -0400
committerDavid S. Miller <davem@davemloft.net>2014-05-16 22:39:01 -0400
commitbeeecd42c3b41d17d0bf1d839db99274c287f514 (patch)
tree65bbae15a9ec1d3368a059f3bd4fad7d7c0a018b /drivers/net/usb
parent43e4c6dfc0fd781e68f20caf563a06f5c6ece995 (diff)
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>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/cdc_mbim.c6
-rw-r--r--drivers/net/usb/cdc_ncm.c91
2 files changed, 97 insertions, 0 deletions
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index bc23273d0455..5ee7a1dbc023 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -420,6 +420,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
420 struct usb_cdc_ncm_dpe16 *dpe16; 420 struct usb_cdc_ncm_dpe16 *dpe16;
421 int ndpoffset; 421 int ndpoffset;
422 int loopcount = 50; /* arbitrary max preventing infinite loop */ 422 int loopcount = 50; /* arbitrary max preventing infinite loop */
423 u32 payload = 0;
423 u8 *c; 424 u8 *c;
424 u16 tci; 425 u16 tci;
425 426
@@ -482,6 +483,7 @@ next_ndp:
482 if (!skb) 483 if (!skb)
483 goto error; 484 goto error;
484 usbnet_skb_return(dev, skb); 485 usbnet_skb_return(dev, skb);
486 payload += len; /* count payload bytes in this NTB */
485 } 487 }
486 } 488 }
487err_ndp: 489err_ndp:
@@ -490,6 +492,10 @@ err_ndp:
490 if (ndpoffset && loopcount--) 492 if (ndpoffset && loopcount--)
491 goto next_ndp; 493 goto next_ndp;
492 494
495 /* update stats */
496 ctx->rx_overhead += skb_in->len - payload;
497 ctx->rx_ntbs++;
498
493 return 1; 499 return 1;
494error: 500error:
495 return 0; 501 return 0;
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 9592d4669435..f4b439847d04 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -65,6 +65,68 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
65static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); 65static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
66static struct usb_driver cdc_ncm_driver; 66static struct usb_driver cdc_ncm_driver;
67 67
68struct 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
80static 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
91static 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
101static 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
116static 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
68static int cdc_ncm_get_coalesce(struct net_device *netdev, 130static int cdc_ncm_get_coalesce(struct net_device *netdev,
69 struct ethtool_coalesce *ec) 131 struct ethtool_coalesce *ec)
70{ 132{
@@ -122,6 +184,9 @@ static const struct ethtool_ops cdc_ncm_ethtool_ops = {
122 .get_msglevel = usbnet_get_msglevel, 184 .get_msglevel = usbnet_get_msglevel,
123 .set_msglevel = usbnet_set_msglevel, 185 .set_msglevel = usbnet_set_msglevel,
124 .get_ts_info = ethtool_op_get_ts_info, 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 .get_coalesce = cdc_ncm_get_coalesce, 190 .get_coalesce = cdc_ncm_get_coalesce,
126 .set_coalesce = cdc_ncm_set_coalesce, 191 .set_coalesce = cdc_ncm_set_coalesce,
127}; 192};
@@ -862,6 +927,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
862 927
863 /* count total number of frames in this NTB */ 928 /* count total number of frames in this NTB */
864 ctx->tx_curr_frame_num = 0; 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 for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { 935 for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
@@ -899,6 +967,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
899 ctx->tx_rem_sign = sign; 967 ctx->tx_rem_sign = sign;
900 skb = NULL; 968 skb = NULL;
901 ready2send = 1; 969 ready2send = 1;
970 ctx->tx_reason_ntb_full++; /* count reason for transmitting */
902 } 971 }
903 break; 972 break;
904 } 973 }
@@ -912,12 +981,14 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
912 ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); 981 ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
913 ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); 982 ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
914 memcpy(skb_put(skb_out, skb->len), skb->data, skb->len); 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 dev_kfree_skb_any(skb); 985 dev_kfree_skb_any(skb);
916 skb = NULL; 986 skb = NULL;
917 987
918 /* send now if this NDP is full */ 988 /* send now if this NDP is full */
919 if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) { 989 if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
920 ready2send = 1; 990 ready2send = 1;
991 ctx->tx_reason_ndp_full++; /* count reason for transmitting */
921 break; 992 break;
922 } 993 }
923 } 994 }
@@ -947,6 +1018,8 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
947 goto exit_no_skb; 1018 goto exit_no_skb;
948 1019
949 } else { 1020 } else {
1021 if (n == ctx->tx_max_datagrams)
1022 ctx->tx_reason_max_datagram++; /* count reason for transmitting */
950 /* frame goes out */ 1023 /* frame goes out */
951 /* variables will be reset at next call */ 1024 /* variables will be reset at next call */
952 } 1025 }
@@ -974,6 +1047,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
974 /* return skb */ 1047 /* return skb */
975 ctx->tx_curr_skb = NULL; 1048 ctx->tx_curr_skb = NULL;
976 dev->net->stats.tx_packets += ctx->tx_curr_frame_num; 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 return skb_out; 1061 return skb_out;
978 1062
979exit_no_skb: 1063exit_no_skb:
@@ -1014,6 +1098,7 @@ static void cdc_ncm_txpath_bh(unsigned long param)
1014 cdc_ncm_tx_timeout_start(ctx); 1098 cdc_ncm_tx_timeout_start(ctx);
1015 spin_unlock_bh(&ctx->mtx); 1099 spin_unlock_bh(&ctx->mtx);
1016 } else if (dev->net != NULL) { 1100 } else if (dev->net != NULL) {
1101 ctx->tx_reason_timeout++; /* count reason for transmitting */
1017 spin_unlock_bh(&ctx->mtx); 1102 spin_unlock_bh(&ctx->mtx);
1018 netif_tx_lock_bh(dev->net); 1103 netif_tx_lock_bh(dev->net);
1019 usbnet_start_xmit(NULL, dev->net); 1104 usbnet_start_xmit(NULL, dev->net);
@@ -1149,6 +1234,7 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
1149 struct usb_cdc_ncm_dpe16 *dpe16; 1234 struct usb_cdc_ncm_dpe16 *dpe16;
1150 int ndpoffset; 1235 int ndpoffset;
1151 int loopcount = 50; /* arbitrary max preventing infinite loop */ 1236 int loopcount = 50; /* arbitrary max preventing infinite loop */
1237 u32 payload = 0;
1152 1238
1153 ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); 1239 ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
1154 if (ndpoffset < 0) 1240 if (ndpoffset < 0)
@@ -1201,6 +1287,7 @@ next_ndp:
1201 skb->data = ((u8 *)skb_in->data) + offset; 1287 skb->data = ((u8 *)skb_in->data) + offset;
1202 skb_set_tail_pointer(skb, len); 1288 skb_set_tail_pointer(skb, len);
1203 usbnet_skb_return(dev, skb); 1289 usbnet_skb_return(dev, skb);
1290 payload += len; /* count payload bytes in this NTB */
1204 } 1291 }
1205 } 1292 }
1206err_ndp: 1293err_ndp:
@@ -1209,6 +1296,10 @@ err_ndp:
1209 if (ndpoffset && loopcount--) 1296 if (ndpoffset && loopcount--)
1210 goto next_ndp; 1297 goto next_ndp;
1211 1298
1299 /* update stats */
1300 ctx->rx_overhead += skb_in->len - payload;
1301 ctx->rx_ntbs++;
1302
1212 return 1; 1303 return 1;
1213error: 1304error:
1214 return 0; 1305 return 0;