diff options
author | Reinhard Speyerer <rspmn@arcor.de> | 2019-06-12 13:02:46 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-06-14 22:05:58 -0400 |
commit | 44f82312fe9113bab6642f4d0eab6b1b7902b6e1 (patch) | |
tree | e633b17fbb334685e9f51f59ab7bf1221c4db8db /drivers/net/usb | |
parent | 61356088ace1866a847a727d4d40da7bf00b67fc (diff) |
qmi_wwan: add network device usage statistics for qmimux devices
Add proper network device usage statistics for qmimux devices
instead of reporting all-zero values for them.
Fixes: c6adf77953bc ("net: usb: qmi_wwan: add qmap mux protocol support")
Cc: Daniele Palmas <dnlplm@gmail.com>
Signed-off-by: Reinhard Speyerer <rspmn@arcor.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 76 |
1 files changed, 71 insertions, 5 deletions
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index fd3d078a1923..b0a96459621f 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/usb/cdc.h> | 22 | #include <linux/usb/cdc.h> |
23 | #include <linux/usb/usbnet.h> | 23 | #include <linux/usb/usbnet.h> |
24 | #include <linux/usb/cdc-wdm.h> | 24 | #include <linux/usb/cdc-wdm.h> |
25 | #include <linux/u64_stats_sync.h> | ||
25 | 26 | ||
26 | /* This driver supports wwan (3G/LTE/?) devices using a vendor | 27 | /* This driver supports wwan (3G/LTE/?) devices using a vendor |
27 | * specific management protocol called Qualcomm MSM Interface (QMI) - | 28 | * specific management protocol called Qualcomm MSM Interface (QMI) - |
@@ -75,6 +76,7 @@ struct qmimux_hdr { | |||
75 | struct qmimux_priv { | 76 | struct qmimux_priv { |
76 | struct net_device *real_dev; | 77 | struct net_device *real_dev; |
77 | u8 mux_id; | 78 | u8 mux_id; |
79 | struct pcpu_sw_netstats __percpu *stats64; | ||
78 | }; | 80 | }; |
79 | 81 | ||
80 | static int qmimux_open(struct net_device *dev) | 82 | static int qmimux_open(struct net_device *dev) |
@@ -101,19 +103,65 @@ static netdev_tx_t qmimux_start_xmit(struct sk_buff *skb, struct net_device *dev | |||
101 | struct qmimux_priv *priv = netdev_priv(dev); | 103 | struct qmimux_priv *priv = netdev_priv(dev); |
102 | unsigned int len = skb->len; | 104 | unsigned int len = skb->len; |
103 | struct qmimux_hdr *hdr; | 105 | struct qmimux_hdr *hdr; |
106 | netdev_tx_t ret; | ||
104 | 107 | ||
105 | hdr = skb_push(skb, sizeof(struct qmimux_hdr)); | 108 | hdr = skb_push(skb, sizeof(struct qmimux_hdr)); |
106 | hdr->pad = 0; | 109 | hdr->pad = 0; |
107 | hdr->mux_id = priv->mux_id; | 110 | hdr->mux_id = priv->mux_id; |
108 | hdr->pkt_len = cpu_to_be16(len); | 111 | hdr->pkt_len = cpu_to_be16(len); |
109 | skb->dev = priv->real_dev; | 112 | skb->dev = priv->real_dev; |
110 | return dev_queue_xmit(skb); | 113 | ret = dev_queue_xmit(skb); |
114 | |||
115 | if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { | ||
116 | struct pcpu_sw_netstats *stats64 = this_cpu_ptr(priv->stats64); | ||
117 | |||
118 | u64_stats_update_begin(&stats64->syncp); | ||
119 | stats64->tx_packets++; | ||
120 | stats64->tx_bytes += len; | ||
121 | u64_stats_update_end(&stats64->syncp); | ||
122 | } else { | ||
123 | dev->stats.tx_dropped++; | ||
124 | } | ||
125 | |||
126 | return ret; | ||
127 | } | ||
128 | |||
129 | static void qmimux_get_stats64(struct net_device *net, | ||
130 | struct rtnl_link_stats64 *stats) | ||
131 | { | ||
132 | struct qmimux_priv *priv = netdev_priv(net); | ||
133 | unsigned int start; | ||
134 | int cpu; | ||
135 | |||
136 | netdev_stats_to_stats64(stats, &net->stats); | ||
137 | |||
138 | for_each_possible_cpu(cpu) { | ||
139 | struct pcpu_sw_netstats *stats64; | ||
140 | u64 rx_packets, rx_bytes; | ||
141 | u64 tx_packets, tx_bytes; | ||
142 | |||
143 | stats64 = per_cpu_ptr(priv->stats64, cpu); | ||
144 | |||
145 | do { | ||
146 | start = u64_stats_fetch_begin_irq(&stats64->syncp); | ||
147 | rx_packets = stats64->rx_packets; | ||
148 | rx_bytes = stats64->rx_bytes; | ||
149 | tx_packets = stats64->tx_packets; | ||
150 | tx_bytes = stats64->tx_bytes; | ||
151 | } while (u64_stats_fetch_retry_irq(&stats64->syncp, start)); | ||
152 | |||
153 | stats->rx_packets += rx_packets; | ||
154 | stats->rx_bytes += rx_bytes; | ||
155 | stats->tx_packets += tx_packets; | ||
156 | stats->tx_bytes += tx_bytes; | ||
157 | } | ||
111 | } | 158 | } |
112 | 159 | ||
113 | static const struct net_device_ops qmimux_netdev_ops = { | 160 | static const struct net_device_ops qmimux_netdev_ops = { |
114 | .ndo_open = qmimux_open, | 161 | .ndo_open = qmimux_open, |
115 | .ndo_stop = qmimux_stop, | 162 | .ndo_stop = qmimux_stop, |
116 | .ndo_start_xmit = qmimux_start_xmit, | 163 | .ndo_start_xmit = qmimux_start_xmit, |
164 | .ndo_get_stats64 = qmimux_get_stats64, | ||
117 | }; | 165 | }; |
118 | 166 | ||
119 | static void qmimux_setup(struct net_device *dev) | 167 | static void qmimux_setup(struct net_device *dev) |
@@ -198,8 +246,19 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | |||
198 | } | 246 | } |
199 | 247 | ||
200 | skb_put_data(skbn, skb->data + offset + qmimux_hdr_sz, pkt_len); | 248 | skb_put_data(skbn, skb->data + offset + qmimux_hdr_sz, pkt_len); |
201 | if (netif_rx(skbn) != NET_RX_SUCCESS) | 249 | if (netif_rx(skbn) != NET_RX_SUCCESS) { |
250 | net->stats.rx_errors++; | ||
202 | return 0; | 251 | return 0; |
252 | } else { | ||
253 | struct pcpu_sw_netstats *stats64; | ||
254 | struct qmimux_priv *priv = netdev_priv(net); | ||
255 | |||
256 | stats64 = this_cpu_ptr(priv->stats64); | ||
257 | u64_stats_update_begin(&stats64->syncp); | ||
258 | stats64->rx_packets++; | ||
259 | stats64->rx_bytes += pkt_len; | ||
260 | u64_stats_update_end(&stats64->syncp); | ||
261 | } | ||
203 | 262 | ||
204 | skip: | 263 | skip: |
205 | offset += len + qmimux_hdr_sz; | 264 | offset += len + qmimux_hdr_sz; |
@@ -223,6 +282,12 @@ static int qmimux_register_device(struct net_device *real_dev, u8 mux_id) | |||
223 | priv->mux_id = mux_id; | 282 | priv->mux_id = mux_id; |
224 | priv->real_dev = real_dev; | 283 | priv->real_dev = real_dev; |
225 | 284 | ||
285 | priv->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); | ||
286 | if (!priv->stats64) { | ||
287 | err = -ENOBUFS; | ||
288 | goto out_free_newdev; | ||
289 | } | ||
290 | |||
226 | err = register_netdevice(new_dev); | 291 | err = register_netdevice(new_dev); |
227 | if (err < 0) | 292 | if (err < 0) |
228 | goto out_free_newdev; | 293 | goto out_free_newdev; |
@@ -252,6 +317,7 @@ static void qmimux_unregister_device(struct net_device *dev) | |||
252 | struct qmimux_priv *priv = netdev_priv(dev); | 317 | struct qmimux_priv *priv = netdev_priv(dev); |
253 | struct net_device *real_dev = priv->real_dev; | 318 | struct net_device *real_dev = priv->real_dev; |
254 | 319 | ||
320 | free_percpu(priv->stats64); | ||
255 | netdev_upper_dev_unlink(real_dev, dev); | 321 | netdev_upper_dev_unlink(real_dev, dev); |
256 | unregister_netdevice(dev); | 322 | unregister_netdevice(dev); |
257 | 323 | ||