netdev: Extend rx_recv to pass multiple packets.
authorPravin <pshelar@nicira.com>
Thu, 20 Mar 2014 17:54:37 +0000 (10:54 -0700)
committerPravin B Shelar <pshelar@nicira.com>
Fri, 21 Mar 2014 18:48:28 +0000 (11:48 -0700)
DPDK can receive multiple packets but current netdev API does
not allow that.  Following patch allows dpif-netdev receive batch
of packet in a rx_recv() call for any netdev port.  This will be
used by dpdk-netdev.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
13 files changed:
lib/automake.mk
lib/dpif-netdev.c
lib/dpif-netdev.h [new file with mode: 0644]
lib/dpif.c
lib/netdev-bsd.c
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-provider.h
lib/netdev.c
lib/netdev.h
lib/odp-execute.c
lib/odp-execute.h
ofproto/ofproto-dpif-xlate.c

index 1460ee2..6b0442c 100644 (file)
@@ -52,6 +52,7 @@ lib_libopenvswitch_la_SOURCES = \
        lib/dhparams.h \
        lib/dirs.h \
        lib/dpif-netdev.c \
+       lib/dpif-netdev.h \
        lib/dpif-provider.h \
        lib/dpif.c \
        lib/dpif.h \
index 3d0c09f..254f8b8 100644 (file)
@@ -69,10 +69,6 @@ VLOG_DEFINE_THIS_MODULE(dpif_netdev);
 /* Configuration parameters. */
 enum { MAX_FLOWS = 65536 };     /* Maximum number of flows in flow table. */
 
-/* Enough headroom to add a vlan tag, plus an extra 2 bytes to allow IP
- * headers to be aligned on a 4-byte boundary.  */
-enum { DP_NETDEV_HEADROOM = 2 + VLAN_HEADER_LEN };
-
 /* Queues. */
 enum { MAX_QUEUE_LEN = 128 };   /* Maximum number of packets per queue. */
 enum { QUEUE_MASK = MAX_QUEUE_LEN - 1 };
@@ -343,7 +339,7 @@ static int dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *,
                                       const struct nlattr *userdata)
     OVS_EXCLUDED(dp->queue_rwlock);
 static void dp_netdev_execute_actions(struct dp_netdev *dp,
-                                      const struct flow *, struct ofpbuf *,
+                                      const struct flow *, struct ofpbuf *, bool may_steal,
                                       struct pkt_metadata *,
                                       const struct nlattr *actions,
                                       size_t actions_len)
@@ -1468,8 +1464,8 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
     flow_extract(execute->packet, md, &key);
 
     ovs_rwlock_rdlock(&dp->port_rwlock);
-    dp_netdev_execute_actions(dp, &key, execute->packet, md, execute->actions,
-                              execute->actions_len);
+    dp_netdev_execute_actions(dp, &key, execute->packet, false, md,
+                              execute->actions, execute->actions_len);
     ovs_rwlock_unlock(&dp->port_rwlock);
 
     return 0;
