|
@@ -316,6 +316,7 @@ static void __usbnet_status_stop_force(struct usbnet *dev)
|
|
|
*/
|
|
|
void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
|
|
|
{
|
|
|
+ struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
|
|
int status;
|
|
|
|
|
|
if (test_bit(EVENT_RX_PAUSED, &dev->flags)) {
|
|
@@ -327,8 +328,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
|
|
|
if (skb->protocol == 0)
|
|
|
skb->protocol = eth_type_trans (skb, dev->net);
|
|
|
|
|
|
- dev->net->stats.rx_packets++;
|
|
|
- dev->net->stats.rx_bytes += skb->len;
|
|
|
+ u64_stats_update_begin(&stats64->syncp);
|
|
|
+ stats64->rx_packets++;
|
|
|
+ stats64->rx_bytes += skb->len;
|
|
|
+ u64_stats_update_end(&stats64->syncp);
|
|
|
|
|
|
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
|
|
|
skb->len + sizeof (struct ethhdr), skb->protocol);
|
|
@@ -981,6 +984,37 @@ int usbnet_set_link_ksettings(struct net_device *net,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(usbnet_set_link_ksettings);
|
|
|
|
|
|
+void usbnet_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats)
|
|
|
+{
|
|
|
+ struct usbnet *dev = netdev_priv(net);
|
|
|
+ unsigned int start;
|
|
|
+ int cpu;
|
|
|
+
|
|
|
+ netdev_stats_to_stats64(stats, &net->stats);
|
|
|
+
|
|
|
+ for_each_possible_cpu(cpu) {
|
|
|
+ struct pcpu_sw_netstats *stats64;
|
|
|
+ u64 rx_packets, rx_bytes;
|
|
|
+ u64 tx_packets, tx_bytes;
|
|
|
+
|
|
|
+ stats64 = per_cpu_ptr(dev->stats64, cpu);
|
|
|
+
|
|
|
+ do {
|
|
|
+ start = u64_stats_fetch_begin_irq(&stats64->syncp);
|
|
|
+ rx_packets = stats64->rx_packets;
|
|
|
+ rx_bytes = stats64->rx_bytes;
|
|
|
+ tx_packets = stats64->tx_packets;
|
|
|
+ tx_bytes = stats64->tx_bytes;
|
|
|
+ } while (u64_stats_fetch_retry_irq(&stats64->syncp, start));
|
|
|
+
|
|
|
+ stats->rx_packets += rx_packets;
|
|
|
+ stats->rx_bytes += rx_bytes;
|
|
|
+ stats->tx_packets += tx_packets;
|
|
|
+ stats->tx_bytes += tx_bytes;
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(usbnet_get_stats64);
|
|
|
+
|
|
|
u32 usbnet_get_link (struct net_device *net)
|
|
|
{
|
|
|
struct usbnet *dev = netdev_priv(net);
|
|
@@ -1212,8 +1246,12 @@ static void tx_complete (struct urb *urb)
|
|
|
struct usbnet *dev = entry->dev;
|
|
|
|
|
|
if (urb->status == 0) {
|
|
|
- dev->net->stats.tx_packets += entry->packets;
|
|
|
- dev->net->stats.tx_bytes += entry->length;
|
|
|
+ struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
|
|
+
|
|
|
+ u64_stats_update_begin(&stats64->syncp);
|
|
|
+ stats64->tx_packets += entry->packets;
|
|
|
+ stats64->tx_bytes += entry->length;
|
|
|
+ u64_stats_update_end(&stats64->syncp);
|
|
|
} else {
|
|
|
dev->net->stats.tx_errors++;
|
|
|
|
|
@@ -1570,6 +1608,7 @@ void usbnet_disconnect (struct usb_interface *intf)
|
|
|
usb_free_urb(dev->interrupt);
|
|
|
kfree(dev->padding_pkt);
|
|
|
|
|
|
+ free_percpu(dev->stats64);
|
|
|
free_netdev(net);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(usbnet_disconnect);
|
|
@@ -1581,6 +1620,7 @@ static const struct net_device_ops usbnet_netdev_ops = {
|
|
|
.ndo_tx_timeout = usbnet_tx_timeout,
|
|
|
.ndo_set_rx_mode = usbnet_set_rx_mode,
|
|
|
.ndo_change_mtu = usbnet_change_mtu,
|
|
|
+ .ndo_get_stats64 = usbnet_get_stats64,
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
};
|
|
@@ -1642,6 +1682,11 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
|
|
|
dev->intf = udev;
|
|
|
dev->driver_info = info;
|
|
|
dev->driver_name = name;
|
|
|
+
|
|
|
+ dev->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
|
|
+ if (!dev->stats64)
|
|
|
+ goto out0;
|
|
|
+
|
|
|
dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
|
|
|
| NETIF_MSG_PROBE | NETIF_MSG_LINK);
|
|
|
init_waitqueue_head(&dev->wait);
|
|
@@ -1781,6 +1826,8 @@ out1:
|
|
|
*/
|
|
|
cancel_work_sync(&dev->kevent);
|
|
|
del_timer_sync(&dev->delay);
|
|
|
+ free_percpu(dev->stats64);
|
|
|
+out0:
|
|
|
free_netdev(net);
|
|
|
out:
|
|
|
return status;
|