ofp-actions: Centralize all OpenFlow action code for maintainability.
[cascardo/ovs.git] / lib / netdev-linux.c
index 5d7bfa1..e50392a 100644 (file)
@@ -47,7 +47,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "connectivity.h"
 #include "coverage.h"
 #include "dpif-linux.h"
 #include "dpif-netdev.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;
 };
@@ -484,11 +483,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,
@@ -616,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++;
@@ -773,17 +772,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;
@@ -869,9 +868,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);
@@ -879,9 +878,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);
 }
@@ -903,7 +902,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;
@@ -919,7 +918,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;
@@ -939,7 +938,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;
@@ -966,13 +965,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) {
@@ -981,15 +980,17 @@ 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 **packet, int *c)
+netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dpif_packet **packets,
+                      int *c)
 {
-    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 dpif_packet *packet;
     struct ofpbuf *buffer;
     ssize_t retval;
     int mtu;
@@ -998,21 +999,23 @@ netdev_linux_rx_recv(struct netdev_rx *rx_, struct ofpbuf **packet, int *c)
         mtu = ETH_PAYLOAD_MAX;
     }
 
-    buffer = ofpbuf_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu, DP_NETDEV_HEADROOM);
+    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));
+              ? 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_rx_get_name(rx_));
+                         ovs_strerror(errno), netdev_rxq_get_name(rxq_));
         }
-        ofpbuf_delete(buffer);
+        dpif_packet_delete(packet);
     } else {
         dp_packet_pad(buffer);
-        packet[0] = buffer;
+        packets[0] = packet;
         *c = 1;
     }
 
@@ -1020,19 +1023,19 @@ netdev_linux_rx_recv(struct netdev_rx *rx_, struct ofpbuf **packet, int *c)
 }
 
 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;
@@ -1054,12 +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_, struct ofpbuf *pkt, bool may_steal)
+netdev_linux_send(struct netdev *netdev_, struct dpif_packet **pkts, int cnt,
+                  bool may_steal)
 {
-    const void *data = pkt->data;
-    size_t size = pkt->size;
+    int i;
+    int error = 0;
 
-    for (;;) {
+    /* '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_)) {
@@ -1109,31 +1116,41 @@ netdev_linux_send(struct netdev *netdev_, struct ofpbuf *pkt, bool may_steal)
             retval = write(netdev->tap_fd, data, size);
         }
 
-        if (may_steal) {
-            ofpbuf_delete(pkt);
-        }
-
         if (retval < 0) {
             /* 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()
@@ -1482,14 +1499,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;
@@ -1565,9 +1582,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;
@@ -1631,6 +1656,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;
 
@@ -1668,14 +1701,14 @@ netdev_internal_set_stats(struct netdev *netdev,
     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;
+    put_32aligned_u64(&vport_stats.rx_packets, stats->rx_packets);
+    put_32aligned_u64(&vport_stats.tx_packets, stats->tx_packets);
+    put_32aligned_u64(&vport_stats.rx_bytes, stats->rx_bytes);
+    put_32aligned_u64(&vport_stats.tx_bytes, stats->tx_bytes);
+    put_32aligned_u64(&vport_stats.rx_errors, stats->rx_errors);
+    put_32aligned_u64(&vport_stats.tx_errors, stats->tx_errors);
+    put_32aligned_u64(&vport_stats.rx_dropped, stats->rx_dropped);
+    put_32aligned_u64(&vport_stats.tx_dropped, stats->tx_dropped);
 
     dpif_linux_vport_init(&vport);
     vport.cmd = OVS_VPORT_CMD_SET;
@@ -2761,13 +2794,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 =