Merge branch 'master' of ssh://github.com/openvswitch/ovs into master_new
[cascardo/ovs.git] / lib / netdev-dpdk.c
index afe1c45..de7e488 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Nicira, Inc.
+ * Copyright (c) 2014, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -65,11 +65,17 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 /*
  * need to reserve tons of extra space in the mbufs so we can align the
  * DMA addresses to 4KB.
+ * The minimum mbuf size is limited to avoid scatter behaviour and drop in
+ * performance for standard Ethernet MTU.
  */
-
 #define MTU_TO_MAX_LEN(mtu)  ((mtu) + ETHER_HDR_LEN + ETHER_CRC_LEN)
-#define MBUF_SIZE(mtu)       (MTU_TO_MAX_LEN(mtu) + (512) + \
-                             sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define MBUF_SIZE_MTU(mtu)   (MTU_TO_MAX_LEN(mtu)        \
+                              + sizeof(struct dp_packet) \
+                              + RTE_PKTMBUF_HEADROOM)
+#define MBUF_SIZE_DRIVER     (2048                       \
+                              + sizeof (struct rte_mbuf) \
+                              + RTE_PKTMBUF_HEADROOM)
+#define MBUF_SIZE(mtu)       MAX(MBUF_SIZE_MTU(mtu), MBUF_SIZE_DRIVER)
 
 /* Max and min number of packets in the mempool.  OVS tries to allocate a
  * mempool with MAX_NB_MBUF: if this fails (because the system doesn't have
@@ -236,7 +242,7 @@ struct netdev_rxq_dpdk {
     int port_id;
 };
 
-static bool thread_is_pmd(void);
+static bool dpdk_thread_is_pmd(void);
 
 static int netdev_dpdk_construct(struct netdev *);
 
@@ -743,6 +749,10 @@ netdev_dpdk_vhost_destruct(struct netdev *netdev_)
                 return;
     }
 
+    if (rte_vhost_driver_unregister(dev->vhost_id)) {
+        VLOG_ERR("Unable to remove vhost-user socket %s", dev->vhost_id);
+    }
+
     ovs_mutex_lock(&dpdk_mutex);
     list_remove(&dev->list_node);
     dpdk_mp_put(dev->dpdk_mp);
@@ -939,6 +949,35 @@ is_vhost_running(struct virtio_net *dev)
     return (dev != NULL && (dev->flags & VIRTIO_DEV_RUNNING));
 }
 
+static inline void
+netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats,
+                                     struct dp_packet **packets, int count)
+{
+    int i;
+    struct dp_packet *packet;
+
+    stats->rx_packets += count;
+    for (i = 0; i < count; i++) {
+        packet = packets[i];
+
+        if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
+            /* This only protects the following multicast counting from
+             * too short packets, but it does not stop the packet from
+             * further processing. */
+            stats->rx_errors++;
+            stats->rx_length_errors++;
+            continue;
+        }
+
+        struct eth_header *eh = (struct eth_header *) dp_packet_data(packet);
+        if (OVS_UNLIKELY(eth_addr_is_multicast(eh->eth_dst))) {
+            stats->multicast++;
+        }
+
+        stats->rx_bytes += dp_packet_size(packet);
+    }
+}
+
 /*
  * The receive path for the vhost port is the TX path out from guest.
  */
@@ -966,7 +1005,7 @@ netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq_,
     }
 
     rte_spinlock_lock(&vhost_dev->stats_lock);
-    vhost_dev->stats.rx_packets += (uint64_t)nb_rx;
+    netdev_dpdk_vhost_update_rx_counters(&vhost_dev->stats, packets, nb_rx);
     rte_spinlock_unlock(&vhost_dev->stats_lock);
 
     *c = (int) nb_rx;
@@ -1003,6 +1042,23 @@ netdev_dpdk_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet **packets,
     return 0;
 }
 
