summaryrefslogtreecommitdiffstats
path: root/drivers/net/macvlan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/macvlan.c')
-rw-r--r--drivers/net/macvlan.c88
1 files changed, 72 insertions, 16 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 3aabfd9..ae2b5c7 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -38,12 +38,27 @@ struct macvlan_port {
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;
};
@@ -110,6 +125,7 @@ static void macvlan_broadcast(struct sk_buff *skb,
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;
@@ -117,17 +133,17 @@ static void macvlan_broadcast(struct sk_buff *skb,
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))
@@ -147,6 +163,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
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)
@@ -166,16 +183,15 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
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;
@@ -202,7 +218,7 @@ static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
} else
txq->tx_dropped++;
- return NETDEV_TX_OK;
+ return ret;
}
static int macvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
@@ -365,9 +381,47 @@ static int macvlan_init(struct net_device *dev)
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)
{
@@ -404,6 +458,7 @@ static const struct ethtool_ops macvlan_ethtool_ops = {
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,
@@ -411,6 +466,7 @@ static const struct net_device_ops macvlan_netdev_ops = {
.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,
};
@@ -504,7 +560,7 @@ static int macvlan_get_tx_queues(struct net *net,
return 0;
}
-static int macvlan_newlink(struct net_device *dev,
+static int macvlan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
struct macvlan_dev *vlan = netdev_priv(dev);
@@ -515,7 +571,7 @@ static int macvlan_newlink(struct net_device *dev,
if (!tb[IFLA_LINK])
return -EINVAL;
- lowerdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
+ lowerdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
if (lowerdev == NULL)
return -ENODEV;
@@ -555,13 +611,13 @@ static int macvlan_newlink(struct net_device *dev,
return 0;
}
-static void macvlan_dellink(struct net_device *dev)
+static void macvlan_dellink(struct net_device *dev, struct list_head *head)
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port = vlan->port;
list_del(&vlan->list);
- unregister_netdevice(dev);
+ unregister_netdevice_queue(dev, head);
if (list_empty(&port->vlans))
macvlan_port_destroy(port->dev);
@@ -601,7 +657,7 @@ static int macvlan_device_event(struct notifier_block *unused,
break;
case NETDEV_UNREGISTER:
list_for_each_entry_safe(vlan, next, &port->vlans, list)
- macvlan_dellink(vlan->dev);
+ macvlan_dellink(vlan->dev, NULL);
break;
}
return NOTIFY_DONE;
OpenPOWER on IntegriCloud