@@ -1682,12 +1678,10 @@ dp_forwarder_main(void *f_)
 {
     struct dp_forwarder *f = f_;
     struct dp_netdev *dp = f->dp;
-    struct ofpbuf packet;
 
     f->name = xasprintf("forwarder_%u", ovsthread_id_self());
     set_subprogram_name("%s", f->name);
 
-    ofpbuf_init(&packet, 0);
     while (!latch_is_set(&dp->exit_latch)) {
         bool received_anything;
         int i;
@@ -1701,25 +1695,19 @@ dp_forwarder_main(void *f_)
                 if (port->rx
                     && port->node.hash >= f->min_hash
                     && port->node.hash <= f->max_hash) {
-                    int buf_size;
+                    struct ofpbuf *packets[NETDEV_MAX_RX_BATCH];
+                    int count;
                     int error;
-                    int mtu;
-
-                    if (netdev_get_mtu(port->netdev, &mtu)) {
-                        mtu = ETH_PAYLOAD_MAX;
-                    }
-                    buf_size = DP_NETDEV_HEADROOM + VLAN_ETH_HEADER_LEN + mtu;
-
-                    ofpbuf_clear(&packet);
-                    ofpbuf_reserve_with_tailroom(&packet, DP_NETDEV_HEADROOM,
-                                                 buf_size);
 
-                    error = netdev_rx_recv(port->rx, &packet);
+                    error = netdev_rx_recv(port->rx, packets, &count);
                     if (!error) {
+                        int i;
                         struct pkt_metadata md
                             = PKT_METADATA_INITIALIZER(port->port_no);
 
-                        dp_netdev_port_input(dp, &packet, &md);
+                        for (i = 0; i < count; i++) {
+                            dp_netdev_port_input(dp, packets[i], &md);
+                        }
                         received_anything = true;
                     } else if (error != EAGAIN && error != EOPNOTSUPP) {
                         static struct vlog_rate_limit rl
@@ -1755,7 +1743,6 @@ dp_forwarder_main(void *f_)
 
         poll_block();
     }
-    ofpbuf_uninit(&packet);
 
     free(f->name);
 
@@ -1853,6 +1840,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
     struct flow key;
 
     if (packet->size < ETH_HEADER_LEN) {
+        ofpbuf_delete(packet);
         return;
     }
     flow_extract(packet, md, &key);
@@ -1863,7 +1851,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
         dp_netdev_flow_used(netdev_flow, packet, &key);
 
         actions = dp_netdev_flow_get_actions(netdev_flow);
-        dp_netdev_execute_actions(dp, &key, packet, md,
+        dp_netdev_execute_actions(dp, &key, packet, true, md,
                                   actions->actions, actions->size);
         dp_netdev_count_packet(dp, DP_STAT_HIT);
     } else if (dp->handler_queues) {
@@ -1871,6 +1859,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
         dp_netdev_output_userspace(dp, packet,
                                    flow_hash_5tuple(&key, 0) % dp->n_handlers,
                                    DPIF_UC_MISS, &key, NULL);
+        ofpbuf_delete(packet);
     }
 }
 
@@ -1899,6 +1888,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
         if (userdata) {
             buf_size += NLA_ALIGN(userdata->nla_len);
         }
+        buf_size += packet->size;
         ofpbuf_init(buf, buf_size);
 
         /* Put ODP flow. */
@@ -1912,10 +1902,8 @@ dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
                                           NLA_ALIGN(userdata->nla_len));
         }
 
-        /* Steal packet data. */
-        ovs_assert(packet->source == OFPBUF_MALLOC);
-        upcall->packet = *packet;
-        ofpbuf_use(packet, NULL, 0);
+        upcall->packet.data = ofpbuf_put(buf, packet->data, packet->size);
+        upcall->packet.size = packet->size;
 
         seq_change(q->seq);
 
@@ -1958,18 +1946,11 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet,
 
         userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
 
-        /* Make a copy if we are not allowed to steal the packet's data. */
-        if (!may_steal) {
-            packet = ofpbuf_clone_with_headroom(packet, DP_NETDEV_HEADROOM);
-        }
         dp_netdev_output_userspace(aux->dp, packet,
                                    flow_hash_5tuple(aux->key, 0)
                                        % aux->dp->n_handlers,
                                    DPIF_UC_ACTION, aux->key,
                                    userdata);
-        if (!may_steal) {
-            ofpbuf_uninit(packet);
-        }
         break;
     }
     case OVS_ACTION_ATTR_PUSH_VLAN:
