diff mbox

[ovs-dev,v2,06/15] dpif-netdev: create batch object

Message ID 1461290070-63896-7-git-send-email-pshelar@ovn.org
State Superseded
Headers show

Commit Message

Pravin Shelar April 22, 2016, 1:54 a.m. UTC
DPDK datapath operate on batch of packets. To pass the batch of
packets around we use packets array and count.  Next patch needs
to associate meta-data with each batch of packets. So Introducing
a batch structure to make handling the metadata easier.

Signed-off-by: Pravin B Shelar <pshelar@ovn.org>
---
 lib/dp-packet.h              |  31 +++++++++
 lib/dpif-netdev.c            | 147 +++++++++++++++++++++----------------------
 lib/dpif.c                   |  12 ++--
 lib/netdev.c                 |  34 +++++-----
 lib/netdev.h                 |  12 ++--
 lib/odp-execute.c            |  10 ++-
 lib/odp-execute.h            |   5 +-
 ofproto/ofproto-dpif-xlate.c |   5 +-
 8 files changed, 146 insertions(+), 110 deletions(-)

Comments

Jesse Gross May 6, 2016, 9:12 p.m. UTC | #1
On Thu, Apr 21, 2016 at 6:54 PM, Pravin B Shelar <pshelar@ovn.org> wrote:

> +netdev_send(struct netdev *netdev, int qid, struct dp_packet_batch *batch,
> +            bool may_steal)
>  {
>      if (!netdev->netdev_class->send) {
>          if (may_steal) {
> -            for (int i = 0; i < cnt; i++) {
> -                dp_packet_delete(buffers[i]);
> +            for (int i = 0; i < batch->count; i++) {
> +                dp_packet_delete(batch->packets[i]);
>              }
>          }

My guess it that is probably worthwhile to introduce a
dp_packet_batch_delete() - we have several similar instances of this.

Acked-by: Jesse Gross <jesse@kernel.org>
diff mbox

Patch

diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index 4a8b5ab..ce223e8 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -563,6 +563,37 @@  dp_packet_rss_invalidate(struct dp_packet *p)
 #endif
 }
 
+enum { NETDEV_MAX_BURST = 32 }; /* Maximum number packets in a batch. */
+
+struct dp_packet_batch {
+    int count;
+    struct dp_packet *packets[NETDEV_MAX_BURST];
+};
+
+static inline void dp_packet_batch_init(struct dp_packet_batch *b)
+{
+    b->count = 0;
+}
+
+static inline void
+dp_packet_batch_clone(struct dp_packet_batch *dst,
+                      struct dp_packet_batch *src)
+{
+    int i;
+
+    for (i = 0; i < src->count; i++) {
+        dst->packets[i] = dp_packet_clone(src->packets[i]);
+    }
+    dst->count = src->count;
+}
+
+static inline void
+packet_batch_init_packet(struct dp_packet_batch *b, struct dp_packet *p)
+{
+    b->count = 1;
+    b->packets[0] = p;
+}
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index b2e8ae0..4eeee94 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -473,14 +473,14 @@  static void do_del_port(struct dp_netdev *dp, struct dp_netdev_port *)
 static int dpif_netdev_open(const struct dpif_class *, const char *name,
                             bool create, struct dpif **);
 static void dp_netdev_execute_actions(struct dp_netdev_pmd_thread *pmd,
-                                      struct dp_packet **, int c,
+                                      struct dp_packet_batch *,
                                       bool may_steal,
                                       const struct nlattr *actions,
                                       size_t actions_len);
 static void dp_netdev_input(struct dp_netdev_pmd_thread *,
-                            struct dp_packet **, int cnt, odp_port_t port_no);
+                            struct dp_packet_batch *, odp_port_t port_no);
 static void dp_netdev_recirculate(struct dp_netdev_pmd_thread *,
-                                  struct dp_packet **, int cnt);
+                                  struct dp_packet_batch *);
 
 static void dp_netdev_disable_upcall(struct dp_netdev *);
 static void dp_netdev_pmd_reload_done(struct dp_netdev_pmd_thread *pmd);
@@ -2344,7 +2344,7 @@  dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_pmd_thread *pmd;
-    struct dp_packet *pp;
+    struct dp_packet_batch pp;
 
     if (dp_packet_size(execute->packet) < ETH_HEADER_LEN ||
         dp_packet_size(execute->packet) > UINT16_MAX) {
@@ -2366,8 +2366,8 @@  dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
         ovs_mutex_lock(&dp->port_mutex);
     }
 
