[ovs-dev,RFC] dp-packet: Don't resize DPBUF_DPDK packets.

Message ID 1532109703-63590-2-git-send-email-tiago.lam@intel.com
State Deferred
Delegated to: Ian Stokes
Headers show
Series
  • [ovs-dev,RFC] dp-packet: Don't resize DPBUF_DPDK packets.
Related show

Commit Message

Lam, Tiago July 20, 2018, 6:01 p.m.
Up until now DPDK packets (DPBUF_DPDK) could call dp_packet_resize__()
without any constraint, leading to call OVS_NOT_REACHED() and premature
termination of the OvS process if one single packet didn't have enough
tailroom or headroom space for a specific operation, such as pushing or
popping a header.

To fix this, two functions are introduced, dp_packet_is_tailroom_avail()
and dp_packet_is_headroom_avail(), which are now used to check if
there's enough tailroom or headroom space, respectively, before
proceeding with calling the dp_packet_prealloc_tailroom() and
dp_packet_prealloc_headroom() functions, thus avoiding the call to
dp_packet_resize__() and OVS_NOT_REACHED() for DPDK packets.

Since put_uninit() and push_uninit() may now return NULL for DPDK
packets, the places where these operations may be called from a DPDK
packet now check for NULL and log a message where appropriate. For
example, eth_push_vlan() in packets.c, now returns 1 if the
push_uninit() operation on the packet fails. Later on, the caller,
odp_execute_actions(), drops the packet upon checking that the return is
different from 0 (error).

CC: Anju Thomas <anju.thomas@ericsson.com>
Reported-at:
https://mail.openvswitch.org/pipermail/ovs-dev/2018-May/346649.html
Signed-off-by: Tiago Lam <tiago.lam@intel.com>
---
 lib/dp-packet.c         | 126 +++++++++++++++++++++++++++++++++++++++++-------
 lib/netdev-native-tnl.c |  24 +++++++--
 lib/netdev-native-tnl.h |   6 +--
 lib/netdev-provider.h   |   2 +-
 lib/netdev.c            |  16 ++++--
 lib/odp-execute.c       |  49 +++++++++++++++----
 lib/packets.c           |  38 +++++++++++++--
 lib/packets.h           |   8 +--
 8 files changed, 224 insertions(+), 45 deletions(-)

Comments

0-day Robot July 20, 2018, 6:54 p.m. | #1
Bleep bloop.  Greetings Tiago Lam, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


build:
/bin/sh ./libtool  --tag=CC   --mode=compile gcc -std=gnu99 -DHAVE_CONFIG_H -I.    -I ./include -I ./include -I ./lib -I ./lib    -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror   -g -O2 -MT lib/dp-packet.lo -MD -MP -MF $depbase.Tpo -c -o lib/dp-packet.lo lib/dp-packet.c &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile:  gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I ./include -I ./include -I ./lib -I ./lib -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror -g -O2 -MT lib/dp-packet.lo -MD -MP -MF lib/.deps/dp-packet.Tpo -c lib/dp-packet.c -o lib/dp-packet.o
lib/dp-packet.c: In function 'dp_packet_is_tailroom_avail':
lib/dp-packet.c:280:47: error: unused parameter 'b' [-Werror=unused-parameter]
 dp_packet_is_tailroom_avail(struct dp_packet *b, size_t size)
                                               ^
lib/dp-packet.c:280:57: error: unused parameter 'size' [-Werror=unused-parameter]
 dp_packet_is_tailroom_avail(struct dp_packet *b, size_t size)
                                                         ^
lib/dp-packet.c: In function 'dp_packet_is_headroom_avail':
lib/dp-packet.c:309:47: error: unused parameter 'b' [-Werror=unused-parameter]
 dp_packet_is_headroom_avail(struct dp_packet *b, size_t size)
                                               ^
lib/dp-packet.c:309:57: error: unused parameter 'size' [-Werror=unused-parameter]
 dp_packet_is_headroom_avail(struct dp_packet *b, size_t size)
                                                         ^