@@ -1982,17 +1963,23 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet,
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
+
+    if (may_steal) {
+        ofpbuf_delete(packet);
+    }
 }
 
 static void
 dp_netdev_execute_actions(struct dp_netdev *dp, const struct flow *key,
-                          struct ofpbuf *packet, struct pkt_metadata *md,
+                          struct ofpbuf *packet, bool may_steal,
+                          struct pkt_metadata *md,
                           const struct nlattr *actions, size_t actions_len)
     OVS_REQ_RDLOCK(dp->port_rwlock)
 {
     struct dp_netdev_execute_aux aux = {dp, key};
 
-    odp_execute_actions(&aux, packet, md, actions, actions_len, dp_execute_cb);
+    odp_execute_actions(&aux, packet, may_steal, md,
+                        actions, actions_len, dp_execute_cb);
 }
 
 const struct dpif_class dpif_netdev_class = {
diff --git a/lib/dpif-netdev.h b/lib/dpif-netdev.h
new file mode 100644 (file)
index 0000000..f844d84
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DPIF_NETDEV_H
+#define DPIF_NETDEV_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "openvswitch/types.h"
+#include "ofpbuf.h"
+#include "packets.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* Enough headroom to add a vlan tag, plus an extra 2 bytes to allow IP
+ * headers to be aligned on a 4-byte boundary.  */
+enum { DP_NETDEV_HEADROOM = 2 + VLAN_HEADER_LEN };
+
+static inline void dp_packet_pad(struct ofpbuf *b)
+{
+    if (b->size < ETH_TOTAL_MIN) {
+        ofpbuf_put_zeros(b, ETH_TOTAL_MIN - b->size);
+    }
+}
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* netdev.h */
index b33d13e..dbf1c10 100644 (file)
@@ -1150,7 +1150,7 @@ dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute)
 
     COVERAGE_INC(dpif_execute_with_help);
 
-    odp_execute_actions(&aux, execute->packet, &execute->md,
+    odp_execute_actions(&aux, execute->packet, false, &execute->md,
                         execute->actions, execute->actions_len,
                         dpif_execute_helper_cb);
     return aux.error;
index 585ca7f..cb99619 100644 (file)
@@ -49,6 +49,7 @@
 #include "rtbsd.h"
 #include "connectivity.h"
 #include "coverage.h"
+#include "dpif-netdev.h"
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "ofpbuf.h"
@@ -151,6 +152,7 @@ static int af_link_ioctl(unsigned long command, const void *arg);
 #endif
 
 static void netdev_bsd_run(void);
+static int netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup);
 
 static bool
 is_netdev_bsd_class(const struct netdev_class *netdev_class)
@@ -620,13 +622,32 @@ netdev_rx_bsd_recv_tap(struct netdev_rx_bsd *rx, struct ofpbuf *buffer)
 }
 
 static int
-netdev_bsd_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
+netdev_bsd_rx_recv(struct netdev_rx *rx_, struct ofpbuf **packet, int *c)
 {
     struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
+    struct netdev *netdev = rx->up.netdev;
+    struct ofpbuf *buffer;
+    ssize_t retval;
+    int mtu;
+
+    if (netdev_bsd_get_mtu(netdev_bsd_cast(netdev), &mtu)) {
+        mtu = ETH_PAYLOAD_MAX;
+    }
+
+    buffer = ofpbuf_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu, DP_NETDEV_HEADROOM);
 