-    pp = execute->packet;
-    dp_netdev_execute_actions(pmd, &pp, 1, false, execute->actions,
+    packet_batch_init_packet(&pp, execute->packet);
+    dp_netdev_execute_actions(pmd, &pp, false, execute->actions,
                               execute->actions_len);
     if (pmd->core_id == NON_PMD_CORE_ID) {
         dp_netdev_pmd_unref(pmd);
@@ -2561,17 +2561,18 @@  dp_netdev_process_rxq_port(struct dp_netdev_pmd_thread *pmd,
                            struct dp_netdev_port *port,
                            struct netdev_rxq *rxq)
 {
-    struct dp_packet *packets[NETDEV_MAX_BURST];
-    int error, cnt;
+    struct dp_packet_batch batch;
+    int error;
 
+    dp_packet_batch_init(&batch);
     cycles_count_start(pmd);
-    error = netdev_rxq_recv(rxq, packets, &cnt);
+    error = netdev_rxq_recv(rxq, &batch);
     cycles_count_end(pmd, PMD_CYCLES_POLLING);
     if (!error) {
         *recirc_depth_get() = 0;
 
         cycles_count_start(pmd);
-        dp_netdev_input(pmd, packets, cnt, port->port_no);
+        dp_netdev_input(pmd, &batch, port->port_no);
         cycles_count_end(pmd, PMD_CYCLES_PROCESSING);
     } else if (error != EAGAIN && error != EOPNOTSUPP) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -3332,13 +3333,11 @@  dpif_netdev_packet_get_rss_hash(struct dp_packet *packet,
 }
 
 struct packet_batch_per_flow {
-    unsigned int packet_count;
     unsigned int byte_count;
     uint16_t tcp_flags;
-
     struct dp_netdev_flow *flow;
 
-    struct dp_packet *packets[NETDEV_MAX_BURST];
+    struct dp_packet_batch array;
 };
 
 static inline void
@@ -3346,9 +3345,9 @@  packet_batch_per_flow_update(struct packet_batch_per_flow *batch,
                              struct dp_packet *packet,
                              const struct miniflow *mf)
 {
-    batch->tcp_flags |= miniflow_get_tcp_flags(mf);
-    batch->packets[batch->packet_count++] = packet;
     batch->byte_count += dp_packet_size(packet);
+    batch->tcp_flags |= miniflow_get_tcp_flags(mf);
+    batch->array.packets[batch->array.count++] = packet;
 }
 
 static inline void
@@ -3358,7 +3357,7 @@  packet_batch_per_flow_init(struct packet_batch_per_flow *batch,
     flow->batch = batch;
 
     batch->flow = flow;
-    batch->packet_count = 0;
+    dp_packet_batch_init(&batch->array);
     batch->byte_count = 0;
     batch->tcp_flags = 0;
 }
@@ -3371,12 +3370,12 @@  packet_batch_per_flow_execute(struct packet_batch_per_flow *batch,
     struct dp_netdev_actions *actions;
     struct dp_netdev_flow *flow = batch->flow;
 
-    dp_netdev_flow_used(flow, batch->packet_count, batch->byte_count,
+    dp_netdev_flow_used(flow, batch->array.count, batch->byte_count,
                         batch->tcp_flags, now);
 
     actions = dp_netdev_flow_get_actions(flow);
 
-    dp_netdev_execute_actions(pmd, batch->packets, batch->packet_count, true,
+    dp_netdev_execute_actions(pmd, &batch->array, true,
                               actions->actions, actions->size);
 }
 
@@ -3407,14 +3406,16 @@  dp_netdev_queue_batches(struct dp_packet *pkt,
  * initialized by this function using 'port_no'.
  */
 static inline size_t
-emc_processing(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets,
-               size_t cnt, struct netdev_flow_key *keys,
+emc_processing(struct dp_netdev_pmd_thread *pmd, struct dp_packet_batch *packets_,
+               struct netdev_flow_key *keys,
                struct packet_batch_per_flow batches[], size_t *n_batches,
                bool md_is_valid, odp_port_t port_no)
 {
     struct emc_cache *flow_cache = &pmd->flow_cache;
     struct netdev_flow_key *key = &keys[0];
     size_t i, n_missed = 0, n_dropped = 0;
+    struct dp_packet **packets = packets_->packets;
+    int cnt = packets_->count;
 
     for (i = 0; i < cnt; i++) {
         struct dp_netdev_flow *flow;
@@ -3461,19 +3462,22 @@  emc_processing(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets,
 
 static inline void
 fast_path_processing(struct dp_netdev_pmd_thread *pmd,
-                     struct dp_packet **packets, size_t cnt,
+                     struct dp_packet_batch *packets_,
                      struct netdev_flow_key *keys,
                      struct packet_batch_per_flow batches[], size_t *n_batches)
 {
+    int cnt = packets_->count;
 #if !defined(__CHECKER__) && !defined(_WIN32)
     const size_t PKT_ARRAY_SIZE = cnt;
 #else
     /* Sparse or MSVC doesn't like variable length array. */
     enum { PKT_ARRAY_SIZE = NETDEV_MAX_BURST };
 #endif
+    struct dp_packet **packets = packets_->packets;
     struct dpcls_rule *rules[PKT_ARRAY_SIZE];
     struct dp_netdev *dp = pmd->dp;
     struct emc_cache *flow_cache = &pmd->flow_cache;
+    struct dp_packet_batch b;
     int miss_cnt = 0, lost_cnt = 0;
     bool any_miss;
     size_t i;
@@ -3541,7 +3545,8 @@  fast_path_processing(struct dp_netdev_pmd_thread *pmd,
             /* We can't allow the packet batching in the next loop to execute
              * the actions.  Otherwise, if there are any slow path actions,
              * we'll send the packet up twice. */
-            dp_netdev_execute_actions(pmd, &packets[i], 1, true,
+            packet_batch_init_packet(&b, packets[i]);
+            dp_netdev_execute_actions(pmd, &b, true,
                                       actions.data, actions.size);
 
             add_actions = put_actions.size ? &put_actions : &actions;
@@ -3606,9 +3611,10 @@  fast_path_processing(struct dp_netdev_pmd_thread *pmd,
  * valid, 'md_is_valid' must be true and 'port_no' will be ignored. */
 static void
 dp_netdev_input__(struct dp_netdev_pmd_thread *pmd,
-                  struct dp_packet **packets, int cnt,
+                  struct dp_packet_batch *packets,
                   bool md_is_valid, odp_port_t port_no)
 {
+    int cnt = packets->count;
 #if !defined(__CHECKER__) && !defined(_WIN32)
     const size_t PKT_ARRAY_SIZE = cnt;
 #else
@@ -3621,10 +3627,11 @@  dp_netdev_input__(struct dp_netdev_pmd_thread *pmd,
     size_t newcnt, n_batches, i;
 
     n_batches = 0;
-    newcnt = emc_processing(pmd, packets, cnt, keys, batches, &n_batches,
+    newcnt = emc_processing(pmd, packets, keys, batches, &n_batches,
                             md_is_valid, port_no);
     if (OVS_UNLIKELY(newcnt)) {
-        fast_path_processing(pmd, packets, newcnt, keys, batches, &n_batches);
+        packets->count = newcnt;
+        fast_path_processing(pmd, packets, keys, batches, &n_batches);
     }
 
     for (i = 0; i < n_batches; i++) {
@@ -3638,17 +3645,17 @@  dp_netdev_input__(struct dp_netdev_pmd_thread *pmd,
 
 static void
 dp_netdev_input(struct dp_netdev_pmd_thread *pmd,
-                struct dp_packet **packets, int cnt,
+                struct dp_packet_batch *packets,
                 odp_port_t port_no)
 {
-     dp_netdev_input__(pmd, packets, cnt, false, port_no);
+     dp_netdev_input__(pmd, packets, false, port_no);
 }
 
 static void
 dp_netdev_recirculate(struct dp_netdev_pmd_thread *pmd,
-                      struct dp_packet **packets, int cnt)
+                      struct dp_packet_batch *packets)
 {
-     dp_netdev_input__(pmd, packets, cnt, true, 0);
+     dp_netdev_input__(pmd, packets, true, 0);
 }
 
 struct dp_netdev_execute_aux {
@@ -3674,21 +3681,21 @@  dpif_netdev_register_upcall_cb(struct dpif *dpif, upcall_callback *cb,
 }
 
 static void
-dp_netdev_drop_packets(struct dp_packet **packets, int cnt, bool may_steal)
+dp_netdev_drop_packets(struct dp_packet_batch *batch, bool may_steal)
 {
     if (may_steal) {
         int i;
 
-        for (i = 0; i < cnt; i++) {
-            dp_packet_delete(packets[i]);
+        for (i = 0; i < batch->count; i++) {
+            dp_packet_delete(batch->packets[i]);
         }
     }
 }
 
 static int
 push_tnl_action(const struct dp_netdev *dp,
-                   const struct nlattr *attr,
-                   struct dp_packet **packets, int cnt)
+                const struct nlattr *attr,
+                struct dp_packet_batch *batch)
 {
     struct dp_netdev_port *tun_port;
     const struct ovs_action_push_tnl *data;
@@ -3699,24 +3706,13 @@  push_tnl_action(const struct dp_netdev *dp,
     if (!tun_port) {
         return -EINVAL;
     }
-    netdev_push_header(tun_port->netdev, packets, cnt, data);
+    netdev_push_header(tun_port->netdev, batch, data);
 
     return 0;
 }
 
 static void
-dp_netdev_clone_pkt_batch(struct dp_packet **dst_pkts,
-                          struct dp_packet **src_pkts, int cnt)
-{
-    int i;
-
-    for (i = 0; i < cnt; i++) {
-        dst_pkts[i] = dp_packet_clone(src_pkts[i]);
-    }
-}
-
-static void
-dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
+dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
               const struct nlattr *a, bool may_steal)
     OVS_NO_THREAD_SAFETY_ANALYSIS
 {
@@ -3735,28 +3731,28 @@  dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
 
             atomic_read_relaxed(&pmd->tx_qid, &tx_qid);
 
-            netdev_send(p->netdev, tx_qid, packets, cnt, may_steal);
+            netdev_send(p->netdev, tx_qid, packets_, may_steal);
             return;
         }
         break;
 
     case OVS_ACTION_ATTR_TUNNEL_PUSH:
         if (*depth < MAX_RECIRC_DEPTH) {
-            struct dp_packet *tnl_pkt[NETDEV_MAX_BURST];
+            struct dp_packet_batch tnl_pkt;
             int err;
 
             if (!may_steal) {
-                dp_netdev_clone_pkt_batch(tnl_pkt, packets, cnt);
-                packets = tnl_pkt;
+                dp_packet_batch_clone(&tnl_pkt, packets_);
+                packets_ = &tnl_pkt;
             }
 
-            err = push_tnl_action(dp, a, packets, cnt);
+            err = push_tnl_action(dp, a, packets_);
             if (!err) {
                 (*depth)++;
-                dp_netdev_recirculate(pmd, packets, cnt);
+                dp_netdev_recirculate(pmd, packets_);
                 (*depth)--;
             } else {
-                dp_netdev_drop_packets(tnl_pkt, cnt, !may_steal);
+                dp_netdev_drop_packets(&tnl_pkt, !may_steal);
             }
             return;
         }
@@ -3768,30 +3764,30 @@  dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
 
             p = dp_netdev_lookup_port(dp, portno);
             if (p) {
-                struct dp_packet *tnl_pkt[NETDEV_MAX_BURST];
+                struct dp_packet_batch tnl_pkt;
                 int err;
 
                 if (!may_steal) {
-                   dp_netdev_clone_pkt_batch(tnl_pkt, packets, cnt);
-                   packets = tnl_pkt;
+                   dp_packet_batch_clone(&tnl_pkt, packets_);
+                   packets_ = &tnl_pkt;
                 }
 
-                err = netdev_pop_header(p->netdev, packets, &cnt);
-                if (!cnt) {
+                err = netdev_pop_header(p->netdev, packets_);
+                if (!packets_->count) {
                     return;
                 }
                 if (!err) {
                     int i;
 
-                    for (i = 0; i < cnt; i++) {
-                        packets[i]->md.in_port.odp_port = portno;
+                    for (i = 0; i < packets_->count; i++) {
+                        packets_->packets[i]->md.in_port.odp_port = portno;
                     }
 
                     (*depth)++;
-                    dp_netdev_recirculate(pmd, packets, cnt);
+                    dp_netdev_recirculate(pmd, packets_);
                     (*depth)--;
                 } else {
-                    dp_netdev_drop_packets(tnl_pkt, cnt, !may_steal);
+                    dp_netdev_drop_packets(&tnl_pkt, !may_steal);
                 }
                 return;
             }
@@ -3800,6 +3796,7 @@  dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
 
     case OVS_ACTION_ATTR_USERSPACE:
         if (!fat_rwlock_tryrdlock(&dp->upcall_rwlock)) {
+            struct dp_packet **packets = packets_->packets;
             const struct nlattr *userdata;
             struct ofpbuf actions;
             struct flow flow;
@@ -3809,8 +3806,9 @@  dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
             userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
             ofpbuf_init(&actions, 0);
 
-            for (i = 0; i < cnt; i++) {
+            for (i = 0; i < packets_->count; i++) {
                 int error;
+                struct dp_packet_batch b;
 
                 ofpbuf_clear(&actions);
 
@@ -3820,7 +3818,8 @@  dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
                                          DPIF_UC_ACTION, userdata,&actions,
                                          NULL);
                 if (!error || error == ENOSPC) {
-                    dp_netdev_execute_actions(pmd, &packets[i], 1, may_steal,
+                    packet_batch_init_packet(&b, packets[i]);
+                    dp_netdev_execute_actions(pmd, &b, may_steal,
                                               actions.data, actions.size);
                 } else if (may_steal) {
                     dp_packet_delete(packets[i]);
@@ -3835,20 +3834,20 @@  dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
 
     case OVS_ACTION_ATTR_RECIRC:
         if (*depth < MAX_RECIRC_DEPTH) {
-            struct dp_packet *recirc_pkts[NETDEV_MAX_BURST];
+            struct dp_packet_batch recirc_pkts;
             int i;
 
             if (!may_steal) {
-               dp_netdev_clone_pkt_batch(recirc_pkts, packets, cnt);
-               packets = recirc_pkts;
+               dp_packet_batch_clone(&recirc_pkts, packets_);
+               packets_ = &recirc_pkts;
             }
 
-            for (i = 0; i < cnt; i++) {
-                packets[i]->md.recirc_id = nl_attr_get_u32(a);
+            for (i = 0; i < packets_->count; i++) {
+                packets_->packets[i]->md.recirc_id = nl_attr_get_u32(a);
             }
 
             (*depth)++;
-            dp_netdev_recirculate(pmd, packets, cnt);
+            dp_netdev_recirculate(pmd, packets_);
             (*depth)--;
 
             return;
@@ -3877,18 +3876,18 @@  dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
         OVS_NOT_REACHED();
     }
 
-    dp_netdev_drop_packets(packets, cnt, may_steal);
+    dp_netdev_drop_packets(packets_, may_steal);
 }
 
 static void
 dp_netdev_execute_actions(struct dp_netdev_pmd_thread *pmd,
-                          struct dp_packet **packets, int cnt,
+                          struct dp_packet_batch *packets,
                           bool may_steal,
                           const struct nlattr *actions, size_t actions_len)
 {
     struct dp_netdev_execute_aux aux = { pmd };
 
-    odp_execute_actions(&aux, packets, cnt, may_steal, actions,
+    odp_execute_actions(&aux, packets, may_steal, actions,
                         actions_len, dp_execute_cb);
 }
 
diff --git a/lib/dpif.c b/lib/dpif.c
index 2c64d9e..acbc173 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1087,14 +1087,14 @@  struct dpif_execute_helper_aux {
 /* This is called for actions that need the context of the datapath to be
  * meaningful. */
 static void
-dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt,
+dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
                        const struct nlattr *action, bool may_steal OVS_UNUSED)
 {
     struct dpif_execute_helper_aux *aux = aux_;
     int type = nl_attr_type(action);
-    struct dp_packet *packet = *packets;
+    struct dp_packet *packet = packets_->packets[0];
 
-    ovs_assert(cnt == 1);
+    ovs_assert(packets_->count == 1);
 
     switch ((enum ovs_action_attr)type) {
     case OVS_ACTION_ATTR_CT:
@@ -1161,12 +1161,12 @@  static int
 dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute)
 {
     struct dpif_execute_helper_aux aux = {dpif, 0};
-    struct dp_packet *pp;
+    struct dp_packet_batch pb;
 
     COVERAGE_INC(dpif_execute_with_help);
 
-    pp = execute->packet;
-    odp_execute_actions(&aux, &pp, 1, false, execute->actions,
+    packet_batch_init_packet(&pb, execute->packet);
+    odp_execute_actions(&aux, &pb, false, execute->actions,
                         execute->actions_len, dpif_execute_helper_cb);
     return aux.error;
 }
diff --git a/lib/netdev.c b/lib/netdev.c
index cac91ea..0a8e15c 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -657,15 +657,15 @@  netdev_rxq_close(struct netdev_rxq *rx)
  * Returns EAGAIN immediately if no packet is ready to be received or another
  * positive errno value if an error was encountered. */
 int
-netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet **pkts, int *cnt)
+netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *batch)
 {
     int retval;
 
-    retval = rx->netdev->netdev_class->rxq_recv(rx, pkts, cnt);
+    retval = rx->netdev->netdev_class->rxq_recv(rx, batch->packets, &batch->count);
     if (!retval) {
         COVERAGE_INC(netdev_received);
     } else {
-        *cnt = 0;
+        batch->count = 0;
     }
     return retval;
 }
@@ -746,19 +746,20 @@  netdev_set_multiq(struct netdev *netdev, unsigned int n_txq,
  * Some network devices may not implement support for this function.  In such
  * cases this function will always return EOPNOTSUPP. */
 int
-netdev_send(struct netdev *netdev, int qid, struct dp_packet **buffers,
-            int cnt, bool may_steal)
+netdev_send(struct netdev *netdev, int qid, struct dp_packet_batch *batch,
+            bool may_steal)
 {
     if (!netdev->netdev_class->send) {
         if (may_steal) {
-            for (int i = 0; i < cnt; i++) {
-                dp_packet_delete(buffers[i]);
+            for (int i = 0; i < batch->count; i++) {
+                dp_packet_delete(batch->packets[i]);
             }
         }
         return EOPNOTSUPP;
     }
 
-    int error = netdev->netdev_class->send(netdev, qid, buffers, cnt,
+    int error = netdev->netdev_class->send(netdev, qid,
+                                           batch->packets, batch->count,
                                            may_steal);
     if (!error) {
         COVERAGE_INC(netdev_sent);
@@ -767,21 +768,22 @@  netdev_send(struct netdev *netdev, int qid, struct dp_packet **buffers,
 }
 
 int
-netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers, int *pcnt)
+netdev_pop_header(struct netdev *netdev, struct dp_packet_batch *batch)
 {
-    int i, cnt = *pcnt, n_cnt = 0;
+    int i, n_cnt = 0;
+    struct dp_packet **buffers = batch->packets;
 
     if (!netdev->netdev_class->pop_header) {
         return EOPNOTSUPP;
     }
 
-    for (i = 0; i < cnt; i++) {
+    for (i = 0; i < batch->count; i++) {
         buffers[i] = netdev->netdev_class->pop_header(buffers[i]);
         if (buffers[i]) {
             buffers[n_cnt++] = buffers[i];
         }
     }
-    *pcnt = n_cnt;
+    batch->count = n_cnt;
     return 0;
 }
 
@@ -797,7 +799,7 @@  netdev_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *dat
 
 int
 netdev_push_header(const struct netdev *netdev,
-                   struct dp_packet **buffers, int cnt,
+                   struct dp_packet_batch *batch,
                    const struct ovs_action_push_tnl *data)
 {
     int i;
@@ -806,9 +808,9 @@  netdev_push_header(const struct netdev *netdev,
         return -EINVAL;
     }
 
-    for (i = 0; i < cnt; i++) {
-        netdev->netdev_class->push_header(buffers[i], data);
-        pkt_metadata_init(&buffers[i]->md, u32_to_odp(data->out_port));
+    for (i = 0; i < batch->count; i++) {
+        netdev->netdev_class->push_header(batch->packets[i], data);
+        pkt_metadata_init(&batch->packets[i]->md, u32_to_odp(data->out_port));
     }
 
     return 0;
diff --git a/lib/netdev.h b/lib/netdev.h
index f31b565..22e4209 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -59,6 +59,7 @@  extern "C" {
  *      netdev and access each of those from a different thread.)
  */
 
+struct dp_packet_batch;
 struct dp_packet;
 struct netdev_class;
 struct netdev_rxq;
@@ -143,23 +144,21 @@  void netdev_rxq_close(struct netdev_rxq *);
 const char *netdev_rxq_get_name(const struct netdev_rxq *);
 int netdev_rxq_get_queue_id(const struct netdev_rxq *);
 
-int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet **buffers,
-                    int *cnt);
+int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *);
 void netdev_rxq_wait(struct netdev_rxq *);
 int netdev_rxq_drain(struct netdev_rxq *);
 
 /* Packet transmission. */
-int netdev_send(struct netdev *, int qid, struct dp_packet **, int cnt,
+int netdev_send(struct netdev *, int qid, struct dp_packet_batch *,
                 bool may_steal);
 void netdev_send_wait(struct netdev *, int qid);
 
 int netdev_build_header(const struct netdev *, struct ovs_action_push_tnl *data,
                         const struct flow *tnl_flow);
 int netdev_push_header(const struct netdev *netdev,
-                       struct dp_packet **buffers, int cnt,
+                       struct dp_packet_batch *,
                        const struct ovs_action_push_tnl *data);
-int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers,
-                      int *pcnt);
+int netdev_pop_header(struct netdev *netdev, struct dp_packet_batch *);
 
 /* Hardware address. */
 int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac);
@@ -276,7 +275,6 @@  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_BURST = 32 }; /* Maximum number packets in a batch. */
 extern struct seq *tnl_conf_seq;
 
 #ifndef _WIN32
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index b5204b2..6a60c90 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -450,6 +450,7 @@  odp_execute_sample(void *dp, struct dp_packet *packet, bool steal,
 {
     const struct nlattr *subactions = NULL;
     const struct nlattr *a;
+    struct dp_packet_batch pb;
     size_t left;
 
     NL_NESTED_FOR_EACH_UNSAFE (a, left, action) {
@@ -476,7 +477,8 @@  odp_execute_sample(void *dp, struct dp_packet *packet, bool steal,
         }
     }
 
-    odp_execute_actions(dp, &packet, 1, steal, nl_attr_get(subactions),
+    packet_batch_init_packet(&pb, packet);
+    odp_execute_actions(dp, &pb, steal, nl_attr_get(subactions),
                         nl_attr_get_size(subactions), dp_execute_action);
 }
 
@@ -514,10 +516,12 @@  requires_datapath_assistance(const struct nlattr *a)
 }
 
 void
-odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
+odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
                     const struct nlattr *actions, size_t actions_len,
                     odp_execute_cb dp_execute_action)
 {
+    struct dp_packet **packets = batch->packets;
+    int cnt = batch->count;
     const struct nlattr *a;
     unsigned int left;
     int i;
@@ -532,7 +536,7 @@  odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
                  * not need it any more. */
                 bool may_steal = steal && last_action;
 
-                dp_execute_action(dp, packets, cnt, a, may_steal);
+                dp_execute_action(dp, batch, a, may_steal);
 
                 if (last_action) {
                     /* We do not need to free the packets. dp_execute_actions()
diff --git a/lib/odp-execute.h b/lib/odp-execute.h
index c602bb4..7223fe8 100644
--- a/lib/odp-execute.h
+++ b/lib/odp-execute.h
@@ -26,15 +26,16 @@ 
 struct nlattr;
 struct dp_packet;
 struct pkt_metadata;
+struct dp_packet_batch;
 
-typedef void (*odp_execute_cb)(void *dp, struct dp_packet **packets, int cnt,
+typedef void (*odp_execute_cb)(void *dp, struct dp_packet_batch *batch,
                                const struct nlattr *action, bool may_steal);
 
 /* Actions that need to be executed in the context of a datapath are handed
  * 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 dp_packet **packets, int cnt,
+void odp_execute_actions(void *dp, struct dp_packet_batch *batch,
                          bool steal,
                          const struct nlattr *actions, size_t actions_len,
                          odp_execute_cb dp_execute_action);
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 5937913..e6bcb91 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3582,6 +3582,7 @@  execute_controller_action(struct xlate_ctx *ctx, int len,
                           uint16_t controller_id,
                           const uint8_t *userdata, size_t userdata_len)
 {
+    struct dp_packet_batch batch;
     struct dp_packet *packet;
 
     ctx->xout->slow |= SLOW_CONTROLLER;
@@ -3591,8 +3592,8 @@  execute_controller_action(struct xlate_ctx *ctx, int len,
     }
 
     packet = dp_packet_clone(ctx->xin->packet);
-
-    odp_execute_actions(NULL, &packet, 1, false,
+    packet_batch_init_packet(&batch, packet);
+    odp_execute_actions(NULL, &batch, false,
                         ctx->odp_actions->data, ctx->odp_actions->size, NULL);
 
     /* A packet sent by an action in a table-miss rule is considered an