tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

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



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.


Home | Main Index | Thread Index | Old Index