-    return (rx->pcap_handle
+    retval = (rx->pcap_handle
             ? netdev_rx_bsd_recv_pcap(rx, buffer)
             : netdev_rx_bsd_recv_tap(rx, buffer));
+
+    if (retval) {
+        ofpbuf_delete(buffer);
+    } else {
+        dp_packet_pad(buffer);
+        packet[0] = buffer;
+        *c = 1;
+    }
+    return retval;
 }
 
 /*
index f23fc9f..ecd7317 100644 (file)
@@ -21,6 +21,7 @@
 #include <errno.h>
 
 #include "connectivity.h"
+#include "dpif-netdev.h"
 #include "flow.h"
 #include "list.h"
 #include "netdev-provider.h"
@@ -755,12 +756,11 @@ netdev_dummy_rx_dealloc(struct netdev_rx *rx_)
 }
 
 static int
-netdev_dummy_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
+netdev_dummy_rx_recv(struct netdev_rx *rx_, struct ofpbuf **arr, int *c)
 {
     struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
     struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev);
     struct ofpbuf *packet;
-    int retval;
 
     ovs_mutex_lock(&netdev->mutex);
     if (!list_is_empty(&rx->recv_queue)) {
@@ -774,22 +774,15 @@ netdev_dummy_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
     if (!packet) {
         return EAGAIN;
     }
+    ovs_mutex_lock(&netdev->mutex);
+    netdev->stats.rx_packets++;
+    netdev->stats.rx_bytes += packet->size;
+    ovs_mutex_unlock(&netdev->mutex);
 
-    if (packet->size <= ofpbuf_tailroom(buffer)) {
-        memcpy(buffer->data, packet->data, packet->size);
-        buffer->size += packet->size;
-        retval = 0;
-
-        ovs_mutex_lock(&netdev->mutex);
-        netdev->stats.rx_packets++;
-        netdev->stats.rx_bytes += packet->size;
-        ovs_mutex_unlock(&netdev->mutex);
-    } else {
-        retval = EMSGSIZE;
-    }
-    ofpbuf_delete(packet);
-
-    return retval;
+    dp_packet_pad(packet);
+    arr[0] = packet;
+    *c = 1;
+    return 0;
 }
 
 static void
index 75ce7c6..2aa674a 100644 (file)
@@ -50,6 +50,7 @@
 #include "connectivity.h"
 #include "coverage.h"
 #include "dpif-linux.h"
+#include "dpif-netdev.h"
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "hash.h"
@@ -461,6 +462,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)
@@ -984,17 +986,34 @@ netdev_linux_rx_recv_tap(int fd, struct ofpbuf *buffer)
 }
 
 static int
-netdev_linux_rx_recv(struct netdev_rx *rx_, struct ofpbuf *buffer)
+netdev_linux_rx_recv(struct netdev_rx *rx_, struct ofpbuf **packet, int *c)
 {
     struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
-    int retval;
+    struct netdev *netdev = rx->up.netdev;
+    struct ofpbuf *buffer;
+    ssize_t retval;
+    int mtu;
+
+    if (netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu)) {
+        mtu = ETH_PAYLOAD_MAX;
+    }
+
+    buffer = ofpbuf_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu, DP_NETDEV_HEADROOM);
 
     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_));
+
+    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_));
+        }
+        ofpbuf_delete(buffer);
+    } else {
+        dp_packet_pad(buffer);
+        packet[0] = buffer;
+        *c = 1;
     }
 
     return retval;
index 673d3ab..0711bb9 100644 (file)
@@ -634,28 +634,18 @@ struct netdev_class {
     void (*rx_destruct)(struct netdev_rx *);
     void (*rx_dealloc)(struct netdev_rx *);
 
-    /* Attempts to receive a packet from 'rx' into the tailroom of 'buffer',
-     * which should initially be empty.  If successful, returns 0 and
-     * increments 'buffer->size' by the number of bytes in the received packet,
-     * otherwise a positive errno value.  Returns EAGAIN immediately if no
-     * packet is ready to be received.
+    /* Attempts to receive batch of packets from 'rx' and place array of pointers
+     * into '*pkt'. netdev is responsible for allocating buffers.
+     * '*cnt' points to packet count for given batch. Once packets are returned
+     * to caller, netdev should give up ownership of ofpbuf data.
      *
-     * Must return EMSGSIZE, and discard the packet, if the received packet
-     * is longer than 'ofpbuf_tailroom(buffer)'.
-     *
-     * Implementations may make use of VLAN_HEADER_LEN bytes of tailroom to
-     * add a VLAN header which is obtained out-of-band to the packet. If
-     * this occurs then VLAN_HEADER_LEN bytes of tailroom will no longer be
-     * available for the packet, otherwise it may be used for the packet
-     * itself.
-     *
-     * It is advised that the tailroom of 'buffer' should be
-     * VLAN_HEADER_LEN bytes longer than the MTU to allow space for an
-     * out-of-band VLAN header to be added to the packet.
+     * Implementations should allocate buffer with DP_NETDEV_HEADROOM headroom
+     * and add a VLAN header which is obtained out-of-band to the packet.
      *
+     * Caller is expected to pass array of size MAX_RX_BATCH.
      * This function may be set to null if it would always return EOPNOTSUPP
      * anyhow. */
