vlan: add 802.1q netpoll support
Benjamin LaHaise [Thu, 8 Dec 2011 06:20:49 +0000 (06:20 +0000)]
Add netpoll support to 802.1q vlan devices.  Based on the netpoll support
in the bridging code.  Tested on a forced_eth device with netconsole.

Signed-off-by: Benjamin LaHaise <bcrl@kvack.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/8021q/vlan.h
net/8021q/vlan_dev.c

index 28d8dc2..a4886d9 100644 (file)
@@ -41,6 +41,8 @@ struct vlan_pcpu_stats {
        u32                     tx_dropped;
 };
 
+struct netpoll;
+
 /**
  *     struct vlan_dev_priv - VLAN private device data
  *     @nr_ingress_mappings: number of ingress priority mappings
@@ -68,6 +70,9 @@ struct vlan_dev_priv {
 
        struct proc_dir_entry                   *dent;
        struct vlan_pcpu_stats __percpu         *vlan_pcpu_stats;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       struct netpoll                          *netpoll;
+#endif
 };
 
 static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev)
index 3b4db82..0390139 100644 (file)
@@ -33,6 +33,7 @@
 #include "vlan.h"
 #include "vlanproc.h"
 #include <linux/if_vlan.h>
+#include <linux/netpoll.h>
 
 /*
  *     Rebuild the Ethernet MAC header. This is called after an ARP
@@ -158,6 +159,8 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
 
        skb_set_dev(skb, vlan_dev_priv(dev)->real_dev);
        len = skb->len;
+       if (netpoll_tx_running(dev))
+               return skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
        ret = dev_queue_xmit(skb);
 
        if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
@@ -660,6 +663,57 @@ static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, st
        return stats;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void vlan_dev_poll_controller(struct net_device *dev)
+{
+       return;
+}
+
+int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo)
+{
+       struct vlan_dev_priv *info = vlan_dev_priv(dev);
+       struct net_device *real_dev = info->real_dev;
+       struct netpoll *netpoll;
+       int err = 0;
+
+       netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
+       err = -ENOMEM;
+       if (!netpoll)
+               goto out;
+
+       netpoll->dev = real_dev;
+       strlcpy(netpoll->dev_name, real_dev->name, IFNAMSIZ);
+
+       err = __netpoll_setup(netpoll);
+       if (err) {
+               kfree(netpoll);
+               goto out;
+       }
+
+       info->netpoll = netpoll;
+
+out:
+       return err;
+}
+
+void vlan_dev_netpoll_cleanup(struct net_device *dev)
+{
+       struct vlan_dev_priv *info = vlan_dev_priv(dev);
+       struct netpoll *netpoll = info->netpoll;
+
+       if (!netpoll)
+               return;
+
+       info->netpoll = NULL;
+
+        /* Wait for transmitting packets to finish before freeing. */
+        synchronize_rcu_bh();
+
+        __netpoll_cleanup(netpoll);
+        kfree(netpoll);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
 static const struct ethtool_ops vlan_ethtool_ops = {
        .get_settings           = vlan_ethtool_get_settings,
        .get_drvinfo            = vlan_ethtool_get_drvinfo,
@@ -688,6 +742,11 @@ static const struct net_device_ops vlan_netdev_ops = {
        .ndo_fcoe_get_wwn       = vlan_dev_fcoe_get_wwn,
        .ndo_fcoe_ddp_target    = vlan_dev_fcoe_ddp_target,
 #endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = vlan_dev_poll_controller,
+       .ndo_netpoll_setup      = vlan_dev_netpoll_setup,
+       .ndo_netpoll_cleanup    = vlan_dev_netpoll_cleanup,
+#endif
        .ndo_fix_features       = vlan_dev_fix_features,
 };