lib/dp-packet.c: At top level:
cc1: error: unrecognized command line option "-Wno-null-pointer-arithmetic" [-Werror]
cc1: all warnings being treated as errors
make[2]: *** [lib/dp-packet.lo] Error 1
make[2]: Leaving directory `/var/lib/jenkins/jobs/upstream_build_from_pw/workspace'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/var/lib/jenkins/jobs/upstream_build_from_pw/workspace'
make: *** [all] Error 2


Please check this out.  If you feel there has been an error, please email aconole@bytheb.org

Thanks,
0-day Robot
Aaron Conole July 25, 2018, 1:58 p.m. | #2
0-day Robot <robot@bytheb.org> writes:

> Bleep bloop.  Greetings Tiago Lam, I am a robot and I have tried out your patch.
> Thanks for your contribution.
>
> I encountered some error that I wasn't expecting.  See the details below.
>
>

Sorry for the noise.  The robot was improperly matching the RFC tag (so
''PATCH RFC' or 'RFC PATCH' would have worked, but only 'RFC'
wouldn't).  It should be fixed now.

-Aaron

Patch

diff --git a/lib/dp-packet.c b/lib/dp-packet.c
index 443c225..058b653 100644
--- a/lib/dp-packet.c
+++ b/lib/dp-packet.c
@@ -239,6 +239,9 @@  dp_packet_resize__(struct dp_packet *b, size_t new_headroom, size_t new_tailroom
 
     switch (b->source) {
     case DPBUF_DPDK:
+        /* DPDK mbufs have a fixed layout, meaning they can't be resized per
+         * say. As a result, this operation shouldn't be called for DPBUF_DPDK
+         * packets */
         OVS_NOT_REACHED();
 
     case DPBUF_MALLOC:
@@ -273,9 +276,27 @@  dp_packet_resize__(struct dp_packet *b, size_t new_headroom, size_t new_tailroom
     }
 }
 
-/* Ensures that 'b' has room for at least 'size' bytes at its tail end,
- * reallocating and copying its data if necessary.  Its headroom, if any, is
- * preserved. */
+static bool
+dp_packet_is_tailroom_avail(struct dp_packet *b, size_t size)
+{
+#ifdef DPDK_NETDEV
+    if (b->source == DPBUF_DPDK) {
+        if (size > dp_packet_tailroom(b)) {
+            return false;
+        }
+
+        return true;
+    }
+#endif
+    return true;
+}
+
+/* For non-DPDK packets, ensures that 'b' has room for at least 'size' bytes at
+ * its tail end, reallocating and copying its data if necessary. Its headroom,
+ * if any, is preserved.
+ *
+ * For DPDK packets, no reallocation is performed. The caller is responsible
+ * for ensuring there's enough tailroom space available. */
 void
 dp_packet_prealloc_tailroom(struct dp_packet *b, size_t size)
 {
@@ -284,9 +305,27 @@  dp_packet_prealloc_tailroom(struct dp_packet *b, size_t size)
     }
 }
 
-/* Ensures that 'b' has room for at least 'size' bytes at its head,
- * reallocating and copying its data if necessary.  Its tailroom, if any, is
- * preserved. */
+static bool
+dp_packet_is_headroom_avail(struct dp_packet *b, size_t size)
+{
+#ifdef DPDK_NETDEV
+    if (b->source == DPBUF_DPDK) {
+        if (size > dp_packet_headroom(b)) {
+            return false;
+        }
+
+        return true;
+    }
+#endif
+    return true;
+}
+
+/* For non-DPDK packets, ensures that 'b' has room for at least 'size' bytes at
+ * its head, reallocating and copying its data if necessary.  Its tailroom, if
+ * any, is preserved.
+ *
+ * For DPDK packets, no reallocation is performed. The caller is responsible
+ * for ensuring there's enough headroom space available. */
 void
 dp_packet_prealloc_headroom(struct dp_packet *b, size_t size)
 {
@@ -315,11 +354,19 @@  dp_packet_shift(struct dp_packet *b, int delta)
 
 /* Appends 'size' bytes of data to the tail end of 'b', reallocating and
  * copying its data if necessary.  Returns a pointer to the first byte of the
- * new data, which is left uninitialized. */
+ * new data, which is left uninitialized.
+ *
+ * For DPDK packets, no reallocation is performed and NULL may be returned if
+ * 'size' is bigger than the available tailroom space. */
 void *
 dp_packet_put_uninit(struct dp_packet *b, size_t size)
 {
     void *p;
+
+    if (!dp_packet_is_tailroom_avail(b, size)) {
+        return NULL;
+    }
+
     dp_packet_prealloc_tailroom(b, size);
     p = dp_packet_tail(b);
     dp_packet_set_size(b, dp_packet_size(b) + size);
@@ -328,22 +375,34 @@  dp_packet_put_uninit(struct dp_packet *b, size_t size)
 
 /* Appends 'size' zeroed bytes to the tail end of 'b'.  Data in 'b' is
  * reallocated and copied if necessary.  Returns a pointer to the first byte of
- * the data's location in the dp_packet. */
+ * the data's location in the dp_packet.
+ *
+ * For DPDK packets, no reallocation is performed and NULL may be returned if
+ * 'size' is bigger than the available tailroom space. */
 void *
 dp_packet_put_zeros(struct dp_packet *b, size_t size)
 {
     void *dst = dp_packet_put_uninit(b, size);
+    if (!dst) {
+        return NULL;
+    }
     memset(dst, 0, size);
     return dst;
 }
 
 /* Appends the 'size' bytes of data in 'p' to the tail end of 'b'.  Data in 'b'
  * is reallocated and copied if necessary.  Returns a pointer to the first
- * byte of the data's location in the dp_packet. */
+ * byte of the data's location in the dp_packet.
+ *
+ * For DPDK packets, no reallocation is performed and NULL may be returned if
+ * 'size' is bigger than the available tailroom space. */
 void *
 dp_packet_put(struct dp_packet *b, const void *p, size_t size)
 {
     void *dst = dp_packet_put_uninit(b, size);
+    if (!dst) {
+        return NULL;
+    }
     memcpy(dst, p, size);
     return dst;
 }
@@ -370,7 +429,9 @@  dp_packet_put_hex(struct dp_packet *b, const char *s, size_t *n)
             return CONST_CAST(char *, s);
         }
 
-        dp_packet_put(b, &byte, 1);
+        if (!dp_packet_put(b, &byte, 1)) {
+            return NULL;
+        }
         s += 2;
     }
 }
@@ -399,10 +460,19 @@  dp_packet_reserve_with_tailroom(struct dp_packet *b, size_t headroom,
 
 /* Prefixes 'size' bytes to the head end of 'b', reallocating and copying its
  * data if necessary.  Returns a pointer to the first byte of the data's
- * location in the dp_packet.  The new data is left uninitialized. */
+ * location in the dp_packet.  The new data is left uninitialized.
+ * It may return NULL for DPDK packets, if 'size' is bigger than the available
+ * tailroom space
+ *
+ * For DPDK packets, no reallocation is performed and NULL may be returned if
+ * 'size' is bigger than the available headroom space. */
 void *
 dp_packet_push_uninit(struct dp_packet *b, size_t size)
 {
+    if (!dp_packet_is_headroom_avail(b, size)) {
+        return NULL;
+    }
+
     dp_packet_prealloc_headroom(b, size);
     dp_packet_set_data(b, (char*)dp_packet_data(b) - size);
     dp_packet_set_size(b, dp_packet_size(b) + size);
@@ -411,22 +481,34 @@  dp_packet_push_uninit(struct dp_packet *b, size_t size)
 
 /* Prefixes 'size' zeroed bytes to the head end of 'b', reallocating and
  * copying its data if necessary.  Returns a pointer to the first byte of the
- * data's location in the dp_packet. */
+ * data's location in the dp_packet.
+ *
+ * For DPDK packets, no reallocation is performed and NULL may be returned if
+ * 'size' is bigger than the available headroom space. */
 void *
 dp_packet_push_zeros(struct dp_packet *b, size_t size)
 {
     void *dst = dp_packet_push_uninit(b, size);
+    if (!dst) {
+        return NULL;
+    }
     memset(dst, 0, size);
     return dst;
 }
 
 /* Copies the 'size' bytes starting at 'p' to the head end of 'b', reallocating
  * and copying its data if necessary.  Returns a pointer to the first byte of
- * the data's location in the dp_packet. */
+ * the data's location in the dp_packet.
+ *
+ * For DPDK packets, no reallocation is performed and NULL may be returned if
+ * 'size' is bigger than the available headroom space. */
 void *
 dp_packet_push(struct dp_packet *b, const void *p, size_t size)
 {
     void *dst = dp_packet_push_uninit(b, size);
+    if (!dst) {
+        return NULL;
+    }
     memcpy(dst, p, size);
     return dst;
 }
@@ -463,12 +545,17 @@  dp_packet_adjust_layer_offset(uint16_t *offset, int increment)
 
 /* Adjust the size of the l2_5 portion of the dp_packet, updating the l2
  * pointer and the layer offsets.  The caller is responsible for
- * modifying the contents. */
+ * modifying the contents.
+ *
+ * For DPDK packets, NULL may be returned if 'increment' is bigger than the
+ * available headroom space. */
 void *
 dp_packet_resize_l2_5(struct dp_packet *b, int increment)
 {
     if (increment >= 0) {
-        dp_packet_push_uninit(b, increment);
+        if (!dp_packet_push_uninit(b, increment)) {
+            return NULL;
+        }
     } else {
         dp_packet_pull(b, -increment);
     }
@@ -482,11 +569,16 @@  dp_packet_resize_l2_5(struct dp_packet *b, int increment)
 
 /* Adjust the size of the l2 portion of the dp_packet, updating the l2
  * pointer and the layer offsets.  The caller is responsible for
- * modifying the contents. */
+ * modifying the contents.
+ *
+ * For DPDK packets, NULL may be returned if 'increment' is bigger than the
+ * available headroom space. */
 void *
 dp_packet_resize_l2(struct dp_packet *b, int increment)
 {
-    dp_packet_resize_l2_5(b, increment);
+    if (!dp_packet_resize_l2_5(b, increment)) {
+        return NULL;
+    }
     dp_packet_adjust_layer_offset(&b->l2_5_ofs, increment);
     return dp_packet_data(b);
 }
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 56baaa2..d1e0f5f 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -152,6 +152,10 @@  netdev_tnl_push_ip_header(struct dp_packet *packet,
     struct ovs_16aligned_ip6_hdr *ip6;
 
     eth = dp_packet_push_uninit(packet, size);
+    if (!eth) {
+        VLOG_WARN_RL(&err_rl, "failed to push ip header");
+        return NULL;
+    }
     *ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);
 
     memcpy(eth, header, size);
@@ -214,7 +218,7 @@  udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
 }
 
 
-void
+int
 netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,
                            struct dp_packet *packet,
                            const struct ovs_action_push_tnl *data)
@@ -223,6 +227,9 @@  netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,
     int ip_tot_size;
 
     udp = netdev_tnl_push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
+    if (!udp) {
+        return 1;
+    }
 
     /* set udp src port */
     udp->udp_src = netdev_tnl_get_src_port(packet);
@@ -243,6 +250,8 @@  netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,
             udp->udp_csum = htons(0xffff);
         }
     }
+
+    return 0;
 }
 
 static void *
@@ -435,7 +444,7 @@  err:
     return NULL;
 }
 
-void
+int
 netdev_gre_push_header(const struct netdev *netdev,
                        struct dp_packet *packet,
                        const struct ovs_action_push_tnl *data)
@@ -446,6 +455,9 @@  netdev_gre_push_header(const struct netdev *netdev,
     int ip_tot_size;
 
     greh = netdev_tnl_push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
+    if (!greh) {
+        return 1;
+    }
 
     if (greh->flags & htons(GRE_CSUM)) {
         ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
@@ -460,6 +472,8 @@  netdev_gre_push_header(const struct netdev *netdev,
         tnl_cfg = &dev->tnl_cfg;
         put_16aligned_be32(seq_opt, htonl(tnl_cfg->seqno++));
     }
+
+    return 0;
 }
 
 int
@@ -588,7 +602,7 @@  err:
     return NULL;
 }
 
-void
+int
 netdev_erspan_push_header(const struct netdev *netdev,
                           struct dp_packet *packet,
                           const struct ovs_action_push_tnl *data)
@@ -602,6 +616,9 @@  netdev_erspan_push_header(const struct netdev *netdev,
 
     greh = netdev_tnl_push_ip_header(packet, data->header,
                                      data->header_len, &ip_tot_size);
+    if (!greh) {
+        return 1;
+    }
 
     /* update GRE seqno */
     tnl_cfg = &dev->tnl_cfg;
@@ -614,6 +631,7 @@  netdev_erspan_push_header(const struct netdev *netdev,
         md2 = ALIGNED_CAST(struct erspan_md2 *, ersh + 1);
         put_16aligned_be32(&md2->timestamp, get_erspan_ts(ERSPAN_100US));
     }
+    return 0;
 }
 
 int
diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h
index 5dc0012..7c36229 100644
--- a/lib/netdev-native-tnl.h
+++ b/lib/netdev-native-tnl.h
@@ -33,7 +33,7 @@  netdev_gre_build_header(const struct netdev *netdev,
                         struct ovs_action_push_tnl *data,
                         const struct netdev_tnl_build_header_params *params);
 
-void
+int
 netdev_gre_push_header(const struct netdev *netdev,
                        struct dp_packet *packet,
                        const struct ovs_action_push_tnl *data);
@@ -45,14 +45,14 @@  netdev_erspan_build_header(const struct netdev *netdev,
                            struct ovs_action_push_tnl *data,
                            const struct netdev_tnl_build_header_params *p);
 
-void
+int
 netdev_erspan_push_header(const struct netdev *netdev,
                           struct dp_packet *packet,
                           const struct ovs_action_push_tnl *data);
 struct dp_packet *
 netdev_erspan_pop_header(struct dp_packet *packet);
 
-void
+int
 netdev_tnl_push_udp_header(const struct netdev *netdev,
                            struct dp_packet *packet,
                            const struct ovs_action_push_tnl *data);
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index 1a572f5..6c43a1f 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -314,7 +314,7 @@  struct netdev_class {
      * flow.  Push header is called for packet to build header specific to
      * a packet on actual transmit.  It uses partial header build by
      * build_header() which is passed as data. */
-    void (*push_header)(const struct netdev *,
+    int (*push_header)(const struct netdev *,
                         struct dp_packet *packet,
                         const struct ovs_action_push_tnl *data);
 
diff --git a/lib/netdev.c b/lib/netdev.c
index 82ffeb9..277b76d 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -857,9 +857,19 @@  netdev_push_header(const struct netdev *netdev,
                    const struct ovs_action_push_tnl *data)
 {
     struct dp_packet *packet;
-    DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-        netdev->netdev_class->push_header(netdev, packet, data);
-        pkt_metadata_init(&packet->md, data->out_port);
+    bool drop = false;
+    const size_t cnt = dp_packet_batch_size(batch);
+
+    size_t i;
+    DP_PACKET_BATCH_REFILL_FOR_EACH (i, cnt, packet, batch) {
+        drop = netdev->netdev_class->push_header(netdev, packet, data);
+        if (drop) {
+            dp_packet_delete(packet);
+        } else {
+            pkt_metadata_init(&packet->md, data->out_port);
+            /* Re-inject packet */
+            dp_packet_batch_refill(batch, packet, i);
+        }
     }
 
     return 0;
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 5831d1f..bfca79c 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -780,9 +780,17 @@  odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
         case OVS_ACTION_ATTR_PUSH_VLAN: {
             const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
 
-            DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-                eth_push_vlan(packet, vlan->vlan_tpid, vlan->vlan_tci);
+            size_t i;
+            const size_t num = dp_packet_batch_size(batch);
+
+            DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {
+                if (!eth_push_vlan(packet, vlan->vlan_tpid, vlan->vlan_tci)) {
+                    dp_packet_batch_refill(batch, packet, i);
+                } else {
+                    dp_packet_delete(packet);
+                }
             }
+
             break;
         }
 
@@ -795,9 +803,17 @@  odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
         case OVS_ACTION_ATTR_PUSH_MPLS: {
             const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
 
-            DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-                push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse);
+            size_t i;
+            const size_t num = dp_packet_batch_size(batch);
+
+            DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {
+                if (!push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse)) {
+                    dp_packet_batch_refill(batch, packet, i);
+                } else {
+                    dp_packet_delete(packet);
+                }
             }
+
             break;
          }
 
@@ -858,10 +874,18 @@  odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
         case OVS_ACTION_ATTR_PUSH_ETH: {
             const struct ovs_action_push_eth *eth = nl_attr_get(a);
 
-            DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-                push_eth(packet, &eth->addresses.eth_dst,
-                         &eth->addresses.eth_src);
+            size_t i;
+            const size_t num = dp_packet_batch_size(batch);
+
+            DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {
+                if (!push_eth(packet, &eth->addresses.eth_dst,
+                              &eth->addresses.eth_src)) {
+                    dp_packet_batch_refill(batch, packet, i);
+                } else {
+                    dp_packet_delete(packet);
+                }
             }
+
             break;
         }
 
@@ -876,8 +900,15 @@  odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
             struct nsh_hdr *nsh_hdr = ALIGNED_CAST(struct nsh_hdr *, buffer);
             nsh_reset_ver_flags_ttl_len(nsh_hdr);
             odp_nsh_hdr_from_attr(nl_attr_get(a), nsh_hdr, NSH_HDR_MAX_LEN);
-            DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-                push_nsh(packet, nsh_hdr);
+            size_t i;
+            const size_t num = dp_packet_batch_size(batch);
+
+            DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {
+                if (!push_nsh(packet, nsh_hdr)) {
+                    dp_packet_batch_refill(batch, packet, i);
+                } else {
+                    dp_packet_delete(packet);
+                }
             }
             break;
         }
diff --git a/lib/packets.c b/lib/packets.c
index 38bfb60..9f6282c 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -34,6 +34,10 @@ 
 #include "odp-util.h"
 #include "dp-packet.h"
 #include "unaligned.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(packets);
+static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
 
 const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
 const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT;
@@ -210,16 +214,22 @@  compose_rarp(struct dp_packet *b, const struct eth_addr eth_src)
  * packet.  Ignores the CFI bit of 'tci' using 0 instead.
  *
  * Also adjusts the layer offsets accordingly. */
-void
+int
 eth_push_vlan(struct dp_packet *packet, ovs_be16 tpid, ovs_be16 tci)
 {
     struct vlan_eth_header *veh;
 
     /* Insert new 802.1Q header. */
     veh = dp_packet_resize_l2(packet, VLAN_HEADER_LEN);
+    if (!veh) {
+        VLOG_WARN_RL(&err_rl, "failed to push vlan header");
+        return 1;
+    }
     memmove(veh, (char *)veh + VLAN_HEADER_LEN, 2 * ETH_ADDR_LEN);
     veh->veth_type = tpid;
     veh->veth_tci = tci & htons(~VLAN_CFI);
+
+    return 0;
 }
 
 /* Removes outermost VLAN header (if any is present) from 'packet'.
@@ -240,7 +250,7 @@  eth_pop_vlan(struct dp_packet *packet)
 }
 
 /* Push Ethernet header onto 'packet' assuming it is layer 3 */
-void
+int
 push_eth(struct dp_packet *packet, const struct eth_addr *dst,
          const struct eth_addr *src)
 {
@@ -248,10 +258,16 @@  push_eth(struct dp_packet *packet, const struct eth_addr *dst,
 
     ovs_assert(packet->packet_type != htonl(PT_ETH));
     eh = dp_packet_resize_l2(packet, ETH_HEADER_LEN);
+    if (!eh) {
+        VLOG_WARN_RL(&err_rl, "failed to push eth header");
+        return 1;
+    }
     eh->eth_dst = *dst;
     eh->eth_src = *src;
     eh->eth_type = pt_ns_type_be(packet->packet_type);
     packet->packet_type = htonl(PT_ETH);
+
+    return 0;
 }
 
 /* Removes Ethernet header, including VLAN header, from 'packet'.
@@ -369,14 +385,14 @@  set_mpls_lse(struct dp_packet *packet, ovs_be32 mpls_lse)
 /* Push MPLS label stack entry 'lse' onto 'packet' as the outermost MPLS
  * header.  If 'packet' does not already have any MPLS labels, then its
  * Ethertype is changed to 'ethtype' (which must be an MPLS Ethertype). */
-void
+int
 push_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse)
 {
     char * header;
     size_t len;
 
     if (!eth_type_mpls(ethtype)) {
-        return;
+        return 0;
     }
 
     if (!is_mpls(packet)) {
@@ -389,8 +405,14 @@  push_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse)
     /* Push new MPLS shim header onto packet. */
     len = packet->l2_5_ofs;
     header = dp_packet_resize_l2_5(packet, MPLS_HLEN);
+    if (!header) {
+        VLOG_WARN_RL(&err_rl, "failed to push mpls header");
+        return 1;
+    }
     memmove(header, header + MPLS_HLEN, len);
     memcpy(header + len, &lse, sizeof lse);
+
+    return 0;
 }
 
 /* If 'packet' is an MPLS packet, removes its outermost MPLS label stack entry.
@@ -414,7 +436,7 @@  pop_mpls(struct dp_packet *packet, ovs_be16 ethtype)
     }
 }
 
-void
+int
 push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src)
 {
     struct nsh_hdr *nsh;
@@ -439,11 +461,17 @@  push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src)
     }
 
     nsh = (struct nsh_hdr *) dp_packet_push_uninit(packet, length);
+    if (!nsh) {
+        VLOG_WARN_RL(&err_rl, "failed to push nsh header");
+        return 1;
+    }
     memcpy(nsh, nsh_hdr_src, length);
     nsh->next_proto = next_proto;
     packet->packet_type = htonl(PT_NSH);
     dp_packet_reset_offsets(packet);
     packet->l3_ofs = 0;
+
+    return 0;
 }
 
 bool
diff --git a/lib/packets.h b/lib/packets.h
index 7645a9d..d8c0795 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -330,7 +330,7 @@  bool eth_addr_from_string(const char *, struct eth_addr *);
 
 void compose_rarp(struct dp_packet *, const struct eth_addr);
 
-void eth_push_vlan(struct dp_packet *, ovs_be16 tpid, ovs_be16 tci);
+int eth_push_vlan(struct dp_packet *, ovs_be16 tpid, ovs_be16 tci);
 void eth_pop_vlan(struct dp_packet *);
 
 const char *eth_from_hex(const char *hex, struct dp_packet **packetp);
@@ -338,7 +338,7 @@  void eth_format_masked(const struct eth_addr ea,
                        const struct eth_addr *mask, struct ds *s);
 
 void set_mpls_lse(struct dp_packet *, ovs_be32 label);
-void push_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse);
+int push_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse);
 void pop_mpls(struct dp_packet *, ovs_be16 ethtype);
 
 void set_mpls_lse_ttl(ovs_be32 *lse, uint8_t ttl);
@@ -438,11 +438,11 @@  struct eth_header {
 };
 BUILD_ASSERT_DECL(ETH_HEADER_LEN == sizeof(struct eth_header));
 
-void push_eth(struct dp_packet *packet, const struct eth_addr *dst,
+int push_eth(struct dp_packet *packet, const struct eth_addr *dst,
               const struct eth_addr *src);
 void pop_eth(struct dp_packet *packet);
 
-void push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src);
+int push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src);
 bool pop_nsh(struct dp_packet *packet);
 
 #define LLC_DSAP_SNAP 0xaa