xref: /freebsd/sys/kern/kern_malloc.c (revision 0781c79d4872a84a8ebeee3b5eb5520a682b8e7b)
19454b2d8SWarner Losh /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
4df8bae1dSRodney W. Grimes  * Copyright (c) 1987, 1991, 1993
563a7e0a3SRobert Watson  *	The Regents of the University of California.
6bb1c7df8SRobert Watson  * Copyright (c) 2005-2009 Robert N. M. Watson
7fd91e076SKristof Provost  * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> (mallocarray)
863a7e0a3SRobert Watson  * All rights reserved.
9df8bae1dSRodney W. Grimes  *
10df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
11df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
12df8bae1dSRodney W. Grimes  * are met:
13df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
14df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
15df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
16df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
17df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
1869a28758SEd Maste  * 3. Neither the name of the University nor the names of its contributors
19df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
20df8bae1dSRodney W. Grimes  *    without specific prior written permission.
21df8bae1dSRodney W. Grimes  *
22df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
33df8bae1dSRodney W. Grimes  *
34df8bae1dSRodney W. Grimes  *	@(#)kern_malloc.c	8.3 (Berkeley) 1/4/94
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
370ce3f16dSRobert Watson /*
380ce3f16dSRobert Watson  * Kernel malloc(9) implementation -- general purpose kernel memory allocator
390ce3f16dSRobert Watson  * based on memory types.  Back end is implemented using the UMA(9) zone
400ce3f16dSRobert Watson  * allocator.  A set of fixed-size buckets are used for smaller allocations,
410ce3f16dSRobert Watson  * and a special UMA allocation interface is used for larger allocations.
420ce3f16dSRobert Watson  * Callers declare memory types, and statistics are maintained independently
430ce3f16dSRobert Watson  * for each memory type.  Statistics are maintained per-CPU for performance
440ce3f16dSRobert Watson  * reasons.  See malloc(9) and comments in malloc.h for a detailed
450ce3f16dSRobert Watson  * description.
460ce3f16dSRobert Watson  */
470ce3f16dSRobert Watson 
48677b542eSDavid E. O'Brien #include <sys/cdefs.h>
49677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
50677b542eSDavid E. O'Brien 
51909ed16cSRobert Watson #include "opt_ddb.h"
528a58a9f6SJohn Dyson #include "opt_vm.h"
538a58a9f6SJohn Dyson 
54df8bae1dSRodney W. Grimes #include <sys/param.h>
5526f9a767SRodney W. Grimes #include <sys/systm.h>
562d50560aSMarcel Moolenaar #include <sys/kdb.h>
57df8bae1dSRodney W. Grimes #include <sys/kernel.h>
58fb919e4dSMark Murray #include <sys/lock.h>
59df8bae1dSRodney W. Grimes #include <sys/malloc.h>
60eec258d2SJohn Baldwin #include <sys/mutex.h>
61efeaf95aSDavid Greenman #include <sys/vmmeter.h>
62a448b62aSJake Burkholder #include <sys/proc.h>
636d6a03d7SJeff Roberson #include <sys/queue.h>
6463a7e0a3SRobert Watson #include <sys/sbuf.h>
659afff6b1SMateusz Guzik #include <sys/smp.h>
666f267175SJeff Roberson #include <sys/sysctl.h>
671fb14a47SPoul-Henning Kamp #include <sys/time.h>
685df87b21SJeff Roberson #include <sys/vmem.h>
694b25d1f2SGleb Smirnoff #ifdef EPOCH_TRACE
704b25d1f2SGleb Smirnoff #include <sys/epoch.h>
714b25d1f2SGleb Smirnoff #endif
729a02e8c6SJason Evans 
73df8bae1dSRodney W. Grimes #include <vm/vm.h>
7499571dc3SJeff Roberson #include <vm/pmap.h>
759978bd99SMark Johnston #include <vm/vm_domainset.h>
765df87b21SJeff Roberson #include <vm/vm_pageout.h>
77efeaf95aSDavid Greenman #include <vm/vm_param.h>
78df8bae1dSRodney W. Grimes #include <vm/vm_kern.h>
79efeaf95aSDavid Greenman #include <vm/vm_extern.h>
803075778bSJohn Dyson #include <vm/vm_map.h>
8199571dc3SJeff Roberson #include <vm/vm_page.h>
826d6a03d7SJeff Roberson #include <vm/vm_phys.h>
836d6a03d7SJeff Roberson #include <vm/vm_pagequeue.h>
848355f576SJeff Roberson #include <vm/uma.h>
858355f576SJeff Roberson #include <vm/uma_int.h>
868efc4effSJeff Roberson #include <vm/uma_dbg.h>
87df8bae1dSRodney W. Grimes 
88e4eb384bSBosko Milekic #ifdef DEBUG_MEMGUARD
89e4eb384bSBosko Milekic #include <vm/memguard.h>
90e4eb384bSBosko Milekic #endif
91847a2a17SPawel Jakub Dawidek #ifdef DEBUG_REDZONE
92847a2a17SPawel Jakub Dawidek #include <vm/redzone.h>
93847a2a17SPawel Jakub Dawidek #endif
94e4eb384bSBosko Milekic 
95984982d6SPoul-Henning Kamp #if defined(INVARIANTS) && defined(__i386__)
96984982d6SPoul-Henning Kamp #include <machine/cpu.h>
97984982d6SPoul-Henning Kamp #endif
98984982d6SPoul-Henning Kamp 
99909ed16cSRobert Watson #include <ddb/ddb.h>
100909ed16cSRobert Watson 
10191dd776cSJohn Birrell #ifdef KDTRACE_HOOKS
10291dd776cSJohn Birrell #include <sys/dtrace_bsd.h>
10391dd776cSJohn Birrell 
1047cd79421SMateusz Guzik bool	__read_frequently			dtrace_malloc_enabled;
1057cd79421SMateusz Guzik dtrace_malloc_probe_func_t __read_mostly	dtrace_malloc_probe;
10691dd776cSJohn Birrell #endif
10791dd776cSJohn Birrell 
108ab3185d1SJeff Roberson #if defined(INVARIANTS) || defined(MALLOC_MAKE_FAILURES) ||		\
109ab3185d1SJeff Roberson     defined(DEBUG_MEMGUARD) || defined(DEBUG_REDZONE)
110ab3185d1SJeff Roberson #define	MALLOC_DEBUG	1
111ab3185d1SJeff Roberson #endif
112ab3185d1SJeff Roberson 
11389deca0aSMateusz Guzik #ifdef DEBUG_REDZONE
11489deca0aSMateusz Guzik #define	DEBUG_REDZONE_ARG_DEF	, unsigned long osize
11589deca0aSMateusz Guzik #define	DEBUG_REDZONE_ARG	, osize
11689deca0aSMateusz Guzik #else
11789deca0aSMateusz Guzik #define	DEBUG_REDZONE_ARG_DEF
11889deca0aSMateusz Guzik #define	DEBUG_REDZONE_ARG
11989deca0aSMateusz Guzik #endif
12089deca0aSMateusz Guzik 
12144a8ff31SArchie Cobbs /*
12244a8ff31SArchie Cobbs  * When realloc() is called, if the new size is sufficiently smaller than
12344a8ff31SArchie Cobbs  * the old size, realloc() will allocate a new, smaller block to avoid
12444a8ff31SArchie Cobbs  * wasting memory. 'Sufficiently smaller' is defined as: newsize <=
12544a8ff31SArchie Cobbs  * oldsize / 2^n, where REALLOC_FRACTION defines the value of 'n'.
12644a8ff31SArchie Cobbs  */
12744a8ff31SArchie Cobbs #ifndef REALLOC_FRACTION
12844a8ff31SArchie Cobbs #define	REALLOC_FRACTION	1	/* new block if <= half the size */
12944a8ff31SArchie Cobbs #endif
13044a8ff31SArchie Cobbs 
1310ce3f16dSRobert Watson /*
1320ce3f16dSRobert Watson  * Centrally define some common malloc types.
1330ce3f16dSRobert Watson  */
1343b6fb885SPoul-Henning Kamp MALLOC_DEFINE(M_CACHE, "cache", "Various Dynamically allocated caches");
1359ef246c6SBruce Evans MALLOC_DEFINE(M_DEVBUF, "devbuf", "device driver memory");
1369ef246c6SBruce Evans MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers");
1379ef246c6SBruce Evans 
138db669378SPeter Wemm static struct malloc_type *kmemstatistics;
139cd814b26SRobert Watson static int kmemcount;
1401f6889a1SMatthew Dillon 
1418355f576SJeff Roberson #define KMEM_ZSHIFT	4
1428355f576SJeff Roberson #define KMEM_ZBASE	16
1438355f576SJeff Roberson #define KMEM_ZMASK	(KMEM_ZBASE - 1)
1448355f576SJeff Roberson 
145bda06553SXin LI #define KMEM_ZMAX	65536
1468355f576SJeff Roberson #define KMEM_ZSIZE	(KMEM_ZMAX >> KMEM_ZSHIFT)
14760ae52f7SEd Schouten static uint8_t kmemsize[KMEM_ZSIZE + 1];
1486f267175SJeff Roberson 
149d7854da1SMatthew D Fleming #ifndef MALLOC_DEBUG_MAXZONES
150d7854da1SMatthew D Fleming #define	MALLOC_DEBUG_MAXZONES	1
151d7854da1SMatthew D Fleming #endif
152d7854da1SMatthew D Fleming static int numzones = MALLOC_DEBUG_MAXZONES;
153d7854da1SMatthew D Fleming 
1540ce3f16dSRobert Watson /*
1550ce3f16dSRobert Watson  * Small malloc(9) memory allocations are allocated from a set of UMA buckets
1560ce3f16dSRobert Watson  * of various sizes.
1570ce3f16dSRobert Watson  *
158828afddaSMateusz Guzik  * Warning: the layout of the struct is duplicated in libmemstat for KVM support.
159828afddaSMateusz Guzik  *
1600ce3f16dSRobert Watson  * XXX: The comment here used to read "These won't be powers of two for
1610ce3f16dSRobert Watson  * long."  It's possible that a significant amount of wasted memory could be
1620ce3f16dSRobert Watson  * recovered by tuning the sizes of these buckets.
1630ce3f16dSRobert Watson  */
1648355f576SJeff Roberson struct {
1656f267175SJeff Roberson 	int kz_size;
166eaa17d42SRyan Libby 	const char *kz_name;
167d7854da1SMatthew D Fleming 	uma_zone_t kz_zone[MALLOC_DEBUG_MAXZONES];
1686f267175SJeff Roberson } kmemzones[] = {
169e1b6a7f8SMateusz Guzik 	{16, "malloc-16", },
170e1b6a7f8SMateusz Guzik 	{32, "malloc-32", },
171e1b6a7f8SMateusz Guzik 	{64, "malloc-64", },
172e1b6a7f8SMateusz Guzik 	{128, "malloc-128", },
173e1b6a7f8SMateusz Guzik 	{256, "malloc-256", },
174f0c90a09SMateusz Guzik 	{384, "malloc-384", },
175e1b6a7f8SMateusz Guzik 	{512, "malloc-512", },
176e1b6a7f8SMateusz Guzik 	{1024, "malloc-1024", },
177e1b6a7f8SMateusz Guzik 	{2048, "malloc-2048", },
178e1b6a7f8SMateusz Guzik 	{4096, "malloc-4096", },
179e1b6a7f8SMateusz Guzik 	{8192, "malloc-8192", },
180e1b6a7f8SMateusz Guzik 	{16384, "malloc-16384", },
181e1b6a7f8SMateusz Guzik 	{32768, "malloc-32768", },
182e1b6a7f8SMateusz Guzik 	{65536, "malloc-65536", },
1838355f576SJeff Roberson 	{0, NULL},
1848355f576SJeff Roberson };
1858355f576SJeff Roberson 
186b89eaf4eSAlan Cox u_long vm_kmem_size;
187d801e824SAndriy Gapon SYSCTL_ULONG(_vm, OID_AUTO, kmem_size, CTLFLAG_RDTUN, &vm_kmem_size, 0,
18884344f9fSDag-Erling Smørgrav     "Size of kernel memory");
1895a34a9f0SJeff Roberson 
1907001d850SXin LI static u_long kmem_zmax = KMEM_ZMAX;
1917001d850SXin LI SYSCTL_ULONG(_vm, OID_AUTO, kmem_zmax, CTLFLAG_RDTUN, &kmem_zmax, 0,
1927001d850SXin LI     "Maximum allocation size that malloc(9) would use UMA as backend");
1937001d850SXin LI 
194b89eaf4eSAlan Cox static u_long vm_kmem_size_min;
195d801e824SAndriy Gapon SYSCTL_ULONG(_vm, OID_AUTO, kmem_size_min, CTLFLAG_RDTUN, &vm_kmem_size_min, 0,
1960e5179e4SStephane E. Potvin     "Minimum size of kernel memory");
1970e5179e4SStephane E. Potvin 
198b89eaf4eSAlan Cox static u_long vm_kmem_size_max;
199d801e824SAndriy Gapon SYSCTL_ULONG(_vm, OID_AUTO, kmem_size_max, CTLFLAG_RDTUN, &vm_kmem_size_max, 0,
200479439b4SDag-Erling Smørgrav     "Maximum size of kernel memory");
201479439b4SDag-Erling Smørgrav 
2024813ad54SHans Petter Selasky static u_int vm_kmem_size_scale;
203d801e824SAndriy Gapon SYSCTL_UINT(_vm, OID_AUTO, kmem_size_scale, CTLFLAG_RDTUN, &vm_kmem_size_scale, 0,
204479439b4SDag-Erling Smørgrav     "Scale factor for kernel memory size");
205479439b4SDag-Erling Smørgrav 
2067814c80aSAndriy Gapon static int sysctl_kmem_map_size(SYSCTL_HANDLER_ARGS);
2077814c80aSAndriy Gapon SYSCTL_PROC(_vm, OID_AUTO, kmem_map_size,
2087814c80aSAndriy Gapon     CTLFLAG_RD | CTLTYPE_ULONG | CTLFLAG_MPSAFE, NULL, 0,
2095df87b21SJeff Roberson     sysctl_kmem_map_size, "LU", "Current kmem allocation size");
2107814c80aSAndriy Gapon 
21195bb9d38SAndriy Gapon static int sysctl_kmem_map_free(SYSCTL_HANDLER_ARGS);
21295bb9d38SAndriy Gapon SYSCTL_PROC(_vm, OID_AUTO, kmem_map_free,
21395bb9d38SAndriy Gapon     CTLFLAG_RD | CTLTYPE_ULONG | CTLFLAG_MPSAFE, NULL, 0,
2145df87b21SJeff Roberson     sysctl_kmem_map_free, "LU", "Free space in kmem");
21595bb9d38SAndriy Gapon 
216828afddaSMateusz Guzik static SYSCTL_NODE(_vm, OID_AUTO, malloc, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
217828afddaSMateusz Guzik     "Malloc information");
218828afddaSMateusz Guzik 
219828afddaSMateusz Guzik static u_int vm_malloc_zone_count = nitems(kmemzones);
220828afddaSMateusz Guzik SYSCTL_UINT(_vm_malloc, OID_AUTO, zone_count,
221828afddaSMateusz Guzik     CTLFLAG_RD, &vm_malloc_zone_count, 0,
222828afddaSMateusz Guzik     "Number of malloc zones");
223828afddaSMateusz Guzik 
224828afddaSMateusz Guzik static int sysctl_vm_malloc_zone_sizes(SYSCTL_HANDLER_ARGS);
225828afddaSMateusz Guzik SYSCTL_PROC(_vm_malloc, OID_AUTO, zone_sizes,
226828afddaSMateusz Guzik     CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0,
227828afddaSMateusz Guzik     sysctl_vm_malloc_zone_sizes, "S", "Zone sizes used by malloc");
228828afddaSMateusz Guzik 
2295a34a9f0SJeff Roberson /*
23099571dc3SJeff Roberson  * The malloc_mtx protects the kmemstatistics linked list.
2315a34a9f0SJeff Roberson  */
2325a34a9f0SJeff Roberson struct mtx malloc_mtx;
23369ef67f9SJason Evans 
234cd814b26SRobert Watson static int sysctl_kern_malloc_stats(SYSCTL_HANDLER_ARGS);
235df8bae1dSRodney W. Grimes 
236d7854da1SMatthew D Fleming #if defined(MALLOC_MAKE_FAILURES) || (MALLOC_DEBUG_MAXZONES > 1)
2377029da5cSPawel Biernacki static SYSCTL_NODE(_debug, OID_AUTO, malloc, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
238d7854da1SMatthew D Fleming     "Kernel malloc debugging options");
239d7854da1SMatthew D Fleming #endif
240d7854da1SMatthew D Fleming 
241eae870cdSRobert Watson /*
2420ce3f16dSRobert Watson  * malloc(9) fault injection -- cause malloc failures every (n) mallocs when
2430ce3f16dSRobert Watson  * the caller specifies M_NOWAIT.  If set to 0, no failures are caused.
244eae870cdSRobert Watson  */
2450ce3f16dSRobert Watson #ifdef MALLOC_MAKE_FAILURES
246eae870cdSRobert Watson static int malloc_failure_rate;
247eae870cdSRobert Watson static int malloc_nowait_count;
248eae870cdSRobert Watson static int malloc_failure_count;
249af3b2549SHans Petter Selasky SYSCTL_INT(_debug_malloc, OID_AUTO, failure_rate, CTLFLAG_RWTUN,
250eae870cdSRobert Watson     &malloc_failure_rate, 0, "Every (n) mallocs with M_NOWAIT will fail");
251eae870cdSRobert Watson SYSCTL_INT(_debug_malloc, OID_AUTO, failure_count, CTLFLAG_RD,
252eae870cdSRobert Watson     &malloc_failure_count, 0, "Number of imposed M_NOWAIT malloc failures");
253eae870cdSRobert Watson #endif
254eae870cdSRobert Watson 
2557814c80aSAndriy Gapon static int
2567814c80aSAndriy Gapon sysctl_kmem_map_size(SYSCTL_HANDLER_ARGS)
2577814c80aSAndriy Gapon {
2587814c80aSAndriy Gapon 	u_long size;
2597814c80aSAndriy Gapon 
2602e47807cSJeff Roberson 	size = uma_size();
2617814c80aSAndriy Gapon 	return (sysctl_handle_long(oidp, &size, 0, req));
2627814c80aSAndriy Gapon }
2637814c80aSAndriy Gapon 
26495bb9d38SAndriy Gapon static int
26595bb9d38SAndriy Gapon sysctl_kmem_map_free(SYSCTL_HANDLER_ARGS)
26695bb9d38SAndriy Gapon {
2672e47807cSJeff Roberson 	u_long size, limit;
26895bb9d38SAndriy Gapon 
2692e47807cSJeff Roberson 	/* The sysctl is unsigned, implement as a saturation value. */
2702e47807cSJeff Roberson 	size = uma_size();
2712e47807cSJeff Roberson 	limit = uma_limit();
2722e47807cSJeff Roberson 	if (size > limit)
2732e47807cSJeff Roberson 		size = 0;
2742e47807cSJeff Roberson 	else
2752e47807cSJeff Roberson 		size = limit - size;
27695bb9d38SAndriy Gapon 	return (sysctl_handle_long(oidp, &size, 0, req));
27795bb9d38SAndriy Gapon }
27895bb9d38SAndriy Gapon 
279828afddaSMateusz Guzik static int
280828afddaSMateusz Guzik sysctl_vm_malloc_zone_sizes(SYSCTL_HANDLER_ARGS)
281828afddaSMateusz Guzik {
282828afddaSMateusz Guzik 	int sizes[nitems(kmemzones)];
283828afddaSMateusz Guzik 	int i;
284828afddaSMateusz Guzik 
285828afddaSMateusz Guzik 	for (i = 0; i < nitems(kmemzones); i++) {
286828afddaSMateusz Guzik 		sizes[i] = kmemzones[i].kz_size;
287828afddaSMateusz Guzik 	}
288828afddaSMateusz Guzik 
289828afddaSMateusz Guzik 	return (SYSCTL_OUT(req, &sizes, sizeof(sizes)));
290828afddaSMateusz Guzik }
291828afddaSMateusz Guzik 
292d7854da1SMatthew D Fleming /*
293d7854da1SMatthew D Fleming  * malloc(9) uma zone separation -- sub-page buffer overruns in one
294d7854da1SMatthew D Fleming  * malloc type will affect only a subset of other malloc types.
295d7854da1SMatthew D Fleming  */
296d7854da1SMatthew D Fleming #if MALLOC_DEBUG_MAXZONES > 1
297d7854da1SMatthew D Fleming static void
298d7854da1SMatthew D Fleming tunable_set_numzones(void)
299d7854da1SMatthew D Fleming {
300d7854da1SMatthew D Fleming 
301d7854da1SMatthew D Fleming 	TUNABLE_INT_FETCH("debug.malloc.numzones",
302d7854da1SMatthew D Fleming 	    &numzones);
303d7854da1SMatthew D Fleming 
304d7854da1SMatthew D Fleming 	/* Sanity check the number of malloc uma zones. */
305d7854da1SMatthew D Fleming 	if (numzones <= 0)
306d7854da1SMatthew D Fleming 		numzones = 1;
307d7854da1SMatthew D Fleming 	if (numzones > MALLOC_DEBUG_MAXZONES)
308d7854da1SMatthew D Fleming 		numzones = MALLOC_DEBUG_MAXZONES;
309d7854da1SMatthew D Fleming }
310d7854da1SMatthew D Fleming SYSINIT(numzones, SI_SUB_TUNABLES, SI_ORDER_ANY, tunable_set_numzones, NULL);
311af3b2549SHans Petter Selasky SYSCTL_INT(_debug_malloc, OID_AUTO, numzones, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
312d7854da1SMatthew D Fleming     &numzones, 0, "Number of malloc uma subzones");
313d7854da1SMatthew D Fleming 
314d7854da1SMatthew D Fleming /*
315d7854da1SMatthew D Fleming  * Any number that changes regularly is an okay choice for the
316d7854da1SMatthew D Fleming  * offset.  Build numbers are pretty good of you have them.
317d7854da1SMatthew D Fleming  */
318d7854da1SMatthew D Fleming static u_int zone_offset = __FreeBSD_version;
319d7854da1SMatthew D Fleming TUNABLE_INT("debug.malloc.zone_offset", &zone_offset);
320d7854da1SMatthew D Fleming SYSCTL_UINT(_debug_malloc, OID_AUTO, zone_offset, CTLFLAG_RDTUN,
321d7854da1SMatthew D Fleming     &zone_offset, 0, "Separate malloc types by examining the "
322d7854da1SMatthew D Fleming     "Nth character in the malloc type short description.");
323d7854da1SMatthew D Fleming 
324c9e05ccdSMateusz Guzik static void
325c9e05ccdSMateusz Guzik mtp_set_subzone(struct malloc_type *mtp)
326d7854da1SMatthew D Fleming {
327c9e05ccdSMateusz Guzik 	struct malloc_type_internal *mtip;
328c9e05ccdSMateusz Guzik 	const char *desc;
329d7854da1SMatthew D Fleming 	size_t len;
330d7854da1SMatthew D Fleming 	u_int val;
331d7854da1SMatthew D Fleming 
332bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
333c9e05ccdSMateusz Guzik 	desc = mtp->ks_shortdesc;
334d7854da1SMatthew D Fleming 	if (desc == NULL || (len = strlen(desc)) == 0)
335c9e05ccdSMateusz Guzik 		val = 0;
336c9e05ccdSMateusz Guzik 	else
337d7854da1SMatthew D Fleming 		val = desc[zone_offset % len];
338c9e05ccdSMateusz Guzik 	mtip->mti_zone = (val % numzones);
339c9e05ccdSMateusz Guzik }
340c9e05ccdSMateusz Guzik 
341c9e05ccdSMateusz Guzik static inline u_int
342c9e05ccdSMateusz Guzik mtp_get_subzone(struct malloc_type *mtp)
343c9e05ccdSMateusz Guzik {
344c9e05ccdSMateusz Guzik 	struct malloc_type_internal *mtip;
345c9e05ccdSMateusz Guzik 
346bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
347c9e05ccdSMateusz Guzik 
348c9e05ccdSMateusz Guzik 	KASSERT(mtip->mti_zone < numzones,
349c9e05ccdSMateusz Guzik 	    ("mti_zone %u out of range %d",
350c9e05ccdSMateusz Guzik 	    mtip->mti_zone, numzones));
351c9e05ccdSMateusz Guzik 	return (mtip->mti_zone);
352d7854da1SMatthew D Fleming }
353d7854da1SMatthew D Fleming #elif MALLOC_DEBUG_MAXZONES == 0
354d7854da1SMatthew D Fleming #error "MALLOC_DEBUG_MAXZONES must be positive."
355d7854da1SMatthew D Fleming #else
356c9e05ccdSMateusz Guzik static void
357c9e05ccdSMateusz Guzik mtp_set_subzone(struct malloc_type *mtp)
358c9e05ccdSMateusz Guzik {
359c9e05ccdSMateusz Guzik 	struct malloc_type_internal *mtip;
360c9e05ccdSMateusz Guzik 
361bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
362c9e05ccdSMateusz Guzik 	mtip->mti_zone = 0;
363c9e05ccdSMateusz Guzik }
364c9e05ccdSMateusz Guzik 
365d7854da1SMatthew D Fleming static inline u_int
366c9e05ccdSMateusz Guzik mtp_get_subzone(struct malloc_type *mtp)
367d7854da1SMatthew D Fleming {
368d7854da1SMatthew D Fleming 
369d7854da1SMatthew D Fleming 	return (0);
370d7854da1SMatthew D Fleming }
371d7854da1SMatthew D Fleming #endif /* MALLOC_DEBUG_MAXZONES > 1 */
372d7854da1SMatthew D Fleming 
373df8bae1dSRodney W. Grimes /*
3740ce3f16dSRobert Watson  * An allocation has succeeded -- update malloc type statistics for the
3750ce3f16dSRobert Watson  * amount of bucket size.  Occurs within a critical section so that the
3760ce3f16dSRobert Watson  * thread isn't preempted and doesn't migrate while updating per-PCU
3770ce3f16dSRobert Watson  * statistics.
3784362fadaSBrian Feldman  */
3794362fadaSBrian Feldman static void
38063a7e0a3SRobert Watson malloc_type_zone_allocated(struct malloc_type *mtp, unsigned long size,
3814362fadaSBrian Feldman     int zindx)
3824362fadaSBrian Feldman {
38363a7e0a3SRobert Watson 	struct malloc_type_internal *mtip;
38463a7e0a3SRobert Watson 	struct malloc_type_stats *mtsp;
38563a7e0a3SRobert Watson 
38663a7e0a3SRobert Watson 	critical_enter();
387bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
3889afff6b1SMateusz Guzik 	mtsp = zpcpu_get(mtip->mti_stats);
38973864adbSPawel Jakub Dawidek 	if (size > 0) {
39063a7e0a3SRobert Watson 		mtsp->mts_memalloced += size;
39163a7e0a3SRobert Watson 		mtsp->mts_numallocs++;
39273864adbSPawel Jakub Dawidek 	}
3934362fadaSBrian Feldman 	if (zindx != -1)
39463a7e0a3SRobert Watson 		mtsp->mts_size |= 1 << zindx;
39591dd776cSJohn Birrell 
39691dd776cSJohn Birrell #ifdef KDTRACE_HOOKS
3977cd79421SMateusz Guzik 	if (__predict_false(dtrace_malloc_enabled)) {
39891dd776cSJohn Birrell 		uint32_t probe_id = mtip->mti_probes[DTMALLOC_PROBE_MALLOC];
39991dd776cSJohn Birrell 		if (probe_id != 0)
40091dd776cSJohn Birrell 			(dtrace_malloc_probe)(probe_id,
40191dd776cSJohn Birrell 			    (uintptr_t) mtp, (uintptr_t) mtip,
40291dd776cSJohn Birrell 			    (uintptr_t) mtsp, size, zindx);
40391dd776cSJohn Birrell 	}
40491dd776cSJohn Birrell #endif
40591dd776cSJohn Birrell 
40663a7e0a3SRobert Watson 	critical_exit();
4074362fadaSBrian Feldman }
4084362fadaSBrian Feldman 
4094362fadaSBrian Feldman void
41063a7e0a3SRobert Watson malloc_type_allocated(struct malloc_type *mtp, unsigned long size)
4114362fadaSBrian Feldman {
41263a7e0a3SRobert Watson 
41373864adbSPawel Jakub Dawidek 	if (size > 0)
41463a7e0a3SRobert Watson 		malloc_type_zone_allocated(mtp, size, -1);
4154362fadaSBrian Feldman }
4164362fadaSBrian Feldman 
4174362fadaSBrian Feldman /*
4183805385eSRobert Watson  * A free operation has occurred -- update malloc type statistics for the
4190ce3f16dSRobert Watson  * amount of the bucket size.  Occurs within a critical section so that the
4200ce3f16dSRobert Watson  * thread isn't preempted and doesn't migrate while updating per-CPU
4210ce3f16dSRobert Watson  * statistics.
4224362fadaSBrian Feldman  */
4234362fadaSBrian Feldman void
42463a7e0a3SRobert Watson malloc_type_freed(struct malloc_type *mtp, unsigned long size)
4254362fadaSBrian Feldman {
42663a7e0a3SRobert Watson 	struct malloc_type_internal *mtip;
42763a7e0a3SRobert Watson 	struct malloc_type_stats *mtsp;
42863a7e0a3SRobert Watson 
42963a7e0a3SRobert Watson 	critical_enter();
430bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
4319afff6b1SMateusz Guzik 	mtsp = zpcpu_get(mtip->mti_stats);
43263a7e0a3SRobert Watson 	mtsp->mts_memfreed += size;
43363a7e0a3SRobert Watson 	mtsp->mts_numfrees++;
43491dd776cSJohn Birrell 
43591dd776cSJohn Birrell #ifdef KDTRACE_HOOKS
4367cd79421SMateusz Guzik 	if (__predict_false(dtrace_malloc_enabled)) {
43791dd776cSJohn Birrell 		uint32_t probe_id = mtip->mti_probes[DTMALLOC_PROBE_FREE];
43891dd776cSJohn Birrell 		if (probe_id != 0)
43991dd776cSJohn Birrell 			(dtrace_malloc_probe)(probe_id,
44091dd776cSJohn Birrell 			    (uintptr_t) mtp, (uintptr_t) mtip,
44191dd776cSJohn Birrell 			    (uintptr_t) mtsp, size, 0);
44291dd776cSJohn Birrell 	}
44391dd776cSJohn Birrell #endif
44491dd776cSJohn Birrell 
44563a7e0a3SRobert Watson 	critical_exit();
4464362fadaSBrian Feldman }
4474362fadaSBrian Feldman 
4484362fadaSBrian Feldman /*
449f346986bSAlan Cox  *	contigmalloc:
450f346986bSAlan Cox  *
451f346986bSAlan Cox  *	Allocate a block of physically contiguous memory.
452f346986bSAlan Cox  *
453f346986bSAlan Cox  *	If M_NOWAIT is set, this routine will not block and return NULL if
454f346986bSAlan Cox  *	the allocation fails.
455f346986bSAlan Cox  */
456f346986bSAlan Cox void *
457f346986bSAlan Cox contigmalloc(unsigned long size, struct malloc_type *type, int flags,
458f346986bSAlan Cox     vm_paddr_t low, vm_paddr_t high, unsigned long alignment,
459831ce4cbSJohn Baldwin     vm_paddr_t boundary)
460f346986bSAlan Cox {
461f346986bSAlan Cox 	void *ret;
462f346986bSAlan Cox 
46344d0efb2SAlan Cox 	ret = (void *)kmem_alloc_contig(size, flags, low, high, alignment,
46444d0efb2SAlan Cox 	    boundary, VM_MEMATTR_DEFAULT);
465f346986bSAlan Cox 	if (ret != NULL)
466f346986bSAlan Cox 		malloc_type_allocated(type, round_page(size));
467f346986bSAlan Cox 	return (ret);
468f346986bSAlan Cox }
469f346986bSAlan Cox 
470ab3185d1SJeff Roberson void *
4719978bd99SMark Johnston contigmalloc_domainset(unsigned long size, struct malloc_type *type,
4729978bd99SMark Johnston     struct domainset *ds, int flags, vm_paddr_t low, vm_paddr_t high,
473ab3185d1SJeff Roberson     unsigned long alignment, vm_paddr_t boundary)
474ab3185d1SJeff Roberson {
475ab3185d1SJeff Roberson 	void *ret;
476ab3185d1SJeff Roberson 
4779978bd99SMark Johnston 	ret = (void *)kmem_alloc_contig_domainset(ds, size, flags, low, high,
478ab3185d1SJeff Roberson 	    alignment, boundary, VM_MEMATTR_DEFAULT);
479ab3185d1SJeff Roberson 	if (ret != NULL)
480ab3185d1SJeff Roberson 		malloc_type_allocated(type, round_page(size));
481ab3185d1SJeff Roberson 	return (ret);
482ab3185d1SJeff Roberson }
483ab3185d1SJeff Roberson 
484f346986bSAlan Cox /*
485f346986bSAlan Cox  *	contigfree:
486f346986bSAlan Cox  *
487f346986bSAlan Cox  *	Free a block of memory allocated by contigmalloc.
488f346986bSAlan Cox  *
489f346986bSAlan Cox  *	This routine may not block.
490f346986bSAlan Cox  */
491f346986bSAlan Cox void
492f346986bSAlan Cox contigfree(void *addr, unsigned long size, struct malloc_type *type)
493f346986bSAlan Cox {
494f346986bSAlan Cox 
49549bfa624SAlan Cox 	kmem_free((vm_offset_t)addr, size);
496f346986bSAlan Cox 	malloc_type_freed(type, round_page(size));
497f346986bSAlan Cox }
498f346986bSAlan Cox 
499ab3185d1SJeff Roberson #ifdef MALLOC_DEBUG
500ab3185d1SJeff Roberson static int
5015a70796aSLi-Wen Hsu malloc_dbg(caddr_t *vap, size_t *sizep, struct malloc_type *mtp,
502ab3185d1SJeff Roberson     int flags)
503df8bae1dSRodney W. Grimes {
504194a0abfSPoul-Henning Kamp #ifdef INVARIANTS
505ab3185d1SJeff Roberson 	int indx;
506ab3185d1SJeff Roberson 
507bdcc2226SMateusz Guzik 	KASSERT(mtp->ks_version == M_VERSION, ("malloc: bad malloc type version"));
508d3c11994SPoul-Henning Kamp 	/*
50923198357SRuslan Ermilov 	 * Check that exactly one of M_WAITOK or M_NOWAIT is specified.
510d3c11994SPoul-Henning Kamp 	 */
51123198357SRuslan Ermilov 	indx = flags & (M_WAITOK | M_NOWAIT);
512d3c11994SPoul-Henning Kamp 	if (indx != M_NOWAIT && indx != M_WAITOK) {
513d3c11994SPoul-Henning Kamp 		static	struct timeval lasterr;
514d3c11994SPoul-Henning Kamp 		static	int curerr, once;
515d3c11994SPoul-Henning Kamp 		if (once == 0 && ppsratecheck(&lasterr, &curerr, 1)) {
516d3c11994SPoul-Henning Kamp 			printf("Bad malloc flags: %x\n", indx);
5172d50560aSMarcel Moolenaar 			kdb_backtrace();
518d3c11994SPoul-Henning Kamp 			flags |= M_WAITOK;
519d3c11994SPoul-Henning Kamp 			once++;
520d3c11994SPoul-Henning Kamp 		}
521d3c11994SPoul-Henning Kamp 	}
522194a0abfSPoul-Henning Kamp #endif
523eae870cdSRobert Watson #ifdef MALLOC_MAKE_FAILURES
524eae870cdSRobert Watson 	if ((flags & M_NOWAIT) && (malloc_failure_rate != 0)) {
525eae870cdSRobert Watson 		atomic_add_int(&malloc_nowait_count, 1);
526eae870cdSRobert Watson 		if ((malloc_nowait_count % malloc_failure_rate) == 0) {
527eae870cdSRobert Watson 			atomic_add_int(&malloc_failure_count, 1);
528ab3185d1SJeff Roberson 			*vap = NULL;
529ab3185d1SJeff Roberson 			return (EJUSTRETURN);
530eae870cdSRobert Watson 		}
531eae870cdSRobert Watson 	}
532eae870cdSRobert Watson #endif
53306bf2a6aSMatt Macy 	if (flags & M_WAITOK) {
534b40ce416SJulian Elischer 		KASSERT(curthread->td_intr_nesting_level == 0,
535a163d034SWarner Losh 		   ("malloc(M_WAITOK) in interrupt context"));
5365757b59fSGleb Smirnoff 		if (__predict_false(!THREAD_CAN_SLEEP())) {
537bac06038SGleb Smirnoff #ifdef EPOCH_TRACE
538bac06038SGleb Smirnoff 			epoch_trace_list(curthread);
539bac06038SGleb Smirnoff #endif
5405757b59fSGleb Smirnoff 			KASSERT(1,
5415757b59fSGleb Smirnoff 			    ("malloc(M_WAITOK) with sleeping prohibited"));
5425757b59fSGleb Smirnoff 		}
54306bf2a6aSMatt Macy 	}
544d9e2e68dSMark Johnston 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
5451067a2baSJonathan T. Looney 	    ("malloc: called with spinlock or critical section held"));
5461067a2baSJonathan T. Looney 
547e4eb384bSBosko Milekic #ifdef DEBUG_MEMGUARD
548ab3185d1SJeff Roberson 	if (memguard_cmp_mtp(mtp, *sizep)) {
549ab3185d1SJeff Roberson 		*vap = memguard_alloc(*sizep, flags);
550ab3185d1SJeff Roberson 		if (*vap != NULL)
551ab3185d1SJeff Roberson 			return (EJUSTRETURN);
552e3813573SMatthew D Fleming 		/* This is unfortunate but should not be fatal. */
553e3813573SMatthew D Fleming 	}
554e4eb384bSBosko Milekic #endif
555e4eb384bSBosko Milekic 
556847a2a17SPawel Jakub Dawidek #ifdef DEBUG_REDZONE
557ab3185d1SJeff Roberson 	*sizep = redzone_size_ntor(*sizep);
558ab3185d1SJeff Roberson #endif
559ab3185d1SJeff Roberson 
560ab3185d1SJeff Roberson 	return (0);
561ab3185d1SJeff Roberson }
562ab3185d1SJeff Roberson #endif
563ab3185d1SJeff Roberson 
564ab3185d1SJeff Roberson /*
5656d6a03d7SJeff Roberson  * Handle large allocations and frees by using kmem_malloc directly.
5666d6a03d7SJeff Roberson  */
5676d6a03d7SJeff Roberson static inline bool
5686d6a03d7SJeff Roberson malloc_large_slab(uma_slab_t slab)
5696d6a03d7SJeff Roberson {
5706d6a03d7SJeff Roberson 	uintptr_t va;
5716d6a03d7SJeff Roberson 
5726d6a03d7SJeff Roberson 	va = (uintptr_t)slab;
5736d6a03d7SJeff Roberson 	return ((va & 1) != 0);
5746d6a03d7SJeff Roberson }
5756d6a03d7SJeff Roberson 
5766d6a03d7SJeff Roberson static inline size_t
5776d6a03d7SJeff Roberson malloc_large_size(uma_slab_t slab)
5786d6a03d7SJeff Roberson {
5796d6a03d7SJeff Roberson 	uintptr_t va;
5806d6a03d7SJeff Roberson 
5816d6a03d7SJeff Roberson 	va = (uintptr_t)slab;
5826d6a03d7SJeff Roberson 	return (va >> 1);
5836d6a03d7SJeff Roberson }
5846d6a03d7SJeff Roberson 
58589deca0aSMateusz Guzik static caddr_t __noinline
58689deca0aSMateusz Guzik malloc_large(size_t *size, struct malloc_type *mtp, struct domainset *policy,
58789deca0aSMateusz Guzik     int flags DEBUG_REDZONE_ARG_DEF)
5886d6a03d7SJeff Roberson {
58989deca0aSMateusz Guzik 	vm_offset_t kva;
59089deca0aSMateusz Guzik 	caddr_t va;
5916d6a03d7SJeff Roberson 	size_t sz;
5926d6a03d7SJeff Roberson 
5936d6a03d7SJeff Roberson 	sz = roundup(*size, PAGE_SIZE);
59489deca0aSMateusz Guzik 	kva = kmem_malloc_domainset(policy, sz, flags);
59589deca0aSMateusz Guzik 	if (kva != 0) {
5966d6a03d7SJeff Roberson 		/* The low bit is unused for slab pointers. */
59789deca0aSMateusz Guzik 		vsetzoneslab(kva, NULL, (void *)((sz << 1) | 1));
5986d6a03d7SJeff Roberson 		uma_total_inc(sz);
5996d6a03d7SJeff Roberson 		*size = sz;
6006d6a03d7SJeff Roberson 	}
60189deca0aSMateusz Guzik 	va = (caddr_t)kva;
60289deca0aSMateusz Guzik 	malloc_type_allocated(mtp, va == NULL ? 0 : sz);
60389deca0aSMateusz Guzik 	if (__predict_false(va == NULL)) {
60489deca0aSMateusz Guzik 		KASSERT((flags & M_WAITOK) == 0,
60589deca0aSMateusz Guzik 		    ("malloc(M_WAITOK) returned NULL"));
60689deca0aSMateusz Guzik 	}
60789deca0aSMateusz Guzik #ifdef DEBUG_REDZONE
60889deca0aSMateusz Guzik 	if (va != NULL)
60989deca0aSMateusz Guzik 		va = redzone_setup(va, osize);
61089deca0aSMateusz Guzik #endif
61189deca0aSMateusz Guzik 	return (va);
6126d6a03d7SJeff Roberson }
6136d6a03d7SJeff Roberson 
6146d6a03d7SJeff Roberson static void
6156d6a03d7SJeff Roberson free_large(void *addr, size_t size)
6166d6a03d7SJeff Roberson {
6176d6a03d7SJeff Roberson 
6186d6a03d7SJeff Roberson 	kmem_free((vm_offset_t)addr, size);
6196d6a03d7SJeff Roberson 	uma_total_dec(size);
6206d6a03d7SJeff Roberson }
6216d6a03d7SJeff Roberson 
6226d6a03d7SJeff Roberson /*
623ab3185d1SJeff Roberson  *	malloc:
624ab3185d1SJeff Roberson  *
625ab3185d1SJeff Roberson  *	Allocate a block of memory.
626ab3185d1SJeff Roberson  *
627ab3185d1SJeff Roberson  *	If M_NOWAIT is set, this routine will not block and return NULL if
628ab3185d1SJeff Roberson  *	the allocation fails.
629ab3185d1SJeff Roberson  */
630ab3185d1SJeff Roberson void *
63134c538c3SMateusz Guzik (malloc)(size_t size, struct malloc_type *mtp, int flags)
632ab3185d1SJeff Roberson {
633ab3185d1SJeff Roberson 	int indx;
634ab3185d1SJeff Roberson 	caddr_t va;
635ab3185d1SJeff Roberson 	uma_zone_t zone;
63689deca0aSMateusz Guzik #ifdef DEBUG_REDZONE
637ab3185d1SJeff Roberson 	unsigned long osize = size;
638ab3185d1SJeff Roberson #endif
639ab3185d1SJeff Roberson 
64082c174a3SMateusz Guzik 	MPASS((flags & M_EXEC) == 0);
64189deca0aSMateusz Guzik 
642ab3185d1SJeff Roberson #ifdef MALLOC_DEBUG
6435072a5f4SMatt Macy 	va = NULL;
644ab3185d1SJeff Roberson 	if (malloc_dbg(&va, &size, mtp, flags) != 0)
645ab3185d1SJeff Roberson 		return (va);
646847a2a17SPawel Jakub Dawidek #endif
647847a2a17SPawel Jakub Dawidek 
64889deca0aSMateusz Guzik 	if (__predict_false(size > kmem_zmax))
64989deca0aSMateusz Guzik 		return (malloc_large(&size, mtp, DOMAINSET_RR(), flags
65089deca0aSMateusz Guzik 		    DEBUG_REDZONE_ARG));
65189deca0aSMateusz Guzik 
6526f267175SJeff Roberson 	if (size & KMEM_ZMASK)
6536f267175SJeff Roberson 		size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;
6546f267175SJeff Roberson 	indx = kmemsize[size >> KMEM_ZSHIFT];
655c9e05ccdSMateusz Guzik 	zone = kmemzones[indx].kz_zone[mtp_get_subzone(mtp)];
6568355f576SJeff Roberson 	va = uma_zalloc(zone, flags);
6574362fadaSBrian Feldman 	if (va != NULL)
658e20a199fSJeff Roberson 		size = zone->uz_size;
65963a7e0a3SRobert Watson 	malloc_type_zone_allocated(mtp, va == NULL ? 0 : size, indx);
66082c174a3SMateusz Guzik 	if (__predict_false(va == NULL)) {
66182c174a3SMateusz Guzik 		KASSERT((flags & M_WAITOK) == 0,
66282c174a3SMateusz Guzik 		    ("malloc(M_WAITOK) returned NULL"));
66382c174a3SMateusz Guzik 	}
664ab3185d1SJeff Roberson #ifdef DEBUG_REDZONE
665ab3185d1SJeff Roberson 	if (va != NULL)
666ab3185d1SJeff Roberson 		va = redzone_setup(va, osize);
6674db4f5c8SPoul-Henning Kamp #endif
668ab3185d1SJeff Roberson 	return ((void *) va);
669ab3185d1SJeff Roberson }
670ab3185d1SJeff Roberson 
6719978bd99SMark Johnston static void *
672dc727127SMark Johnston malloc_domain(size_t *sizep, int *indxp, struct malloc_type *mtp, int domain,
6736d6a03d7SJeff Roberson     int flags)
674ab3185d1SJeff Roberson {
675ab3185d1SJeff Roberson 	uma_zone_t zone;
676dc727127SMark Johnston 	caddr_t va;
677dc727127SMark Johnston 	size_t size;
678dc727127SMark Johnston 	int indx;
679ab3185d1SJeff Roberson 
680dc727127SMark Johnston 	size = *sizep;
6816d6a03d7SJeff Roberson 	KASSERT(size <= kmem_zmax && (flags & M_EXEC) == 0,
6826d6a03d7SJeff Roberson 	    ("malloc_domain: Called with bad flag / size combination."));
683ab3185d1SJeff Roberson 	if (size & KMEM_ZMASK)
684ab3185d1SJeff Roberson 		size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;
685ab3185d1SJeff Roberson 	indx = kmemsize[size >> KMEM_ZSHIFT];
686c9e05ccdSMateusz Guzik 	zone = kmemzones[indx].kz_zone[mtp_get_subzone(mtp)];
687ab3185d1SJeff Roberson 	va = uma_zalloc_domain(zone, NULL, domain, flags);
688ab3185d1SJeff Roberson 	if (va != NULL)
689dc727127SMark Johnston 		*sizep = zone->uz_size;
6906d6a03d7SJeff Roberson 	*indxp = indx;
691df8bae1dSRodney W. Grimes 	return ((void *)va);
692df8bae1dSRodney W. Grimes }
693df8bae1dSRodney W. Grimes 
694fd91e076SKristof Provost void *
6959978bd99SMark Johnston malloc_domainset(size_t size, struct malloc_type *mtp, struct domainset *ds,
6969978bd99SMark Johnston     int flags)
6979978bd99SMark Johnston {
6989978bd99SMark Johnston 	struct vm_domainset_iter di;
69982c174a3SMateusz Guzik 	caddr_t va;
7009978bd99SMark Johnston 	int domain;
7016d6a03d7SJeff Roberson 	int indx;
70289deca0aSMateusz Guzik #ifdef DEBUG_REDZONE
7036d6a03d7SJeff Roberson 	unsigned long osize = size;
7046d6a03d7SJeff Roberson #endif
70589deca0aSMateusz Guzik 
70682c174a3SMateusz Guzik 	MPASS((flags & M_EXEC) == 0);
70789deca0aSMateusz Guzik 
7086d6a03d7SJeff Roberson #ifdef MALLOC_DEBUG
70982c174a3SMateusz Guzik 	va = NULL;
71082c174a3SMateusz Guzik 	if (malloc_dbg(&va, &size, mtp, flags) != 0)
71182c174a3SMateusz Guzik 		return (va);
7126d6a03d7SJeff Roberson #endif
71389deca0aSMateusz Guzik 
71489deca0aSMateusz Guzik 	if (__predict_false(size > kmem_zmax))
71589deca0aSMateusz Guzik 		return (malloc_large(&size, mtp, DOMAINSET_RR(), flags
71689deca0aSMateusz Guzik 		    DEBUG_REDZONE_ARG));
71789deca0aSMateusz Guzik 
7189978bd99SMark Johnston 	vm_domainset_iter_policy_init(&di, ds, &domain, &flags);
7199978bd99SMark Johnston 	do {
72082c174a3SMateusz Guzik 		va = malloc_domain(&size, &indx, mtp, domain, flags);
72189deca0aSMateusz Guzik 	} while (va == NULL && vm_domainset_iter_policy(&di, &domain) == 0);
72282c174a3SMateusz Guzik 	malloc_type_zone_allocated(mtp, va == NULL ? 0 : size, indx);
72382c174a3SMateusz Guzik 	if (__predict_false(va == NULL)) {
72482c174a3SMateusz Guzik 		KASSERT((flags & M_WAITOK) == 0,
72582c174a3SMateusz Guzik 		    ("malloc(M_WAITOK) returned NULL"));
72682c174a3SMateusz Guzik 	}
72782c174a3SMateusz Guzik #ifdef DEBUG_REDZONE
72882c174a3SMateusz Guzik 	if (va != NULL)
72982c174a3SMateusz Guzik 		va = redzone_setup(va, osize);
73082c174a3SMateusz Guzik #endif
73182c174a3SMateusz Guzik 	return (va);
7326d6a03d7SJeff Roberson }
7339978bd99SMark Johnston 
73482c174a3SMateusz Guzik /*
73582c174a3SMateusz Guzik  * Allocate an executable area.
73682c174a3SMateusz Guzik  */
73782c174a3SMateusz Guzik void *
73882c174a3SMateusz Guzik malloc_exec(size_t size, struct malloc_type *mtp, int flags)
73982c174a3SMateusz Guzik {
74082c174a3SMateusz Guzik 
74189deca0aSMateusz Guzik 	return (malloc_domainset_exec(size, mtp, DOMAINSET_RR(), flags));
74282c174a3SMateusz Guzik }
74382c174a3SMateusz Guzik 
74482c174a3SMateusz Guzik void *
74582c174a3SMateusz Guzik malloc_domainset_exec(size_t size, struct malloc_type *mtp, struct domainset *ds,
74682c174a3SMateusz Guzik     int flags)
74782c174a3SMateusz Guzik {
74889deca0aSMateusz Guzik #ifdef DEBUG_REDZONE
74982c174a3SMateusz Guzik 	unsigned long osize = size;
75082c174a3SMateusz Guzik #endif
75189deca0aSMateusz Guzik #ifdef MALLOC_DEBUG
75289deca0aSMateusz Guzik 	caddr_t va;
75389deca0aSMateusz Guzik #endif
75482c174a3SMateusz Guzik 
75582c174a3SMateusz Guzik 	flags |= M_EXEC;
75689deca0aSMateusz Guzik 
75782c174a3SMateusz Guzik #ifdef MALLOC_DEBUG
75882c174a3SMateusz Guzik 	va = NULL;
75982c174a3SMateusz Guzik 	if (malloc_dbg(&va, &size, mtp, flags) != 0)
76082c174a3SMateusz Guzik 		return (va);
76182c174a3SMateusz Guzik #endif
76289deca0aSMateusz Guzik 
76389deca0aSMateusz Guzik 	return (malloc_large(&size, mtp, ds, flags DEBUG_REDZONE_ARG));
7649978bd99SMark Johnston }
7659978bd99SMark Johnston 
7669978bd99SMark Johnston void *
7673b15beb3SKonstantin Belousov malloc_domainset_aligned(size_t size, size_t align,
7683b15beb3SKonstantin Belousov     struct malloc_type *mtp, struct domainset *ds, int flags)
7693b15beb3SKonstantin Belousov {
7703b15beb3SKonstantin Belousov 	void *res;
7713b15beb3SKonstantin Belousov 
7723b15beb3SKonstantin Belousov 	KASSERT(align != 0 && powerof2(align),
7733b15beb3SKonstantin Belousov 	    ("malloc_domainset_aligned: wrong align %#zx size %#zx",
7743b15beb3SKonstantin Belousov 	    align, size));
775*0781c79dSKonstantin Belousov 	KASSERT(align <= PAGE_SIZE,
7763b15beb3SKonstantin Belousov 	    ("malloc_domainset_aligned: align %#zx (size %#zx) too large",
7773b15beb3SKonstantin Belousov 	    align, size));
7783b15beb3SKonstantin Belousov 
7793b15beb3SKonstantin Belousov 	if (size < align)
7803b15beb3SKonstantin Belousov 		size = align;
7813b15beb3SKonstantin Belousov 	res = malloc_domainset(size, mtp, ds, flags);
7823b15beb3SKonstantin Belousov 	KASSERT(res == NULL || ((uintptr_t)res & (align - 1)) == 0,
7833b15beb3SKonstantin Belousov 	    ("malloc_domainset_aligned: result not aligned %p size %#zx "
7843b15beb3SKonstantin Belousov 	    "align %#zx", res, size, align));
7853b15beb3SKonstantin Belousov 	return (res);
7863b15beb3SKonstantin Belousov }
7873b15beb3SKonstantin Belousov 
7883b15beb3SKonstantin Belousov void *
789fd91e076SKristof Provost mallocarray(size_t nmemb, size_t size, struct malloc_type *type, int flags)
790fd91e076SKristof Provost {
791fd91e076SKristof Provost 
792c02fc960SConrad Meyer 	if (WOULD_OVERFLOW(nmemb, size))
793c02fc960SConrad Meyer 		panic("mallocarray: %zu * %zu overflowed", nmemb, size);
794fd91e076SKristof Provost 
795fd91e076SKristof Provost 	return (malloc(size * nmemb, type, flags));
796fd91e076SKristof Provost }
797fd91e076SKristof Provost 
798ab3185d1SJeff Roberson #ifdef INVARIANTS
799ab3185d1SJeff Roberson static void
800ab3185d1SJeff Roberson free_save_type(void *addr, struct malloc_type *mtp, u_long size)
801ab3185d1SJeff Roberson {
802ab3185d1SJeff Roberson 	struct malloc_type **mtpp = addr;
803ab3185d1SJeff Roberson 
804ab3185d1SJeff Roberson 	/*
805ab3185d1SJeff Roberson 	 * Cache a pointer to the malloc_type that most recently freed
806ab3185d1SJeff Roberson 	 * this memory here.  This way we know who is most likely to
807ab3185d1SJeff Roberson 	 * have stepped on it later.
808ab3185d1SJeff Roberson 	 *
809ab3185d1SJeff Roberson 	 * This code assumes that size is a multiple of 8 bytes for
810ab3185d1SJeff Roberson 	 * 64 bit machines
811ab3185d1SJeff Roberson 	 */
812ab3185d1SJeff Roberson 	mtpp = (struct malloc_type **) ((unsigned long)mtpp & ~UMA_ALIGN_PTR);
813ab3185d1SJeff Roberson 	mtpp += (size - sizeof(struct malloc_type *)) /
814ab3185d1SJeff Roberson 	    sizeof(struct malloc_type *);
815ab3185d1SJeff Roberson 	*mtpp = mtp;
816ab3185d1SJeff Roberson }
817ab3185d1SJeff Roberson #endif
818ab3185d1SJeff Roberson 
819ab3185d1SJeff Roberson #ifdef MALLOC_DEBUG
820ab3185d1SJeff Roberson static int
821ab3185d1SJeff Roberson free_dbg(void **addrp, struct malloc_type *mtp)
822ab3185d1SJeff Roberson {
823ab3185d1SJeff Roberson 	void *addr;
824ab3185d1SJeff Roberson 
825ab3185d1SJeff Roberson 	addr = *addrp;
826bdcc2226SMateusz Guzik 	KASSERT(mtp->ks_version == M_VERSION, ("free: bad malloc type version"));
827ab3185d1SJeff Roberson 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
828ab3185d1SJeff Roberson 	    ("free: called with spinlock or critical section held"));
829ab3185d1SJeff Roberson 
830ab3185d1SJeff Roberson 	/* free(NULL, ...) does nothing */
831ab3185d1SJeff Roberson 	if (addr == NULL)
832ab3185d1SJeff Roberson 		return (EJUSTRETURN);
833ab3185d1SJeff Roberson 
834ab3185d1SJeff Roberson #ifdef DEBUG_MEMGUARD
835ab3185d1SJeff Roberson 	if (is_memguard_addr(addr)) {
836ab3185d1SJeff Roberson 		memguard_free(addr);
837ab3185d1SJeff Roberson 		return (EJUSTRETURN);
838ab3185d1SJeff Roberson 	}
839ab3185d1SJeff Roberson #endif
840ab3185d1SJeff Roberson 
841ab3185d1SJeff Roberson #ifdef DEBUG_REDZONE
842ab3185d1SJeff Roberson 	redzone_check(addr);
843ab3185d1SJeff Roberson 	*addrp = redzone_addr_ntor(addr);
844ab3185d1SJeff Roberson #endif
845ab3185d1SJeff Roberson 
846ab3185d1SJeff Roberson 	return (0);
847ab3185d1SJeff Roberson }
848ab3185d1SJeff Roberson #endif
849ab3185d1SJeff Roberson 
850fd91e076SKristof Provost /*
8511c7c3c6aSMatthew Dillon  *	free:
8521c7c3c6aSMatthew Dillon  *
853df8bae1dSRodney W. Grimes  *	Free a block of memory allocated by malloc.
8541c7c3c6aSMatthew Dillon  *
8551c7c3c6aSMatthew Dillon  *	This routine may not block.
856df8bae1dSRodney W. Grimes  */
857df8bae1dSRodney W. Grimes void
85863a7e0a3SRobert Watson free(void *addr, struct malloc_type *mtp)
859df8bae1dSRodney W. Grimes {
860584061b4SJeff Roberson 	uma_zone_t zone;
86199571dc3SJeff Roberson 	uma_slab_t slab;
86299571dc3SJeff Roberson 	u_long size;
863254c6cb3SPoul-Henning Kamp 
864ab3185d1SJeff Roberson #ifdef MALLOC_DEBUG
865ab3185d1SJeff Roberson 	if (free_dbg(&addr, mtp) != 0)
866ab3185d1SJeff Roberson 		return;
867ab3185d1SJeff Roberson #endif
86844a8ff31SArchie Cobbs 	/* free(NULL, ...) does nothing */
86944a8ff31SArchie Cobbs 	if (addr == NULL)
87044a8ff31SArchie Cobbs 		return;
87144a8ff31SArchie Cobbs 
872584061b4SJeff Roberson 	vtozoneslab((vm_offset_t)addr & (~UMA_SLAB_MASK), &zone, &slab);
8738355f576SJeff Roberson 	if (slab == NULL)
8746f267175SJeff Roberson 		panic("free: address %p(%p) has not been allocated.\n",
87599571dc3SJeff Roberson 		    addr, (void *)((u_long)addr & (~UMA_SLAB_MASK)));
87699571dc3SJeff Roberson 
8776d6a03d7SJeff Roberson 	if (__predict_true(!malloc_large_slab(slab))) {
878584061b4SJeff Roberson 		size = zone->uz_size;
8798f70816cSJeff Roberson #ifdef INVARIANTS
880ab3185d1SJeff Roberson 		free_save_type(addr, mtp, size);
8818f70816cSJeff Roberson #endif
882584061b4SJeff Roberson 		uma_zfree_arg(zone, addr, slab);
88314bf02f8SJohn Dyson 	} else {
8846d6a03d7SJeff Roberson 		size = malloc_large_size(slab);
8856d6a03d7SJeff Roberson 		free_large(addr, size);
88614bf02f8SJohn Dyson 	}
88763a7e0a3SRobert Watson 	malloc_type_freed(mtp, size);
888df8bae1dSRodney W. Grimes }
889df8bae1dSRodney W. Grimes 
89045035becSMatt Macy /*
89145035becSMatt Macy  *	zfree:
89245035becSMatt Macy  *
89345035becSMatt Macy  *	Zero then free a block of memory allocated by malloc.
89445035becSMatt Macy  *
89545035becSMatt Macy  *	This routine may not block.
89645035becSMatt Macy  */
89745035becSMatt Macy void
89845035becSMatt Macy zfree(void *addr, struct malloc_type *mtp)
89945035becSMatt Macy {
90045035becSMatt Macy 	uma_zone_t zone;
90145035becSMatt Macy 	uma_slab_t slab;
90245035becSMatt Macy 	u_long size;
90345035becSMatt Macy 
90445035becSMatt Macy #ifdef MALLOC_DEBUG
90545035becSMatt Macy 	if (free_dbg(&addr, mtp) != 0)
90645035becSMatt Macy 		return;
90745035becSMatt Macy #endif
90845035becSMatt Macy 	/* free(NULL, ...) does nothing */
90945035becSMatt Macy 	if (addr == NULL)
91045035becSMatt Macy 		return;
91145035becSMatt Macy 
91245035becSMatt Macy 	vtozoneslab((vm_offset_t)addr & (~UMA_SLAB_MASK), &zone, &slab);
91345035becSMatt Macy 	if (slab == NULL)
91445035becSMatt Macy 		panic("free: address %p(%p) has not been allocated.\n",
91545035becSMatt Macy 		    addr, (void *)((u_long)addr & (~UMA_SLAB_MASK)));
91645035becSMatt Macy 
91745035becSMatt Macy 	if (__predict_true(!malloc_large_slab(slab))) {
91845035becSMatt Macy 		size = zone->uz_size;
91945035becSMatt Macy #ifdef INVARIANTS
92045035becSMatt Macy 		free_save_type(addr, mtp, size);
92145035becSMatt Macy #endif
92245035becSMatt Macy 		explicit_bzero(addr, size);
92345035becSMatt Macy 		uma_zfree_arg(zone, addr, slab);
92445035becSMatt Macy 	} else {
92545035becSMatt Macy 		size = malloc_large_size(slab);
92645035becSMatt Macy 		explicit_bzero(addr, size);
92745035becSMatt Macy 		free_large(addr, size);
92845035becSMatt Macy 	}
92945035becSMatt Macy 	malloc_type_freed(mtp, size);
93045035becSMatt Macy }
93145035becSMatt Macy 
932df8bae1dSRodney W. Grimes /*
93344a8ff31SArchie Cobbs  *	realloc: change the size of a memory block
93444a8ff31SArchie Cobbs  */
93544a8ff31SArchie Cobbs void *
936bd555da9SConrad Meyer realloc(void *addr, size_t size, struct malloc_type *mtp, int flags)
93744a8ff31SArchie Cobbs {
938584061b4SJeff Roberson 	uma_zone_t zone;
9398355f576SJeff Roberson 	uma_slab_t slab;
94044a8ff31SArchie Cobbs 	unsigned long alloc;
94144a8ff31SArchie Cobbs 	void *newaddr;
94244a8ff31SArchie Cobbs 
943bdcc2226SMateusz Guzik 	KASSERT(mtp->ks_version == M_VERSION,
944bdcc2226SMateusz Guzik 	    ("realloc: bad malloc type version"));
945d9e2e68dSMark Johnston 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
9461067a2baSJonathan T. Looney 	    ("realloc: called with spinlock or critical section held"));
9471067a2baSJonathan T. Looney 
94844a8ff31SArchie Cobbs 	/* realloc(NULL, ...) is equivalent to malloc(...) */
94944a8ff31SArchie Cobbs 	if (addr == NULL)
95063a7e0a3SRobert Watson 		return (malloc(size, mtp, flags));
95163a7e0a3SRobert Watson 
95263a7e0a3SRobert Watson 	/*
95363a7e0a3SRobert Watson 	 * XXX: Should report free of old memory and alloc of new memory to
95463a7e0a3SRobert Watson 	 * per-CPU stats.
95563a7e0a3SRobert Watson 	 */
95644a8ff31SArchie Cobbs 
957e4eb384bSBosko Milekic #ifdef DEBUG_MEMGUARD
9586d3ed393SMatthew D Fleming 	if (is_memguard_addr(addr))
9596d3ed393SMatthew D Fleming 		return (memguard_realloc(addr, size, mtp, flags));
960e4eb384bSBosko Milekic #endif
961e4eb384bSBosko Milekic 
962847a2a17SPawel Jakub Dawidek #ifdef DEBUG_REDZONE
963847a2a17SPawel Jakub Dawidek 	slab = NULL;
964b476ae7fSJeff Roberson 	zone = NULL;
965847a2a17SPawel Jakub Dawidek 	alloc = redzone_get_size(addr);
966847a2a17SPawel Jakub Dawidek #else
967584061b4SJeff Roberson 	vtozoneslab((vm_offset_t)addr & (~UMA_SLAB_MASK), &zone, &slab);
9688355f576SJeff Roberson 
96944a8ff31SArchie Cobbs 	/* Sanity check */
9708355f576SJeff Roberson 	KASSERT(slab != NULL,
97144a8ff31SArchie Cobbs 	    ("realloc: address %p out of range", (void *)addr));
97244a8ff31SArchie Cobbs 
97344a8ff31SArchie Cobbs 	/* Get the size of the original block */
9746d6a03d7SJeff Roberson 	if (!malloc_large_slab(slab))
975584061b4SJeff Roberson 		alloc = zone->uz_size;
9768355f576SJeff Roberson 	else
9776d6a03d7SJeff Roberson 		alloc = malloc_large_size(slab);
97844a8ff31SArchie Cobbs 
97944a8ff31SArchie Cobbs 	/* Reuse the original block if appropriate */
98044a8ff31SArchie Cobbs 	if (size <= alloc
98144a8ff31SArchie Cobbs 	    && (size > (alloc >> REALLOC_FRACTION) || alloc == MINALLOCSIZE))
98244a8ff31SArchie Cobbs 		return (addr);
983847a2a17SPawel Jakub Dawidek #endif /* !DEBUG_REDZONE */
98444a8ff31SArchie Cobbs 
98544a8ff31SArchie Cobbs 	/* Allocate a new, bigger (or smaller) block */
98663a7e0a3SRobert Watson 	if ((newaddr = malloc(size, mtp, flags)) == NULL)
98744a8ff31SArchie Cobbs 		return (NULL);
98844a8ff31SArchie Cobbs 
98944a8ff31SArchie Cobbs 	/* Copy over original contents */
99044a8ff31SArchie Cobbs 	bcopy(addr, newaddr, min(size, alloc));
99163a7e0a3SRobert Watson 	free(addr, mtp);
99244a8ff31SArchie Cobbs 	return (newaddr);
99344a8ff31SArchie Cobbs }
99444a8ff31SArchie Cobbs 
99544a8ff31SArchie Cobbs /*
99644a8ff31SArchie Cobbs  *	reallocf: same as realloc() but free memory on failure.
99744a8ff31SArchie Cobbs  */
99844a8ff31SArchie Cobbs void *
999bd555da9SConrad Meyer reallocf(void *addr, size_t size, struct malloc_type *mtp, int flags)
100044a8ff31SArchie Cobbs {
100144a8ff31SArchie Cobbs 	void *mem;
100244a8ff31SArchie Cobbs 
100363a7e0a3SRobert Watson 	if ((mem = realloc(addr, size, mtp, flags)) == NULL)
100463a7e0a3SRobert Watson 		free(addr, mtp);
100544a8ff31SArchie Cobbs 	return (mem);
100644a8ff31SArchie Cobbs }
100744a8ff31SArchie Cobbs 
10085d4bf057SVladimir Kondratyev /*
100916b971edSMateusz Guzik  * 	malloc_size: returns the number of bytes allocated for a request of the
101016b971edSMateusz Guzik  * 		     specified size
101116b971edSMateusz Guzik  */
101216b971edSMateusz Guzik size_t
101316b971edSMateusz Guzik malloc_size(size_t size)
101416b971edSMateusz Guzik {
101516b971edSMateusz Guzik 	int indx;
101616b971edSMateusz Guzik 
101716b971edSMateusz Guzik 	if (size > kmem_zmax)
101816b971edSMateusz Guzik 		return (0);
101916b971edSMateusz Guzik 	if (size & KMEM_ZMASK)
102016b971edSMateusz Guzik 		size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;
102116b971edSMateusz Guzik 	indx = kmemsize[size >> KMEM_ZSHIFT];
102216b971edSMateusz Guzik 	return (kmemzones[indx].kz_size);
102316b971edSMateusz Guzik }
102416b971edSMateusz Guzik 
102516b971edSMateusz Guzik /*
10265d4bf057SVladimir Kondratyev  *	malloc_usable_size: returns the usable size of the allocation.
10275d4bf057SVladimir Kondratyev  */
10285d4bf057SVladimir Kondratyev size_t
10295d4bf057SVladimir Kondratyev malloc_usable_size(const void *addr)
10305d4bf057SVladimir Kondratyev {
10315d4bf057SVladimir Kondratyev #ifndef DEBUG_REDZONE
10325d4bf057SVladimir Kondratyev 	uma_zone_t zone;
10335d4bf057SVladimir Kondratyev 	uma_slab_t slab;
10345d4bf057SVladimir Kondratyev #endif
10355d4bf057SVladimir Kondratyev 	u_long size;
10365d4bf057SVladimir Kondratyev 
10375d4bf057SVladimir Kondratyev 	if (addr == NULL)
10385d4bf057SVladimir Kondratyev 		return (0);
10395d4bf057SVladimir Kondratyev 
10405d4bf057SVladimir Kondratyev #ifdef DEBUG_MEMGUARD
10415d4bf057SVladimir Kondratyev 	if (is_memguard_addr(__DECONST(void *, addr)))
10425d4bf057SVladimir Kondratyev 		return (memguard_get_req_size(addr));
10435d4bf057SVladimir Kondratyev #endif
10445d4bf057SVladimir Kondratyev 
10455d4bf057SVladimir Kondratyev #ifdef DEBUG_REDZONE
10465d4bf057SVladimir Kondratyev 	size = redzone_get_size(__DECONST(void *, addr));
10475d4bf057SVladimir Kondratyev #else
10485d4bf057SVladimir Kondratyev 	vtozoneslab((vm_offset_t)addr & (~UMA_SLAB_MASK), &zone, &slab);
10495d4bf057SVladimir Kondratyev 	if (slab == NULL)
10505d4bf057SVladimir Kondratyev 		panic("malloc_usable_size: address %p(%p) is not allocated.\n",
10515d4bf057SVladimir Kondratyev 		    addr, (void *)((u_long)addr & (~UMA_SLAB_MASK)));
10525d4bf057SVladimir Kondratyev 
10535d4bf057SVladimir Kondratyev 	if (!malloc_large_slab(slab))
10545d4bf057SVladimir Kondratyev 		size = zone->uz_size;
10555d4bf057SVladimir Kondratyev 	else
10565d4bf057SVladimir Kondratyev 		size = malloc_large_size(slab);
10575d4bf057SVladimir Kondratyev #endif
10585d4bf057SVladimir Kondratyev 	return (size);
10595d4bf057SVladimir Kondratyev }
10605d4bf057SVladimir Kondratyev 
1061c70af487SAlan Cox CTASSERT(VM_KMEM_SIZE_SCALE >= 1);
1062c70af487SAlan Cox 
10635df87b21SJeff Roberson /*
1064c70af487SAlan Cox  * Initialize the kernel memory (kmem) arena.
10655df87b21SJeff Roberson  */
10665df87b21SJeff Roberson void
10675df87b21SJeff Roberson kmeminit(void)
10685df87b21SJeff Roberson {
1069af3b2549SHans Petter Selasky 	u_long mem_size;
1070af3b2549SHans Petter Selasky 	u_long tmp;
107169ef67f9SJason Evans 
1072af3b2549SHans Petter Selasky #ifdef VM_KMEM_SIZE
1073af3b2549SHans Petter Selasky 	if (vm_kmem_size == 0)
1074af3b2549SHans Petter Selasky 		vm_kmem_size = VM_KMEM_SIZE;
1075af3b2549SHans Petter Selasky #endif
1076af3b2549SHans Petter Selasky #ifdef VM_KMEM_SIZE_MIN
1077af3b2549SHans Petter Selasky 	if (vm_kmem_size_min == 0)
1078af3b2549SHans Petter Selasky 		vm_kmem_size_min = VM_KMEM_SIZE_MIN;
1079af3b2549SHans Petter Selasky #endif
1080af3b2549SHans Petter Selasky #ifdef VM_KMEM_SIZE_MAX
1081af3b2549SHans Petter Selasky 	if (vm_kmem_size_max == 0)
1082af3b2549SHans Petter Selasky 		vm_kmem_size_max = VM_KMEM_SIZE_MAX;
1083af3b2549SHans Petter Selasky #endif
10848a58a9f6SJohn Dyson 	/*
1085c70af487SAlan Cox 	 * Calculate the amount of kernel virtual address (KVA) space that is
1086c70af487SAlan Cox 	 * preallocated to the kmem arena.  In order to support a wide range
1087c70af487SAlan Cox 	 * of machines, it is a function of the physical memory size,
1088c70af487SAlan Cox 	 * specifically,
10898a58a9f6SJohn Dyson 	 *
1090c70af487SAlan Cox 	 *	min(max(physical memory size / VM_KMEM_SIZE_SCALE,
1091c70af487SAlan Cox 	 *	    VM_KMEM_SIZE_MIN), VM_KMEM_SIZE_MAX)
1092c70af487SAlan Cox 	 *
1093c70af487SAlan Cox 	 * Every architecture must define an integral value for
1094c70af487SAlan Cox 	 * VM_KMEM_SIZE_SCALE.  However, the definitions of VM_KMEM_SIZE_MIN
1095c70af487SAlan Cox 	 * and VM_KMEM_SIZE_MAX, which represent respectively the floor and
1096c70af487SAlan Cox 	 * ceiling on this preallocation, are optional.  Typically,
1097c70af487SAlan Cox 	 * VM_KMEM_SIZE_MAX is itself a function of the available KVA space on
1098c70af487SAlan Cox 	 * a given architecture.
10998a58a9f6SJohn Dyson 	 */
110044f1c916SBryan Drewery 	mem_size = vm_cnt.v_page_count;
11017c51714eSSean Bruno 	if (mem_size <= 32768) /* delphij XXX 128MB */
11027c51714eSSean Bruno 		kmem_zmax = PAGE_SIZE;
11038a58a9f6SJohn Dyson 
1104c70af487SAlan Cox 	if (vm_kmem_size_scale < 1)
1105c70af487SAlan Cox 		vm_kmem_size_scale = VM_KMEM_SIZE_SCALE;
1106c70af487SAlan Cox 
1107af3b2549SHans Petter Selasky 	/*
1108af3b2549SHans Petter Selasky 	 * Check if we should use defaults for the "vm_kmem_size"
1109af3b2549SHans Petter Selasky 	 * variable:
1110af3b2549SHans Petter Selasky 	 */
1111af3b2549SHans Petter Selasky 	if (vm_kmem_size == 0) {
111228b740daSKonstantin Belousov 		vm_kmem_size = mem_size / vm_kmem_size_scale;
111328b740daSKonstantin Belousov 		vm_kmem_size = vm_kmem_size * PAGE_SIZE < vm_kmem_size ?
111428b740daSKonstantin Belousov 		    vm_kmem_size_max : vm_kmem_size * PAGE_SIZE;
1115c70af487SAlan Cox 		if (vm_kmem_size_min > 0 && vm_kmem_size < vm_kmem_size_min)
11160e5179e4SStephane E. Potvin 			vm_kmem_size = vm_kmem_size_min;
1117479439b4SDag-Erling Smørgrav 		if (vm_kmem_size_max > 0 && vm_kmem_size >= vm_kmem_size_max)
1118479439b4SDag-Erling Smørgrav 			vm_kmem_size = vm_kmem_size_max;
1119af3b2549SHans Petter Selasky 	}
112028b740daSKonstantin Belousov 	if (vm_kmem_size == 0)
112128b740daSKonstantin Belousov 		panic("Tune VM_KMEM_SIZE_* for the platform");
11228a58a9f6SJohn Dyson 
112327b8623fSDavid Greenman 	/*
1124af3b2549SHans Petter Selasky 	 * The amount of KVA space that is preallocated to the
1125c70af487SAlan Cox 	 * kmem arena can be set statically at compile-time or manually
1126c70af487SAlan Cox 	 * through the kernel environment.  However, it is still limited to
1127c70af487SAlan Cox 	 * twice the physical memory size, which has been sufficient to handle
1128c70af487SAlan Cox 	 * the most severe cases of external fragmentation in the kmem arena.
112927b8623fSDavid Greenman 	 */
1130c749c003SAlan Cox 	if (vm_kmem_size / 2 / PAGE_SIZE > mem_size)
1131c749c003SAlan Cox 		vm_kmem_size = 2 * mem_size * PAGE_SIZE;
11328a58a9f6SJohn Dyson 
1133e137643eSOlivier Houchard 	vm_kmem_size = round_page(vm_kmem_size);
1134e3813573SMatthew D Fleming #ifdef DEBUG_MEMGUARD
1135f806cdcfSMatthew D Fleming 	tmp = memguard_fudge(vm_kmem_size, kernel_map);
1136e3813573SMatthew D Fleming #else
1137e3813573SMatthew D Fleming 	tmp = vm_kmem_size;
1138e3813573SMatthew D Fleming #endif
11392e47807cSJeff Roberson 	uma_set_limit(tmp);
11408355f576SJeff Roberson 
1141e4eb384bSBosko Milekic #ifdef DEBUG_MEMGUARD
1142e4eb384bSBosko Milekic 	/*
1143e4eb384bSBosko Milekic 	 * Initialize MemGuard if support compiled in.  MemGuard is a
1144e4eb384bSBosko Milekic 	 * replacement allocator used for detecting tamper-after-free
1145e4eb384bSBosko Milekic 	 * scenarios as they occur.  It is only used for debugging.
1146e4eb384bSBosko Milekic 	 */
11472e47807cSJeff Roberson 	memguard_init(kernel_arena);
1148e4eb384bSBosko Milekic #endif
11495df87b21SJeff Roberson }
11505df87b21SJeff Roberson 
11515df87b21SJeff Roberson /*
11525df87b21SJeff Roberson  * Initialize the kernel memory allocator
11535df87b21SJeff Roberson  */
11545df87b21SJeff Roberson /* ARGSUSED*/
11555df87b21SJeff Roberson static void
11565df87b21SJeff Roberson mallocinit(void *dummy)
11575df87b21SJeff Roberson {
11585df87b21SJeff Roberson 	int i;
11595df87b21SJeff Roberson 	uint8_t indx;
11605df87b21SJeff Roberson 
11615df87b21SJeff Roberson 	mtx_init(&malloc_mtx, "malloc", NULL, MTX_DEF);
11625df87b21SJeff Roberson 
11635df87b21SJeff Roberson 	kmeminit();
1164e4eb384bSBosko Milekic 
11657001d850SXin LI 	if (kmem_zmax < PAGE_SIZE || kmem_zmax > KMEM_ZMAX)
11667001d850SXin LI 		kmem_zmax = KMEM_ZMAX;
11677001d850SXin LI 
11686f267175SJeff Roberson 	for (i = 0, indx = 0; kmemzones[indx].kz_size != 0; indx++) {
11696f267175SJeff Roberson 		int size = kmemzones[indx].kz_size;
1170eaa17d42SRyan Libby 		const char *name = kmemzones[indx].kz_name;
11713b15beb3SKonstantin Belousov 		size_t align;
1172d7854da1SMatthew D Fleming 		int subzone;
11738355f576SJeff Roberson 
11743b15beb3SKonstantin Belousov 		align = UMA_ALIGN_PTR;
11753b15beb3SKonstantin Belousov 		if (powerof2(size) && size > sizeof(void *))
1176*0781c79dSKonstantin Belousov 			align = MIN(size, PAGE_SIZE) - 1;
1177d7854da1SMatthew D Fleming 		for (subzone = 0; subzone < numzones; subzone++) {
1178d7854da1SMatthew D Fleming 			kmemzones[indx].kz_zone[subzone] =
1179d7854da1SMatthew D Fleming 			    uma_zcreate(name, size,
11808efc4effSJeff Roberson #ifdef INVARIANTS
11818f70816cSJeff Roberson 			    mtrash_ctor, mtrash_dtor, mtrash_init, mtrash_fini,
11828efc4effSJeff Roberson #else
11838efc4effSJeff Roberson 			    NULL, NULL, NULL, NULL,
11848efc4effSJeff Roberson #endif
11853b15beb3SKonstantin Belousov 			    align, UMA_ZONE_MALLOC);
1186d7854da1SMatthew D Fleming 		}
11878355f576SJeff Roberson 		for (;i <= size; i+= KMEM_ZBASE)
11886f267175SJeff Roberson 			kmemsize[i >> KMEM_ZSHIFT] = indx;
1189df8bae1dSRodney W. Grimes 	}
1190254c6cb3SPoul-Henning Kamp }
1191af3b2549SHans Petter Selasky SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_SECOND, mallocinit, NULL);
1192254c6cb3SPoul-Henning Kamp 
1193db669378SPeter Wemm void
119487efd4d5SRobert Watson malloc_init(void *data)
1195254c6cb3SPoul-Henning Kamp {
119663a7e0a3SRobert Watson 	struct malloc_type_internal *mtip;
119763a7e0a3SRobert Watson 	struct malloc_type *mtp;
119863a7e0a3SRobert Watson 
119944f1c916SBryan Drewery 	KASSERT(vm_cnt.v_page_count != 0, ("malloc_register before vm_init"));
120063a7e0a3SRobert Watson 
120163a7e0a3SRobert Watson 	mtp = data;
1202bdcc2226SMateusz Guzik 	if (mtp->ks_version != M_VERSION)
1203e25d8b67SMateusz Guzik 		panic("malloc_init: type %s with unsupported version %lu",
1204e25d8b67SMateusz Guzik 		    mtp->ks_shortdesc, mtp->ks_version);
1205bb1c7df8SRobert Watson 
1206bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
12078e6526e9SMateusz Guzik 	mtip->mti_stats = uma_zalloc_pcpu(pcpu_zone_64, M_WAITOK | M_ZERO);
1208c9e05ccdSMateusz Guzik 	mtp_set_subzone(mtp);
1209254c6cb3SPoul-Henning Kamp 
12106f267175SJeff Roberson 	mtx_lock(&malloc_mtx);
121163a7e0a3SRobert Watson 	mtp->ks_next = kmemstatistics;
121263a7e0a3SRobert Watson 	kmemstatistics = mtp;
1213cd814b26SRobert Watson 	kmemcount++;
12146f267175SJeff Roberson 	mtx_unlock(&malloc_mtx);
1215df8bae1dSRodney W. Grimes }
1216db669378SPeter Wemm 
1217db669378SPeter Wemm void
121887efd4d5SRobert Watson malloc_uninit(void *data)
1219db669378SPeter Wemm {
122063a7e0a3SRobert Watson 	struct malloc_type_internal *mtip;
12212a143d5bSPawel Jakub Dawidek 	struct malloc_type_stats *mtsp;
122263a7e0a3SRobert Watson 	struct malloc_type *mtp, *temp;
12232a143d5bSPawel Jakub Dawidek 	long temp_allocs, temp_bytes;
12242a143d5bSPawel Jakub Dawidek 	int i;
1225db669378SPeter Wemm 
122663a7e0a3SRobert Watson 	mtp = data;
1227bdcc2226SMateusz Guzik 	KASSERT(mtp->ks_version == M_VERSION,
1228bdcc2226SMateusz Guzik 	    ("malloc_uninit: bad malloc type version"));
1229bb1c7df8SRobert Watson 
12306f267175SJeff Roberson 	mtx_lock(&malloc_mtx);
1231bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
123263a7e0a3SRobert Watson 	if (mtp != kmemstatistics) {
123363a7e0a3SRobert Watson 		for (temp = kmemstatistics; temp != NULL;
123463a7e0a3SRobert Watson 		    temp = temp->ks_next) {
1235f121baaaSBrian Somers 			if (temp->ks_next == mtp) {
123663a7e0a3SRobert Watson 				temp->ks_next = mtp->ks_next;
1237f121baaaSBrian Somers 				break;
1238db669378SPeter Wemm 			}
1239f121baaaSBrian Somers 		}
1240f121baaaSBrian Somers 		KASSERT(temp,
1241f121baaaSBrian Somers 		    ("malloc_uninit: type '%s' not found", mtp->ks_shortdesc));
124263a7e0a3SRobert Watson 	} else
124363a7e0a3SRobert Watson 		kmemstatistics = mtp->ks_next;
1244cd814b26SRobert Watson 	kmemcount--;
12456f267175SJeff Roberson 	mtx_unlock(&malloc_mtx);
12462a143d5bSPawel Jakub Dawidek 
12472a143d5bSPawel Jakub Dawidek 	/*
12482a143d5bSPawel Jakub Dawidek 	 * Look for memory leaks.
12492a143d5bSPawel Jakub Dawidek 	 */
12502a143d5bSPawel Jakub Dawidek 	temp_allocs = temp_bytes = 0;
12519afff6b1SMateusz Guzik 	for (i = 0; i <= mp_maxid; i++) {
12529afff6b1SMateusz Guzik 		mtsp = zpcpu_get_cpu(mtip->mti_stats, i);
12532a143d5bSPawel Jakub Dawidek 		temp_allocs += mtsp->mts_numallocs;
12542a143d5bSPawel Jakub Dawidek 		temp_allocs -= mtsp->mts_numfrees;
12552a143d5bSPawel Jakub Dawidek 		temp_bytes += mtsp->mts_memalloced;
12562a143d5bSPawel Jakub Dawidek 		temp_bytes -= mtsp->mts_memfreed;
12572a143d5bSPawel Jakub Dawidek 	}
12582a143d5bSPawel Jakub Dawidek 	if (temp_allocs > 0 || temp_bytes > 0) {
12592a143d5bSPawel Jakub Dawidek 		printf("Warning: memory type %s leaked memory on destroy "
12602a143d5bSPawel Jakub Dawidek 		    "(%ld allocations, %ld bytes leaked).\n", mtp->ks_shortdesc,
12612a143d5bSPawel Jakub Dawidek 		    temp_allocs, temp_bytes);
12622a143d5bSPawel Jakub Dawidek 	}
12632a143d5bSPawel Jakub Dawidek 
12648e6526e9SMateusz Guzik 	uma_zfree_pcpu(pcpu_zone_64, mtip->mti_stats);
1265db669378SPeter Wemm }
12666f267175SJeff Roberson 
1267d362c40dSPawel Jakub Dawidek struct malloc_type *
1268d362c40dSPawel Jakub Dawidek malloc_desc2type(const char *desc)
1269d362c40dSPawel Jakub Dawidek {
1270d362c40dSPawel Jakub Dawidek 	struct malloc_type *mtp;
1271d362c40dSPawel Jakub Dawidek 
1272d362c40dSPawel Jakub Dawidek 	mtx_assert(&malloc_mtx, MA_OWNED);
1273d362c40dSPawel Jakub Dawidek 	for (mtp = kmemstatistics; mtp != NULL; mtp = mtp->ks_next) {
1274d362c40dSPawel Jakub Dawidek 		if (strcmp(mtp->ks_shortdesc, desc) == 0)
1275d362c40dSPawel Jakub Dawidek 			return (mtp);
1276d362c40dSPawel Jakub Dawidek 	}
1277d362c40dSPawel Jakub Dawidek 	return (NULL);
1278d362c40dSPawel Jakub Dawidek }
1279d362c40dSPawel Jakub Dawidek 
12806f267175SJeff Roberson static int
1281cd814b26SRobert Watson sysctl_kern_malloc_stats(SYSCTL_HANDLER_ARGS)
1282cd814b26SRobert Watson {
1283cd814b26SRobert Watson 	struct malloc_type_stream_header mtsh;
1284cd814b26SRobert Watson 	struct malloc_type_internal *mtip;
12859afff6b1SMateusz Guzik 	struct malloc_type_stats *mtsp, zeromts;
1286cd814b26SRobert Watson 	struct malloc_type_header mth;
1287cd814b26SRobert Watson 	struct malloc_type *mtp;
12884e657159SMatthew D Fleming 	int error, i;
1289cd814b26SRobert Watson 	struct sbuf sbuf;
1290cd814b26SRobert Watson 
129100f0e671SMatthew D Fleming 	error = sysctl_wire_old_buffer(req, 0);
129200f0e671SMatthew D Fleming 	if (error != 0)
129300f0e671SMatthew D Fleming 		return (error);
12944e657159SMatthew D Fleming 	sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
12951eafc078SIan Lepore 	sbuf_clear_flags(&sbuf, SBUF_INCLUDENUL);
1296cd814b26SRobert Watson 	mtx_lock(&malloc_mtx);
1297cd814b26SRobert Watson 
12989afff6b1SMateusz Guzik 	bzero(&zeromts, sizeof(zeromts));
12999afff6b1SMateusz Guzik 
1300cd814b26SRobert Watson 	/*
1301cd814b26SRobert Watson 	 * Insert stream header.
1302cd814b26SRobert Watson 	 */
1303cd814b26SRobert Watson 	bzero(&mtsh, sizeof(mtsh));
1304cd814b26SRobert Watson 	mtsh.mtsh_version = MALLOC_TYPE_STREAM_VERSION;
1305cd814b26SRobert Watson 	mtsh.mtsh_maxcpus = MAXCPU;
1306cd814b26SRobert Watson 	mtsh.mtsh_count = kmemcount;
13074e657159SMatthew D Fleming 	(void)sbuf_bcat(&sbuf, &mtsh, sizeof(mtsh));
1308cd814b26SRobert Watson 
1309cd814b26SRobert Watson 	/*
1310cd814b26SRobert Watson 	 * Insert alternating sequence of type headers and type statistics.
1311cd814b26SRobert Watson 	 */
1312cd814b26SRobert Watson 	for (mtp = kmemstatistics; mtp != NULL; mtp = mtp->ks_next) {
1313bdcc2226SMateusz Guzik 		mtip = &mtp->ks_mti;
1314cd814b26SRobert Watson 
1315cd814b26SRobert Watson 		/*
1316cd814b26SRobert Watson 		 * Insert type header.
1317cd814b26SRobert Watson 		 */
1318cd814b26SRobert Watson 		bzero(&mth, sizeof(mth));
1319cd814b26SRobert Watson 		strlcpy(mth.mth_name, mtp->ks_shortdesc, MALLOC_MAX_NAME);
13204e657159SMatthew D Fleming 		(void)sbuf_bcat(&sbuf, &mth, sizeof(mth));
1321cd814b26SRobert Watson 
1322cd814b26SRobert Watson 		/*
1323cd814b26SRobert Watson 		 * Insert type statistics for each CPU.
1324cd814b26SRobert Watson 		 */
13259afff6b1SMateusz Guzik 		for (i = 0; i <= mp_maxid; i++) {
13269afff6b1SMateusz Guzik 			mtsp = zpcpu_get_cpu(mtip->mti_stats, i);
13279afff6b1SMateusz Guzik 			(void)sbuf_bcat(&sbuf, mtsp, sizeof(*mtsp));
1328cd814b26SRobert Watson 		}
13299afff6b1SMateusz Guzik 		/*
13309afff6b1SMateusz Guzik 		 * Fill in the missing CPUs.
13319afff6b1SMateusz Guzik 		 */
13329afff6b1SMateusz Guzik 		for (; i < MAXCPU; i++) {
13339afff6b1SMateusz Guzik 			(void)sbuf_bcat(&sbuf, &zeromts, sizeof(zeromts));
13349afff6b1SMateusz Guzik 		}
1335cd814b26SRobert Watson 	}
1336cd814b26SRobert Watson 	mtx_unlock(&malloc_mtx);
13374e657159SMatthew D Fleming 	error = sbuf_finish(&sbuf);
1338cd814b26SRobert Watson 	sbuf_delete(&sbuf);
1339cd814b26SRobert Watson 	return (error);
1340cd814b26SRobert Watson }
1341cd814b26SRobert Watson 
13427029da5cSPawel Biernacki SYSCTL_PROC(_kern, OID_AUTO, malloc_stats,
13437029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRUCT | CTLFLAG_MPSAFE, 0, 0,
13447029da5cSPawel Biernacki     sysctl_kern_malloc_stats, "s,malloc_type_ustats",
1345cd814b26SRobert Watson     "Return malloc types");
1346cd814b26SRobert Watson 
1347cd814b26SRobert Watson SYSCTL_INT(_kern, OID_AUTO, malloc_count, CTLFLAG_RD, &kmemcount, 0,
1348cd814b26SRobert Watson     "Count of kernel malloc types");
1349cd814b26SRobert Watson 
135091dd776cSJohn Birrell void
135191dd776cSJohn Birrell malloc_type_list(malloc_type_list_func_t *func, void *arg)
135291dd776cSJohn Birrell {
135391dd776cSJohn Birrell 	struct malloc_type *mtp, **bufmtp;
135491dd776cSJohn Birrell 	int count, i;
135591dd776cSJohn Birrell 	size_t buflen;
135691dd776cSJohn Birrell 
135791dd776cSJohn Birrell 	mtx_lock(&malloc_mtx);
135891dd776cSJohn Birrell restart:
135991dd776cSJohn Birrell 	mtx_assert(&malloc_mtx, MA_OWNED);
136091dd776cSJohn Birrell 	count = kmemcount;
136191dd776cSJohn Birrell 	mtx_unlock(&malloc_mtx);
136291dd776cSJohn Birrell 
136391dd776cSJohn Birrell 	buflen = sizeof(struct malloc_type *) * count;
136491dd776cSJohn Birrell 	bufmtp = malloc(buflen, M_TEMP, M_WAITOK);
136591dd776cSJohn Birrell 
136691dd776cSJohn Birrell 	mtx_lock(&malloc_mtx);
136791dd776cSJohn Birrell 
136891dd776cSJohn Birrell 	if (count < kmemcount) {
136991dd776cSJohn Birrell 		free(bufmtp, M_TEMP);
137091dd776cSJohn Birrell 		goto restart;
137191dd776cSJohn Birrell 	}
137291dd776cSJohn Birrell 
137391dd776cSJohn Birrell 	for (mtp = kmemstatistics, i = 0; mtp != NULL; mtp = mtp->ks_next, i++)
137491dd776cSJohn Birrell 		bufmtp[i] = mtp;
137591dd776cSJohn Birrell 
137691dd776cSJohn Birrell 	mtx_unlock(&malloc_mtx);
137791dd776cSJohn Birrell 
137891dd776cSJohn Birrell 	for (i = 0; i < count; i++)
137991dd776cSJohn Birrell 		(func)(bufmtp[i], arg);
138091dd776cSJohn Birrell 
138191dd776cSJohn Birrell 	free(bufmtp, M_TEMP);
138291dd776cSJohn Birrell }
138391dd776cSJohn Birrell 
1384909ed16cSRobert Watson #ifdef DDB
138546d70077SConrad Meyer static int64_t
138646d70077SConrad Meyer get_malloc_stats(const struct malloc_type_internal *mtip, uint64_t *allocs,
138746d70077SConrad Meyer     uint64_t *inuse)
1388909ed16cSRobert Watson {
138946d70077SConrad Meyer 	const struct malloc_type_stats *mtsp;
139046d70077SConrad Meyer 	uint64_t frees, alloced, freed;
1391909ed16cSRobert Watson 	int i;
1392909ed16cSRobert Watson 
139346d70077SConrad Meyer 	*allocs = 0;
1394909ed16cSRobert Watson 	frees = 0;
139524076d13SRobert Watson 	alloced = 0;
139624076d13SRobert Watson 	freed = 0;
13979afff6b1SMateusz Guzik 	for (i = 0; i <= mp_maxid; i++) {
13989afff6b1SMateusz Guzik 		mtsp = zpcpu_get_cpu(mtip->mti_stats, i);
139946d70077SConrad Meyer 
140046d70077SConrad Meyer 		*allocs += mtsp->mts_numallocs;
140126e9d9b0SMark Johnston 		frees += mtsp->mts_numfrees;
140226e9d9b0SMark Johnston 		alloced += mtsp->mts_memalloced;
140326e9d9b0SMark Johnston 		freed += mtsp->mts_memfreed;
1404909ed16cSRobert Watson 	}
140546d70077SConrad Meyer 	*inuse = *allocs - frees;
140646d70077SConrad Meyer 	return (alloced - freed);
140746d70077SConrad Meyer }
140846d70077SConrad Meyer 
140946d70077SConrad Meyer DB_SHOW_COMMAND(malloc, db_show_malloc)
141046d70077SConrad Meyer {
141146d70077SConrad Meyer 	const char *fmt_hdr, *fmt_entry;
141246d70077SConrad Meyer 	struct malloc_type *mtp;
141346d70077SConrad Meyer 	uint64_t allocs, inuse;
141446d70077SConrad Meyer 	int64_t size;
141546d70077SConrad Meyer 	/* variables for sorting */
141646d70077SConrad Meyer 	struct malloc_type *last_mtype, *cur_mtype;
141746d70077SConrad Meyer 	int64_t cur_size, last_size;
141846d70077SConrad Meyer 	int ties;
141946d70077SConrad Meyer 
142046d70077SConrad Meyer 	if (modif[0] == 'i') {
142146d70077SConrad Meyer 		fmt_hdr = "%s,%s,%s,%s\n";
142246d70077SConrad Meyer 		fmt_entry = "\"%s\",%ju,%jdK,%ju\n";
142346d70077SConrad Meyer 	} else {
142446d70077SConrad Meyer 		fmt_hdr = "%18s %12s  %12s %12s\n";
142546d70077SConrad Meyer 		fmt_entry = "%18s %12ju %12jdK %12ju\n";
142646d70077SConrad Meyer 	}
142746d70077SConrad Meyer 
142846d70077SConrad Meyer 	db_printf(fmt_hdr, "Type", "InUse", "MemUse", "Requests");
142946d70077SConrad Meyer 
143046d70077SConrad Meyer 	/* Select sort, largest size first. */
143146d70077SConrad Meyer 	last_mtype = NULL;
143246d70077SConrad Meyer 	last_size = INT64_MAX;
143346d70077SConrad Meyer 	for (;;) {
143446d70077SConrad Meyer 		cur_mtype = NULL;
143546d70077SConrad Meyer 		cur_size = -1;
143646d70077SConrad Meyer 		ties = 0;
143746d70077SConrad Meyer 
143846d70077SConrad Meyer 		for (mtp = kmemstatistics; mtp != NULL; mtp = mtp->ks_next) {
143946d70077SConrad Meyer 			/*
144046d70077SConrad Meyer 			 * In the case of size ties, print out mtypes
144146d70077SConrad Meyer 			 * in the order they are encountered.  That is,
144246d70077SConrad Meyer 			 * when we encounter the most recently output
144346d70077SConrad Meyer 			 * mtype, we have already printed all preceding
144446d70077SConrad Meyer 			 * ties, and we must print all following ties.
144546d70077SConrad Meyer 			 */
144646d70077SConrad Meyer 			if (mtp == last_mtype) {
144746d70077SConrad Meyer 				ties = 1;
144846d70077SConrad Meyer 				continue;
144946d70077SConrad Meyer 			}
1450bdcc2226SMateusz Guzik 			size = get_malloc_stats(&mtp->ks_mti, &allocs,
145146d70077SConrad Meyer 			    &inuse);
145246d70077SConrad Meyer 			if (size > cur_size && size < last_size + ties) {
145346d70077SConrad Meyer 				cur_size = size;
145446d70077SConrad Meyer 				cur_mtype = mtp;
145546d70077SConrad Meyer 			}
145646d70077SConrad Meyer 		}
145746d70077SConrad Meyer 		if (cur_mtype == NULL)
145846d70077SConrad Meyer 			break;
145946d70077SConrad Meyer 
1460bdcc2226SMateusz Guzik 		size = get_malloc_stats(&cur_mtype->ks_mti, &allocs, &inuse);
146146d70077SConrad Meyer 		db_printf(fmt_entry, cur_mtype->ks_shortdesc, inuse,
146246d70077SConrad Meyer 		    howmany(size, 1024), allocs);
146346d70077SConrad Meyer 
1464687c94aaSJohn Baldwin 		if (db_pager_quit)
1465687c94aaSJohn Baldwin 			break;
146646d70077SConrad Meyer 
146746d70077SConrad Meyer 		last_mtype = cur_mtype;
146846d70077SConrad Meyer 		last_size = cur_size;
1469909ed16cSRobert Watson 	}
1470909ed16cSRobert Watson }
1471d7854da1SMatthew D Fleming 
1472d7854da1SMatthew D Fleming #if MALLOC_DEBUG_MAXZONES > 1
1473d7854da1SMatthew D Fleming DB_SHOW_COMMAND(multizone_matches, db_show_multizone_matches)
1474d7854da1SMatthew D Fleming {
1475d7854da1SMatthew D Fleming 	struct malloc_type_internal *mtip;
1476d7854da1SMatthew D Fleming 	struct malloc_type *mtp;
1477d7854da1SMatthew D Fleming 	u_int subzone;
1478d7854da1SMatthew D Fleming 
1479d7854da1SMatthew D Fleming 	if (!have_addr) {
1480d7854da1SMatthew D Fleming 		db_printf("Usage: show multizone_matches <malloc type/addr>\n");
1481d7854da1SMatthew D Fleming 		return;
1482d7854da1SMatthew D Fleming 	}
1483d7854da1SMatthew D Fleming 	mtp = (void *)addr;
1484bdcc2226SMateusz Guzik 	if (mtp->ks_version != M_VERSION) {
1485bdcc2226SMateusz Guzik 		db_printf("Version %lx does not match expected %x\n",
1486bdcc2226SMateusz Guzik 		    mtp->ks_version, M_VERSION);
1487d7854da1SMatthew D Fleming 		return;
1488d7854da1SMatthew D Fleming 	}
1489d7854da1SMatthew D Fleming 
1490bdcc2226SMateusz Guzik 	mtip = &mtp->ks_mti;
1491d7854da1SMatthew D Fleming 	subzone = mtip->mti_zone;
1492d7854da1SMatthew D Fleming 
1493d7854da1SMatthew D Fleming 	for (mtp = kmemstatistics; mtp != NULL; mtp = mtp->ks_next) {
1494bdcc2226SMateusz Guzik 		mtip = &mtp->ks_mti;
1495d7854da1SMatthew D Fleming 		if (mtip->mti_zone != subzone)
1496d7854da1SMatthew D Fleming 			continue;
1497d7854da1SMatthew D Fleming 		db_printf("%s\n", mtp->ks_shortdesc);
1498687c94aaSJohn Baldwin 		if (db_pager_quit)
1499687c94aaSJohn Baldwin 			break;
1500d7854da1SMatthew D Fleming 	}
1501d7854da1SMatthew D Fleming }
1502d7854da1SMatthew D Fleming #endif /* MALLOC_DEBUG_MAXZONES > 1 */
1503d7854da1SMatthew D Fleming #endif /* DDB */
1504