Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 12 Dec 2000 14:32:15 -0800
From:      Jason Evans <jasone@canonware.com>
To:        Alfred Perlstein <bright@wintelcom.net>
Cc:        Bosko Milekic <bmilekic@technokratis.com>, net@FreeBSD.ORG, jhb@FreeBSD.ORG
Subject:   Re: MEXT_IS_REF broken.
Message-ID:  <20001212143214.H2312@canonware.com>
In-Reply-To: <20001212015059.Z16205@fw.wintelcom.net>; from bright@wintelcom.net on Tue, Dec 12, 2000 at 01:51:00AM -0800
References:  <20001211014837.W16205@fw.wintelcom.net> <Pine.BSF.4.21.0012111223350.21769-100000@jehovah.technokratis.com> <20001212014429.Y16205@fw.wintelcom.net> <20001212015059.Z16205@fw.wintelcom.net>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, Dec 12, 2000 at 01:51:00AM -0800, Alfred Perlstein wrote:
> * Alfred Perlstein <bright@wintelcom.net> [001212 01:44] wrote:
> > grr...
> > 
> > considering this:
> > 
> > #define MEXT_IS_REF(m) ((m)->m_ext.ref_cnt->refcnt > 1)
> > 
> > #define MEXT_REM_REF(m) do {                        \
> >     KASSERT((m)->m_ext.ref_cnt->refcnt > 0, ("m_ext refcnt < 0"));  \
> >     atomic_subtract_long(&((m)->m_ext.ref_cnt->refcnt), 1);     \
> > } while(0)
> > 
> > this:
> > 
> > #define MEXTFREE(m) do {                        \
> >     struct mbuf *_mmm = (m);                    \
> >                                     \
> >     if (MEXT_IS_REF(_mmm))                      \
> >         MEXT_REM_REF(_mmm);                 \
> > 
> >  
> > is not mpsafe.  we _NEED_ some type that allows atomic dec and test
> > for 0.
> 
> Sorry, I didn't explain why this is broken, it looks safe because
> if it's 1 then only "we" reference it.  This is incorrect:
> 
> (m)->m_ext.ref_cnt->refcnt == 2
> 
> thread a                       thread b
> 
> if (MEXT_IS_REF(m))
>                                if (MEXT_IS_REF(m))
>   MEXT_REM_REF(m);
>                                   MEXT_REM_REF(m);
> 
> now... (m)->m_ext.ref_cnt->refcnt == 0.
> 
> oops, now the destructor never gets called and we just leaked a
> mbuf cluster.

The current macro:
---------------------------------------------------------------------------
#define	MEXTFREE(m) do {						\
	struct mbuf *_mmm = (m);					\
									\
	if (MEXT_IS_REF(_mmm))						\
		MEXT_REM_REF(_mmm);					\
	else if (_mmm->m_ext.ext_type != EXT_CLUSTER) {			\
		(*(_mmm->m_ext.ext_free))(_mmm->m_ext.ext_buf,		\
		    _mmm->m_ext.ext_args);				\
		_MEXT_DEALLOC_CNT(_mmm->m_ext.ref_cnt);			\
	} else {							\
		_MCLFREE(_mmm->m_ext.ext_buf);				\
		_MEXT_DEALLOC_CNT(_mmm->m_ext.ref_cnt);			\
	}								\
	_mmm->m_flags &= ~M_EXT;					\
} while (0)
---------------------------------------------------------------------------

A safe version:
---------------------------------------------------------------------------
#define	MEXTFREE(m) do {						\
	struct mbuf *_mmm = (m);					\
									\
	MEXT_REM_REF(_mmm);						\
	if (!atomic_cmpset_long(&_mmm->m_ext.ref_cnt->refcnt, 0, 1)) {	\
		/*							\
		 * Do not free; there are still references, or another	\
		 * thread is freeing.					\
		 */							\
	} else if (_mmm->m_ext.ext_type != EXT_CLUSTER) {		\
		(*(_mmm->m_ext.ext_free))(_mmm->m_ext.ext_buf,		\
		    _mmm->m_ext.ext_args);				\
		_MEXT_DEALLOC_CNT(_mmm->m_ext.ref_cnt);			\
	} else {							\
		_MCLFREE(_mmm->m_ext.ext_buf);				\
		_MEXT_DEALLOC_CNT(_mmm->m_ext.ref_cnt);			\
	}								\
	_mmm->m_flags &= ~M_EXT;					\
} while (0)
---------------------------------------------------------------------------

What this does is have all threads unconditionally atomically decrement.
Then, in order to determine whether to clean up, the threads to an atomic
compare and set on the refcount, so that if it is 0, only one thread
manages to modify the refcount, which then signifies that it will do the
cleanup.

It looks like this should work to me, without the need for an atomic
refcount type.  Yes, it requires two atomic operations, but it avoids the
need for a mutex, which seems to be your main concern.

Jason


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-net" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20001212143214.H2312>