Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/uipc_mbuf.c
Context not available. | |||||
#include <sys/domain.h> | #include <sys/domain.h> | ||||
#include <sys/protosw.h> | #include <sys/protosw.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/queue.h> | |||||
#ifdef MBUF_TRACKING | |||||
#include <sys/sbuf.h> | |||||
#include <sys/hash.h> | |||||
#include <ddb/ddb.h> | |||||
#include <ddb/db_sym.h> | |||||
#endif | |||||
int max_linkhdr; | int max_linkhdr; | ||||
int max_protohdr; | int max_protohdr; | ||||
Context not available. | |||||
int m_defragrandomfailures; | int m_defragrandomfailures; | ||||
#endif | #endif | ||||
#ifdef MBUF_TRACKING | |||||
#define MBUF_TRACK_HASH_SIZE 32 | |||||
struct mbuflist mbufs = LIST_HEAD_INITIALIZER(mbufs); | |||||
struct mtx mbuf_track_mtx; | |||||
struct mbufbin { | |||||
const char *name; | |||||
const void *data; | |||||
uint32_t count; | |||||
LIST_ENTRY(mbufbin) next; | |||||
}; | |||||
MALLOC_DEFINE(M_MBUFTRACK, "mbuftrack", "mbuf tracking data"); | |||||
static int get_mbuf_usage(struct sbuf *sb); | |||||
static int sysctl_kern_ipc_mbufs(SYSCTL_HANDLER_ARGS); | |||||
SYSCTL_PROC(_kern_ipc, OID_AUTO, mbufs, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, | |||||
NULL, 0, sysctl_kern_ipc_mbufs, "A", "Show who owns allocated mbufs"); | |||||
#endif | |||||
/* | /* | ||||
* sysctl(8) exported objects | * sysctl(8) exported objects | ||||
*/ | */ | ||||
Context not available. | |||||
* comments. | * comments. | ||||
*/ | */ | ||||
#if defined(__LP64__) | #if defined(__LP64__) | ||||
#ifndef MBUF_TRACKING | |||||
CTASSERT(offsetof(struct mbuf, m_dat) == 32); | CTASSERT(offsetof(struct mbuf, m_dat) == 32); | ||||
#endif | |||||
CTASSERT(sizeof(struct pkthdr) == 56); | CTASSERT(sizeof(struct pkthdr) == 56); | ||||
#ifndef MBUF_TRACKING | |||||
CTASSERT(sizeof(struct m_ext) == 48); | CTASSERT(sizeof(struct m_ext) == 48); | ||||
#endif | |||||
#else | #else | ||||
CTASSERT(offsetof(struct mbuf, m_dat) == 24); | CTASSERT(offsetof(struct mbuf, m_dat) == 24); | ||||
CTASSERT(sizeof(struct pkthdr) == 48); | CTASSERT(sizeof(struct pkthdr) == 48); | ||||
Context not available. | |||||
args.flags = flags; | args.flags = flags; | ||||
args.type = type; | args.type = type; | ||||
if (size <= MHLEN || (size <= MLEN && (flags & M_PKTHDR) == 0)) | if (size <= MHLEN || (size <= MLEN && (flags & M_PKTHDR) == 0)) { | ||||
return (uma_zalloc_arg(zone_mbuf, &args, how)); | m = uma_zalloc_arg(zone_mbuf, &args, how); | ||||
if (size <= MCLBYTES) | MBUF_TRACK_INSERT(m); | ||||
return (uma_zalloc_arg(zone_pack, &args, how)); | return (m); | ||||
} | |||||
if (size <= MCLBYTES) { | |||||
m = uma_zalloc_arg(zone_pack, &args, how); | |||||
MBUF_TRACK_INSERT(m); | |||||
return (m); | |||||
} | |||||
if (size > MJUMPAGESIZE) | if (size > MJUMPAGESIZE) | ||||
return (NULL); | return (NULL); | ||||
Context not available. | |||||
return (NULL); | return (NULL); | ||||
} | } | ||||
MBUF_TRACK_INSERT(m); | |||||
return (m); | return (m); | ||||
} | } | ||||
Context not available. | |||||
uma_zfree(zone_mbuf, m); | uma_zfree(zone_mbuf, m); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
MBUF_TRACK_INSERT(m); | |||||
return (m); | return (m); | ||||
} | } | ||||
Context not available. | |||||
return (m0); | return (m0); | ||||
} | } | ||||
#ifdef INVARIANTS | |||||
/** | |||||
* Check buffer boundaries when data is stored within the mbuf. | |||||
*/ | |||||
void | |||||
m_check(struct mbuf *m) | |||||
{ | |||||
char *m_data_begin; | |||||
char *m_data_end; | |||||
m_data_begin = mtod(m, char *); | |||||
m_data_end = m_data_begin + m->m_len; | |||||
if (m->m_flags & M_EXT) { | |||||
if ((m_data_begin >= (char *)m) && | |||||
(m_data_end < (char *)(m+1))) { | |||||
panic("m_check: external mbuf appears to be internal %p", m); | |||||
} | |||||
} else { | |||||
/* No local mbuf should ever overrun the buffer */ | |||||
if (m->m_len > MLEN) { | |||||
panic("m_check: mbuf at %p local data %p len %d " | |||||
"exceeds mbuf upper boundary", m, | |||||
m_data_end, m->m_len); | |||||
} | |||||
if (m->m_flags & M_PKTHDR) { | |||||
/* Check for bad buffers for pkthdr mbufs */ | |||||
if (m->m_len > MHLEN) { | |||||
panic("m_check: mbuf at %p has m_len %d " | |||||
"exceeding %zd available for pkthdr mbuf", | |||||
m, m->m_len, MHLEN); | |||||
} | |||||
if ((m_data_begin >= (char *)m) && | |||||
(m_data_end < ((char *)(m+1) - MHLEN))) { | |||||
panic("m_check: mbuf at %p local data %p " | |||||
" len %d overlays pkthdr mbuf header", | |||||
m, m_data_begin, m->m_len); | |||||
} | |||||
} else { | |||||
/* Check for bad buffers for non-pkthdr mbufs */ | |||||
if (m->m_len > MLEN) { | |||||
panic("m_check: mbuf at %p has m_len %d " | |||||
"exceeding %zd available for data mbuf", | |||||
m, m->m_len, MLEN); | |||||
} | |||||
if ((m_data_begin >= (char *)m) && | |||||
(m_data_end < ((char *)(m+1) - MLEN))) { | |||||
panic("m_check: mbuf at %p local data %p " | |||||
" len %d overlays data mbuf header", | |||||
m, m_data_begin, m->m_len); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Check buffer boundaries of an mbuf chain. | |||||
*/ | |||||
void | |||||
m_checkm(struct mbuf *m) | |||||
{ | |||||
size_t len; | |||||
struct mbuf *n; | |||||
/* Check buffer boundaries of mbufs in the chain */ | |||||
len = 0; | |||||
for (n = m; n != NULL; n = n->m_next) { | |||||
m_check(n); | |||||
len += n->m_len; | |||||
} | |||||
/* | |||||
* If the leading mbuf is a pkthdr, check the length recorded | |||||
* in the header against the summed length from the chain. | |||||
*/ | |||||
if (m->m_flags & M_PKTHDR) { | |||||
if (len != m->m_pkthdr.len) { | |||||
panic("m_checkm: mbuf chain at %p pkthdr len " | |||||
"%d mismatches length of all mbufs %zd", | |||||
m, m->m_pkthdr.len, len); | |||||
} | |||||
} | |||||
} | |||||
#endif /* INVARIANTS */ | |||||
#ifdef MBUF_PROFILING | #ifdef MBUF_PROFILING | ||||
#define MP_BUCKETS 32 /* don't just change this as things may overflow.*/ | #define MP_BUCKETS 32 /* don't just change this as things may overflow.*/ | ||||
Context not available. | |||||
NULL, 0, mbprof_clr_handler, "I", "clear mbuf profiling statistics"); | NULL, 0, mbprof_clr_handler, "I", "clear mbuf profiling statistics"); | ||||
#endif | #endif | ||||
#ifdef MBUF_TRACKING | |||||
static int | |||||
sysctl_kern_ipc_mbufs(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct sbuf sb; | |||||
int error = 0; | |||||
sbuf_new_for_sysctl(&sb, NULL, 128, req); | |||||
error = get_mbuf_usage(&sb); | |||||
if (error) | |||||
goto out; | |||||
error = sbuf_finish(&sb); | |||||
out: | |||||
sbuf_delete(&sb); | |||||
return error; | |||||
} | |||||
/* | |||||
* Fill sbuf with information on who owns which mbufs | |||||
*/ | |||||
int | |||||
get_mbuf_usage(struct sbuf *sb) | |||||
{ | |||||
int error = 0; | |||||
u_long hashmask, hashtmp; | |||||
c_db_sym_t sym; | |||||
db_expr_t diff, symval; | |||||
struct mbuf *m; | |||||
struct mbufbin *mbi, *tmp; | |||||
const char *symname; | |||||
LIST_HEAD(mbufbinhead, mbufbin) *mbufhash, *bin; | |||||
mbufhash = hashinit(MBUF_TRACK_HASH_SIZE, M_MBUFTRACK, &hashmask); | |||||
/* Lock to prevent mbufs moving around. */ | |||||
mtx_lock(&mbuf_track_mtx); | |||||
/* Build histogram of mbuf consumers */ | |||||
LIST_FOREACH(m, &mbufs, m_mlist) { | |||||
/* Generate hash from ower and data */ | |||||
hashtmp = hash32_str(m->m_owner, hashmask); | |||||
hashtmp = hash32_buf(m->m_ownerdata, sizeof(m->m_ownerdata), hashtmp); | |||||
bin = mbufhash + (hashtmp & hashmask); | |||||
/* Search the bin for a match */ | |||||
tmp = NULL; | |||||
LIST_FOREACH(mbi, bin, next) { | |||||
if (!strcmp(mbi->name, m->m_owner) && | |||||
mbi->data == m->m_ownerdata) { | |||||
tmp = mbi; | |||||
break; | |||||
} | |||||
} | |||||
if (tmp != NULL) | |||||
tmp->count++; | |||||
else { | |||||
mbi = malloc(sizeof(*mbi), M_MBUFTRACK, M_NOWAIT); | |||||
if (mbi == NULL) { | |||||
error = ENOMEM; | |||||
goto out; | |||||
} | |||||
mbi->name = m->m_owner; | |||||
mbi->data = m->m_ownerdata; | |||||
mbi->count = 1; | |||||
LIST_INSERT_HEAD(bin, mbi, next); | |||||
} | |||||
} | |||||
/* Built the histogram, safe to unlock */ | |||||
mtx_unlock(&mbuf_track_mtx); | |||||
/* Display */ | |||||
sbuf_printf(sb, "Allocated mbufs:"); | |||||
for (int i = 0; i < MBUF_TRACK_HASH_SIZE; i++) { | |||||
bin = mbufhash + i; | |||||
LIST_FOREACH(mbi, bin, next) { | |||||
if (mbi->name && !strcmp(mbi->name, "function")) { | |||||
sym = db_search_symbol((db_addr_t)mbi->data, DB_STGY_PROC, &diff); | |||||
db_symbol_values(sym, &symname, &symval); | |||||
sbuf_printf(sb, "\n\t%5d owned by %s (%p)", | |||||
mbi->count, symname, mbi->data); | |||||
} else if (mbi->name) { | |||||
sbuf_printf(sb, "\n\t%5d owned by %s %p", | |||||
mbi->count, mbi->name, mbi->data); | |||||
} else { | |||||
sbuf_printf(sb, "\n\t%5d owned by (null) %p", | |||||
mbi->count, mbi->data); | |||||
} | |||||
} | |||||
} | |||||
out: | |||||
/* Free up */ | |||||
for (int i = 0; i < MBUF_TRACK_HASH_SIZE; i++) { | |||||
bin = mbufhash + i; | |||||
LIST_FOREACH_SAFE(mbi, bin, next, tmp) { | |||||
LIST_REMOVE(mbi, next); | |||||
free(mbi, M_MBUFTRACK); | |||||
} | |||||
} | |||||
hashdestroy(mbufhash, M_MBUFTRACK, hashmask); | |||||
return error; | |||||
} | |||||
#endif | |||||
Context not available. |