datapath-windows: Avoid BSOD when no event queue found.
[cascardo/ovs.git] / lib / netdev-linux.c
index 828540d..ab5ef7d 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/pkt_sched.h>
 #include <linux/rtnetlink.h>
 #include <linux/sockios.h>
-#include <linux/version.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
@@ -48,9 +47,9 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "connectivity.h"
 #include "coverage.h"
-#include "dpif-linux.h"
+#include "dpif-netlink.h"
+#include "dpif-netdev.h"
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "hash.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "ovs-atomic.h"
+#include "packet-dpif.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "rtnetlink-link.h"
-#include "seq.h"
 #include "shash.h"
 #include "socket-util.h"
 #include "sset.h"
@@ -424,8 +423,8 @@ struct netdev_linux {
     int tap_fd;
 };
 
-struct netdev_rx_linux {
-    struct netdev_rx up;
+struct netdev_rxq_linux {
+    struct netdev_rxq up;
     bool is_tap;
     int fd;
 };
@@ -436,8 +435,11 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
 /* Polling miimon status for all ports causes performance degradation when
  * handling a large number of ports. If there are no devices using miimon, then
- * we skip netdev_linux_miimon_run() and netdev_linux_miimon_wait(). */
-static atomic_int miimon_cnt = ATOMIC_VAR_INIT(0);
+ * we skip netdev_linux_miimon_run() and netdev_linux_miimon_wait().
+ *
+ * Readers do not depend on this variable synchronizing with the related
+ * changes in the device miimon status, so we can use atomic_count. */
+static atomic_count miimon_cnt = ATOMIC_COUNT_INIT(0);
 
 static void netdev_linux_run(void);
 
@@ -462,6 +464,7 @@ static int af_packet_sock(void);
 static bool netdev_linux_miimon_enabled(void);
 static void netdev_linux_miimon_run(void);
 static void netdev_linux_miimon_wait(void);
+static int netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup);
 
 static bool
 is_netdev_linux_class(const struct netdev_class *netdev_class)
@@ -483,11 +486,11 @@ netdev_linux_cast(const struct netdev *netdev)
     return CONTAINER_OF(netdev, struct netdev_linux, up);
 }
 