+static inline void
+netdev_dpdk_vhost_update_tx_counters(struct netdev_stats *stats,
+                                     struct dp_packet **packets,
+                                     int attempted,
+                                     int dropped)
+{
+    int i;
+    int sent = attempted - dropped;
+
+    stats->tx_packets += sent;
+    stats->tx_dropped += dropped;
+
+    for (i = 0; i < sent; i++) {
+        stats->tx_bytes += dp_packet_size(packets[i]);
+    }
+}
+
 static void
 __netdev_dpdk_vhost_send(struct netdev *netdev, struct dp_packet **pkts,
                          int cnt, bool may_steal)
@@ -1060,8 +1116,8 @@ __netdev_dpdk_vhost_send(struct netdev *netdev, struct dp_packet **pkts,
     rte_spinlock_unlock(&vhost_dev->vhost_tx_lock);
 
     rte_spinlock_lock(&vhost_dev->stats_lock);
-    vhost_dev->stats.tx_packets += (total_pkts - cnt);
-    vhost_dev->stats.tx_dropped += cnt;
+    netdev_dpdk_vhost_update_tx_counters(&vhost_dev->stats, pkts, total_pkts,
+                                         cnt);
     rte_spinlock_unlock(&vhost_dev->stats_lock);
 
 out:
@@ -1124,7 +1180,7 @@ dpdk_do_tx_copy(struct netdev *netdev, int qid, struct dp_packet **pkts,
     /* If we are on a non pmd thread we have to use the mempool mutex, because
      * every non pmd thread shares the same mempool cache */
 
-    if (!thread_is_pmd()) {
+    if (!dpdk_thread_is_pmd()) {
         ovs_mutex_lock(&nonpmd_mempool_mutex);
     }
 
@@ -1168,7 +1224,7 @@ dpdk_do_tx_copy(struct netdev *netdev, int qid, struct dp_packet **pkts,
         dpdk_queue_flush(dev, qid);
     }
 
-    if (!thread_is_pmd()) {
+    if (!dpdk_thread_is_pmd()) {
         ovs_mutex_unlock(&nonpmd_mempool_mutex);
     }
 }
@@ -1362,14 +1418,10 @@ netdev_dpdk_vhost_get_stats(const struct netdev *netdev,
     ovs_mutex_lock(&dev->mutex);
     memset(stats, 0, sizeof(*stats));
     /* Unsupported Stats */
-    stats->rx_errors = UINT64_MAX;
-    stats->tx_errors = UINT64_MAX;
-    stats->multicast = UINT64_MAX;
     stats->collisions = UINT64_MAX;
     stats->rx_crc_errors = UINT64_MAX;
     stats->rx_fifo_errors = UINT64_MAX;
     stats->rx_frame_errors = UINT64_MAX;
-    stats->rx_length_errors = UINT64_MAX;
     stats->rx_missed_errors = UINT64_MAX;
     stats->rx_over_errors = UINT64_MAX;
     stats->tx_aborted_errors = UINT64_MAX;
@@ -1378,16 +1430,20 @@ netdev_dpdk_vhost_get_stats(const struct netdev *netdev,
     stats->tx_fifo_errors = UINT64_MAX;
     stats->tx_heartbeat_errors = UINT64_MAX;
     stats->tx_window_errors = UINT64_MAX;
-    stats->rx_bytes += UINT64_MAX;
     stats->rx_dropped += UINT64_MAX;
-    stats->tx_bytes += UINT64_MAX;
 
     rte_spinlock_lock(&dev->stats_lock);
     /* Supported Stats */
     stats->rx_packets += dev->stats.rx_packets;
     stats->tx_packets += dev->stats.tx_packets;
     stats->tx_dropped += dev->stats.tx_dropped;
+    stats->multicast = dev->stats.multicast;
+    stats->rx_bytes = dev->stats.rx_bytes;
+    stats->tx_bytes = dev->stats.tx_bytes;
+    stats->rx_errors = dev->stats.rx_errors;
+    stats->rx_length_errors = dev->stats.rx_length_errors;
     rte_spinlock_unlock(&dev->stats_lock);
+
     ovs_mutex_unlock(&dev->mutex);
 
     return 0;
@@ -1410,13 +1466,33 @@ netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
     stats->tx_packets = rte_stats.opackets;
     stats->rx_bytes = rte_stats.ibytes;
     stats->tx_bytes = rte_stats.obytes;
-    stats->rx_errors = rte_stats.ierrors;
+    /* DPDK counts imissed as errors, but count them here as dropped instead */
+    stats->rx_errors = rte_stats.ierrors - rte_stats.imissed;
     stats->tx_errors = rte_stats.oerrors;
     stats->multicast = rte_stats.imcasts;
 
     rte_spinlock_lock(&dev->stats_lock);
     stats->tx_dropped = dev->stats.tx_dropped;
     rte_spinlock_unlock(&dev->stats_lock);
+
+    /* These are the available DPDK counters for packets not received due to
+     * local resource constraints in DPDK and NIC respectively. */
+    stats->rx_dropped = rte_stats.rx_nombuf + rte_stats.imissed;
+    stats->collisions = UINT64_MAX;
+
+    stats->rx_length_errors = rte_stats.ibadlen;
+    stats->rx_over_errors = UINT64_MAX;
+    stats->rx_crc_errors = rte_stats.ibadcrc;
+    stats->rx_frame_errors = UINT64_MAX;
+    stats->rx_fifo_errors = UINT64_MAX;
+    stats->rx_missed_errors = rte_stats.imissed;
+
+    stats->tx_aborted_errors = UINT64_MAX;
+    stats->tx_carrier_errors = UINT64_MAX;
+    stats->tx_fifo_errors = UINT64_MAX;
+    stats->tx_heartbeat_errors = UINT64_MAX;
+    stats->tx_window_errors = UINT64_MAX;
+
     ovs_mutex_unlock(&dev->mutex);
 
     return 0;
@@ -1614,8 +1690,12 @@ netdev_dpdk_get_status(const struct netdev *netdev_, struct smap *args)
     smap_add_format(args, "max_vfs", "%u", dev_info.max_vfs);
     smap_add_format(args, "max_vmdq_pools", "%u", dev_info.max_vmdq_pools);
 
-    smap_add_format(args, "pci-vendor_id", "0x%u", dev_info.pci_dev->id.vendor_id);
-    smap_add_format(args, "pci-device_id", "0x%x", dev_info.pci_dev->id.device_id);
+    if (dev_info.pci_dev) {
+        smap_add_format(args, "pci-vendor_id", "0x%u",
+                        dev_info.pci_dev->id.vendor_id);
+        smap_add_format(args, "pci-device_id", "0x%x",
+                        dev_info.pci_dev->id.device_id);
+    }
 
     return 0;
 }
@@ -1713,14 +1793,14 @@ new_device(struct virtio_net *dev)
     ovs_mutex_unlock(&dpdk_mutex);
 
     if (!exists) {
-        VLOG_INFO("vHost Device '%s' (%ld) can't be added - name not found",
-                   dev->ifname, dev->device_fh);
+        VLOG_INFO("vHost Device '%s' %"PRIu64" can't be added - name not "
+                  "found", dev->ifname, dev->device_fh);
 
         return -1;
     }
 
-    VLOG_INFO("vHost Device '%s' (%ld) has been added",
-               dev->ifname, dev->device_fh);
+    VLOG_INFO("vHost Device '%s' %"PRIu64" has been added", dev->ifname,
+              dev->device_fh);
     return 0;
 }
 
@@ -1758,8 +1838,8 @@ destroy_device(volatile struct virtio_net *dev)
     }
     ovs_mutex_unlock(&dpdk_mutex);
 
-    VLOG_INFO("vHost Device '%s' (%ld) has been removed",
-               dev->ifname, dev->device_fh);
+    VLOG_INFO("vHost Device '%s' %"PRIu64" has been removed", dev->ifname,
+              dev->device_fh);
 }
 
 struct virtio_net *
@@ -1855,9 +1935,9 @@ dpdk_ring_create(const char dev_name[], unsigned int port_no,
         return -err;
     }
 
-    /* Create single consumer/producer rings, netdev does explicit locking. */
+    /* Create single producer tx ring, netdev does explicit locking. */
     ivshmem->cring_tx = rte_ring_create(ring_name, DPDK_RING_SIZE, SOCKET0,
-                                        RING_F_SP_ENQ | RING_F_SC_DEQ);
+                                        RING_F_SP_ENQ);
     if (ivshmem->cring_tx == NULL) {
         rte_free(ivshmem);
         return ENOMEM;
@@ -1868,9 +1948,9 @@ dpdk_ring_create(const char dev_name[], unsigned int port_no,
         return -err;
     }
 
-    /* Create single consumer/producer rings, netdev does explicit locking. */
+    /* Create single consumer rx ring, netdev does explicit locking. */
     ivshmem->cring_rx = rte_ring_create(ring_name, DPDK_RING_SIZE, SOCKET0,
-                                        RING_F_SP_ENQ | RING_F_SC_DEQ);
+                                        RING_F_SC_DEQ);
     if (ivshmem->cring_rx == NULL) {
         rte_free(ivshmem);
         return ENOMEM;
@@ -1929,7 +2009,7 @@ netdev_dpdk_ring_send(struct netdev *netdev_, int qid,
      * the consumer of the ring and return into the datapath without recalculating
      * the RSS hash. */
     for (i = 0; i < cnt; i++) {
-        dp_packet_set_rss_hash(pkts[i], 0);
+        dp_packet_rss_invalidate(pkts[i]);
     }
 
     netdev_dpdk_send__(netdev, qid, pkts, cnt, may_steal);
@@ -2039,14 +2119,14 @@ process_vhost_flags(char *flag, char *default_val, int size,
      * flag if it is provided on the vswitchd command line, otherwise resort to
      * a default value.
      *
-     * For vhost-user: Process "-cuse_dev_name" to set the custom location of
+     * For vhost-user: Process "-vhost_sock_dir" to set the custom location of
      * the vhost-user socket(s).
-     * For vhost-cuse: Process "-vhost_sock_dir" to set the custom name of the
+     * For vhost-cuse: Process "-cuse_dev_name" to set the custom name of the
      * vhost-cuse character device.
      */
     if (!strcmp(argv[1], flag) && (strlen(argv[2]) <= size)) {
         changed = 1;
-        *new_val = strdup(argv[2]);
+        *new_val = xstrdup(argv[2]);
         VLOG_INFO("User-provided %s in use: %s", flag, *new_val);
     } else {
         VLOG_INFO("No %s provided - defaulting to %s", flag, default_val);
@@ -2070,11 +2150,19 @@ dpdk_init(int argc, char **argv)
     argc--;
     argv++;
 
+    /* Reject --user option */
+    int i;
+    for (i = 0; i < argc; i++) {
+        if (!strcmp(argv[i], "--user")) {
+            VLOG_ERR("Can not mix --dpdk and --user options, aborting.");
+        }
+    }
+
 #ifdef VHOST_CUSE
-    if (process_vhost_flags("-cuse_dev_name", strdup("vhost-net"),
+    if (process_vhost_flags("-cuse_dev_name", xstrdup("vhost-net"),
                             PATH_MAX, argv, &cuse_dev_name)) {
 #else
-    if (process_vhost_flags("-vhost_sock_dir", strdup(ovs_rundir()),
+    if (process_vhost_flags("-vhost_sock_dir", xstrdup(ovs_rundir()),
                             NAME_MAX, argv, &vhost_sock_dir)) {
         struct stat s;
         int err;
@@ -2218,7 +2306,7 @@ pmd_thread_setaffinity_cpu(unsigned cpu)
 }
 
 static bool
-thread_is_pmd(void)
+dpdk_thread_is_pmd(void)
 {
     return rte_lcore_id() != NON_PMD_CORE_ID;
 }