-    int (*rx_recv)(struct netdev_rx *rx, struct ofpbuf *buffer);
+    int (*rx_recv)(struct netdev_rx *rx, struct ofpbuf **pkt, int *cnt);
 
     /* Registers with the poll loop to wake up from the next call to
      * poll_block() when a packet is ready to be received with netdev_rx_recv()
index e9c8d8f..0d7b69d 100644 (file)
@@ -551,22 +551,13 @@ netdev_rx_close(struct netdev_rx *rx)
     }
 }
 
-/* Attempts to receive a packet from 'rx' into the tailroom of 'buffer', which
- * must initially be empty.  If successful, returns 0 and increments
- * 'buffer->size' by the number of bytes in the received packet, otherwise a
- * positive errno value.
+/* Attempts to receive batch of packets from 'rx'.
  *
  * Returns EAGAIN immediately if no packet is ready to be received.
  *
  * Returns EMSGSIZE, and discards the packet, if the received packet is longer
  * than 'ofpbuf_tailroom(buffer)'.
  *
- * Implementations may make use of VLAN_HEADER_LEN bytes of tailroom to
- * add a VLAN header which is obtained out-of-band to the packet. If
- * this occurs then VLAN_HEADER_LEN bytes of tailroom will no longer be
- * available for the packet, otherwise it may be used for the packet
- * itself.
- *
  * It is advised that the tailroom of 'buffer' should be
  * VLAN_HEADER_LEN bytes longer than the MTU to allow space for an
  * out-of-band VLAN header to be added to the packet.  At the very least,
@@ -575,23 +566,15 @@ netdev_rx_close(struct netdev_rx *rx)
  * This function may be set to null if it would always return EOPNOTSUPP
  * anyhow. */
 int
-netdev_rx_recv(struct netdev_rx *rx, struct ofpbuf *buffer)
+netdev_rx_recv(struct netdev_rx *rx, struct ofpbuf **buffers, int *cnt)
 {
     int retval;
 
-    ovs_assert(buffer->size == 0);
-    ovs_assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN);
-
-    retval = rx->netdev->netdev_class->rx_recv(rx, buffer);
+    retval = rx->netdev->netdev_class->rx_recv(rx, buffers, cnt);
     if (!retval) {
         COVERAGE_INC(netdev_received);
-        if (buffer->size < ETH_TOTAL_MIN) {
-            ofpbuf_put_zeros(buffer, ETH_TOTAL_MIN - buffer->size);
-        }
-        return 0;
-    } else {
-        return retval;
     }
+    return retval;
 }
 
 /* Arranges for poll_block() to wake up when a packet is ready to be received
index 410c35b..a4dd99b 100644 (file)
@@ -161,7 +161,7 @@ void netdev_rx_close(struct netdev_rx *);
 
 const char *netdev_rx_get_name(const struct netdev_rx *);
 
-int netdev_rx_recv(struct netdev_rx *, struct ofpbuf *);
+int netdev_rx_recv(struct netdev_rx *rx, struct ofpbuf **buffers, int *cnt);
 void netdev_rx_wait(struct netdev_rx *);
 int netdev_rx_drain(struct netdev_rx *);
 
@@ -313,6 +313,8 @@ typedef void netdev_dump_queue_stats_cb(unsigned int queue_id,
 int netdev_dump_queue_stats(const struct netdev *,
                             netdev_dump_queue_stats_cb *, void *aux);
 
+enum { NETDEV_MAX_RX_BATCH = 256 };     /* Maximum number packets in rx_recv() batch. */
+
 #ifdef  __cplusplus
 }
 #endif
index 096c113..cf33eb7 100644 (file)
@@ -141,13 +141,14 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
 }
 
 static void
