Well, problem is still there.
You are doing something on a packet that may not exist anymore.

On 17 February 2017 at 22:08, Bill Fischofer <bill.fischo...@linaro.org>
wrote:

> I've posted patch http://patches.opendataplane.org/patch/8155/ to
> address this issue.  It goes on api-next on top of patches
> http://patches.opendataplane.org/patch/7879/ and
> http://patches.opendataplane.org/patch/8154/
>
> On Fri, Feb 17, 2017 at 2:39 PM, Bill Fischofer
> <bill.fischo...@linaro.org> wrote:
> > First off, thank you very much for this review.
> >
> > Please note that this code has been streamlined in patch
> > http://patches.opendataplane.org/patch/7879/ and has been further
> > refined with patch http://patches.opendataplane.org/patch/8145/ but
> > the exposure you identify still exists in that code.
> >
> > On Fri, Feb 17, 2017 at 11:31 AM, Peltonen, Janne (Nokia - FI/Espoo)
> > <janne.pelto...@nokia.com> wrote:
> >> Hi,
> >>
> >> I took a look at the packet references and it seems to me that
> >> either the implementation is a bit racy or I confused myself
> >> when reading the code. Or maybe I got the intended concurrency
> >> semantics of the packet references wrong?
> >>
> >> My first issue is that packet_free() may access freed packet
> >> header or corrupt unshared_len.
> >>
> >> The packet free function looks like this:
> >>
> >> static inline void packet_free(odp_packet_hdr_t *pkt_hdr)
> >> {
> >>         odp_packet_hdr_t *ref_hdr;
> >>         uint32_t ref_count;
> >>
> >>         do {
> >>                 ref_hdr = pkt_hdr->ref_hdr;
> >>                 ref_count = packet_ref_count(pkt_hdr) - 1;
> >>                 free_bufs(pkt_hdr, 0, pkt_hdr->buf_hdr.segcount);
> >>
> >>                 if (ref_count == 1)
> >>                         pkt_hdr->unshared_len = pkt_hdr->frame_len;
> >>
> >>                 pkt_hdr = ref_hdr;
> >>         } while (pkt_hdr);
> >> }
> >>
> >> The problem here is that decrementing the ref_count, checking
> >> its value and updating unshared_len is not single atomic
> >> operation. By the time packet_free() checks if ref_count == 1
> >> (i.e. if there is exactly one other reference left somewhere),
> >> the true ref_count may have already been changed by another
> >> thread doing packet_free() or packet_ref().
> >>
> >> For example, if two threads have a reference to the same packet
> >> then execution (or the relevant memory ops) may get "interleaved"
> >> as follows:
> >>
> >> T1: call packet_free()
> >> T1: ref_count = packet_ref_count(pkt_hdr) - 1;
> >> At this point ref_count variable is 1
> >> T1: call free_bufs()
> >> T1: call packet_ref_dec()
> >> Now the ref_count of the packet header is 1.
> >> T2: call and complete packet_free()
> >> Thread 2 sees refcount 1 in the packet and frees the buffers
> >> T1: pkt_hdr->unshared_len = pkt_hdr->frame_len;
> >> Thread 1 accesses freed buffer for reading and writing.
> >
> > I agree. These steps should be reversed so that the code should read:
> >
> > if (ref_count == 1)
> >    pkt_hdr->unshared_len = pkt_hdr->frame_len;
> >
> > free_bufs(pkt_hdr, 0, pkt_hdr->buf_hdr.segcount);
> >
> > Or using the code with the above two patches applied, the code should
> read:
> >
> > static inline void packet_free(odp_packet_hdr_t *pkt_hdr)
> > {
> >         odp_packet_hdr_t *ref_hdr;
> >         uint32_t ref_count;
> >         int num_seg;
> >
> >         do {
> >                 ref_count = packet_ref_count(pkt_hdr);
> >                 num_seg = pkt_hdr->buf_hdr.segcount;
> >                 ref_hdr = pkt_hdr->ref_hdr;
> >
> >                 if (odp_likely((CONFIG_PACKET_MAX_SEGS == 1 || num_seg
> == 1) &&
> >                     ref_count == 1)) {
> >                         buffer_free_multi((odp_buffer_t
> > *)&pkt_hdr->buf_hdr.handle.handle, 1);
> >                 } else {
> >                         if (ref_count == 2)
> >                                 pkt_hdr->unshared_len =
> pkt_hdr->frame_len;
> >
> >                         free_bufs(pkt_hdr, 0, num_seg);
> >                  }
> >
> >                  pkt_hdr = ref_hdr;
> >         } while (pkt_hdr);
> > }
> >
> > The mistake was trying to optimize things so that unshared_len is not
> > set if the buffers are being freed, but that exposes these race
> > conditions. So the worst that should now happen is that it is set
> > unnecessarily before being freed.
> >
> > If you concur I'll fold this fix into a v3 for patch
> > http://patches.opendataplane.org/patch/8145/
> >
> >>
> >> Similarly, if T2 created a new reference, T1 would have
> >> a wrong idea of the number of remaining references and
> >> would adjust the unshared_len to an incorrect value.
> >>
> >> Right?
> >>
> >> Maybe other modifications of unshared_len are also racy.
> >
> > I don't believe so, because references do not change the existing ODP
> > restriction that two threads cannot share the same odp_packet_t.  When
> > a packet reference is created it returns a separate odp_packet_t that
> > has its own metadata. So unshared_len is always private to an
> > individual odp_packet_t. The exception is static references but in
> > this case the entire
> > packet along with its metadata must be treated as read only so
> > operations like odp_packet_push_head() that would try to modify
> > unshared_len are prohibited.
> >
> >>
> >>
> >>
> >> The second issue is that the atomic ops for setting and
> >> reading the ref count seem to have too relaxed memory
> >> ordering. In particular, packet_ref_dec() must not happen
> >> (be visible to other threads) before its caller is done
> >> with the packet and the related memory accesses have
> >> completed. Now there does not seem to be any optimization
> >> and memory barrier to prevent the ref count decrementing
> >> happening too early. So I think it is at least theoretically
> >> possible that a thread e.g. reads from a packet buffer
> >> after it has already been freed by another thread, somehow
> >> like this:
> >>
> >> Source code order:
> >> T1: interesting_data = read_from_pkt(pkt)
> >> T1: packet_free(pkt)
> >>
> >> Order visible to T2:
> >> 1: ref count decr
> >> 2: read from pkt
> >>
> >> Now if T2 goes and frees the remaining reference between
> >> steps 1 and 2, T1 may get even more interesting data.
> >>
> >> Right?
> >
> > I don't believe so. The semantics of odp_atomic_fetch_dec_u32(), which
> > is what packet_ref_dec() uses, says that no two calls can see the same
> > fetched value, so only one thread will return ref_count == 1 and free
> > the buffer. Note that if I see ref_count == 1 no other thread can be
> > trying to increment it via a concurrent odp_packet_ref() call because
> > that would mean that two threads were trying to manipulate the same
> > odp_packet_t, which is prohibited.
> >
> > For CPUs that support out of order instruction execution, this is only
> > permitted providing the reordering and speculative executions are
> > semantically consistent with sequential execution. If this were not
> > the case you'd constantly have to worry about a processor turning
> >
> > T1: interesting_data = read_from_pkt(pkt)
> > T1: packet_free(pkt)
> >
> > into
> >
> > T1: packet_free(pkt)
> > T1: interesting_data = read_from_pkt(pkt)
> >
> > In your scenario above: T2 cannot be issuing a read to pkt after
> > ref_count is decremented because the only way that T2 could be
> > decrementing ref_count would be if T2 issued an odp_packet_free() call
> > for it. Obviously if it tries to reference pkt after such a call that
> > is an application error.
> >
> > Thanks again for your much-appreciated help in looking at this!
> >
> >>
> >>         Janne
> >>
> >>
> >>> -----Original Message-----
> >>> From: lng-odp [mailto:lng-odp-boun...@lists.linaro.org] On Behalf Of
> Bill Fischofer
> >>> Sent: Wednesday, January 11, 2017 4:34 AM
> >>> To: lng-odp@lists.linaro.org
> >>> Subject: [lng-odp] [API-NEXT PATCHv7 2/5] linux-generic: packet:
> implement reference apis
> >>>
> >>> Implement the APIs:
> >>> - odp_packet_ref_static()
> >>> - odp_packet_ref()
> >>> - odp_packet_ref_pkt()
> >>> - odp_packet_has_ref()
> >>> - odp_packet_unshared_len()
> >>>
> >>> This also involves functional upgrades to the existing packet
> manipulation
> >>> APIs to work with packet references as input arguments.
> >>>
> >>> Signed-off-by: Bill Fischofer <bill.fischo...@linaro.org>
> >>> ---
> >>>  .../linux-generic/include/odp_packet_internal.h    |  87 +++-
> >>>  platform/linux-generic/odp_packet.c                | 536
> +++++++++++++++++----
> >>>  2 files changed, 516 insertions(+), 107 deletions(-)
> >>>
> >>> diff --git a/platform/linux-generic/include/odp_packet_internal.h
> b/platform/linux-
> >>> generic/include/odp_packet_internal.h
> >>> index e6e9d74..607560d 100644
> >>> --- a/platform/linux-generic/include/odp_packet_internal.h
> >>> +++ b/platform/linux-generic/include/odp_packet_internal.h
> >>> @@ -19,6 +19,7 @@ extern "C" {
> >>>
> >>>  #include <odp/api/align.h>
> >>>  #include <odp/api/debug.h>
> >>> +#include <odp_debug_internal.h>
> >>>  #include <odp_buffer_internal.h>
> >>>  #include <odp_pool_internal.h>
> >>>  #include <odp_buffer_inlines.h>
> >>> @@ -168,7 +169,7 @@ typedef struct {
> >>>   * packet_init(). Because of this any new fields added must be
> reviewed for
> >>>   * initialization requirements.
> >>>   */
> >>> -typedef struct {
> >>> +typedef struct odp_packet_hdr_t {
> >>>       /* common buffer header */
> >>>       odp_buffer_hdr_t buf_hdr;
> >>>
> >>> @@ -184,6 +185,13 @@ typedef struct {
> >>>       uint32_t headroom;
> >>>       uint32_t tailroom;
> >>>
> >>> +     /* Fields used to support packet references */
> >>> +     uint32_t unshared_len;
> >>> +     struct odp_packet_hdr_t *ref_hdr;
> >>> +     uint32_t ref_offset;
> >>> +     uint32_t ref_len;
> >>> +     odp_atomic_u32_t ref_count;
> >>> +
> >>>       /*
> >>>        * Members below are not initialized by packet_init()
> >>>        */
> >>> @@ -212,6 +220,55 @@ static inline odp_packet_hdr_t
> *odp_packet_hdr(odp_packet_t pkt)
> >>>       return (odp_packet_hdr_t *)buf_hdl_to_hdr((odp_buffer_t)pkt);
> >>>  }
> >>>
> >>> +static inline odp_packet_hdr_t *odp_packet_last_hdr(odp_packet_t pkt,
> >>> +                                                 uint32_t *offset)
> >>> +{
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     odp_packet_hdr_t *prev_hdr = pkt_hdr;
> >>> +     uint32_t ref_offset = 0;
> >>> +
> >>> +     while (pkt_hdr->ref_hdr) {
> >>> +             ref_offset = pkt_hdr->ref_offset;
> >>> +             prev_hdr   = pkt_hdr;
> >>> +             pkt_hdr    = pkt_hdr->ref_hdr;
> >>> +     }
> >>> +
> >>> +     if (offset) {
> >>> +             if (prev_hdr != pkt_hdr)
> >>> +                     ref_offset += pkt_hdr->frame_len -
> prev_hdr->ref_len;
> >>> +             *offset = ref_offset;
> >>> +     }
> >>> +
> >>> +     return pkt_hdr;
> >>> +}
> >>> +
> >>> +static inline odp_packet_hdr_t *odp_packet_prev_hdr(odp_packet_hdr_t
> *pkt_hdr,
> >>> +                                                 odp_packet_hdr_t
> *cur_hdr,
> >>> +                                                 uint32_t *offset)
> >>> +{
> >>> +     uint32_t ref_offset = 0;
> >>> +     odp_packet_hdr_t *prev_hdr = pkt_hdr;
> >>> +
> >>> +     while (pkt_hdr->ref_hdr != cur_hdr) {
> >>> +             ref_offset = pkt_hdr->ref_offset;
> >>> +             prev_hdr   = pkt_hdr;
> >>> +             pkt_hdr    = pkt_hdr->ref_hdr;
> >>> +     }
> >>> +
> >>> +     if (offset) {
> >>> +             if (prev_hdr != pkt_hdr)
> >>> +                     ref_offset += pkt_hdr->frame_len -
> prev_hdr->ref_len;
> >>> +             *offset = ref_offset;
> >>> +     }
> >>> +
> >>> +     return pkt_hdr;
> >>> +}
> >>> +
> >>> +static inline odp_packet_t _odp_packet_hdl(odp_packet_hdr_t *pkt_hdr)
> >>> +{
> >>> +     return (odp_packet_t)odp_hdr_to_buf(&pkt_hdr->buf_hdr);
> >>> +}
> >>> +
> >>>  static inline void copy_packet_parser_metadata(odp_packet_hdr_t
> *src_hdr,
> >>>                                              odp_packet_hdr_t *dst_hdr)
> >>>  {
> >>> @@ -234,17 +291,43 @@ static inline void pull_tail(odp_packet_hdr_t
> *pkt_hdr, uint32_t
> >>> len)
> >>>
> >>>       pkt_hdr->tailroom  += len;
> >>>       pkt_hdr->frame_len -= len;
> >>> +     pkt_hdr->unshared_len -= len;
> >>>       pkt_hdr->buf_hdr.seg[last].len -= len;
> >>>  }
> >>>
> >>>  static inline uint32_t packet_len(odp_packet_hdr_t *pkt_hdr)
> >>>  {
> >>> -     return pkt_hdr->frame_len;
> >>> +     uint32_t pkt_len = 0;
> >>> +     uint32_t offset  = 0;
> >>> +
> >>> +     do {
> >>> +             pkt_len += pkt_hdr->frame_len - offset;
> >>> +             offset   = pkt_hdr->ref_offset;
> >>> +             if (pkt_hdr->ref_hdr)
> >>> +                     offset += (pkt_hdr->ref_hdr->frame_len -
> >>> +                                pkt_hdr->ref_len);
> >>> +             pkt_hdr  = pkt_hdr->ref_hdr;
> >>> +     } while (pkt_hdr);
> >>> +
> >>> +     return pkt_len;
> >>> +}
> >>> +
> >>> +static inline uint32_t packet_ref_count(odp_packet_hdr_t *pkt_hdr)
> >>> +{
> >>> +     return odp_atomic_load_u32(&pkt_hdr->ref_count);
> >>> +}
> >>> +
> >>> +static inline void packet_ref_count_set(odp_packet_hdr_t *pkt_hdr,
> uint32_t n)
> >>> +{
> >>> +     odp_atomic_init_u32(&pkt_hdr->ref_count, n);
> >>>  }
> >>>
> >>>  static inline void packet_set_len(odp_packet_hdr_t *pkt_hdr,
> uint32_t len)
> >>>  {
> >>> +     ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
> >>> +
> >>>       pkt_hdr->frame_len = len;
> >>> +     pkt_hdr->unshared_len = len;
> >>>  }
> >>>
> >>>  static inline int packet_parse_l2_not_done(packet_parser_t *prs)
> >>> diff --git a/platform/linux-generic/odp_packet.c
> b/platform/linux-generic/odp_packet.c
> >>> index f632a51..170965a 100644
> >>> --- a/platform/linux-generic/odp_packet.c
> >>> +++ b/platform/linux-generic/odp_packet.c
> >>> @@ -33,13 +33,24 @@ static inline odp_buffer_t
> buffer_handle(odp_packet_hdr_t *pkt_hdr)
> >>>       return pkt_hdr->buf_hdr.handle.handle;
> >>>  }
> >>>
> >>> +static inline uint32_t packet_ref_inc(odp_packet_hdr_t *pkt_hdr)
> >>> +{
> >>> +     return odp_atomic_fetch_inc_u32(&pkt_hdr->ref_count);
> >>> +}
> >>> +
> >>> +static inline uint32_t packet_ref_dec(odp_packet_hdr_t *pkt_hdr)
> >>> +{
> >>> +     return odp_atomic_fetch_dec_u32(&pkt_hdr->ref_count);
> >>> +}
> >>> +
> >>>  static inline uint32_t packet_seg_len(odp_packet_hdr_t *pkt_hdr,
> >>>                                     uint32_t seg_idx)
> >>>  {
> >>>       return pkt_hdr->buf_hdr.seg[seg_idx].len;
> >>>  }
> >>>
> >>> -static inline void *packet_seg_data(odp_packet_hdr_t *pkt_hdr,
> uint32_t seg_idx)
> >>> +static inline uint8_t *packet_seg_data(odp_packet_hdr_t *pkt_hdr,
> >>> +                                    uint32_t seg_idx)
> >>>  {
> >>>       return pkt_hdr->buf_hdr.seg[seg_idx].data;
> >>>  }
> >>> @@ -52,6 +63,11 @@ static inline int packet_last_seg(odp_packet_hdr_t
> *pkt_hdr)
> >>>               return pkt_hdr->buf_hdr.segcount - 1;
> >>>  }
> >>>
> >>> +static inline void *packet_data(odp_packet_hdr_t *pkt_hdr)
> >>> +{
> >>> +     return pkt_hdr->buf_hdr.seg[0].data;
> >>> +}
> >>> +
> >>>  static inline uint32_t packet_first_seg_len(odp_packet_hdr_t
> *pkt_hdr)
> >>>  {
> >>>       return packet_seg_len(pkt_hdr, 0);
> >>> @@ -64,11 +80,6 @@ static inline uint32_t 
> >>> packet_last_seg_len(odp_packet_hdr_t
> *pkt_hdr)
> >>>       return packet_seg_len(pkt_hdr, last);
> >>>  }
> >>>
> >>> -static inline void *packet_data(odp_packet_hdr_t *pkt_hdr)
> >>> -{
> >>> -     return pkt_hdr->buf_hdr.seg[0].data;
> >>> -}
> >>> -
> >>>  static inline void *packet_tail(odp_packet_hdr_t *pkt_hdr)
> >>>  {
> >>>       int last = packet_last_seg(pkt_hdr);
> >>> @@ -99,6 +110,7 @@ static inline void push_head(odp_packet_hdr_t
> *pkt_hdr, uint32_t len)
> >>>  {
> >>>       pkt_hdr->headroom  -= len;
> >>>       pkt_hdr->frame_len += len;
> >>> +     pkt_hdr->unshared_len += len;
> >>>       pkt_hdr->buf_hdr.seg[0].data -= len;
> >>>       pkt_hdr->buf_hdr.seg[0].len  += len;
> >>>  }
> >>> @@ -107,6 +119,7 @@ static inline void pull_head(odp_packet_hdr_t
> *pkt_hdr, uint32_t len)
> >>>  {
> >>>       pkt_hdr->headroom  += len;
> >>>       pkt_hdr->frame_len -= len;
> >>> +     pkt_hdr->unshared_len -= len;
> >>>       pkt_hdr->buf_hdr.seg[0].data += len;
> >>>       pkt_hdr->buf_hdr.seg[0].len  -= len;
> >>>  }
> >>> @@ -117,6 +130,7 @@ static inline void push_tail(odp_packet_hdr_t
> *pkt_hdr, uint32_t len)
> >>>
> >>>       pkt_hdr->tailroom  -= len;
> >>>       pkt_hdr->frame_len += len;
> >>> +     pkt_hdr->unshared_len += len;
> >>>       pkt_hdr->buf_hdr.seg[last].len += len;
> >>>  }
> >>>
> >>> @@ -144,6 +158,10 @@ static inline void 
> >>> packet_seg_copy_md(odp_packet_hdr_t
> *dst,
> >>>       dst->buf_hdr.uarea_addr = src->buf_hdr.uarea_addr;
> >>>       dst->buf_hdr.uarea_size = src->buf_hdr.uarea_size;
> >>>
> >>> +     /* reference related metadata */
> >>> +     dst->ref_len      = src->ref_len;
> >>> +     dst->unshared_len = src->unshared_len;
> >>> +
> >>>       /* segmentation data is not copied:
> >>>        *   buf_hdr.seg[]
> >>>        *   buf_hdr.segcount
> >>> @@ -158,7 +176,15 @@ static inline void *packet_map(odp_packet_hdr_t
> *pkt_hdr,
> >>>       int seg = 0;
> >>>       int seg_count = pkt_hdr->buf_hdr.segcount;
> >>>
> >>> -     if (odp_unlikely(offset >= pkt_hdr->frame_len))
> >>> +     /* Special processing for references */
> >>> +     while (offset >= pkt_hdr->frame_len && pkt_hdr->ref_hdr) {
> >>> +             offset   -= (pkt_hdr->frame_len - pkt_hdr->ref_offset);
> >>> +             offset   += (pkt_hdr->ref_hdr->frame_len -
> pkt_hdr->ref_len);
> >>> +             pkt_hdr   = pkt_hdr->ref_hdr;
> >>> +             seg_count = pkt_hdr->buf_hdr.segcount;
> >>> +     }
> >>> +
> >>> +     if (odp_unlikely(offset > pkt_hdr->frame_len))
> >>>               return NULL;
> >>>
> >>>       if (odp_likely(CONFIG_PACKET_MAX_SEGS == 1 || seg_count == 1)) {
> >>> @@ -207,6 +233,9 @@ void packet_parse_reset(odp_packet_hdr_t *pkt_hdr)
> >>>       pkt_hdr->p.l2_offset        = 0;
> >>>       pkt_hdr->p.l3_offset        = ODP_PACKET_OFFSET_INVALID;
> >>>       pkt_hdr->p.l4_offset        = ODP_PACKET_OFFSET_INVALID;
> >>> +
> >>> +     /* Ensure dummy pkt_hdrs used in I/O recv classification are
> valid */
> >>> +     pkt_hdr->ref_hdr = NULL;
> >>>  }
> >>>
> >>>  /**
> >>> @@ -252,6 +281,10 @@ static inline void packet_init(odp_packet_hdr_t
> *pkt_hdr, uint32_t
> >>> len,
> >>>                            CONFIG_PACKET_TAILROOM;
> >>>
> >>>       pkt_hdr->input = ODP_PKTIO_INVALID;
> >>> +
> >>> +     /* By default packet has no references */
> >>> +     pkt_hdr->unshared_len = len;
> >>> +     pkt_hdr->ref_hdr = NULL;
> >>>  }
> >>>
> >>>  static inline void init_segments(odp_packet_hdr_t *pkt_hdr[], int num)
> >>> @@ -264,6 +297,7 @@ static inline void init_segments(odp_packet_hdr_t
> *pkt_hdr[], int num)
> >>>
> >>>       hdr->buf_hdr.seg[0].data = hdr->buf_hdr.base_data;
> >>>       hdr->buf_hdr.seg[0].len  = BASE_LEN;
> >>> +     packet_ref_count_set(hdr, 1);
> >>>
> >>>       /* Link segments */
> >>>       if (CONFIG_PACKET_MAX_SEGS != 1) {
> >>> @@ -273,6 +307,7 @@ static inline void init_segments(odp_packet_hdr_t
> *pkt_hdr[], int num)
> >>>                       for (i = 1; i < num; i++) {
> >>>                               odp_buffer_hdr_t *buf_hdr;
> >>>
> >>> +                             packet_ref_count_set(pkt_hdr[i], 1);
> >>>                               buf_hdr = &pkt_hdr[i]->buf_hdr;
> >>>                               hdr->buf_hdr.seg[i].hdr  = buf_hdr;
> >>>                               hdr->buf_hdr.seg[i].data =
> buf_hdr->base_data;
> >>> @@ -376,9 +411,10 @@ static inline odp_packet_hdr_t
> *add_segments(odp_packet_hdr_t
> >>> *pkt_hdr,
> >>>               new_hdr->buf_hdr.seg[0].len   = seg_len;
> >>>
> >>>               packet_seg_copy_md(new_hdr, pkt_hdr);
> >>> -             new_hdr->frame_len = pkt_hdr->frame_len + len;
> >>> -             new_hdr->headroom  = pool->headroom + offset;
> >>> -             new_hdr->tailroom  = pkt_hdr->tailroom;
> >>> +             new_hdr->frame_len    = pkt_hdr->frame_len + len;
> >>> +             new_hdr->unshared_len = pkt_hdr->unshared_len + len;
> >>> +             new_hdr->headroom     = pool->headroom + offset;
> >>> +             new_hdr->tailroom     = pkt_hdr->tailroom;
> >>>
> >>>               pkt_hdr = new_hdr;
> >>>       } else {
> >>> @@ -391,8 +427,9 @@ static inline odp_packet_hdr_t
> *add_segments(odp_packet_hdr_t
> >>> *pkt_hdr,
> >>>               last = packet_last_seg(pkt_hdr);
> >>>               pkt_hdr->buf_hdr.seg[last].len = seg_len;
> >>>
> >>> -             pkt_hdr->frame_len += len;
> >>> -             pkt_hdr->tailroom   = pool->tailroom + offset;
> >>> +             pkt_hdr->frame_len    += len;
> >>> +             pkt_hdr->unshared_len += len;
> >>> +             pkt_hdr->tailroom      = pool->tailroom + offset;
> >>>       }
> >>>
> >>>       return pkt_hdr;
> >>> @@ -400,13 +437,18 @@ static inline odp_packet_hdr_t
> *add_segments(odp_packet_hdr_t
> >>> *pkt_hdr,
> >>>
> >>>  static inline void free_bufs(odp_packet_hdr_t *pkt_hdr, int first,
> int num)
> >>>  {
> >>> -     int i;
> >>> +     int i, nfree;
> >>>       odp_buffer_t buf[num];
> >>>
> >>> -     for (i = 0; i < num; i++)
> >>> -             buf[i] = buffer_handle(pkt_hdr->buf_hdr.seg[first +
> i].hdr);
> >>> +     for (i = 0, nfree = 0; i < num; i++) {
> >>> +             odp_packet_hdr_t *hdr = pkt_hdr->buf_hdr.seg[first +
> i].hdr;
> >>> +
> >>> +             if (packet_ref_dec(hdr) == 1)
> >>> +                     buf[nfree++] = buffer_handle(hdr);
> >>> +     }
> >>>
> >>> -     buffer_free_multi(buf, num);
> >>> +     if (nfree > 0)
> >>> +             buffer_free_multi(buf, nfree);
> >>>  }
> >>>
> >>>  static inline odp_packet_hdr_t *free_segments(odp_packet_hdr_t
> *pkt_hdr,
> >>> @@ -417,11 +459,15 @@ static inline odp_packet_hdr_t
> *free_segments(odp_packet_hdr_t
> >>> *pkt_hdr,
> >>>
> >>>       if (head) {
> >>>               odp_packet_hdr_t *new_hdr;
> >>> -             int i;
> >>> +             int i, nfree;
> >>>               odp_buffer_t buf[num];
> >>>
> >>> -             for (i = 0; i < num; i++)
> >>> -                     buf[i] = buffer_handle(pkt_hdr->buf_
> hdr.seg[i].hdr);
> >>> +             for (i = 0, nfree = 0; i < num; i++) {
> >>> +                     new_hdr = pkt_hdr->buf_hdr.seg[i].hdr;
> >>> +
> >>> +                     if (packet_ref_dec(new_hdr) == 1)
> >>> +                             buf[nfree++] = buffer_handle(new_hdr);
> >>> +             }
> >>>
> >>>               /* First remaining segment is the new packet descriptor
> */
> >>>               new_hdr = pkt_hdr->buf_hdr.seg[num].hdr;
> >>> @@ -430,15 +476,17 @@ static inline odp_packet_hdr_t
> *free_segments(odp_packet_hdr_t
> >>> *pkt_hdr,
> >>>               packet_seg_copy_md(new_hdr, pkt_hdr);
> >>>
> >>>               /* Tailroom not changed */
> >>> -             new_hdr->tailroom  = pkt_hdr->tailroom;
> >>> -             new_hdr->headroom  = seg_headroom(new_hdr, 0);
> >>> -             new_hdr->frame_len = pkt_hdr->frame_len - free_len;
> >>> +             new_hdr->tailroom     = pkt_hdr->tailroom;
> >>> +             new_hdr->headroom     = seg_headroom(new_hdr, 0);
> >>> +             new_hdr->frame_len    = pkt_hdr->frame_len - free_len;
> >>> +             new_hdr->unshared_len = pkt_hdr->unshared_len - free_len;
> >>>
> >>>               pull_head(new_hdr, pull_len);
> >>>
> >>>               pkt_hdr = new_hdr;
> >>>
> >>> -             buffer_free_multi(buf, num);
> >>> +             if (nfree > 0)
> >>> +                     buffer_free_multi(buf, nfree);
> >>>       } else {
> >>>               /* Free last 'num' bufs */
> >>>               free_bufs(pkt_hdr, num_remain, num);
> >>> @@ -447,6 +495,7 @@ static inline odp_packet_hdr_t
> *free_segments(odp_packet_hdr_t
> >>> *pkt_hdr,
> >>>                * of the metadata. */
> >>>               pkt_hdr->buf_hdr.segcount = num_remain;
> >>>               pkt_hdr->frame_len -= free_len;
> >>> +             pkt_hdr->unshared_len -= free_len;
> >>>               pkt_hdr->tailroom = seg_tailroom(pkt_hdr, num_remain -
> 1);
> >>>
> >>>               pull_tail(pkt_hdr, pull_len);
> >>> @@ -550,45 +599,34 @@ int odp_packet_alloc_multi(odp_pool_t pool_hdl,
> uint32_t len,
> >>>       return num;
> >>>  }
> >>>
> >>> -void odp_packet_free(odp_packet_t pkt)
> >>> +static inline void packet_free(odp_packet_hdr_t *pkt_hdr)
> >>>  {
> >>> -     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> -     int num_seg = pkt_hdr->buf_hdr.segcount;
> >>> +     odp_packet_hdr_t *ref_hdr;
> >>> +     uint32_t ref_count;
> >>>
> >>> -     if (odp_likely(CONFIG_PACKET_MAX_SEGS == 1 || num_seg == 1))
> >>> -             buffer_free_multi((odp_buffer_t *)&pkt, 1);
> >>> -     else
> >>> -             free_bufs(pkt_hdr, 0, num_seg);
> >>> -}
> >>> +     do {
> >>> +             ref_hdr = pkt_hdr->ref_hdr;
> >>> +             ref_count = packet_ref_count(pkt_hdr) - 1;
> >>> +             free_bufs(pkt_hdr, 0, pkt_hdr->buf_hdr.segcount);
> >>>
> >>> -void odp_packet_free_multi(const odp_packet_t pkt[], int num)
> >>> -{
> >>> -     if (CONFIG_PACKET_MAX_SEGS == 1) {
> >>> -             buffer_free_multi((const odp_buffer_t * const)pkt, num);
> >>> -     } else {
> >>> -             odp_buffer_t buf[num * CONFIG_PACKET_MAX_SEGS];
> >>> -             int i, j;
> >>> -             int bufs = 0;
> >>> +             if (ref_count == 1)
> >>> +                     pkt_hdr->unshared_len = pkt_hdr->frame_len;
> >>>
> >>> -             for (i = 0; i < num; i++) {
> >>> -                     odp_packet_hdr_t *pkt_hdr =
> odp_packet_hdr(pkt[i]);
> >>> -                     int num_seg = pkt_hdr->buf_hdr.segcount;
> >>> -                     odp_buffer_hdr_t *buf_hdr = &pkt_hdr->buf_hdr;
> >>> -
> >>> -                     buf[bufs] = (odp_buffer_t)pkt[i];
> >>> -                     bufs++;
> >>> +             pkt_hdr = ref_hdr;
> >>> +     } while (pkt_hdr);
> >>> +}
> >>>
> >>> -                     if (odp_likely(num_seg == 1))
> >>> -                             continue;
> >>> +void odp_packet_free(odp_packet_t pkt)
> >>> +{
> >>> +     packet_free(odp_packet_hdr(pkt));
> >>> +}
> >>>
> >>> -                     for (j = 1; j < num_seg; j++) {
> >>> -                             buf[bufs] =
> buffer_handle(buf_hdr->seg[j].hdr);
> >>> -                             bufs++;
> >>> -                     }
> >>> -             }
> >>> +void odp_packet_free_multi(const odp_packet_t pkt[], int num)
> >>> +{
> >>> +     int i;
> >>>
> >>> -             buffer_free_multi(buf, bufs);
> >>> -     }
> >>> +     for (i = 0; i < num; i++)
> >>> +             packet_free(odp_packet_hdr(pkt[i]));
> >>>  }
> >>>
> >>>  int odp_packet_reset(odp_packet_t pkt, uint32_t len)
> >>> @@ -599,6 +637,9 @@ int odp_packet_reset(odp_packet_t pkt, uint32_t
> len)
> >>>       if (len > pool->headroom + pool->data_size + pool->tailroom)
> >>>               return -1;
> >>>
> >>> +     if (pkt_hdr->ref_hdr)
> >>> +             packet_free(pkt_hdr->ref_hdr);
> >>> +
> >>>       packet_init(pkt_hdr, len, 0);
> >>>
> >>>       return 0;
> >>> @@ -641,15 +682,21 @@ void *odp_packet_head(odp_packet_t pkt)
> >>>  uint32_t odp_packet_buf_len(odp_packet_t pkt)
> >>>  {
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     uint32_t buf_len = 0;
> >>>
> >>> -     return pkt_hdr->buf_hdr.size * pkt_hdr->buf_hdr.segcount;
> >>> +     do {
> >>> +             buf_len += pkt_hdr->buf_hdr.size *
> pkt_hdr->buf_hdr.segcount;
> >>> +             pkt_hdr  = pkt_hdr->ref_hdr;
> >>> +     } while (pkt_hdr);
> >>> +
> >>> +     return buf_len;
> >>>  }
> >>>
> >>>  void *odp_packet_data(odp_packet_t pkt)
> >>>  {
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>>
> >>> -     return packet_data(pkt_hdr);
> >>> +     return packet_map(pkt_hdr, 0, NULL, NULL);
> >>>  }
> >>>
> >>>  uint32_t odp_packet_seg_len(odp_packet_t pkt)
> >>> @@ -661,7 +708,32 @@ uint32_t odp_packet_seg_len(odp_packet_t pkt)
> >>>
> >>>  uint32_t odp_packet_len(odp_packet_t pkt)
> >>>  {
> >>> -     return odp_packet_hdr(pkt)->frame_len;
> >>> +     return packet_len(odp_packet_hdr(pkt));
> >>> +}
> >>> +
> >>> +uint32_t odp_packet_unshared_len(odp_packet_t pkt)
> >>> +{
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     uint32_t pkt_len = 0, offset = 0;
> >>> +
> >>> +     do {
> >>> +             if (packet_ref_count(pkt_hdr) > 1) {
> >>> +                     if (offset == 0)
> >>> +                             pkt_len += pkt_hdr->unshared_len;
> >>> +                     break;
> >>> +             }
> >>> +
> >>> +             pkt_len += pkt_hdr->frame_len - offset;
> >>> +             offset   = pkt_hdr->ref_offset;
> >>> +
> >>> +             if (pkt_hdr->ref_hdr)
> >>> +                     offset += (pkt_hdr->ref_hdr->frame_len -
> >>> +                                pkt_hdr->ref_len);
> >>> +
> >>> +             pkt_hdr = pkt_hdr->ref_hdr;
> >>> +     } while (pkt_hdr);
> >>> +
> >>> +     return pkt_len;
> >>>  }
> >>>
> >>>  uint32_t odp_packet_headroom(odp_packet_t pkt)
> >>> @@ -671,12 +743,12 @@ uint32_t odp_packet_headroom(odp_packet_t pkt)
> >>>
> >>>  uint32_t odp_packet_tailroom(odp_packet_t pkt)
> >>>  {
> >>> -     return odp_packet_hdr(pkt)->tailroom;
> >>> +     return odp_packet_last_hdr(pkt, NULL)->tailroom;
> >>>  }
> >>>
> >>>  void *odp_packet_tail(odp_packet_t pkt)
> >>>  {
> >>> -     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_last_hdr(pkt, NULL);
> >>>
> >>>       return packet_tail(pkt_hdr);
> >>>  }
> >>> @@ -870,7 +942,7 @@ int odp_packet_extend_head(odp_packet_t *pkt,
> uint32_t len,
> >>>  {
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(*pkt);
> >>>       uint32_t frame_len = pkt_hdr->frame_len;
> >>> -     uint32_t headroom  = pkt_hdr->headroom;
> >>> +     uint32_t headroom = pkt_hdr->headroom;
> >>>       int ret = 0;
> >>>
> >>>       if (len > headroom) {
> >>> @@ -885,6 +957,46 @@ int odp_packet_extend_head(odp_packet_t *pkt,
> uint32_t len,
> >>>               segs = pkt_hdr->buf_hdr.segcount;
> >>>
> >>>               if (odp_unlikely((segs + num) > CONFIG_PACKET_MAX_SEGS))
> {
> >>> +                     /* Handle recursively via references when
> >>> +                      * working with referenced packets since another
> >>> +                      * thread may be accessing it concurrently via
> >>> +                      * its reference to it. */
> >>> +                     if (packet_ref_count(pkt_hdr) > 1) {
> >>> +                             odp_packet_t ref;
> >>> +                             uint32_t unshared_len;
> >>> +
> >>> +                             push_head(pkt_hdr, headroom);
> >>> +                             unshared_len = pkt_hdr->unshared_len;
> >>> +                             ref = odp_packet_ref(*pkt, 0);
> >>> +
> >>> +                             if (ref == ODP_PACKET_INVALID) {
> >>> +                                     pull_head(pkt_hdr, headroom);
> >>> +                                     return -1;
> >>> +                             }
> >>> +
> >>> +                             ret = odp_packet_extend_head(&ref,
> >>> +                                                          len -
> headroom,
> >>> +                                                          data_ptr,
> >>> +                                                          seg_len);
> >>> +
> >>> +                             if (ret < 0) {
> >>> +                                     odp_packet_free(ref);
> >>> +                                     pull_head(pkt_hdr, headroom);
> >>> +                                     return -1;
> >>> +                             }
> >>> +
> >>> +                             /* Since this is a special ref, the
> >>> +                              * base pkt's unshared len is unchanged
> */
> >>> +                             pkt_hdr->unshared_len = unshared_len;
> >>> +
> >>> +                             /* Remove extra ref to the base pkt */
> >>> +                             odp_packet_free(*pkt);
> >>> +
> >>> +                             /* Return the ref as the extension
> result */
> >>> +                             *pkt = ref;
> >>> +                             return 1;
> >>> +                     }
> >>> +
> >>>                       /* Cannot directly add new segments */
> >>>                       odp_packet_hdr_t *new_hdr;
> >>>                       int new_segs = 0;
> >>> @@ -936,6 +1048,7 @@ int odp_packet_extend_head(odp_packet_t *pkt,
> uint32_t len,
> >>>
> >>>                       pkt_hdr->buf_hdr.segcount = segs;
> >>>                       pkt_hdr->frame_len        = frame_len;
> >>> +                     pkt_hdr->unshared_len     = frame_len;
> >>>                       pkt_hdr->headroom         = offset +
> pool->headroom;
> >>>                       pkt_hdr->tailroom         = pool->tailroom;
> >>>
> >>> @@ -961,11 +1074,16 @@ int odp_packet_extend_head(odp_packet_t *pkt,
> uint32_t len,
> >>>               push_head(pkt_hdr, len);
> >>>       }
> >>>
> >>> -     if (data_ptr)
> >>> -             *data_ptr = packet_data(pkt_hdr);
> >>> +     if (data_ptr || seg_len) {
> >>> +             uint32_t seg_ln = 0;
> >>> +             void *data = packet_map(pkt_hdr, 0, &seg_ln, NULL);
> >>>
> >>> -     if (seg_len)
> >>> -             *seg_len = packet_first_seg_len(pkt_hdr);
> >>> +             if (data_ptr)
> >>> +                     *data_ptr = data;
> >>> +
> >>> +             if (seg_len)
> >>> +                     *seg_len = seg_ln;
> >>> +     }
> >>>
> >>>       return ret;
> >>>  }
> >>> @@ -977,6 +1095,8 @@ void *odp_packet_pull_head(odp_packet_t pkt,
> uint32_t len)
> >>>       if (len > pkt_hdr->frame_len)
> >>>               return NULL;
> >>>
> >>> +     ODP_ASSERT(len <= pkt_hdr->unshared_len);
> >>> +
> >>>       pull_head(pkt_hdr, len);
> >>>       return packet_data(pkt_hdr);
> >>>  }
> >>> @@ -984,15 +1104,35 @@ void *odp_packet_pull_head(odp_packet_t pkt,
> uint32_t len)
> >>>  int odp_packet_trunc_head(odp_packet_t *pkt, uint32_t len,
> >>>                         void **data_ptr, uint32_t *seg_len_out)
> >>>  {
> >>> -     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(*pkt);
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(*pkt), *nxt_hdr;
> >>>       uint32_t seg_len = packet_first_seg_len(pkt_hdr);
> >>> +     int ret = 0;
> >>>
> >>> -     if (len > pkt_hdr->frame_len)
> >>> +     if (len > packet_len(pkt_hdr))
> >>>               return -1;
> >>>
> >>> -     if (len < seg_len) {
> >>> +     ODP_ASSERT(len <= odp_packet_unshared_len(*pkt));
> >>> +
> >>> +     /* Special processing for references */
> >>> +     while (len >= pkt_hdr->frame_len && pkt_hdr->ref_hdr) {
> >>> +             ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
> >>> +             nxt_hdr = pkt_hdr->ref_hdr;
> >>> +             len -= pkt_hdr->frame_len;
> >>> +             len += pkt_hdr->ref_offset +
> >>> +                     (nxt_hdr->frame_len - pkt_hdr->ref_len);
> >>> +             pkt_hdr->ref_hdr = NULL;
> >>> +             packet_free(pkt_hdr);
> >>> +             pkt_hdr = nxt_hdr;
> >>> +             seg_len = packet_first_seg_len(pkt_hdr);
> >>> +             *pkt = packet_handle(pkt_hdr);
> >>> +             ret = 1;
> >>> +     }
> >>> +
> >>> +     if (CONFIG_PACKET_MAX_SEGS == 1 ||
> >>> +         len < seg_len ||
> >>> +         pkt_hdr->buf_hdr.segcount == 1) {
> >>>               pull_head(pkt_hdr, len);
> >>> -     } else if (CONFIG_PACKET_MAX_SEGS != 1) {
> >>> +     } else {
> >>>               int num = 0;
> >>>               uint32_t pull_len = 0;
> >>>
> >>> @@ -1007,23 +1147,29 @@ int odp_packet_trunc_head(odp_packet_t *pkt,
> uint32_t len,
> >>>               *pkt    = packet_handle(pkt_hdr);
> >>>       }
> >>>
> >>> -     if (data_ptr)
> >>> -             *data_ptr = packet_data(pkt_hdr);
> >>> +     if (data_ptr || seg_len_out) {
> >>> +             void *data_head = packet_map(pkt_hdr, 0, &seg_len, NULL);
> >>>
> >>> -     if (seg_len_out)
> >>> -             *seg_len_out = packet_first_seg_len(pkt_hdr);
> >>> +             if (data_ptr)
> >>> +                     *data_ptr = data_head;
> >>>
> >>> -     return 0;
> >>> +             if (seg_len_out)
> >>> +                     *seg_len_out = seg_len;
> >>> +     }
> >>> +
> >>> +     return ret;
> >>>  }
> >>>
> >>>  void *odp_packet_push_tail(odp_packet_t pkt, uint32_t len)
> >>>  {
> >>> -     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_last_hdr(pkt, NULL);
> >>>       void *old_tail;
> >>>
> >>>       if (len > pkt_hdr->tailroom)
> >>>               return NULL;
> >>>
> >>> +     ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
> >>> +
> >>>       old_tail = packet_tail(pkt_hdr);
> >>>       push_tail(pkt_hdr, len);
> >>>
> >>> @@ -1033,12 +1179,14 @@ void *odp_packet_push_tail(odp_packet_t pkt,
> uint32_t len)
> >>>  int odp_packet_extend_tail(odp_packet_t *pkt, uint32_t len,
> >>>                          void **data_ptr, uint32_t *seg_len_out)
> >>>  {
> >>> -     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(*pkt);
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_last_hdr(*pkt, NULL);
> >>>       uint32_t frame_len = pkt_hdr->frame_len;
> >>>       uint32_t tailroom  = pkt_hdr->tailroom;
> >>>       uint32_t tail_off  = frame_len;
> >>>       int ret = 0;
> >>>
> >>> +     ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
> >>> +
> >>>       if (len > tailroom) {
> >>>               pool_t *pool = pool_entry_from_hdl(pkt_hdr->
> buf_hdr.pool_hdl);
> >>>               int num;
> >>> @@ -1129,6 +1277,7 @@ void *odp_packet_pull_tail(odp_packet_t pkt,
> uint32_t len)
> >>>       if (len > packet_last_seg_len(pkt_hdr))
> >>>               return NULL;
> >>>
> >>> +     ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
> >>>       pull_tail(pkt_hdr, len);
> >>>
> >>>       return packet_tail(pkt_hdr);
> >>> @@ -1139,17 +1288,34 @@ int odp_packet_trunc_tail(odp_packet_t *pkt,
> uint32_t len,
> >>>  {
> >>>       int last;
> >>>       uint32_t seg_len;
> >>> -     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(*pkt);
> >>> +     uint32_t offset;
> >>> +     odp_packet_hdr_t *first_hdr = odp_packet_hdr(*pkt);
> >>> +     odp_packet_hdr_t *pkt_hdr, *prev_hdr;
> >>>
> >>> -     if (len > pkt_hdr->frame_len)
> >>> +     if (len > packet_len(first_hdr))
> >>>               return -1;
> >>>
> >>> +     pkt_hdr = odp_packet_last_hdr(*pkt, &offset);
> >>> +
> >>> +     /* Special processing for references */
> >>> +     while (len >= pkt_hdr->frame_len - offset && first_hdr->ref_hdr)
> {
> >>> +             len -= (pkt_hdr->frame_len - offset);
> >>> +             prev_hdr = odp_packet_prev_hdr(first_hdr, pkt_hdr,
> &offset);
> >>> +             ODP_ASSERT(packet_ref_count(prev_hdr) == 1);
> >>> +             prev_hdr->ref_hdr = NULL;
> >>> +             packet_free(pkt_hdr);
> >>> +             pkt_hdr = prev_hdr;
> >>> +     }
> >>> +
> >>> +     ODP_ASSERT(packet_ref_count(pkt_hdr) == 1);
> >>>       last    = packet_last_seg(pkt_hdr);
> >>>       seg_len = packet_seg_len(pkt_hdr, last);
> >>>
> >>> -     if (len < seg_len) {
> >>> +     if (CONFIG_PACKET_MAX_SEGS == 1 ||
> >>> +         len < seg_len ||
> >>> +         pkt_hdr->buf_hdr.segcount == 1) {
> >>>               pull_tail(pkt_hdr, len);
> >>> -     } else if (CONFIG_PACKET_MAX_SEGS != 1) {
> >>> +     } else {
> >>>               int num = 0;
> >>>               uint32_t pull_len = 0;
> >>>
> >>> @@ -1356,35 +1522,50 @@ void odp_packet_ts_set(odp_packet_t pkt,
> odp_time_t timestamp)
> >>>
> >>>  int odp_packet_is_segmented(odp_packet_t pkt)
> >>>  {
> >>> -     return odp_packet_hdr(pkt)->buf_hdr.segcount > 1;
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +
> >>> +     return pkt_hdr->buf_hdr.segcount > 1 || pkt_hdr->ref_hdr != NULL;
> >>>  }
> >>>
> >>>  int odp_packet_num_segs(odp_packet_t pkt)
> >>>  {
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     uint32_t segcount = 0, i;
> >>> +     uint32_t seg_offset = 0, offset;
> >>> +
> >>> +     do {
> >>> +             segcount += pkt_hdr->buf_hdr.segcount - seg_offset;
> >>> +             offset    = pkt_hdr->ref_offset;
> >>> +             pkt_hdr   = pkt_hdr->ref_hdr;
> >>> +             if (pkt_hdr) {
> >>> +                     for (i = 0, seg_offset = 0;
> >>> +                          i < pkt_hdr->buf_hdr.segcount;
> >>> +                          i++, seg_offset++) {
> >>> +                             if (offset < pkt_hdr->buf_hdr.seg[i].len)
> >>> +                                     break;
> >>> +                             offset -= pkt_hdr->buf_hdr.seg[i].len;
> >>> +                     }
> >>> +             }
> >>> +     } while (pkt_hdr);
> >>>
> >>> -     return pkt_hdr->buf_hdr.segcount;
> >>> +     return segcount;
> >>>  }
> >>>
> >>> -odp_packet_seg_t odp_packet_first_seg(odp_packet_t pkt)
> >>> +odp_packet_seg_t odp_packet_first_seg(odp_packet_t pkt ODP_UNUSED)
> >>>  {
> >>> -     (void)pkt;
> >>> -
> >>>       return 0;
> >>>  }
> >>>
> >>>  odp_packet_seg_t odp_packet_last_seg(odp_packet_t pkt)
> >>>  {
> >>> -     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> -
> >>> -     return packet_last_seg(pkt_hdr);
> >>> +     return (odp_packet_seg_t)(odp_packet_num_segs(pkt) - 1);
> >>>  }
> >>>
> >>>  odp_packet_seg_t odp_packet_next_seg(odp_packet_t pkt,
> odp_packet_seg_t seg)
> >>>  {
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>>
> >>> -     if (odp_unlikely(seg >= (odp_packet_seg_t)packet_last_
> seg(pkt_hdr)))
> >>> +     if (odp_unlikely(seg >= packet_last_seg(pkt_hdr)))
> >>>               return ODP_PACKET_SEG_INVALID;
> >>>
> >>>       return seg + 1;
> >>> @@ -1400,21 +1581,51 @@ odp_packet_seg_t odp_packet_next_seg(odp_packet_t
> pkt,
> >>> odp_packet_seg_t seg)
> >>>  void *odp_packet_seg_data(odp_packet_t pkt, odp_packet_seg_t seg)
> >>>  {
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     uint32_t seg_offset = 0, offset = 0, i;
> >>> +
> >>> +     while (seg >= pkt_hdr->buf_hdr.segcount - seg_offset &&
> >>> +            pkt_hdr->ref_hdr) {
> >>> +             seg    -= (pkt_hdr->buf_hdr.segcount - seg_offset);
> >>> +             offset  = pkt_hdr->ref_offset;
> >>> +             pkt_hdr = pkt_hdr->ref_hdr;
> >>> +             for (i = 0, seg_offset = 0;
> >>> +                  i < pkt_hdr->buf_hdr.segcount;
> >>> +                  i++, seg_offset++) {
> >>> +                     if (offset < pkt_hdr->buf_hdr.seg[i].len)
> >>> +                             break;
> >>> +                     offset -= pkt_hdr->buf_hdr.seg[i].len;
> >>> +             }
> >>> +     }
> >>>
> >>> -     if (odp_unlikely(seg >= pkt_hdr->buf_hdr.segcount))
> >>> +     if (odp_unlikely(seg + seg_offset >= pkt_hdr->buf_hdr.segcount))
> >>>               return NULL;
> >>>
> >>> -     return packet_seg_data(pkt_hdr, seg);
> >>> +     return packet_seg_data(pkt_hdr, seg + seg_offset) + offset;
> >>>  }
> >>>
> >>>  uint32_t odp_packet_seg_data_len(odp_packet_t pkt, odp_packet_seg_t
> seg)
> >>>  {
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +     uint32_t seg_offset = 0, offset = 0, i;
> >>> +
> >>> +     while (seg >= pkt_hdr->buf_hdr.segcount - seg_offset &&
> >>> +            pkt_hdr->ref_hdr) {
> >>> +             seg    -= (pkt_hdr->buf_hdr.segcount - seg_offset);
> >>> +             offset  = pkt_hdr->ref_offset;
> >>> +             pkt_hdr = pkt_hdr->ref_hdr;
> >>> +             for (i = 0, seg_offset = 0;
> >>> +                  i < pkt_hdr->buf_hdr.segcount;
> >>> +                  i++, seg_offset++) {
> >>> +                     if (offset < pkt_hdr->buf_hdr.seg[i].len)
> >>> +                             break;
> >>> +                     offset -= pkt_hdr->buf_hdr.seg[i].len;
> >>> +             }
> >>> +     }
> >>>
> >>> -     if (odp_unlikely(seg >= pkt_hdr->buf_hdr.segcount))
> >>> +     if (odp_unlikely(seg + seg_offset >= pkt_hdr->buf_hdr.segcount))
> >>>               return 0;
> >>>
> >>> -     return packet_seg_len(pkt_hdr, seg);
> >>> +     return packet_seg_len(pkt_hdr, seg + seg_offset) - offset;
> >>>  }
> >>>
> >>>  /*
> >>> @@ -1428,12 +1639,14 @@ int odp_packet_add_data(odp_packet_t
> *pkt_ptr, uint32_t offset,
> >>> uint32_t len)
> >>>  {
> >>>       odp_packet_t pkt = *pkt_ptr;
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> -     uint32_t pktlen = pkt_hdr->frame_len;
> >>> +     uint32_t pktlen = packet_len(pkt_hdr);
> >>>       odp_packet_t newpkt;
> >>>
> >>>       if (offset > pktlen)
> >>>               return -1;
> >>>
> >>> +     ODP_ASSERT(odp_packet_unshared_len(*pkt_ptr) >= offset);
> >>> +
> >>>       newpkt = odp_packet_alloc(pkt_hdr->buf_hdr.pool_hdl, pktlen +
> len);
> >>>
> >>>       if (newpkt == ODP_PACKET_INVALID)
> >>> @@ -1496,6 +1709,8 @@ int odp_packet_align(odp_packet_t *pkt, uint32_t
> offset, uint32_t
> >>> len,
> >>>       if (align > ODP_CACHE_LINE_SIZE)
> >>>               return -1;
> >>>
> >>> +     ODP_ASSERT(odp_packet_has_ref(*pkt) == 0);
> >>> +
> >>>       if (seglen >= len) {
> >>>               misalign = align <= 1 ? 0 :
> >>>                       ODP_ALIGN_ROUNDUP(uaddr, align) - uaddr;
> >>> @@ -1535,10 +1750,13 @@ int odp_packet_concat(odp_packet_t *dst,
> odp_packet_t src)
> >>>       uint32_t dst_len    = dst_hdr->frame_len;
> >>>       uint32_t src_len    = src_hdr->frame_len;
> >>>
> >>> +     ODP_ASSERT(packet_ref_count(dst_hdr) == 1);
> >>> +
> >>>       /* Do a copy if resulting packet would be out of segments or
> packets
> >>> -      * are from different pools. */
> >>> +      * are from different pools or src is a reference. */
> >>>       if (odp_unlikely((dst_segs + src_segs) > CONFIG_PACKET_MAX_SEGS)
> ||
> >>> -         odp_unlikely(dst_pool != src_pool)) {
> >>> +         odp_unlikely(dst_pool != src_pool) ||
> >>> +         odp_unlikely(packet_ref_count(src_hdr)) > 1) {
> >>>               if (odp_packet_extend_tail(dst, src_len, NULL, NULL) >=
> 0) {
> >>>                       (void)odp_packet_copy_from_pkt(*dst, dst_len,
> >>>                                                      src, 0, src_len);
> >>> @@ -1553,8 +1771,9 @@ int odp_packet_concat(odp_packet_t *dst,
> odp_packet_t src)
> >>>
> >>>       add_all_segs(dst_hdr, src_hdr);
> >>>
> >>> -     dst_hdr->frame_len = dst_len + src_len;
> >>> -     dst_hdr->tailroom  = src_hdr->tailroom;
> >>> +     dst_hdr->frame_len    = dst_len + src_len;
> >>> +     dst_hdr->unshared_len = dst_len + src_len;
> >>> +     dst_hdr->tailroom     = src_hdr->tailroom;
> >>>
> >>>       /* Data was not moved in memory */
> >>>       return 0;
> >>> @@ -1567,6 +1786,7 @@ int odp_packet_split(odp_packet_t *pkt, uint32_t
> len, odp_packet_t
> >>> *tail)
> >>>       if (len >= pktlen || tail == NULL)
> >>>               return -1;
> >>>
> >>> +     ODP_ASSERT(odp_packet_unshared_len(*pkt) >= len);
> >>>       *tail = odp_packet_copy_part(*pkt, len, pktlen - len,
> >>>                                    odp_packet_pool(*pkt));
> >>>
> >>> @@ -1577,6 +1797,109 @@ int odp_packet_split(odp_packet_t *pkt,
> uint32_t len, odp_packet_t
> >>> *tail)
> >>>  }
> >>>
> >>>  /*
> >>> + * References
> >>> + */
> >>> +
> >>> +static inline void packet_ref(odp_packet_hdr_t *pkt_hdr)
> >>> +{
> >>> +     uint32_t i;
> >>> +     odp_packet_hdr_t *hdr;
> >>> +
> >>> +     do {
> >>> +             for (i = 0; i < pkt_hdr->buf_hdr.segcount; i++) {
> >>> +                     hdr = pkt_hdr->buf_hdr.seg[i].hdr;
> >>> +                     packet_ref_inc(hdr);
> >>> +             }
> >>> +
> >>> +             pkt_hdr = pkt_hdr->ref_hdr;
> >>> +     } while (pkt_hdr);
> >>> +}
> >>> +
> >>> +static inline odp_packet_t packet_splice(odp_packet_hdr_t *pkt_hdr,
> >>> +                                      uint32_t offset,
> >>> +                                      odp_packet_hdr_t *ref_hdr)
> >>> +{
> >>> +     /* Catch attempted references to stale handles in debug builds */
> >>> +     ODP_ASSERT(packet_ref_count(pkt_hdr) > 0);
> >>> +
> >>> +     /* Splicing is from the last section of src pkt */
> >>> +     while (ref_hdr->ref_hdr)
> >>> +             ref_hdr = ref_hdr->ref_hdr;
> >>> +
> >>> +     /* Find section where splice begins */
> >>> +     while (offset >= pkt_hdr->frame_len && pkt_hdr->ref_hdr) {
> >>> +             offset   -= (pkt_hdr->frame_len - pkt_hdr->ref_offset);
> >>> +             offset   += (pkt_hdr->ref_hdr->frame_len -
> pkt_hdr->ref_len);
> >>> +             pkt_hdr   = pkt_hdr->ref_hdr;
> >>> +     }
> >>> +
> >>> +     ref_hdr->ref_hdr    = pkt_hdr;
> >>> +     ref_hdr->ref_offset = offset;
> >>> +     ref_hdr->ref_len    = pkt_hdr->frame_len;
> >>> +
> >>> +     if (offset < pkt_hdr->unshared_len)
> >>> +             pkt_hdr->unshared_len = offset;
> >>> +
> >>> +     packet_ref(pkt_hdr);
> >>> +     return _odp_packet_hdl(ref_hdr);
> >>> +}
> >>> +
> >>> +odp_packet_t odp_packet_ref_static(odp_packet_t pkt)
> >>> +{
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +
> >>> +     pkt_hdr->unshared_len = 0;
> >>> +     packet_ref(pkt_hdr);
> >>> +     return pkt;
> >>> +}
> >>> +
> >>> +odp_packet_t odp_packet_ref(odp_packet_t pkt, uint32_t offset)
> >>> +{
> >>> +     odp_packet_t hdr;
> >>> +     odp_packet_hdr_t *pkt_hdr;
> >>> +
> >>> +     if (pkt == ODP_PACKET_INVALID)
> >>> +             return ODP_PACKET_INVALID;
> >>> +
> >>> +     pkt_hdr = odp_packet_hdr(pkt);
> >>> +     if (offset >= packet_len(pkt_hdr))
> >>> +             return ODP_PACKET_INVALID;
> >>> +
> >>> +     hdr = odp_packet_alloc(odp_packet_pool(pkt), 0);
> >>> +
> >>> +     if (hdr == ODP_PACKET_INVALID)
> >>> +             return ODP_PACKET_INVALID;
> >>> +
> >>> +     return packet_splice(pkt_hdr, offset, odp_packet_hdr(hdr));
> >>> +}
> >>> +
> >>> +odp_packet_t odp_packet_ref_pkt(odp_packet_t pkt, uint32_t offset,
> >>> +                             odp_packet_t hdr)
> >>> +{
> >>> +     odp_packet_hdr_t *pkt_hdr;
> >>> +
> >>> +     if (pkt == ODP_PACKET_INVALID ||
> >>> +         hdr == ODP_PACKET_INVALID ||
> >>> +         pkt == hdr)
> >>> +             return ODP_PACKET_INVALID;
> >>> +
> >>> +     ODP_ASSERT(odp_packet_has_ref(hdr) == 0);
> >>> +
> >>> +     pkt_hdr = odp_packet_hdr(pkt);
> >>> +     if (offset >= packet_len(pkt_hdr))
> >>> +             return ODP_PACKET_INVALID;
> >>> +
> >>> +     return packet_splice(pkt_hdr, offset, odp_packet_hdr(hdr));
> >>> +}
> >>> +
> >>> +int odp_packet_has_ref(odp_packet_t pkt)
> >>> +{
> >>> +     odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>> +
> >>> +     return pkt_hdr->ref_hdr != NULL || packet_ref_count(pkt_hdr) > 1;
> >>> +}
> >>> +
> >>> +/*
> >>>   *
> >>>   * Copy
> >>>   * ********************************************************
> >>> @@ -1585,8 +1908,7 @@ int odp_packet_split(odp_packet_t *pkt, uint32_t
> len, odp_packet_t
> >>> *tail)
> >>>
> >>>  odp_packet_t odp_packet_copy(odp_packet_t pkt, odp_pool_t pool)
> >>>  {
> >>> -     odp_packet_hdr_t *srchdr = odp_packet_hdr(pkt);
> >>> -     uint32_t pktlen = srchdr->frame_len;
> >>> +     uint32_t pktlen = odp_packet_len(pkt);
> >>>       odp_packet_t newpkt = odp_packet_alloc(pool, pktlen);
> >>>
> >>>       if (newpkt != ODP_PACKET_INVALID) {
> >>> @@ -1625,7 +1947,7 @@ int odp_packet_copy_to_mem(odp_packet_t pkt,
> uint32_t offset,
> >>>       uint8_t *dstaddr = (uint8_t *)dst;
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>>
> >>> -     if (offset + len > pkt_hdr->frame_len)
> >>> +     if (offset + len > packet_len(pkt_hdr))
> >>>               return -1;
> >>>
> >>>       while (len > 0) {
> >>> @@ -1649,9 +1971,11 @@ int odp_packet_copy_from_mem(odp_packet_t pkt,
> uint32_t offset,
> >>>       const uint8_t *srcaddr = (const uint8_t *)src;
> >>>       odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
> >>>
> >>> -     if (offset + len > pkt_hdr->frame_len)
> >>> +     if (offset + len > packet_len(pkt_hdr))
> >>>               return -1;
> >>>
> >>> +     ODP_ASSERT(odp_packet_unshared_len(pkt) >= offset + len);
> >>> +
> >>>       while (len > 0) {
> >>>               mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
> >>>               cpylen = len > seglen ? seglen : len;
> >>> @@ -1677,10 +2001,12 @@ int odp_packet_copy_from_pkt(odp_packet_t
> dst, uint32_t
> >>> dst_offset,
> >>>       uint32_t src_seglen = 0; /* GCC */
> >>>       int overlap;
> >>>
> >>> -     if (dst_offset + len > dst_hdr->frame_len ||
> >>> -         src_offset + len > src_hdr->frame_len)
> >>> +     if (dst_offset + len > packet_len(dst_hdr) ||
> >>> +         src_offset + len > packet_len(src_hdr))
> >>>               return -1;
> >>>
> >>> +     ODP_ASSERT(odp_packet_unshared_len(dst) >= dst_offset + len);
> >>> +
> >>>       overlap = (dst_hdr == src_hdr &&
> >>>                  ((dst_offset <= src_offset &&
> >>>                    dst_offset + len >= src_offset) ||
> >>> @@ -1764,7 +2090,7 @@ void odp_packet_print(odp_packet_t pkt)
> >>>       len += snprintf(&str[len], n - len,
> >>>                       "  l4_offset    %" PRIu32 "\n",
> hdr->p.l4_offset);
> >>>       len += snprintf(&str[len], n - len,
> >>> -                     "  frame_len    %" PRIu32 "\n", hdr->frame_len);
> >>> +                     "  frame_len    %" PRIu32 "\n", packet_len(hdr));
> >>>       len += snprintf(&str[len], n - len,
> >>>                       "  input        %" PRIu64 "\n",
> >>>                       odp_pktio_to_u64(hdr->input));
> >>> --
> >>> 2.9.3
> >>
>



-- 
[image: Linaro] <http://www.linaro.org/>
François-Frédéric Ozog | *Director Linaro Networking Group*
T: +33.67221.6485
francois.o...@linaro.org | Skype: ffozog

Reply via email to