diff mbox series

[ovs-dev,v8,08/13] dp-packet: Handle multi-seg mubfs in shift() func.

Message ID 1528734090-220990-10-git-send-email-tiago.lam@intel.com
State Superseded
Headers show
Series Support multi-segment mbufs | expand

Commit Message

Lam, Tiago June 11, 2018, 4:21 p.m. UTC
In its current implementation dp_packet_shift() is also unaware of
multi-seg mbufs (that holds data in memory non-contiguously) and assumes
that data exists contiguously in memory, memmove'ing data to perform the
shift.

To add support for multi-seg mbuds a new set of functions was
introduced, dp_packet_mbuf_shift() and dp_packet_mbuf_write(). These
functions are used by dp_packet_shift(), when handling multi-seg mbufs,
to shift and write data within a chain of mbufs.

Signed-off-by: Tiago Lam <tiago.lam@intel.com>
---
 lib/dp-packet.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/dp-packet.h |   8 +++++
 2 files changed, 110 insertions(+)

Comments

Eelco Chaudron June 18, 2018, 11:55 a.m. UTC | #1
On 11 Jun 2018, at 18:21, Tiago Lam wrote:

> In its current implementation dp_packet_shift() is also unaware of
> multi-seg mbufs (that holds data in memory non-contiguously) and 
> assumes
> that data exists contiguously in memory, memmove'ing data to perform 
> the
> shift.
>
> To add support for multi-seg mbuds a new set of functions was
> introduced, dp_packet_mbuf_shift() and dp_packet_mbuf_write(). These
> functions are used by dp_packet_shift(), when handling multi-seg 
> mbufs,
> to shift and write data within a chain of mbufs.
>
> Signed-off-by: Tiago Lam <tiago.lam@intel.com>
> ---
>  lib/dp-packet.c | 102 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  lib/dp-packet.h |   8 +++++
>  2 files changed, 110 insertions(+)
>
> diff --git a/lib/dp-packet.c b/lib/dp-packet.c
> index 9f8503e..399fadb 100644
> --- a/lib/dp-packet.c
> +++ b/lib/dp-packet.c
> @@ -294,6 +294,102 @@ dp_packet_prealloc_headroom(struct dp_packet *b, 
> size_t size)
>      }
>  }
>
> +#ifdef DPDK_NETDEV
> +/* Write len data bytes in a mbuf at specified offset.
> + *
> + * 'mbuf', pointer to the destination mbuf where 'ofs' is, and the 
> mbuf where
> + * the data will first be written.
> + * 'ofs', the offset within the provided 'mbuf' where 'data' is to be 
> written.
> + * 'len', the size of the to be written 'data'.
> + * 'data', pointer to the to be written bytes.
> + *
> + * XXX: This function is the counterpart of the `rte_pktmbuf_read()` 
> function
> + * available with DPDK, in the rte_mbuf.h */
> +void
> +dp_packet_mbuf_write(struct rte_mbuf *mbuf, int16_t ofs, uint32_t 
> len,
> +                     const void *data)
> +{
> +    char *dst_addr;
> +    uint16_t data_len;
> +    int len_copy;
> +    while (mbuf) {
> +        if (len == 0) {
> +            break;
> +        }
> +
> +        dst_addr = rte_pktmbuf_mtod_offset(mbuf, char *, ofs);
> +        data_len = MBUF_BUF_END(mbuf->buf_addr, mbuf->buf_len) - 
> dst_addr;
> +
> +        len_copy = MIN(len, data_len);
> +        /* We don't know if 'data' is the result of a 
> rte_pktmbuf_read() call,
> +         * in which case we may end up writing to the same region of 
> memory we
> +         * are reading from and overlapping. Hence the use of 
> memmove() here */
> +        memmove(dst_addr, data, len_copy);
> +
> +        data = ((char *) data) + len_copy;
> +        len -= len_copy;
> +        ofs = 0;
> +
> +        mbuf = mbuf->next;
> +    }
> +}
> +
> +static void
> +dp_packet_mbuf_shift_(struct rte_mbuf *dbuf, int16_t dst_ofs,
> +                      const struct rte_mbuf *sbuf, uint16_t src_ofs, 
> int len)
> +{
> +    char rd[len];
> +    const char *wd = rte_pktmbuf_read(sbuf, src_ofs, len, rd);
> +
> +    ovs_assert(wd);
> +

This implementation copies the entire packet to a temp buffer and then 
copies it back. Would it be "faster" to do a direct memmove, starting 
with the last/first segment depending on the direction?

> +    dp_packet_mbuf_write(dbuf, dst_ofs, len, wd);
> +}
> +
> +/* Similarly to dp_packet_shift(), shifts the data within the mbufs 
> of a
> + * dp_packet of DPBUF_DPDK source by 'delta' bytes.
> + * Caller must make sure of the following conditions:
> + * - When shifting left, delta can't be bigger than the data_len 
> available in
> + *   the last mbuf;
> + * - When shifting right, delta can't be bigger than the space 
> available in the
> + *   first mbuf (buf_len - data_off).
> + * Both these conditions guarantee that a shift operation doesn't 
> fall outside
> + * the bounds of the existing mbufs, so that the first and last mbufs 
> (when
> + * using multi-segment mbufs), remain the same. */
> +static void
> +dp_packet_mbuf_shift(struct dp_packet *b, int delta)
> +{
> +    uint16_t src_ofs;
> +    int16_t dst_ofs;
> +
> +    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
> +    struct rte_mbuf *tmbuf = rte_pktmbuf_lastseg(mbuf);
> +
> +    if (delta < 0) {
> +        ovs_assert(-delta <= tmbuf->data_len);
> +    } else {
> +        ovs_assert(delta < (mbuf->buf_len - mbuf->data_off));
> +    }
> +
> +    /* Set the destination and source offsets to copy to */
> +    dst_ofs = delta;
> +    src_ofs = 0;
> +
> +    /* Shift data from src mbuf and offset to dst mbuf and offset */
> +    dp_packet_mbuf_shift_(mbuf, dst_ofs, mbuf, src_ofs,
> +                          rte_pktmbuf_pkt_len(mbuf));
> +
> +    /* Update mbufs' properties, and if using multi-segment mbufs, 
> first and
> +     * last mbuf's data_len also needs to be adjusted */
> +    mbuf->data_off = mbuf->data_off + dst_ofs;
> +
> +    if (mbuf != tmbuf) {
> +        tmbuf->data_len += delta;
> +        mbuf->data_len -= delta;
> +    }
> +}
> +#endif
> +
>  /* Shifts all of the data within the allocated space in 'b' by 
> 'delta' bytes.
>   * For example, a 'delta' of 1 would cause each byte of data to move 
> one byte
>   * forward (from address 'p' to 'p+1'), and a 'delta' of -1 would 
> cause each
> @@ -306,6 +402,12 @@ dp_packet_shift(struct dp_packet *b, int delta)
>                 : true);
>
>      if (delta != 0) {
> +#ifdef DPDK_NETDEV
> +        if (b->source == DPBUF_DPDK) {
> +            dp_packet_mbuf_shift(b, delta);
> +            return;
> +        }
> +#endif
>          char *dst = (char *) dp_packet_data(b) + delta;
>          memmove(dst, dp_packet_data(b), dp_packet_size(b));
>          dp_packet_set_data(b, dst);
> diff --git a/lib/dp-packet.h b/lib/dp-packet.h
> index 272597f..4946fa3 100644
> --- a/lib/dp-packet.h
> +++ b/lib/dp-packet.h
> @@ -80,6 +80,11 @@ struct dp_packet {
>      };
>  };
>
> +#ifdef DPDK_NETDEV
> +#define MBUF_BUF_END(BUF_ADDR, BUF_LEN) \
> +    (char *) (((char *) BUF_ADDR) + BUF_LEN)
> +#endif
> +
>  static inline void *dp_packet_data(const struct dp_packet *);
>  static inline void dp_packet_set_data(struct dp_packet *, void *);
>  static inline void *dp_packet_base(const struct dp_packet *);
> @@ -136,6 +141,9 @@ static inline void *dp_packet_at_assert(const 
> struct dp_packet *,
>  #ifdef DPDK_NETDEV
>  static inline void * dp_packet_at_offset(const struct dp_packet *b,
>                                           size_t offset);
> +void
> +dp_packet_mbuf_write(struct rte_mbuf *mbuf, int16_t ofs, uint32_t 
> len,
> +                     const void *data);
>  #endif
>  static inline void *dp_packet_tail(const struct dp_packet *);
>  static inline void *dp_packet_end(const struct dp_packet *);
> -- 
> 2.7.4
Lam, Tiago June 22, 2018, 7:04 p.m. UTC | #2
On 18/06/2018 12:55, Eelco Chaudron wrote:
> 
> 
> On 11 Jun 2018, at 18:21, Tiago Lam wrote:
> 
>> In its current implementation dp_packet_shift() is also unaware of
>> multi-seg mbufs (that holds data in memory non-contiguously) and 
>> assumes
>> that data exists contiguously in memory, memmove'ing data to perform 
>> the
>> shift.
>>
>> To add support for multi-seg mbuds a new set of functions was
>> introduced, dp_packet_mbuf_shift() and dp_packet_mbuf_write(). These
>> functions are used by dp_packet_shift(), when handling multi-seg 
>> mbufs,
>> to shift and write data within a chain of mbufs.
>>
>> Signed-off-by: Tiago Lam <tiago.lam@intel.com>
>> ---
>>  lib/dp-packet.c | 102 
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  lib/dp-packet.h |   8 +++++
>>  2 files changed, 110 insertions(+)
>>
>> diff --git a/lib/dp-packet.c b/lib/dp-packet.c
>> index 9f8503e..399fadb 100644
>> --- a/lib/dp-packet.c
>> +++ b/lib/dp-packet.c
>> @@ -294,6 +294,102 @@ dp_packet_prealloc_headroom(struct dp_packet *b, 
>> size_t size)
>>      }
>>  }
>>
>> +#ifdef DPDK_NETDEV
>> +/* Write len data bytes in a mbuf at specified offset.
>> + *
>> + * 'mbuf', pointer to the destination mbuf where 'ofs' is, and the 
>> mbuf where
>> + * the data will first be written.
>> + * 'ofs', the offset within the provided 'mbuf' where 'data' is to be 
>> written.
>> + * 'len', the size of the to be written 'data'.
>> + * 'data', pointer to the to be written bytes.
>> + *
>> + * XXX: This function is the counterpart of the `rte_pktmbuf_read()` 
>> function
>> + * available with DPDK, in the rte_mbuf.h */
>> +void
>> +dp_packet_mbuf_write(struct rte_mbuf *mbuf, int16_t ofs, uint32_t 
>> len,
>> +                     const void *data)
>> +{
>> +    char *dst_addr;
>> +    uint16_t data_len;
>> +    int len_copy;
>> +    while (mbuf) {
>> +        if (len == 0) {
>> +            break;
>> +        }
>> +
>> +        dst_addr = rte_pktmbuf_mtod_offset(mbuf, char *, ofs);
>> +        data_len = MBUF_BUF_END(mbuf->buf_addr, mbuf->buf_len) - 
>> dst_addr;
>> +
>> +        len_copy = MIN(len, data_len);
>> +        /* We don't know if 'data' is the result of a 
>> rte_pktmbuf_read() call,
>> +         * in which case we may end up writing to the same region of 
>> memory we
>> +         * are reading from and overlapping. Hence the use of 
>> memmove() here */
>> +        memmove(dst_addr, data, len_copy);
>> +
>> +        data = ((char *) data) + len_copy;
>> +        len -= len_copy;
>> +        ofs = 0;
>> +
>> +        mbuf = mbuf->next;
>> +    }
>> +}
>> +
>> +static void
>> +dp_packet_mbuf_shift_(struct rte_mbuf *dbuf, int16_t dst_ofs,
>> +                      const struct rte_mbuf *sbuf, uint16_t src_ofs, 
>> int len)
>> +{
>> +    char rd[len];
>> +    const char *wd = rte_pktmbuf_read(sbuf, src_ofs, len, rd);
>> +
>> +    ovs_assert(wd);
>> +
> 
> This implementation copies the entire packet to a temp buffer and then 
> copies it back. Would it be "faster" to do a direct memmove, starting 
> with the last/first segment depending on the direction?
> 

If I understand the question correctly, that would assume that memory is
contiguous in memory, which isn't guaranteed with multi-segment mbufs.
Hence why we read to a temp buffer and write all at once (when writing,
in dp_packet_prealloc_tailroom(), we do use memmove() within the same
mbuf, where memory is contiguous).

Tiago.
Eelco Chaudron June 26, 2018, 12:28 p.m. UTC | #3
On 22 Jun 2018, at 21:04, Lam, Tiago wrote:

> On 18/06/2018 12:55, Eelco Chaudron wrote:
>>
>>
>> On 11 Jun 2018, at 18:21, Tiago Lam wrote:
>>
>>> In its current implementation dp_packet_shift() is also unaware of
>>> multi-seg mbufs (that holds data in memory non-contiguously) and
>>> assumes
>>> that data exists contiguously in memory, memmove'ing data to perform
>>> the
>>> shift.
>>>
>>> To add support for multi-seg mbuds a new set of functions was
>>> introduced, dp_packet_mbuf_shift() and dp_packet_mbuf_write(). These
>>> functions are used by dp_packet_shift(), when handling multi-seg
>>> mbufs,
>>> to shift and write data within a chain of mbufs.
>>>
>>> Signed-off-by: Tiago Lam <tiago.lam@intel.com>
>>> ---
>>>  lib/dp-packet.c | 102
>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  lib/dp-packet.h |   8 +++++
>>>  2 files changed, 110 insertions(+)
>>>
>>> diff --git a/lib/dp-packet.c b/lib/dp-packet.c
>>> index 9f8503e..399fadb 100644
>>> --- a/lib/dp-packet.c
>>> +++ b/lib/dp-packet.c
>>> @@ -294,6 +294,102 @@ dp_packet_prealloc_headroom(struct dp_packet 
>>> *b,
>>> size_t size)
>>>      }
>>>  }
>>>
>>> +#ifdef DPDK_NETDEV
>>> +/* Write len data bytes in a mbuf at specified offset.
>>> + *
>>> + * 'mbuf', pointer to the destination mbuf where 'ofs' is, and the
>>> mbuf where
>>> + * the data will first be written.
>>> + * 'ofs', the offset within the provided 'mbuf' where 'data' is to 
>>> be
>>> written.
>>> + * 'len', the size of the to be written 'data'.
>>> + * 'data', pointer to the to be written bytes.
>>> + *
>>> + * XXX: This function is the counterpart of the 
>>> `rte_pktmbuf_read()`
>>> function
>>> + * available with DPDK, in the rte_mbuf.h */
>>> +void
>>> +dp_packet_mbuf_write(struct rte_mbuf *mbuf, int16_t ofs, uint32_t
>>> len,
>>> +                     const void *data)
>>> +{
>>> +    char *dst_addr;
>>> +    uint16_t data_len;
>>> +    int len_copy;
>>> +    while (mbuf) {
>>> +        if (len == 0) {
>>> +            break;
>>> +        }
>>> +
>>> +        dst_addr = rte_pktmbuf_mtod_offset(mbuf, char *, ofs);
>>> +        data_len = MBUF_BUF_END(mbuf->buf_addr, mbuf->buf_len) -
>>> dst_addr;
>>> +
>>> +        len_copy = MIN(len, data_len);
>>> +        /* We don't know if 'data' is the result of a
>>> rte_pktmbuf_read() call,
>>> +         * in which case we may end up writing to the same region 
>>> of
>>> memory we
>>> +         * are reading from and overlapping. Hence the use of
>>> memmove() here */
>>> +        memmove(dst_addr, data, len_copy);
>>> +
>>> +        data = ((char *) data) + len_copy;
>>> +        len -= len_copy;
>>> +        ofs = 0;
>>> +
>>> +        mbuf = mbuf->next;
>>> +    }
>>> +}
>>> +
>>> +static void
>>> +dp_packet_mbuf_shift_(struct rte_mbuf *dbuf, int16_t dst_ofs,
>>> +                      const struct rte_mbuf *sbuf, uint16_t 
>>> src_ofs,
>>> int len)
>>> +{
>>> +    char rd[len];
>>> +    const char *wd = rte_pktmbuf_read(sbuf, src_ofs, len, rd);
>>> +
>>> +    ovs_assert(wd);
>>> +
>>
>> This implementation copies the entire packet to a temp buffer and 
>> then
>> copies it back. Would it be "faster" to do a direct memmove, starting
>> with the last/first segment depending on the direction?
>>
>
> If I understand the question correctly, that would assume that memory 
> is
> contiguous in memory, which isn't guaranteed with multi-segment mbufs.
> Hence why we read to a temp buffer and write all at once (when 
> writing,
> in dp_packet_prealloc_tailroom(), we do use memmove() within the same
> mbuf, where memory is contiguous).

Assuming the sbuf is continues, no memory is copied to rd, however if 
it’s not it’s copied, and we move memory twice.
I’m suggesting to optimise for this case, skip the extra copy, but not 
sure if this is worth the effort.
Lam, Tiago June 27, 2018, 10:09 a.m. UTC | #4
On 26/06/2018 13:28, Eelco Chaudron wrote:
> 
> 
> On 22 Jun 2018, at 21:04, Lam, Tiago wrote:
> 
>> On 18/06/2018 12:55, Eelco Chaudron wrote:
>>>
>>>
>>> On 11 Jun 2018, at 18:21, Tiago Lam wrote:
>>>
>>>> In its current implementation dp_packet_shift() is also unaware of
>>>> multi-seg mbufs (that holds data in memory non-contiguously) and
>>>> assumes
>>>> that data exists contiguously in memory, memmove'ing data to perform
>>>> the
>>>> shift.
>>>>
>>>> To add support for multi-seg mbuds a new set of functions was
>>>> introduced, dp_packet_mbuf_shift() and dp_packet_mbuf_write(). These
>>>> functions are used by dp_packet_shift(), when handling multi-seg
>>>> mbufs,
>>>> to shift and write data within a chain of mbufs.
>>>>
>>>> Signed-off-by: Tiago Lam <tiago.lam@intel.com>
>>>> ---
>>>>  lib/dp-packet.c | 102
>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>  lib/dp-packet.h |   8 +++++
>>>>  2 files changed, 110 insertions(+)
>>>>
>>>> diff --git a/lib/dp-packet.c b/lib/dp-packet.c
>>>> index 9f8503e..399fadb 100644
>>>> --- a/lib/dp-packet.c
>>>> +++ b/lib/dp-packet.c
>>>> @@ -294,6 +294,102 @@ dp_packet_prealloc_headroom(struct dp_packet 
>>>> *b,
>>>> size_t size)
>>>>      }
>>>>  }
>>>>
>>>> +#ifdef DPDK_NETDEV
>>>> +/* Write len data bytes in a mbuf at specified offset.
>>>> + *
>>>> + * 'mbuf', pointer to the destination mbuf where 'ofs' is, and the
>>>> mbuf where
>>>> + * the data will first be written.
>>>> + * 'ofs', the offset within the provided 'mbuf' where 'data' is to 
>>>> be
>>>> written.
>>>> + * 'len', the size of the to be written 'data'.
>>>> + * 'data', pointer to the to be written bytes.
>>>> + *
>>>> + * XXX: This function is the counterpart of the 
>>>> `rte_pktmbuf_read()`
>>>> function
>>>> + * available with DPDK, in the rte_mbuf.h */
>>>> +void
>>>> +dp_packet_mbuf_write(struct rte_mbuf *mbuf, int16_t ofs, uint32_t
>>>> len,
>>>> +                     const void *data)
>>>> +{
>>>> +    char *dst_addr;
>>>> +    uint16_t data_len;
>>>> +    int len_copy;
>>>> +    while (mbuf) {
>>>> +        if (len == 0) {
>>>> +            break;
>>>> +        }
>>>> +
>>>> +        dst_addr = rte_pktmbuf_mtod_offset(mbuf, char *, ofs);
>>>> +        data_len = MBUF_BUF_END(mbuf->buf_addr, mbuf->buf_len) -
>>>> dst_addr;
>>>> +
>>>> +        len_copy = MIN(len, data_len);
>>>> +        /* We don't know if 'data' is the result of a
>>>> rte_pktmbuf_read() call,
>>>> +         * in which case we may end up writing to the same region 
>>>> of
>>>> memory we
>>>> +         * are reading from and overlapping. Hence the use of
>>>> memmove() here */
>>>> +        memmove(dst_addr, data, len_copy);
>>>> +
>>>> +        data = ((char *) data) + len_copy;
>>>> +        len -= len_copy;
>>>> +        ofs = 0;
>>>> +
>>>> +        mbuf = mbuf->next;
>>>> +    }
>>>> +}
>>>> +
>>>> +static void
>>>> +dp_packet_mbuf_shift_(struct rte_mbuf *dbuf, int16_t dst_ofs,
>>>> +                      const struct rte_mbuf *sbuf, uint16_t 
>>>> src_ofs,
>>>> int len)
>>>> +{
>>>> +    char rd[len];
>>>> +    const char *wd = rte_pktmbuf_read(sbuf, src_ofs, len, rd);
>>>> +
>>>> +    ovs_assert(wd);
>>>> +
>>>
>>> This implementation copies the entire packet to a temp buffer and 
>>> then
>>> copies it back. Would it be "faster" to do a direct memmove, starting
>>> with the last/first segment depending on the direction?
>>>
>>
>> If I understand the question correctly, that would assume that memory 
>> is
>> contiguous in memory, which isn't guaranteed with multi-segment mbufs.
>> Hence why we read to a temp buffer and write all at once (when 
>> writing,
>> in dp_packet_prealloc_tailroom(), we do use memmove() within the same
>> mbuf, where memory is contiguous).
> 
> Assuming the sbuf is continues, no memory is copied to rd, however if 
> it’s not it’s copied, and we move memory twice.
> I’m suggesting to optimise for this case, skip the extra copy, but not 
> sure if this is worth the effort.
> 

OK, I understand your point now. rte_pktmbuf_read() does that internally
- if the data to copy is within the same mbuf (`sbuf` in this case), the
pointer returned by the function is to the data section in the `sbuf`
itself. Only otherwise it copies to `rd` and returns a pointer to `rd`.

Tiago.
diff mbox series

Patch

diff --git a/lib/dp-packet.c b/lib/dp-packet.c
index 9f8503e..399fadb 100644
--- a/lib/dp-packet.c
+++ b/lib/dp-packet.c
@@ -294,6 +294,102 @@  dp_packet_prealloc_headroom(struct dp_packet *b, size_t size)
     }
 }
 