-odp_execute_actions__(void *dp, struct ofpbuf *packet, struct pkt_metadata *,
+odp_execute_actions__(void *dp, struct ofpbuf *packet, bool steal,
+                      struct pkt_metadata *,
                       const struct nlattr *actions, size_t actions_len,
                       odp_execute_cb dp_execute_action, bool more_actions);
 
 static void
-odp_execute_sample(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
-                   const struct nlattr *action,
+odp_execute_sample(void *dp, struct ofpbuf *packet, bool steal,
+                   struct pkt_metadata *md, const struct nlattr *action,
                    odp_execute_cb dp_execute_action, bool more_actions)
 {
     const struct nlattr *subactions = NULL;
@@ -175,13 +176,14 @@ odp_execute_sample(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
         }
     }
 
-    odp_execute_actions__(dp, packet, md, nl_attr_get(subactions),
+    odp_execute_actions__(dp, packet, steal, md, nl_attr_get(subactions),
                           nl_attr_get_size(subactions), dp_execute_action,
                           more_actions);
 }
 
 static void
-odp_execute_actions__(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
+odp_execute_actions__(void *dp, struct ofpbuf *packet, bool steal,
+                      struct pkt_metadata *md,
                       const struct nlattr *actions, size_t actions_len,
                       odp_execute_cb dp_execute_action, bool more_actions)
 {
@@ -196,10 +198,11 @@ odp_execute_actions__(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
         case OVS_ACTION_ATTR_OUTPUT:
         case OVS_ACTION_ATTR_USERSPACE:
             if (dp_execute_action) {
+                bool may_steal;
                 /* Allow 'dp_execute_action' to steal the packet data if we do
                  * not need it any more. */
-                bool steal = !more_actions && left <= NLA_ALIGN(a->nla_len);
-                dp_execute_action(dp, packet, md, a, steal);
+                may_steal = steal && (!more_actions && left <= NLA_ALIGN(a->nla_len));
+                dp_execute_action(dp, packet, md, a, may_steal);
             }
             break;
 
@@ -228,7 +231,7 @@ odp_execute_actions__(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
             break;
 
         case OVS_ACTION_ATTR_SAMPLE:
-            odp_execute_sample(dp, packet, md, a, dp_execute_action,
+            odp_execute_sample(dp, packet, steal, md, a, dp_execute_action,
                                more_actions || left > NLA_ALIGN(a->nla_len));
             break;
 
@@ -240,10 +243,16 @@ odp_execute_actions__(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
 }
 
 void
-odp_execute_actions(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
+odp_execute_actions(void *dp, struct ofpbuf *packet, bool steal,
+                    struct pkt_metadata *md,
                     const struct nlattr *actions, size_t actions_len,
                     odp_execute_cb dp_execute_action)
 {
-    odp_execute_actions__(dp, packet, md, actions, actions_len,
+    odp_execute_actions__(dp, packet, steal, md, actions, actions_len,
                           dp_execute_action, false);
+
+    if (!actions_len && steal) {
+        /* Drop action. */
+        ofpbuf_delete(packet);
+    }
 }
index 670e8ea..6f1b9bd 100644 (file)
@@ -35,8 +35,8 @@ typedef void (*odp_execute_cb)(void *dp, struct ofpbuf *packet,
  * to 'dp_execute_action', if non-NULL.  Currently this is called only for
  * actions OVS_ACTION_ATTR_OUTPUT and OVS_ACTION_ATTR_USERSPACE so
  * 'dp_execute_action' needs to handle only these. */
-void
-odp_execute_actions(void *dp, struct ofpbuf *packet, struct pkt_metadata *,
+void odp_execute_actions(void *dp, struct ofpbuf *packet, bool steal,
+                    struct pkt_metadata *,
                     const struct nlattr *actions, size_t actions_len,
                     odp_execute_cb dp_execute_action);
 #endif
index 9b6ce10..df7cf7d 100644 (file)
@@ -2135,7 +2135,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
                                           &ctx->xout->odp_actions,
                                           &ctx->xout->wc);
 
-    odp_execute_actions(NULL, packet, &md, ctx->xout->odp_actions.data,
+    odp_execute_actions(NULL, packet, false, &md, ctx->xout->odp_actions.data,
                         ctx->xout->odp_actions.size, NULL);
 
     pin = xmalloc(sizeof *pin);