-static struct netdev_rx_linux *
-netdev_rx_linux_cast(const struct netdev_rx *rx)
+static struct netdev_rxq_linux *
+netdev_rxq_linux_cast(const struct netdev_rxq *rx)
 {
     ovs_assert(is_netdev_linux_class(netdev_get_class(rx->netdev)));
-    return CONTAINER_OF(rx, struct netdev_rx_linux, up);
+    return CONTAINER_OF(rx, struct netdev_rxq_linux, up);
 }
 \f
 static void netdev_linux_update(struct netdev_linux *netdev,
@@ -525,10 +528,7 @@ netdev_linux_notify_sock(void)
 static bool
 netdev_linux_miimon_enabled(void)
 {
-    int miimon;
-
-    atomic_read(&miimon_cnt, &miimon);
-    return miimon > 0;
+    return atomic_count_get(&miimon_cnt) > 0;
 }
 
 static void
@@ -615,7 +615,7 @@ netdev_linux_changed(struct netdev_linux *dev,
                      unsigned int ifi_flags, unsigned int mask)
     OVS_REQUIRES(dev->mutex)
 {
-    seq_change(connectivity_seq_get());
+    netdev_change_seq_changed(&dev->up);
 
     if ((dev->ifi_flags ^ ifi_flags) & IFF_RUNNING) {
         dev->carrier_resets++;
@@ -758,8 +758,7 @@ netdev_linux_destruct(struct netdev *netdev_)
     }
 
     if (netdev->miimon_interval > 0) {
-        int junk;
-        atomic_sub(&miimon_cnt, 1, &junk);
+        atomic_count_dec(&miimon_cnt);
     }
 
     ovs_mutex_destroy(&netdev->mutex);
@@ -772,17 +771,17 @@ netdev_linux_dealloc(struct netdev *netdev_)
     free(netdev);
 }
 
-static struct netdev_rx *
-netdev_linux_rx_alloc(void)
+static struct netdev_rxq *
+netdev_linux_rxq_alloc(void)
 {
-    struct netdev_rx_linux *rx = xzalloc(sizeof *rx);
+    struct netdev_rxq_linux *rx = xzalloc(sizeof *rx);
     return &rx->up;
 }
 
 static int
-netdev_linux_rx_construct(struct netdev_rx *rx_)
+netdev_linux_rxq_construct(struct netdev_rxq *rxq_)
 {
-    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+    struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
     struct netdev *netdev_ = rx->up.netdev;
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
     int error;
@@ -868,9 +867,9 @@ error:
 }
 
 static void
-netdev_linux_rx_destruct(struct netdev_rx *rx_)
+netdev_linux_rxq_destruct(struct netdev_rxq *rxq_)
 {
-    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+    struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
 
     if (!rx->is_tap) {
         close(rx->fd);
@@ -878,9 +877,9 @@ netdev_linux_rx_destruct(struct netdev_rx *rx_)
 }
 
 static void
-netdev_linux_rx_dealloc(struct netdev_rx *rx_)
+netdev_linux_rxq_dealloc(struct netdev_rxq *rxq_)
 {
-    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+    struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
 
     free(rx);
 }
@@ -902,7 +901,7 @@ auxdata_has_vlan_tci(const struct tpacket_auxdata *aux)
 }
 
 static int
-netdev_linux_rx_recv_sock(int fd, struct ofpbuf *buffer)
+netdev_linux_rxq_recv_sock(int fd, struct ofpbuf *buffer)
 {
     size_t size;
     ssize_t retval;
@@ -918,7 +917,7 @@ netdev_linux_rx_recv_sock(int fd, struct ofpbuf *buffer)
     ofpbuf_reserve(buffer, VLAN_HEADER_LEN);
     size = ofpbuf_tailroom(buffer);
 
-    iov.iov_base = buffer->data;
+    iov.iov_base = ofpbuf_data(buffer);
     iov.iov_len = size;
     msgh.msg_name = NULL;
     msgh.msg_namelen = 0;
@@ -938,7 +937,7 @@ netdev_linux_rx_recv_sock(int fd, struct ofpbuf *buffer)
         return EMSGSIZE;
     }
 
-    buffer->size += retval;
+    ofpbuf_set_size(buffer, ofpbuf_size(buffer) + retval);
 
     for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
         const struct tpacket_auxdata *aux;
@@ -965,13 +964,13 @@ netdev_linux_rx_recv_sock(int fd, struct ofpbuf *buffer)
 }
 
 static int
-netdev_linux_rx_recv_tap(int fd, struct ofpbuf *buffer)
+netdev_linux_rxq_recv_tap(int fd, struct ofpbuf *buffer)
 {
     ssize_t retval;
     size_t size = ofpbuf_tailroom(buffer);
 
     do {
-        retval = read(fd, buffer->data, size);
+        retval = read(fd, ofpbuf_data(buffer), size);
     } while (retval < 0 && errno == EINTR);
 
     if (retval < 0) {
@@ -980,41 +979,63 @@ netdev_linux_rx_recv_tap(int fd, struct ofpbuf *buffer)
         return EMSGSIZE;
     }
 
-    buffer->size += retval;
+    ofpbuf_set_size(buffer, ofpbuf_size(buffer) + retval);
     return 0;
 }
 
 static int
-netdev_linux_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
+netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dpif_packet **packets,
+                      int *c)
 {
-    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
-    int retval;
+    struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
+    struct netdev *netdev = rx->up.netdev;
+    struct dpif_packet *packet;
+    struct ofpbuf *buffer;
+    ssize_t retval;
+    int mtu;
+
+    if (netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu)) {
+        mtu = ETH_PAYLOAD_MAX;
+    }
+
+    packet = dpif_packet_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu,
+                                           DP_NETDEV_HEADROOM);
+    buffer = &packet->ofpbuf;
 
     retval = (rx->is_tap
-              ? netdev_linux_rx_recv_tap(rx->fd, buffer)
-              : netdev_linux_rx_recv_sock(rx->fd, buffer));
-    if (retval && retval != EAGAIN && retval != EMSGSIZE) {
-        VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
-                     ovs_strerror(errno), netdev_rx_get_name(rx_));
+              ? netdev_linux_rxq_recv_tap(rx->fd, buffer)
+              : netdev_linux_rxq_recv_sock(rx->fd, buffer));
+
+    if (retval) {
+        if (retval != EAGAIN && retval != EMSGSIZE) {
+            VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
+                         ovs_strerror(errno), netdev_rxq_get_name(rxq_));
+        }
+        dpif_packet_delete(packet);
+    } else {
+        dp_packet_pad(buffer);
+        dpif_packet_set_dp_hash(packet, 0);
+        packets[0] = packet;
+        *c = 1;
     }
 
     return retval;
 }
 
 static void
