struct list_head vlans;
};
+/**
+ * struct macvlan_rx_stats - MACVLAN percpu rx stats
+ * @rx_packets: number of received packets
+ * @rx_bytes: number of received bytes
+ * @multicast: number of received multicast packets
+ * @rx_errors: number of errors
+ */
+struct macvlan_rx_stats {
+ unsigned long rx_packets;
+ unsigned long rx_bytes;
+ unsigned long multicast;
+ unsigned long rx_errors;
+};
+
struct macvlan_dev {
struct net_device *dev;
struct list_head list;
struct hlist_node hlist;
struct macvlan_port *port;
struct net_device *lowerdev;
+ struct macvlan_rx_stats *rx_stats;
};
struct net_device *dev;
struct sk_buff *nskb;
unsigned int i;
+ struct macvlan_rx_stats *rx_stats;
if (skb->protocol == htons(ETH_P_PAUSE))
return;
for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) {
dev = vlan->dev;
+ rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id());
nskb = skb_clone(skb, GFP_ATOMIC);
if (nskb == NULL) {
- dev->stats.rx_errors++;
- dev->stats.rx_dropped++;
+ rx_stats->rx_errors++;
continue;
}
- dev->stats.rx_bytes += skb->len + ETH_HLEN;
- dev->stats.rx_packets++;
- dev->stats.multicast++;
+ rx_stats->rx_bytes += skb->len + ETH_HLEN;
+ rx_stats->rx_packets++;
+ rx_stats->multicast++;
nskb->dev = dev;
if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast))
const struct macvlan_port *port;
const struct macvlan_dev *vlan;
struct net_device *dev;
+ struct macvlan_rx_stats *rx_stats;
port = rcu_dereference(skb->dev->macvlan_port);
if (port == NULL)
kfree_skb(skb);
return NULL;
}
-
+ rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id());
skb = skb_share_check(skb, GFP_ATOMIC);
if (skb == NULL) {
- dev->stats.rx_errors++;
- dev->stats.rx_dropped++;
+ rx_stats->rx_errors++;
return NULL;
}
- dev->stats.rx_bytes += skb->len + ETH_HLEN;
- dev->stats.rx_packets++;
+ rx_stats->rx_bytes += skb->len + ETH_HLEN;
+ rx_stats->rx_packets++;
skb->dev = dev;
skb->pkt_type = PACKET_HOST;
macvlan_set_lockdep_class(dev);
+ vlan->rx_stats = alloc_percpu(struct macvlan_rx_stats);
+ if (!vlan->rx_stats)
+ return -ENOMEM;
+
return 0;
}
+static void macvlan_uninit(struct net_device *dev)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ free_percpu(vlan->rx_stats);
+}
+
+static struct net_device_stats *macvlan_dev_get_stats(struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ dev_txq_stats_fold(dev, stats);
+
+ if (vlan->rx_stats) {
+ struct macvlan_rx_stats *p, rx = {0};
+ int i;
+
+ for_each_possible_cpu(i) {
+ p = per_cpu_ptr(vlan->rx_stats, i);
+ rx.rx_packets += p->rx_packets;
+ rx.rx_bytes += p->rx_bytes;
+ rx.rx_errors += p->rx_errors;
+ rx.multicast += p->multicast;
+ }
+ stats->rx_packets = rx.rx_packets;
+ stats->rx_bytes = rx.rx_bytes;
+ stats->rx_errors = rx.rx_errors;
+ stats->rx_dropped = rx.rx_errors;
+ stats->multicast = rx.multicast;
+ }
+ return stats;
+}
+
static void macvlan_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *drvinfo)
{
static const struct net_device_ops macvlan_netdev_ops = {
.ndo_init = macvlan_init,
+ .ndo_uninit = macvlan_uninit,
.ndo_open = macvlan_open,
.ndo_stop = macvlan_stop,
.ndo_start_xmit = macvlan_start_xmit,
.ndo_change_rx_flags = macvlan_change_rx_flags,
.ndo_set_mac_address = macvlan_set_mac_address,
.ndo_set_multicast_list = macvlan_set_multicast_list,
+ .ndo_get_stats = macvlan_dev_get_stats,
.ndo_validate_addr = eth_validate_addr,
};