From a944e196da46dd13fa7a2b4e6563d1e97d81f488 Mon Sep 17 00:00:00 2001 From: Bill Paul Date: Sat, 26 Feb 2005 00:22:16 +0000 Subject: [PATCH] MDLs are supposed to be variable size (they include an array of pages that describe a buffer of variable size). The problem is, allocating MDLs off the heap is slow, and it can happen that drivers will allocate lots and lots of lots of MDLs as they run. As a compromise, we now do the following: we pre-allocate a zone for MDLs big enough to describe any buffer with 16 or less pages. If IoAllocateMdl() needs a MDL for a buffer with 16 or less pages, we'll allocate it from the zone. Otherwise, we allocate it from the heap. MDLs allocate from the zone have a flag set in their mdl_flags field. When the MDL is released, IoMdlFree() will uma_zfree() the MDL if it has the MDL_ZONE_ALLOCED flag set, otherwise it will release it to the heap. The assumption is that 16 pages is a "big number" and we will rarely need MDLs larger than that. - Moved the ndis_buffer zone to subr_ntoskrnl.c from kern_ndis.c and named it mdl_zone. - Modified IoAllocateMdl() and IoFreeMdl() to use uma_zalloc() and uma_zfree() if necessary. - Made ndis_mtop() use IoAllocateMdl() instead of calling uma_zalloc() directly. Inspired by: discussion with Giridhar Pemmasani --- sys/compat/ndis/kern_ndis.c | 17 ++----------- sys/compat/ndis/ntoskrnl_var.h | 4 +++ sys/compat/ndis/subr_ntoskrnl.c | 44 ++++++++++++++++++++++++++++++--- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c index b6027f5d32a7..5694fb337640 100644 --- a/sys/compat/ndis/kern_ndis.c +++ b/sys/compat/ndis/kern_ndis.c @@ -56,8 +56,6 @@ __FBSDID("$FreeBSD$"); #include #include -#include - #include #include #include @@ -120,7 +118,6 @@ static int ndis_enlarge_thrqueue(int); static int ndis_shrink_thrqueue(int); static void ndis_runq(void *); -static uma_zone_t ndis_buffer_zone; struct mtx ndis_thr_mtx; struct mtx ndis_req_mtx; static STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo; @@ -160,11 +157,6 @@ ndis_modevent(module_t mod, int cmd, void *arg) patch++; } - /* Initialize TX buffer UMA zone. */ - ndis_buffer_zone = uma_zcreate("NDIS buffer", - sizeof(struct mdl) + (sizeof(vm_offset_t *) * 16), - NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); - ndis_create_kthreads(); TAILQ_INIT(&ndis_devhead); @@ -186,9 +178,6 @@ ndis_modevent(module_t mod, int cmd, void *arg) windrv_unwrap(patch->ipt_wrap); patch++; } - - /* Remove zones */ - uma_zdestroy(ndis_buffer_zone); } break; case MOD_UNLOAD: @@ -208,8 +197,6 @@ ndis_modevent(module_t mod, int cmd, void *arg) patch++; } - /* Remove zones */ - uma_zdestroy(ndis_buffer_zone); break; default: error = EINVAL; @@ -878,7 +865,7 @@ ndis_free_bufs(b0) while(b0 != NULL) { next = b0->mdl_next; - uma_zfree (ndis_buffer_zone, b0); + IoFreeMdl(b0); b0 = next; } @@ -1100,7 +1087,7 @@ ndis_mtop(m0, p) for (m = m0; m != NULL; m = m->m_next) { if (m->m_len == 0) continue; - buf = uma_zalloc(ndis_buffer_zone, M_NOWAIT | M_ZERO); + buf = IoAllocateMdl(m->m_data, m->m_len, FALSE, FALSE, NULL); if (buf == NULL) { ndis_free_packet(*p); *p = NULL; diff --git a/sys/compat/ndis/ntoskrnl_var.h b/sys/compat/ndis/ntoskrnl_var.h index 0a2447181945..7a11d53fc763 100644 --- a/sys/compat/ndis/ntoskrnl_var.h +++ b/sys/compat/ndis/ntoskrnl_var.h @@ -100,6 +100,10 @@ typedef struct mdl mdl, ndis_buffer; #define MDL_NETWORK_HEADER 0x1000 #define MDL_MAPPING_CAN_FAIL 0x2000 #define MDL_ALLOCATED_MUST_SUCCEED 0x4000 +#define MDL_ZONE_ALLOCED 0x8000 /* BSD private */ + +#define MDL_ZONE_PAGES 16 +#define MDL_ZONE_SIZE (sizeof(mdl) + (sizeof(vm_offset_t) * MDL_ZONE_PAGES)) /* Note: assumes x86 page size of 4K. */ diff --git a/sys/compat/ndis/subr_ntoskrnl.c b/sys/compat/ndis/subr_ntoskrnl.c index 858bd74a181c..bcd1e7028caf 100644 --- a/sys/compat/ndis/subr_ntoskrnl.c +++ b/sys/compat/ndis/subr_ntoskrnl.c @@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -186,6 +187,7 @@ static kspin_lock ntoskrnl_global; static kspin_lock ntoskrnl_cancellock; static int ntoskrnl_kth = 0; static struct nt_objref_head ntoskrnl_reflist; +static uma_zone_t mdl_zone; int ntoskrnl_libinit() @@ -204,6 +206,22 @@ ntoskrnl_libinit() patch++; } + /* + * MDLs are supposed to be variable size (they describe + * buffers containing some number of pages, but we don't + * know ahead of time how many pages that will be). But + * always allocating them off the heap is very slow. As + * a compromize, we create an MDL UMA zone big enough to + * handle any buffer requiring up to 16 pages, and we + * use those for any MDLs for buffers of 16 pages or less + * in size. For buffers larger than that (which we assume + * will be few and far between, we allocate the MDLs off + * the heap. + */ + + mdl_zone = uma_zcreate("Windows MDL", MDL_ZONE_SIZE, + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + return(0); } @@ -219,6 +237,8 @@ ntoskrnl_libfini() patch++; } + uma_zdestroy(mdl_zone); + return(0); } @@ -1726,15 +1746,30 @@ IoAllocateMdl(vaddr, len, secondarybuf, chargequota, iopkt) irp *iopkt; { mdl *m; + int zone = 0; - m = ExAllocatePoolWithTag(NonPagedPool, - MmSizeOfMdl(vaddr, len), 0); + if (MmSizeOfMdl(vaddr, len) > MDL_ZONE_SIZE) + m = ExAllocatePoolWithTag(NonPagedPool, + MmSizeOfMdl(vaddr, len), 0); + else { + m = uma_zalloc(mdl_zone, M_NOWAIT | M_ZERO); + zone++; + } if (m == NULL) return (NULL); MmInitializeMdl(m, vaddr, len); + /* + * MmInitializMdl() clears the flags field, so we + * have to set this here. If the MDL came from the + * MDL UMA zone, tag it so we can release it to + * the right place later. + */ + if (zone) + m->mdl_flags = MDL_ZONE_ALLOCED; + if (iopkt != NULL) { if (secondarybuf == TRUE) { mdl *last; @@ -1759,7 +1794,10 @@ IoFreeMdl(m) if (m == NULL) return; - free (m, M_DEVBUF); + if (m->mdl_flags & MDL_ZONE_ALLOCED) + uma_zfree(mdl_zone, m); + else + ExFreePool(m); return; }