On Wed, 9 Apr 2025 23:22:50 +0000
"Lombardo, Ed" <ed.lomba...@netscout.com> wrote:

> Hi,
> I just finished modifying and testing our application to just do transmit of 
> packets received on an NIC interface and let the rte_eth_tx_burst () free the 
> mbuf and all works fine for both traffic types.  This proves to me that my 
> implementation of processing the packets and queueing them to tx ring and 
> transmit from the tx ring is not buggy, which I had carefully verified in gdb 
> early on.  I still believe there is a problem with our application with many 
> threads that can do rte_pktmbuf_free() on the same mbuf.  
> 
> I added these lines in my driver source file:
> #define RTE_LITRTE_MBUF_DEBUG 1
> #define RTE_LIBRTE_MEMPOOL_DEBUG 1
> #define RTE_ENABLE_ASSERT 1
> 
> I don't see any asserts occur during my tx packet testing.
> 
> The dpdk header files show the Atomic ifdef checks
> rte_build_config.h:#define RTE_MBUF_REFCNT_ATOMIC
> rte_mbuf_core.h:                         * or non-atomic) is controlled by 
> the RTE_MBUF_REFCNT_ATOMIC flag.
> rte_mbuf.h:#ifdef RTE_MBUF_REFCNT_ATOMIC
> rte_mbuf.h:#else /* ! RTE_MBUF_REFCNT_ATOMIC */
> rte_mbuf.h:#endif /* RTE_MBUF_REFCNT_ATOMIC */
> 
> I verified in building our application with DPDK rte_mbuf.h header file that 
> the atomic functions for mbuf refcnt read/writes are turned ON.  I added junk 
> characters, and the compiler spotted syntax errors.
> 
> So, I am back to the question as to why I get mbuf issue with multiple 
> threads processing the same mbuf?
> 
> Any more suggestions.
> 
> Thanks,
> Ed
> 
> -----Original Message-----
> From: Stephen Hemminger <step...@networkplumber.org> 
> Sent: Wednesday, April 9, 2025 12:24 PM
> To: Lombardo, Ed <ed.lomba...@netscout.com>
> Cc: Dmitry Kozlyuk <dmitry.kozl...@gmail.com>; users@dpdk.org
> Subject: Re: mbuf refcnt issue
> 
> External Email: This message originated outside of NETSCOUT. Do not click 
> links or open attachments unless you recognize the sender and know the 
> content is safe.
> 
> On Wed, 9 Apr 2025 04:46:09 +0000
> "Lombardo, Ed" <ed.lomba...@netscout.com> wrote:
> 
> > Hi Stephen,
> > I am looking a the rte_mbuf.h file for rte_pktmbuf_free() and it is not 
> > clear to me that it checks if the mbuf refcnt is 1 before decrementing it 
> > and allowing the mbuf and segments (if any) to be returned to free pool.
> > 
> > Could my application issue be I have tx threads that transmit packets and 
> > does rte_pktmbuf_free(), while one other thread will perform 
> > rte_pktmbuf_free() on the same mbuf?  I ensured I bump the mbuf refcnt to 2 
> > before other threads can process the same mbuf.
> > 
> > Thanks,
> > Ed  
> 
> It doesn't need to check refcnt there. The check is done later (since mbuf 
> can be multi segment).
> 
> rte_pktmbuf_free
>  -> rte_pktmbuf_free_seg
>     -> rte_pktmbuf_prefree_seg  
>               
> static __rte_always_inline struct rte_mbuf * rte_pktmbuf_prefree_seg(struct 
> rte_mbuf *m) {
>       __rte_mbuf_sanity_check(m, 0);
> 
>       if (likely(rte_mbuf_refcnt_read(m) == 1)) {
>             normal fast path. breaks the chain.
>       } else if (__rte_mbuf_refcnt_update(m, -1) == 0) {
>             refcnt > 1 logic
> 
> Note, the refcnt doesn't always go to zero when the mbuf is put back in the 
> pool.
> The refcnt for a freed mbuf (in the pool) doesn't matter, it is free, it is 
> dead.
> The refcnt is reset to 1 when mbuf is extracted from the pool.
> 
> 
> 


You might find something by poisoning the mbuf when it is freed, so that any 
attempt to use
the data would get junk. Also, add check at start of rte_pktmbuf_free_seg() to 
catch dup free.
Something like this, but only compile tested.
It will break some of the functional tests, because they tend to make bogus 
dummy mbufs.

diff --git a/lib/mbuf/rte_mbuf.h b/lib/mbuf/rte_mbuf.h
index 06ab7502a5..6088b34506 100644
--- a/lib/mbuf/rte_mbuf.h
+++ b/lib/mbuf/rte_mbuf.h
@@ -1423,10 +1423,11 @@ static inline int 
__rte_pktmbuf_pinned_extbuf_decref(struct rte_mbuf *m)
 static __rte_always_inline struct rte_mbuf *
 rte_pktmbuf_prefree_seg(struct rte_mbuf *m)
 {
-       __rte_mbuf_sanity_check(m, 0);
 
-       if (likely(rte_mbuf_refcnt_read(m) == 1)) {
+       __rte_mbuf_sanity_check(m, 0);
 
+       unsigned int refcnt = rte_mbuf_refcnt_read(m);
+       if (likely(refcnt == 1)) {
                if (!RTE_MBUF_DIRECT(m)) {
                        rte_pktmbuf_detach(m);
                        if (RTE_MBUF_HAS_EXTBUF(m) &&
@@ -1435,13 +1436,15 @@ rte_pktmbuf_prefree_seg(struct rte_mbuf *m)
                                return NULL;
                }
 
+               m->refcnt = 0;
                if (m->next != NULL)
                        m->next = NULL;
                if (m->nb_segs != 1)
                        m->nb_segs = 1;
 
                return m;
-
+       } else if (unlikely(refcnt == 0 || refcnt >= UINT16_MAX - 1)) {
+               rte_panic("mbuf refcnt underflow %u\n", refcnt);
        } else if (__rte_mbuf_refcnt_update(m, -1) == 0) {
 
                if (!RTE_MBUF_DIRECT(m)) {
@@ -1452,6 +1455,7 @@ rte_pktmbuf_prefree_seg(struct rte_mbuf *m)
                                return NULL;
                }
 
+               m->refcnt = 0;
                if (m->next != NULL)
                        m->next = NULL;
                if (m->nb_segs != 1)

Reply via email to