-netdev_linux_rx_wait(struct netdev_rx *rx_)
+netdev_linux_rxq_wait(struct netdev_rxq *rxq_)
 {
-    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+    struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
     poll_fd_wait(rx->fd, POLLIN);
 }
 
 static int
-netdev_linux_rx_drain(struct netdev_rx *rx_)
+netdev_linux_rxq_drain(struct netdev_rxq *rxq_)
 {
-    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+    struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
     if (rx->is_tap) {
         struct ifreq ifr;
-        int error = af_inet_ifreq_ioctl(netdev_rx_get_name(rx_), &ifr,
+        int error = af_inet_ifreq_ioctl(netdev_rxq_get_name(rxq_), &ifr,
                                         SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
         if (error) {
             return error;
@@ -1036,9 +1057,16 @@ netdev_linux_rx_drain(struct netdev_rx *rx_)
  * The kernel maintains a packet transmission queue, so the caller is not
  * expected to do additional queuing of packets. */
 static int
-netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
+netdev_linux_send(struct netdev *netdev_, int qid OVS_UNUSED,
+                  struct dpif_packet **pkts, int cnt, bool may_steal)
 {
-    for (;;) {
+    int i;
+    int error = 0;
+
+    /* 'i' is incremented only if there's no error */
+    for (i = 0; i < cnt;) {
+        const void *data = ofpbuf_data(&pkts[i]->ofpbuf);
+        size_t size = ofpbuf_size(&pkts[i]->ofpbuf);
         ssize_t retval;
 
         if (!is_tap_netdev(netdev_)) {
@@ -1092,23 +1120,37 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
             /* The Linux AF_PACKET implementation never blocks waiting for room
              * for packets, instead returning ENOBUFS.  Translate this into
              * EAGAIN for the caller. */
-            if (errno == ENOBUFS) {
-                return EAGAIN;
-            } else if (errno == EINTR) {
+            error = errno == ENOBUFS ? EAGAIN : errno;
+            if (error == EINTR) {
+                /* continue without incrementing 'i', i.e. retry this packet */
                 continue;
-            } else if (errno != EAGAIN) {
-                VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
-                             netdev_get_name(netdev_), ovs_strerror(errno));
             }
-            return errno;
+            break;
         } else if (retval != size) {
-            VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIuSIZE"d bytes of "
-                         "%"PRIuSIZE") on %s", retval, size, netdev_get_name(netdev_));
-            return EMSGSIZE;
-        } else {
-            return 0;
+            VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIuSIZE" bytes"
+                              " of %"PRIuSIZE") on %s", retval, size,
+                         netdev_get_name(netdev_));
+            error = EMSGSIZE;
+            break;
         }
+
+        /* Process the next packet in the batch */
+        i++;
     }
+
+    if (may_steal) {
+        for (i = 0; i < cnt; i++) {
+            dpif_packet_delete(pkts[i]);
+        }
+    }
+
+    if (error && error != EAGAIN) {
+            VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
+                         netdev_get_name(netdev_), ovs_strerror(error));
+    }
+
+    return error;
+
 }
 
 /* Registers with the poll loop to wake up from the next call to poll_block()
@@ -1119,7 +1161,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
  * expected to do additional queuing of packets.  Thus, this function is
  * unlikely to ever be used.  It is included for completeness. */
 static void
-netdev_linux_send_wait(struct netdev *netdev)
+netdev_linux_send_wait(struct netdev *netdev, int qid OVS_UNUSED)
 {
     if (is_tap_netdev(netdev)) {
         /* TAP device always accepts packets.*/
@@ -1374,12 +1416,10 @@ netdev_linux_set_miimon_interval(struct netdev *netdev_,
     ovs_mutex_lock(&netdev->mutex);
     interval = interval > 0 ? MAX(interval, 100) : 0;
     if (netdev->miimon_interval != interval) {
-        int junk;
-
         if (interval && !netdev->miimon_interval) {
-            atomic_add(&miimon_cnt, 1, &junk);
+            atomic_count_inc(&miimon_cnt);
         } else if (!interval && netdev->miimon_interval) {
-            atomic_sub(&miimon_cnt, 1, &junk);
+            atomic_count_dec(&miimon_cnt);
         }
 
         netdev->miimon_interval = interval;
@@ -1457,14 +1497,14 @@ static void
 netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
                                   const struct ovs_vport_stats *src)
 {
-    dst->rx_packets = get_unaligned_u64(&src->rx_packets);
-    dst->tx_packets = get_unaligned_u64(&src->tx_packets);
-    dst->rx_bytes = get_unaligned_u64(&src->rx_bytes);
-    dst->tx_bytes = get_unaligned_u64(&src->tx_bytes);
-    dst->rx_errors = get_unaligned_u64(&src->rx_errors);
-    dst->tx_errors = get_unaligned_u64(&src->tx_errors);
-    dst->rx_dropped = get_unaligned_u64(&src->rx_dropped);
-    dst->tx_dropped = get_unaligned_u64(&src->tx_dropped);
+    dst->rx_packets = get_32aligned_u64(&src->rx_packets);
+    dst->tx_packets = get_32aligned_u64(&src->tx_packets);
+    dst->rx_bytes = get_32aligned_u64(&src->rx_bytes);
+    dst->tx_bytes = get_32aligned_u64(&src->tx_bytes);
+    dst->rx_errors = get_32aligned_u64(&src->rx_errors);
+    dst->tx_errors = get_32aligned_u64(&src->tx_errors);
+    dst->rx_dropped = get_32aligned_u64(&src->rx_dropped);
+    dst->tx_dropped = get_32aligned_u64(&src->tx_dropped);
     dst->multicast = 0;
     dst->collisions = 0;
     dst->rx_length_errors = 0;
@@ -1483,11 +1523,11 @@ netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
 static int
 get_stats_via_vport__(const struct netdev *netdev, struct netdev_stats *stats)
 {
-    struct dpif_linux_vport reply;
+    struct dpif_netlink_vport reply;
     struct ofpbuf *buf;
     int error;
 
-    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+    error = dpif_netlink_vport_get(netdev_get_name(netdev), &reply, &buf);
     if (error) {
         return error;
     } else if (!reply.stats) {
@@ -1540,9 +1580,17 @@ netdev_linux_get_stats(const struct netdev *netdev_,
             error = 0;
         }
     } else if (netdev->vport_stats_error) {
-        /* stats not available from OVS then use ioctl stats. */
+        /* stats not available from OVS then use netdev stats. */
         *stats = dev_stats;
     } else {
+        /* Use kernel netdev's packet and byte counts since vport's counters
+         * do not reflect packet counts on the wire when GSO, TSO or GRO are
+         * enabled. */
+        stats->rx_packets = dev_stats.rx_packets;
+        stats->rx_bytes = dev_stats.rx_bytes;
+        stats->tx_packets = dev_stats.tx_packets;
+        stats->tx_bytes = dev_stats.tx_bytes;
+
         stats->rx_errors           += dev_stats.rx_errors;
         stats->tx_errors           += dev_stats.tx_errors;
         stats->rx_dropped          += dev_stats.rx_dropped;
@@ -1606,6 +1654,14 @@ netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
         stats->tx_heartbeat_errors = 0;
         stats->tx_window_errors = 0;
     } else {
+        /* Use kernel netdev's packet and byte counts since vport counters
+         * do not reflect packet counts on the wire when GSO, TSO or GRO
+         * are enabled. */
+        stats->rx_packets = dev_stats.tx_packets;
+        stats->rx_bytes = dev_stats.tx_bytes;
+        stats->tx_packets = dev_stats.rx_packets;
+        stats->tx_bytes = dev_stats.rx_bytes;
+
         stats->rx_dropped          += dev_stats.tx_dropped;
         stats->tx_dropped          += dev_stats.rx_dropped;
 
@@ -1635,41 +1691,6 @@ netdev_internal_get_stats(const struct netdev *netdev_,
     return error;
 }
 
-static int
-netdev_internal_set_stats(struct netdev *netdev,
-                          const struct netdev_stats *stats)
-{
-    struct ovs_vport_stats vport_stats;
-    struct dpif_linux_vport vport;
-    int err;
-
-    vport_stats.rx_packets = stats->rx_packets;
-    vport_stats.tx_packets = stats->tx_packets;
-    vport_stats.rx_bytes = stats->rx_bytes;
-    vport_stats.tx_bytes = stats->tx_bytes;
-    vport_stats.rx_errors = stats->rx_errors;
-    vport_stats.tx_errors = stats->tx_errors;
-    vport_stats.rx_dropped = stats->rx_dropped;
-    vport_stats.tx_dropped = stats->tx_dropped;
-
-    dpif_linux_vport_init(&vport);
-    vport.cmd = OVS_VPORT_CMD_SET;
-    vport.name = netdev_get_name(netdev);
-    vport.stats = &vport_stats;
-
-    err = dpif_linux_vport_transact(&vport, NULL, NULL);
-
-    /* If the vport layer doesn't know about the device, that doesn't mean it
-     * doesn't exist (after all were able to open it when netdev_open() was
-     * called), it just means that it isn't attached and we'll be getting
-     * stats a different way. */
-    if (err == ENODEV) {
-        err = EOPNOTSUPP;
-    }
-
-    return err;
-}
-
 static void
 netdev_linux_read_features(struct netdev_linux *netdev)
 {
@@ -2678,7 +2699,7 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
     return error;
 }
 
-#define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS,  \
+#define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS,          \
                            GET_FEATURES, GET_STATUS)            \
 {                                                               \
     NAME,                                                       \
@@ -2694,6 +2715,8 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
     NULL,                       /* get_config */                \
     NULL,                       /* set_config */                \
     NULL,                       /* get_tunnel_config */         \
+    NULL,                       /* get_numa_id */               \
+    NULL,                       /* set_multiq */                \
                                                                 \
     netdev_linux_send,                                          \
     netdev_linux_send_wait,                                     \
@@ -2707,7 +2730,6 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
     netdev_linux_get_carrier_resets,                            \
     netdev_linux_set_miimon_interval,                           \
     GET_STATS,                                                  \
-    SET_STATS,                                                  \
                                                                 \
     GET_FEATURES,                                               \
     netdev_linux_set_advertisements,                            \
@@ -2736,13 +2758,13 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
                                                                 \
     netdev_linux_update_flags,                                  \
                                                                 \
-    netdev_linux_rx_alloc,                                      \
-    netdev_linux_rx_construct,                                  \
-    netdev_linux_rx_destruct,                                   \
-    netdev_linux_rx_dealloc,                                    \
-    netdev_linux_rx_recv,                                       \
-    netdev_linux_rx_wait,                                       \
-    netdev_linux_rx_drain,                                      \
+    netdev_linux_rxq_alloc,                                     \
+    netdev_linux_rxq_construct,                                 \
+    netdev_linux_rxq_destruct,                                  \
+    netdev_linux_rxq_dealloc,                                   \
+    netdev_linux_rxq_recv,                                      \
+    netdev_linux_rxq_wait,                                      \
+    netdev_linux_rxq_drain,                                     \
 }
 
 const struct netdev_class netdev_linux_class =
@@ -2750,7 +2772,6 @@ const struct netdev_class netdev_linux_class =
         "system",
         netdev_linux_construct,
         netdev_linux_get_stats,
-        NULL,                    /* set_stats */
         netdev_linux_get_features,
         netdev_linux_get_status);
 
@@ -2759,7 +2780,6 @@ const struct netdev_class netdev_tap_class =
         "tap",
         netdev_linux_construct_tap,
         netdev_tap_get_stats,
-        NULL,                   /* set_stats */
         netdev_linux_get_features,
         netdev_linux_get_status);
 
@@ -2768,7 +2788,6 @@ const struct netdev_class netdev_internal_class =
         "internal",
         netdev_linux_construct,
         netdev_internal_get_stats,
-        netdev_internal_set_stats,
         NULL,                  /* get_features */
         netdev_internal_get_status);
 \f
@@ -3987,7 +4006,7 @@ tc_add_policer(struct netdev *netdev, int kbits_rate, int kbits_burst)
     memset(&tc_police, 0, sizeof tc_police);
     tc_police.action = TC_POLICE_SHOT;
     tc_police.mtu = mtu;
-    tc_fill_rate(&tc_police.rate, (kbits_rate * 1000)/8, mtu);
+    tc_fill_rate(&tc_police.rate, ((uint64_t) kbits_rate * 1000)/8, mtu);
     tc_police.burst = tc_bytes_to_ticks(tc_police.rate.rate,
                                         kbits_burst * 1024);