+#ifdef DPDK_NETDEV
+/* Write len data bytes in a mbuf at specified offset.
+ *
+ * 'mbuf', pointer to the destination mbuf where 'ofs' is, and the mbuf where
+ * the data will first be written.
+ * 'ofs', the offset within the provided 'mbuf' where 'data' is to be written.
+ * 'len', the size of the to be written 'data'.
+ * 'data', pointer to the to be written bytes.
+ *
+ * XXX: This function is the counterpart of the `rte_pktmbuf_read()` function
+ * available with DPDK, in the rte_mbuf.h */
+void
+dp_packet_mbuf_write(struct rte_mbuf *mbuf, int16_t ofs, uint32_t len,
+                     const void *data)
+{
+    char *dst_addr;
+    uint16_t data_len;
+    int len_copy;
+    while (mbuf) {
+        if (len == 0) {
+            break;
+        }
+
+        dst_addr = rte_pktmbuf_mtod_offset(mbuf, char *, ofs);
+        data_len = MBUF_BUF_END(mbuf->buf_addr, mbuf->buf_len) - dst_addr;
+
+        len_copy = MIN(len, data_len);
+        /* We don't know if 'data' is the result of a rte_pktmbuf_read() call,
+         * in which case we may end up writing to the same region of memory we
+         * are reading from and overlapping. Hence the use of memmove() here */
+        memmove(dst_addr, data, len_copy);
+
+        data = ((char *) data) + len_copy;
+        len -= len_copy;
+        ofs = 0;
+
+        mbuf = mbuf->next;
+    }
+}
+
+static void
+dp_packet_mbuf_shift_(struct rte_mbuf *dbuf, int16_t dst_ofs,
+                      const struct rte_mbuf *sbuf, uint16_t src_ofs, int len)
+{
+    char rd[len];
+    const char *wd = rte_pktmbuf_read(sbuf, src_ofs, len, rd);
+
+    ovs_assert(wd);
+
+    dp_packet_mbuf_write(dbuf, dst_ofs, len, wd);
+}
+
+/* Similarly to dp_packet_shift(), shifts the data within the mbufs of a
+ * dp_packet of DPBUF_DPDK source by 'delta' bytes.
+ * Caller must make sure of the following conditions:
+ * - When shifting left, delta can't be bigger than the data_len available in
+ *   the last mbuf;
+ * - When shifting right, delta can't be bigger than the space available in the
+ *   first mbuf (buf_len - data_off).
+ * Both these conditions guarantee that a shift operation doesn't fall outside
+ * the bounds of the existing mbufs, so that the first and last mbufs (when
+ * using multi-segment mbufs), remain the same. */
+static void
+dp_packet_mbuf_shift(struct dp_packet *b, int delta)
+{
+    uint16_t src_ofs;
+    int16_t dst_ofs;
+
+    struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
+    struct rte_mbuf *tmbuf = rte_pktmbuf_lastseg(mbuf);
+
+    if (delta < 0) {
+        ovs_assert(-delta <= tmbuf->data_len);
+    } else {
+        ovs_assert(delta < (mbuf->buf_len - mbuf->data_off));
+    }
+
+    /* Set the destination and source offsets to copy to */
+    dst_ofs = delta;
+    src_ofs = 0;
+
+    /* Shift data from src mbuf and offset to dst mbuf and offset */
+    dp_packet_mbuf_shift_(mbuf, dst_ofs, mbuf, src_ofs,
+                          rte_pktmbuf_pkt_len(mbuf));
+
+    /* Update mbufs' properties, and if using multi-segment mbufs, first and
+     * last mbuf's data_len also needs to be adjusted */
+    mbuf->data_off = mbuf->data_off + dst_ofs;
+
+    if (mbuf != tmbuf) {
+        tmbuf->data_len += delta;
+        mbuf->data_len -= delta;
+    }
+}
+#endif
+
 /* Shifts all of the data within the allocated space in 'b' by 'delta' bytes.
  * For example, a 'delta' of 1 would cause each byte of data to move one byte
  * forward (from address 'p' to 'p+1'), and a 'delta' of -1 would cause each
@@ -306,6 +402,12 @@  dp_packet_shift(struct dp_packet *b, int delta)
                : true);
 
     if (delta != 0) {
+#ifdef DPDK_NETDEV
+        if (b->source == DPBUF_DPDK) {
+            dp_packet_mbuf_shift(b, delta);
+            return;
+        }
+#endif
         char *dst = (char *) dp_packet_data(b) + delta;
         memmove(dst, dp_packet_data(b), dp_packet_size(b));
         dp_packet_set_data(b, dst);
diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index 272597f..4946fa3 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -80,6 +80,11 @@  struct dp_packet {
     };
 };
 
+#ifdef DPDK_NETDEV
+#define MBUF_BUF_END(BUF_ADDR, BUF_LEN) \
+    (char *) (((char *) BUF_ADDR) + BUF_LEN)
+#endif
+
 static inline void *dp_packet_data(const struct dp_packet *);
 static inline void dp_packet_set_data(struct dp_packet *, void *);
 static inline void *dp_packet_base(const struct dp_packet *);
@@ -136,6 +141,9 @@  static inline void *dp_packet_at_assert(const struct dp_packet *,
 #ifdef DPDK_NETDEV
 static inline void * dp_packet_at_offset(const struct dp_packet *b,
                                          size_t offset);
+void
+dp_packet_mbuf_write(struct rte_mbuf *mbuf, int16_t ofs, uint32_t len,
+                     const void *data);
 #endif
 static inline void *dp_packet_tail(const struct dp_packet *);
 static inline void *dp_packet_end(const struct dp_packet *);