m_ext_free() (was: mbuf (cluster) leak?)

Edgar Fuß <[email protected]>
Newsgroups gmane.os.netbsd.devel.network
Message-ID <[email protected]>
I'm trying to understand m_ext_free() (in kern/uipc_mbuf.c).

It approximately (MEXT_ISEMBEDDED() expanded and __predict_true() and KASSERT()s 
removed) looks like this:

void
m_ext_free(struct mbuf *m)
{
	bool embedded = (m->m_ext_ref == m);
	bool dofree = true;
	u_int refcnt;

	if (m->m_ext.ext_refcnt == 1) {
		refcnt = m->m_ext.ext_refcnt = 0;
	} else {
		refcnt = atomic_dec_uint_nv(&m->m_ext.ext_refcnt);
	}
	if (refcnt > 0) {
		if (embedded) {
			/*
			 * other mbuf's m_ext_ref still points to us.
			 */
			dofree = false;
		} else {
			m->m_ext_ref = m;
		}
	} else {
		if (!embedded) {
			m->m_ext.ext_refcnt++; /* XXX */
			m_ext_free(m->m_ext_ref);
			m->m_ext_ref = m;
		} else if ((m->m_flags & M_EXT_CLUSTER) != 0) {
			pool_cache_put_paddr((struct pool_cache *)
			    m->m_ext.ext_arg,
			    m->m_ext.ext_buf, m->m_ext.ext_paddr);
		} else [...]
		}
	}
	if (dofree) {
		m->m_type = MT_FREE;
		pool_cache_put(mb_cache, m);
	}
}

What I don't understand are the two
	m->m_ext_ref = m;
assignments and the ext_refcnt++ operation.

If I have two mbufs, m1 and m2, where both are M_EXT (and M_EXT_CLUSTER), 
m1 is embedded (pointing to m2) while m2 isn't, then m_ext_free(m2) will not 
actually free anything. If m_ext_free(m1) will then drop the reference count 
from 2 to 1, it will not free anything, but the reference to m2 will be lost. 
If then another m_ext_free(m1) drops the count to 0, m1 will be freed, but 
m2 will hang around forever.

I guess I'm missing something.


I also noted that various custom ext_free routines (like bge_jfree(), sk_jfree()),
while performing pool_cache_put(), will not set m_type to MT_FREE.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.