shash: New function shash_replace().
[sliver-openvswitch.git] / datapath / vport-internal_dev.c
index 6d52db0..c4937ed 100644 (file)
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
-#include <linux/percpu.h>
-#include <linux/preempt.h>
 #include <linux/rcupdate.h>
 #include <linux/skbuff.h>
-#include <linux/workqueue.h>
 
 #include "datapath.h"
+#include "vport-generic.h"
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
-struct pcpu_lstats {
-       unsigned long rx_packets;
-       unsigned long rx_bytes;
-       unsigned long tx_packets;
-       unsigned long tx_bytes;
-};
-
 struct internal_dev {
        struct vport *vport;
-
        struct net_device_stats stats;
-       struct pcpu_lstats *lstats;
 };
 
-struct vport_ops internal_vport_ops;
-
 static inline struct internal_dev *internal_dev_priv(struct net_device *netdev)
 {
        return netdev_priv(netdev);
 }
 
-static struct net_device_stats *internal_dev_get_stats(struct net_device *netdev)
+/* This function is only called by the kernel network layer.  It is not a vport
+ * get_stats() function.  If a vport get_stats() function is defined that
+ * results in this being called it will cause infinite recursion. */
+static struct net_device_stats *internal_dev_sys_stats(struct net_device *netdev)
 {
-       struct internal_dev *internal_dev = internal_dev_priv(netdev);
-       struct net_device_stats *stats;
-       int i;
-
-       stats = &internal_dev->stats;
-       memset(stats, 0, sizeof(struct net_device_stats));
-       for_each_possible_cpu(i) {
-               const struct pcpu_lstats *lb_stats;
-
-               lb_stats = per_cpu_ptr(internal_dev->lstats, i);
-               stats->rx_bytes   += lb_stats->rx_bytes;
-               stats->rx_packets += lb_stats->rx_packets;
-               stats->tx_bytes   += lb_stats->tx_bytes;
-               stats->tx_packets += lb_stats->tx_packets;
+       struct vport *vport = internal_dev_get_vport(netdev);
+       struct net_device_stats *stats = &internal_dev_priv(netdev)->stats;
+
+       if (vport) {
+               struct odp_vport_stats vport_stats;
+
+               vport_get_stats(vport, &vport_stats);
+
+               /* The tx and rx stats need to be swapped because the switch
+                * and host OS have opposite perspectives. */
+               stats->rx_packets       = vport_stats.tx_packets;
+               stats->tx_packets       = vport_stats.rx_packets;
+               stats->rx_bytes         = vport_stats.tx_bytes;
+               stats->tx_bytes         = vport_stats.rx_bytes;
+               stats->rx_errors        = vport_stats.tx_errors;
+               stats->tx_errors        = vport_stats.rx_errors;
+               stats->rx_dropped       = vport_stats.tx_dropped;
+               stats->tx_dropped       = vport_stats.rx_dropped;
+               stats->collisions       = vport_stats.collisions;
        }
+
        return stats;
 }
 
@@ -71,27 +67,22 @@ static int internal_dev_mac_addr(struct net_device *dev, void *p)
        return 0;
 }
 
-/* Not reentrant (because it is called with BHs disabled), but may be called
- * simultaneously on different CPUs. */
+/* Called with rcu_read_lock and bottom-halves disabled. */
 static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
-       struct internal_dev *internal_dev = internal_dev_priv(netdev);
        struct vport *vport = internal_dev_get_vport(netdev);
-       struct pcpu_lstats *lb_stats;
 
        /* We need our own clone. */
        skb = skb_share_check(skb, GFP_ATOMIC);
-       if (!skb)
+       if (!skb) {
+               vport_record_error(vport, VPORT_E_RX_DROPPED);
                return 0;
-
-       lb_stats = per_cpu_ptr(internal_dev->lstats, smp_processor_id());
-       lb_stats->tx_packets++;
-       lb_stats->tx_bytes += skb->len;
+       }
 
        skb_reset_mac_header(skb);
-       rcu_read_lock_bh();
+       compute_ip_summed(skb, true);
+
        vport_receive(vport, skb);
-       rcu_read_unlock_bh();
 
        return 0;
 }
@@ -119,11 +110,14 @@ static void internal_dev_getinfo(struct net_device *netdev,
 }
 
 static struct ethtool_ops internal_dev_ethtool_ops = {
-       .get_drvinfo = internal_dev_getinfo,
-       .get_link = ethtool_op_get_link,
-       .get_sg = ethtool_op_get_sg,
-       .get_tx_csum = ethtool_op_get_tx_csum,
-       .get_tso = ethtool_op_get_tso,
+       .get_drvinfo    = internal_dev_getinfo,
+       .get_link       = ethtool_op_get_link,
+       .get_sg         = ethtool_op_get_sg,
+       .set_sg         = ethtool_op_set_sg,
+       .get_tx_csum    = ethtool_op_get_tx_csum,
+       .set_tx_csum    = ethtool_op_set_tx_hw_csum,
+       .get_tso        = ethtool_op_get_tso,
+       .set_tso        = ethtool_op_set_tso,
 };
 
 static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
@@ -134,13 +128,7 @@ static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
                return -EINVAL;
 
        if (dp_port) {
-               int min_mtu;
-
-               mutex_lock(&dp_port->dp->mutex);
-               min_mtu = dp_min_mtu(dp_port->dp);
-               mutex_unlock(&dp_port->dp->mutex);
-
-               if (new_mtu > min_mtu)
+               if (new_mtu > dp_min_mtu(dp_port->dp))
                        return -EINVAL;
        }
 
@@ -148,22 +136,8 @@ static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
        return 0;
 }
 
-static int internal_dev_init(struct net_device *netdev)
-{
-       struct internal_dev *internal_dev = internal_dev_priv(netdev);
-
-       internal_dev->lstats = alloc_percpu(struct pcpu_lstats);
-       if (!internal_dev->lstats)
-               return -ENOMEM;
-
-       return 0;
-}
-
 static void internal_dev_free(struct net_device *netdev)
 {
-       struct internal_dev *internal_dev = internal_dev_priv(netdev);
-
-       free_percpu(internal_dev->lstats);
        free_netdev(netdev);
 }
 
@@ -171,19 +145,19 @@ static int internal_dev_do_ioctl(struct net_device *dev, struct ifreq *ifr, int
 {
        if (dp_ioctl_hook)
                return dp_ioctl_hook(dev, ifr, cmd);
+
        return -EOPNOTSUPP;
 }
 
 #ifdef HAVE_NET_DEVICE_OPS
 static const struct net_device_ops internal_dev_netdev_ops = {
-       .ndo_init = internal_dev_init,
        .ndo_open = internal_dev_open,
        .ndo_stop = internal_dev_stop,
        .ndo_start_xmit = internal_dev_xmit,
        .ndo_set_mac_address = internal_dev_mac_addr,
        .ndo_do_ioctl = internal_dev_do_ioctl,
        .ndo_change_mtu = internal_dev_change_mtu,
-       .ndo_get_stats = internal_dev_get_stats,
+       .ndo_get_stats = internal_dev_sys_stats,
 };
 #endif
 
@@ -196,13 +170,12 @@ do_setup(struct net_device *netdev)
        netdev->netdev_ops = &internal_dev_netdev_ops;
 #else
        netdev->do_ioctl = internal_dev_do_ioctl;
-       netdev->get_stats = internal_dev_get_stats;
+       netdev->get_stats = internal_dev_sys_stats;
        netdev->hard_start_xmit = internal_dev_xmit;
        netdev->open = internal_dev_open;
        netdev->stop = internal_dev_stop;
        netdev->set_mac_address = internal_dev_mac_addr;
        netdev->change_mtu = internal_dev_change_mtu;
-       netdev->init = internal_dev_init;
 #endif
 
        netdev->destructor = internal_dev_free;
@@ -210,9 +183,10 @@ do_setup(struct net_device *netdev)
        netdev->tx_queue_len = 0;
 
        netdev->flags = IFF_BROADCAST | IFF_MULTICAST;
-       netdev->features = NETIF_F_LLTX; /* XXX other features? */
+       netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_HIGHDMA
+                               | NETIF_F_HW_CSUM | NETIF_F_TSO;
 
-       vport_gen_ether_addr(netdev->dev_addr);
+       vport_gen_rand_ether_addr(netdev->dev_addr);
 }
 
 static struct vport *
@@ -299,8 +273,6 @@ static int
 internal_dev_recv(struct vport *vport, struct sk_buff *skb)
 {
        struct net_device *netdev = netdev_vport_priv(vport)->dev;
-       struct internal_dev *internal_dev = internal_dev_priv(netdev);
-       struct pcpu_lstats *lb_stats;
        int len;
 
        skb->dev = netdev;
@@ -314,18 +286,12 @@ internal_dev_recv(struct vport *vport, struct sk_buff *skb)
                netif_rx_ni(skb);
        netdev->last_rx = jiffies;
 
-       preempt_disable();
-       lb_stats = per_cpu_ptr(internal_dev->lstats, smp_processor_id());
-       lb_stats->rx_packets++;
-       lb_stats->rx_bytes += len;
-       preempt_enable();
-
        return len;
 }
 
 struct vport_ops internal_vport_ops = {
        .type           = "internal",
-       .flags          = VPORT_F_REQUIRED,
+       .flags          = VPORT_F_REQUIRED | VPORT_F_GEN_STATS,
        .create         = internal_dev_create,
        .destroy        = internal_dev_destroy,
        .attach         = internal_dev_attach,
@@ -335,7 +301,6 @@ struct vport_ops internal_vport_ops = {
        .get_name       = netdev_get_name,
        .get_addr       = netdev_get_addr,
        .get_kobj       = netdev_get_kobj,
-       .get_stats      = netdev_get_stats,
        .get_dev_flags  = netdev_get_dev_flags,
        .is_running     = netdev_is_running,
        .get_operstate  = netdev_get_operstate,