xref: /freebsd/sys/kern/kern_malloc.c (revision 3b6fb88590bce72fcdbf17c09a593f5efb7bff24)
1df8bae1dSRodney W. Grimes /*
2df8bae1dSRodney W. Grimes  * Copyright (c) 1987, 1991, 1993
3df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
4df8bae1dSRodney W. Grimes  *
5df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
6df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
7df8bae1dSRodney W. Grimes  * are met:
8df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
9df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
10df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
11df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
12df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
13df8bae1dSRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
14df8bae1dSRodney W. Grimes  *    must display the following acknowledgement:
15df8bae1dSRodney W. Grimes  *	This product includes software developed by the University of
16df8bae1dSRodney W. Grimes  *	California, Berkeley and its contributors.
17df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
18df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
19df8bae1dSRodney W. Grimes  *    without specific prior written permission.
20df8bae1dSRodney W. Grimes  *
21df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
32df8bae1dSRodney W. Grimes  *
33df8bae1dSRodney W. Grimes  *	@(#)kern_malloc.c	8.3 (Berkeley) 1/4/94
34c3aac50fSPeter Wemm  * $FreeBSD$
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
378a58a9f6SJohn Dyson #include "opt_vm.h"
388a58a9f6SJohn Dyson 
39df8bae1dSRodney W. Grimes #include <sys/param.h>
4026f9a767SRodney W. Grimes #include <sys/systm.h>
41df8bae1dSRodney W. Grimes #include <sys/kernel.h>
42df8bae1dSRodney W. Grimes #include <sys/malloc.h>
4354e7152cSDavid Greenman #include <sys/mbuf.h>
44efeaf95aSDavid Greenman #include <sys/vmmeter.h>
453075778bSJohn Dyson #include <sys/lock.h>
46df8bae1dSRodney W. Grimes 
47df8bae1dSRodney W. Grimes #include <vm/vm.h>
48efeaf95aSDavid Greenman #include <vm/vm_param.h>
49df8bae1dSRodney W. Grimes #include <vm/vm_kern.h>
50efeaf95aSDavid Greenman #include <vm/vm_extern.h>
513075778bSJohn Dyson #include <vm/pmap.h>
523075778bSJohn Dyson #include <vm/vm_map.h>
53df8bae1dSRodney W. Grimes 
54984982d6SPoul-Henning Kamp #if defined(INVARIANTS) && defined(__i386__)
55984982d6SPoul-Henning Kamp #include <machine/cpu.h>
56984982d6SPoul-Henning Kamp #endif
57984982d6SPoul-Henning Kamp 
583b6fb885SPoul-Henning Kamp MALLOC_DEFINE(M_CACHE, "cache", "Various Dynamically allocated caches");
599ef246c6SBruce Evans MALLOC_DEFINE(M_DEVBUF, "devbuf", "device driver memory");
609ef246c6SBruce Evans MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers");
619ef246c6SBruce Evans 
624590fd3aSDavid Greenman static void kmeminit __P((void *));
632b14f991SJulian Elischer SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL)
642b14f991SJulian Elischer 
65a1c995b6SPoul-Henning Kamp static MALLOC_DEFINE(M_FREE, "free", "should be on free list");
66a1c995b6SPoul-Henning Kamp 
67db669378SPeter Wemm static struct malloc_type *kmemstatistics;
68254c6cb3SPoul-Henning Kamp static struct kmembuckets bucket[MINBUCKET + 16];
69254c6cb3SPoul-Henning Kamp static struct kmemusage *kmemusage;
70254c6cb3SPoul-Henning Kamp static char *kmembase;
71043a2f3bSBruce Evans static char *kmemlimit;
728a58a9f6SJohn Dyson static int vm_kmem_size;
73df8bae1dSRodney W. Grimes 
74219cbf59SEivind Eklund #ifdef INVARIANTS
75df8bae1dSRodney W. Grimes /*
76df8bae1dSRodney W. Grimes  * This structure provides a set of masks to catch unaligned frees.
77df8bae1dSRodney W. Grimes  */
7887b6de2bSPoul-Henning Kamp static long addrmask[] = { 0,
79df8bae1dSRodney W. Grimes 	0x00000001, 0x00000003, 0x00000007, 0x0000000f,
80df8bae1dSRodney W. Grimes 	0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
81df8bae1dSRodney W. Grimes 	0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
82df8bae1dSRodney W. Grimes 	0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
83df8bae1dSRodney W. Grimes };
84df8bae1dSRodney W. Grimes 
85df8bae1dSRodney W. Grimes /*
86df8bae1dSRodney W. Grimes  * The WEIRD_ADDR is used as known text to copy into free objects so
87df8bae1dSRodney W. Grimes  * that modifications after frees can be detected.
88df8bae1dSRodney W. Grimes  */
895124d598SDavid Greenman #define WEIRD_ADDR	0xdeadc0de
905124d598SDavid Greenman #define MAX_COPY	64
91df8bae1dSRodney W. Grimes 
92df8bae1dSRodney W. Grimes /*
93df8bae1dSRodney W. Grimes  * Normally the first word of the structure is used to hold the list
94df8bae1dSRodney W. Grimes  * pointer for free objects. However, when running with diagnostics,
95df8bae1dSRodney W. Grimes  * we use the third and fourth fields, so as to catch modifications
96df8bae1dSRodney W. Grimes  * in the most commonly trashed first two words.
97df8bae1dSRodney W. Grimes  */
98df8bae1dSRodney W. Grimes struct freelist {
99df8bae1dSRodney W. Grimes 	long	spare0;
10060a513e9SPoul-Henning Kamp 	struct malloc_type *type;
101df8bae1dSRodney W. Grimes 	long	spare1;
102df8bae1dSRodney W. Grimes 	caddr_t	next;
103df8bae1dSRodney W. Grimes };
1045526d2d9SEivind Eklund #else /* !INVARIANTS */
105df8bae1dSRodney W. Grimes struct freelist {
106df8bae1dSRodney W. Grimes 	caddr_t	next;
107df8bae1dSRodney W. Grimes };
1085526d2d9SEivind Eklund #endif /* INVARIANTS */
109df8bae1dSRodney W. Grimes 
110df8bae1dSRodney W. Grimes /*
1111c7c3c6aSMatthew Dillon  *	malloc:
1121c7c3c6aSMatthew Dillon  *
1131c7c3c6aSMatthew Dillon  *	Allocate a block of memory.
1141c7c3c6aSMatthew Dillon  *
1151c7c3c6aSMatthew Dillon  *	If M_NOWAIT is set, this routine will not block and return NULL if
1161c7c3c6aSMatthew Dillon  *	the allocation fails.
1171c7c3c6aSMatthew Dillon  *
1181c7c3c6aSMatthew Dillon  *	If M_ASLEEP is set (M_NOWAIT must also be set), this routine
1191c7c3c6aSMatthew Dillon  *	will have the side effect of calling asleep() if it returns NULL,
1201c7c3c6aSMatthew Dillon  *	allowing the parent to await() at some future time.
121df8bae1dSRodney W. Grimes  */
122df8bae1dSRodney W. Grimes void *
123df8bae1dSRodney W. Grimes malloc(size, type, flags)
124df8bae1dSRodney W. Grimes 	unsigned long size;
12560a513e9SPoul-Henning Kamp 	struct malloc_type *type;
126254c6cb3SPoul-Henning Kamp 	int flags;
127df8bae1dSRodney W. Grimes {
128df8bae1dSRodney W. Grimes 	register struct kmembuckets *kbp;
129df8bae1dSRodney W. Grimes 	register struct kmemusage *kup;
130df8bae1dSRodney W. Grimes 	register struct freelist *freep;
131df8bae1dSRodney W. Grimes 	long indx, npg, allocsize;
132df8bae1dSRodney W. Grimes 	int s;
133df8bae1dSRodney W. Grimes 	caddr_t va, cp, savedlist;
1345526d2d9SEivind Eklund #ifdef INVARIANTS
135df8bae1dSRodney W. Grimes 	long *end, *lp;
136df8bae1dSRodney W. Grimes 	int copysize;
137d254af07SMatthew Dillon 	const char *savedtype;
138df8bae1dSRodney W. Grimes #endif
13960a513e9SPoul-Henning Kamp 	register struct malloc_type *ksp = type;
140df8bae1dSRodney W. Grimes 
141984982d6SPoul-Henning Kamp #if defined(INVARIANTS) && defined(__i386__)
142984982d6SPoul-Henning Kamp 	if (flags == M_WAITOK)
143984982d6SPoul-Henning Kamp 		KASSERT(intr_nesting_level == 0,
144984982d6SPoul-Henning Kamp 		   ("malloc(M_WAITOK) in interrupt context"));
145984982d6SPoul-Henning Kamp #endif
1461c7c3c6aSMatthew Dillon 	/*
1471c7c3c6aSMatthew Dillon 	 * Must be at splmem() prior to initializing segment to handle
1481c7c3c6aSMatthew Dillon 	 * potential initialization race.
1491c7c3c6aSMatthew Dillon 	 */
1501c7c3c6aSMatthew Dillon 
1511c7c3c6aSMatthew Dillon 	s = splmem();
1521c7c3c6aSMatthew Dillon 
153ce45b512SBruce Evans 	if (type->ks_limit == 0)
154254c6cb3SPoul-Henning Kamp 		malloc_init(type);
155254c6cb3SPoul-Henning Kamp 
156df8bae1dSRodney W. Grimes 	indx = BUCKETINDX(size);
157df8bae1dSRodney W. Grimes 	kbp = &bucket[indx];
1581c7c3c6aSMatthew Dillon 
159df8bae1dSRodney W. Grimes 	while (ksp->ks_memuse >= ksp->ks_limit) {
1601c7c3c6aSMatthew Dillon 		if (flags & M_ASLEEP) {
1611c7c3c6aSMatthew Dillon 			if (ksp->ks_limblocks < 65535)
1621c7c3c6aSMatthew Dillon 				ksp->ks_limblocks++;
1631c7c3c6aSMatthew Dillon 			asleep((caddr_t)ksp, PSWP+2, type->ks_shortdesc, 0);
1641c7c3c6aSMatthew Dillon 		}
165df8bae1dSRodney W. Grimes 		if (flags & M_NOWAIT) {
166df8bae1dSRodney W. Grimes 			splx(s);
167df8bae1dSRodney W. Grimes 			return ((void *) NULL);
168df8bae1dSRodney W. Grimes 		}
169df8bae1dSRodney W. Grimes 		if (ksp->ks_limblocks < 65535)
170df8bae1dSRodney W. Grimes 			ksp->ks_limblocks++;
171254c6cb3SPoul-Henning Kamp 		tsleep((caddr_t)ksp, PSWP+2, type->ks_shortdesc, 0);
172df8bae1dSRodney W. Grimes 	}
173df8bae1dSRodney W. Grimes 	ksp->ks_size |= 1 << indx;
1745526d2d9SEivind Eklund #ifdef INVARIANTS
175df8bae1dSRodney W. Grimes 	copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY;
176df8bae1dSRodney W. Grimes #endif
177df8bae1dSRodney W. Grimes 	if (kbp->kb_next == NULL) {
178df8bae1dSRodney W. Grimes 		kbp->kb_last = NULL;
179df8bae1dSRodney W. Grimes 		if (size > MAXALLOCSAVE)
180f8845af0SPoul-Henning Kamp 			allocsize = roundup(size, PAGE_SIZE);
181df8bae1dSRodney W. Grimes 		else
182df8bae1dSRodney W. Grimes 			allocsize = 1 << indx;
183e911eafcSPoul-Henning Kamp 		npg = btoc(allocsize);
1849f518539SDavid Greenman 		va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg), flags);
185df8bae1dSRodney W. Grimes 		if (va == NULL) {
186df8bae1dSRodney W. Grimes 			splx(s);
187df8bae1dSRodney W. Grimes 			return ((void *) NULL);
188df8bae1dSRodney W. Grimes 		}
189df8bae1dSRodney W. Grimes 		kbp->kb_total += kbp->kb_elmpercl;
190df8bae1dSRodney W. Grimes 		kup = btokup(va);
191df8bae1dSRodney W. Grimes 		kup->ku_indx = indx;
192df8bae1dSRodney W. Grimes 		if (allocsize > MAXALLOCSAVE) {
193df8bae1dSRodney W. Grimes 			if (npg > 65535)
194df8bae1dSRodney W. Grimes 				panic("malloc: allocation too large");
195df8bae1dSRodney W. Grimes 			kup->ku_pagecnt = npg;
196df8bae1dSRodney W. Grimes 			ksp->ks_memuse += allocsize;
197df8bae1dSRodney W. Grimes 			goto out;
198df8bae1dSRodney W. Grimes 		}
199df8bae1dSRodney W. Grimes 		kup->ku_freecnt = kbp->kb_elmpercl;
200df8bae1dSRodney W. Grimes 		kbp->kb_totalfree += kbp->kb_elmpercl;
201df8bae1dSRodney W. Grimes 		/*
202df8bae1dSRodney W. Grimes 		 * Just in case we blocked while allocating memory,
203df8bae1dSRodney W. Grimes 		 * and someone else also allocated memory for this
204df8bae1dSRodney W. Grimes 		 * bucket, don't assume the list is still empty.
205df8bae1dSRodney W. Grimes 		 */
206df8bae1dSRodney W. Grimes 		savedlist = kbp->kb_next;
207e911eafcSPoul-Henning Kamp 		kbp->kb_next = cp = va + (npg * PAGE_SIZE) - allocsize;
208df8bae1dSRodney W. Grimes 		for (;;) {
209df8bae1dSRodney W. Grimes 			freep = (struct freelist *)cp;
2105526d2d9SEivind Eklund #ifdef INVARIANTS
211df8bae1dSRodney W. Grimes 			/*
212df8bae1dSRodney W. Grimes 			 * Copy in known text to detect modification
213df8bae1dSRodney W. Grimes 			 * after freeing.
214df8bae1dSRodney W. Grimes 			 */
215df8bae1dSRodney W. Grimes 			end = (long *)&cp[copysize];
216df8bae1dSRodney W. Grimes 			for (lp = (long *)cp; lp < end; lp++)
217df8bae1dSRodney W. Grimes 				*lp = WEIRD_ADDR;
218df8bae1dSRodney W. Grimes 			freep->type = M_FREE;
2195526d2d9SEivind Eklund #endif /* INVARIANTS */
220df8bae1dSRodney W. Grimes 			if (cp <= va)
221df8bae1dSRodney W. Grimes 				break;
222df8bae1dSRodney W. Grimes 			cp -= allocsize;
223df8bae1dSRodney W. Grimes 			freep->next = cp;
224df8bae1dSRodney W. Grimes 		}
225df8bae1dSRodney W. Grimes 		freep->next = savedlist;
226df8bae1dSRodney W. Grimes 		if (kbp->kb_last == NULL)
227df8bae1dSRodney W. Grimes 			kbp->kb_last = (caddr_t)freep;
228df8bae1dSRodney W. Grimes 	}
229df8bae1dSRodney W. Grimes 	va = kbp->kb_next;
230df8bae1dSRodney W. Grimes 	kbp->kb_next = ((struct freelist *)va)->next;
2315526d2d9SEivind Eklund #ifdef INVARIANTS
232df8bae1dSRodney W. Grimes 	freep = (struct freelist *)va;
233d254af07SMatthew Dillon 	savedtype = (const char *) type->ks_shortdesc;
234df8bae1dSRodney W. Grimes #if BYTE_ORDER == BIG_ENDIAN
23560a513e9SPoul-Henning Kamp 	freep->type = (struct malloc_type *)WEIRD_ADDR >> 16;
236df8bae1dSRodney W. Grimes #endif
237df8bae1dSRodney W. Grimes #if BYTE_ORDER == LITTLE_ENDIAN
23860a513e9SPoul-Henning Kamp 	freep->type = (struct malloc_type *)WEIRD_ADDR;
239df8bae1dSRodney W. Grimes #endif
24086a14a7aSBruce Evans 	if ((intptr_t)(void *)&freep->next & 0x2)
241df8bae1dSRodney W. Grimes 		freep->next = (caddr_t)((WEIRD_ADDR >> 16)|(WEIRD_ADDR << 16));
242df8bae1dSRodney W. Grimes 	else
243df8bae1dSRodney W. Grimes 		freep->next = (caddr_t)WEIRD_ADDR;
244df8bae1dSRodney W. Grimes 	end = (long *)&va[copysize];
245df8bae1dSRodney W. Grimes 	for (lp = (long *)va; lp < end; lp++) {
246df8bae1dSRodney W. Grimes 		if (*lp == WEIRD_ADDR)
247df8bae1dSRodney W. Grimes 			continue;
248d974cf4dSBruce Evans 		printf("%s %ld of object %p size %lu %s %s (0x%lx != 0x%lx)\n",
249d974cf4dSBruce Evans 			"Data modified on freelist: word",
250d974cf4dSBruce Evans 			(long)(lp - (long *)va), (void *)va, size,
251d974cf4dSBruce Evans 			"previous type", savedtype, *lp, (u_long)WEIRD_ADDR);
252df8bae1dSRodney W. Grimes 		break;
253df8bae1dSRodney W. Grimes 	}
254df8bae1dSRodney W. Grimes 	freep->spare0 = 0;
2555526d2d9SEivind Eklund #endif /* INVARIANTS */
256df8bae1dSRodney W. Grimes 	kup = btokup(va);
257df8bae1dSRodney W. Grimes 	if (kup->ku_indx != indx)
258df8bae1dSRodney W. Grimes 		panic("malloc: wrong bucket");
259df8bae1dSRodney W. Grimes 	if (kup->ku_freecnt == 0)
260df8bae1dSRodney W. Grimes 		panic("malloc: lost data");
261df8bae1dSRodney W. Grimes 	kup->ku_freecnt--;
262df8bae1dSRodney W. Grimes 	kbp->kb_totalfree--;
263df8bae1dSRodney W. Grimes 	ksp->ks_memuse += 1 << indx;
264df8bae1dSRodney W. Grimes out:
265df8bae1dSRodney W. Grimes 	kbp->kb_calls++;
266df8bae1dSRodney W. Grimes 	ksp->ks_inuse++;
267df8bae1dSRodney W. Grimes 	ksp->ks_calls++;
268df8bae1dSRodney W. Grimes 	if (ksp->ks_memuse > ksp->ks_maxused)
269df8bae1dSRodney W. Grimes 		ksp->ks_maxused = ksp->ks_memuse;
270df8bae1dSRodney W. Grimes 	splx(s);
271df8bae1dSRodney W. Grimes 	return ((void *) va);
272df8bae1dSRodney W. Grimes }
273df8bae1dSRodney W. Grimes 
274df8bae1dSRodney W. Grimes /*
2751c7c3c6aSMatthew Dillon  *	free:
2761c7c3c6aSMatthew Dillon  *
277df8bae1dSRodney W. Grimes  *	Free a block of memory allocated by malloc.
2781c7c3c6aSMatthew Dillon  *
2791c7c3c6aSMatthew Dillon  *	This routine may not block.
280df8bae1dSRodney W. Grimes  */
281df8bae1dSRodney W. Grimes void
282df8bae1dSRodney W. Grimes free(addr, type)
283df8bae1dSRodney W. Grimes 	void *addr;
28460a513e9SPoul-Henning Kamp 	struct malloc_type *type;
285df8bae1dSRodney W. Grimes {
286df8bae1dSRodney W. Grimes 	register struct kmembuckets *kbp;
287df8bae1dSRodney W. Grimes 	register struct kmemusage *kup;
288df8bae1dSRodney W. Grimes 	register struct freelist *freep;
289df8bae1dSRodney W. Grimes 	long size;
290df8bae1dSRodney W. Grimes 	int s;
2915526d2d9SEivind Eklund #ifdef INVARIANTS
292ca67a4e4SPoul-Henning Kamp 	struct freelist *fp;
293df8bae1dSRodney W. Grimes 	long *end, *lp, alloc, copysize;
294df8bae1dSRodney W. Grimes #endif
29560a513e9SPoul-Henning Kamp 	register struct malloc_type *ksp = type;
296254c6cb3SPoul-Henning Kamp 
297ce45b512SBruce Evans 	if (type->ks_limit == 0)
29822c64348SPoul-Henning Kamp 		panic("freeing with unknown type (%s)", type->ks_shortdesc);
299df8bae1dSRodney W. Grimes 
3005526d2d9SEivind Eklund 	KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit,
3015526d2d9SEivind Eklund 	    ("free: address %p out of range", (void *)addr));
302df8bae1dSRodney W. Grimes 	kup = btokup(addr);
303df8bae1dSRodney W. Grimes 	size = 1 << kup->ku_indx;
304df8bae1dSRodney W. Grimes 	kbp = &bucket[kup->ku_indx];
305b1897c19SJulian Elischer 	s = splmem();
3065526d2d9SEivind Eklund #ifdef INVARIANTS
307df8bae1dSRodney W. Grimes 	/*
308df8bae1dSRodney W. Grimes 	 * Check for returns of data that do not point to the
309df8bae1dSRodney W. Grimes 	 * beginning of the allocation.
310df8bae1dSRodney W. Grimes 	 */
311f8845af0SPoul-Henning Kamp 	if (size > PAGE_SIZE)
312f8845af0SPoul-Henning Kamp 		alloc = addrmask[BUCKETINDX(PAGE_SIZE)];
313df8bae1dSRodney W. Grimes 	else
314df8bae1dSRodney W. Grimes 		alloc = addrmask[kup->ku_indx];
31586a14a7aSBruce Evans 	if (((uintptr_t)(void *)addr & alloc) != 0)
316d974cf4dSBruce Evans 		panic("free: unaligned addr %p, size %ld, type %s, mask %ld",
317d974cf4dSBruce Evans 		    (void *)addr, size, type->ks_shortdesc, alloc);
3185526d2d9SEivind Eklund #endif /* INVARIANTS */
319df8bae1dSRodney W. Grimes 	if (size > MAXALLOCSAVE) {
320df8bae1dSRodney W. Grimes 		kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt));
321e911eafcSPoul-Henning Kamp 		size = kup->ku_pagecnt << PAGE_SHIFT;
322df8bae1dSRodney W. Grimes 		ksp->ks_memuse -= size;
323df8bae1dSRodney W. Grimes 		kup->ku_indx = 0;
324df8bae1dSRodney W. Grimes 		kup->ku_pagecnt = 0;
325df8bae1dSRodney W. Grimes 		if (ksp->ks_memuse + size >= ksp->ks_limit &&
326df8bae1dSRodney W. Grimes 		    ksp->ks_memuse < ksp->ks_limit)
327df8bae1dSRodney W. Grimes 			wakeup((caddr_t)ksp);
328df8bae1dSRodney W. Grimes 		ksp->ks_inuse--;
329df8bae1dSRodney W. Grimes 		kbp->kb_total -= 1;
330df8bae1dSRodney W. Grimes 		splx(s);
331df8bae1dSRodney W. Grimes 		return;
332df8bae1dSRodney W. Grimes 	}
333df8bae1dSRodney W. Grimes 	freep = (struct freelist *)addr;
3345526d2d9SEivind Eklund #ifdef INVARIANTS
335df8bae1dSRodney W. Grimes 	/*
336df8bae1dSRodney W. Grimes 	 * Check for multiple frees. Use a quick check to see if
337df8bae1dSRodney W. Grimes 	 * it looks free before laboriously searching the freelist.
338df8bae1dSRodney W. Grimes 	 */
339df8bae1dSRodney W. Grimes 	if (freep->spare0 == WEIRD_ADDR) {
340ca67a4e4SPoul-Henning Kamp 		fp = (struct freelist *)kbp->kb_next;
341ca67a4e4SPoul-Henning Kamp 		while (fp) {
342219cbf59SEivind Eklund 			if (fp->spare0 != WEIRD_ADDR)
3435526d2d9SEivind Eklund 				panic("free: free item %p modified", fp);
344219cbf59SEivind Eklund 			else if (addr == (caddr_t)fp)
3455526d2d9SEivind Eklund 				panic("free: multiple freed item %p", addr);
346ca67a4e4SPoul-Henning Kamp 			fp = (struct freelist *)fp->next;
347df8bae1dSRodney W. Grimes 		}
348df8bae1dSRodney W. Grimes 	}
349df8bae1dSRodney W. Grimes 	/*
350df8bae1dSRodney W. Grimes 	 * Copy in known text to detect modification after freeing
351df8bae1dSRodney W. Grimes 	 * and to make it look free. Also, save the type being freed
352df8bae1dSRodney W. Grimes 	 * so we can list likely culprit if modification is detected
353df8bae1dSRodney W. Grimes 	 * when the object is reallocated.
354df8bae1dSRodney W. Grimes 	 */
355df8bae1dSRodney W. Grimes 	copysize = size < MAX_COPY ? size : MAX_COPY;
356df8bae1dSRodney W. Grimes 	end = (long *)&((caddr_t)addr)[copysize];
357df8bae1dSRodney W. Grimes 	for (lp = (long *)addr; lp < end; lp++)
358df8bae1dSRodney W. Grimes 		*lp = WEIRD_ADDR;
359df8bae1dSRodney W. Grimes 	freep->type = type;
3605526d2d9SEivind Eklund #endif /* INVARIANTS */
361df8bae1dSRodney W. Grimes 	kup->ku_freecnt++;
362dfd5dee1SPeter Wemm 	if (kup->ku_freecnt >= kbp->kb_elmpercl) {
363df8bae1dSRodney W. Grimes 		if (kup->ku_freecnt > kbp->kb_elmpercl)
364df8bae1dSRodney W. Grimes 			panic("free: multiple frees");
365df8bae1dSRodney W. Grimes 		else if (kbp->kb_totalfree > kbp->kb_highwat)
366df8bae1dSRodney W. Grimes 			kbp->kb_couldfree++;
367dfd5dee1SPeter Wemm 	}
368df8bae1dSRodney W. Grimes 	kbp->kb_totalfree++;
369df8bae1dSRodney W. Grimes 	ksp->ks_memuse -= size;
370df8bae1dSRodney W. Grimes 	if (ksp->ks_memuse + size >= ksp->ks_limit &&
371df8bae1dSRodney W. Grimes 	    ksp->ks_memuse < ksp->ks_limit)
372df8bae1dSRodney W. Grimes 		wakeup((caddr_t)ksp);
373df8bae1dSRodney W. Grimes 	ksp->ks_inuse--;
37414bf02f8SJohn Dyson #ifdef OLD_MALLOC_MEMORY_POLICY
375df8bae1dSRodney W. Grimes 	if (kbp->kb_next == NULL)
376df8bae1dSRodney W. Grimes 		kbp->kb_next = addr;
377df8bae1dSRodney W. Grimes 	else
378df8bae1dSRodney W. Grimes 		((struct freelist *)kbp->kb_last)->next = addr;
379df8bae1dSRodney W. Grimes 	freep->next = NULL;
380df8bae1dSRodney W. Grimes 	kbp->kb_last = addr;
38114bf02f8SJohn Dyson #else
38214bf02f8SJohn Dyson 	/*
38314bf02f8SJohn Dyson 	 * Return memory to the head of the queue for quick reuse.  This
38414bf02f8SJohn Dyson 	 * can improve performance by improving the probability of the
38514bf02f8SJohn Dyson 	 * item being in the cache when it is reused.
38614bf02f8SJohn Dyson 	 */
38714bf02f8SJohn Dyson 	if (kbp->kb_next == NULL) {
38814bf02f8SJohn Dyson 		kbp->kb_next = addr;
38914bf02f8SJohn Dyson 		kbp->kb_last = addr;
39014bf02f8SJohn Dyson 		freep->next = NULL;
39114bf02f8SJohn Dyson 	} else {
39214bf02f8SJohn Dyson 		freep->next = kbp->kb_next;
39314bf02f8SJohn Dyson 		kbp->kb_next = addr;
39414bf02f8SJohn Dyson 	}
39514bf02f8SJohn Dyson #endif
396df8bae1dSRodney W. Grimes 	splx(s);
397df8bae1dSRodney W. Grimes }
398df8bae1dSRodney W. Grimes 
399df8bae1dSRodney W. Grimes /*
400df8bae1dSRodney W. Grimes  * Initialize the kernel memory allocator
401df8bae1dSRodney W. Grimes  */
4022b14f991SJulian Elischer /* ARGSUSED*/
4032b14f991SJulian Elischer static void
404d841aaa7SBruce Evans kmeminit(dummy)
405d841aaa7SBruce Evans 	void *dummy;
406df8bae1dSRodney W. Grimes {
407df8bae1dSRodney W. Grimes 	register long indx;
408df8bae1dSRodney W. Grimes 	int npg;
4098a58a9f6SJohn Dyson 	int mem_size;
4108de6e8e1SMike Smith 	int xvm_kmem_size;
411df8bae1dSRodney W. Grimes 
412df8bae1dSRodney W. Grimes #if	((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0)
413cb7545a9SGarrett Wollman #error "kmeminit: MAXALLOCSAVE not power of 2"
414df8bae1dSRodney W. Grimes #endif
415df8bae1dSRodney W. Grimes #if	(MAXALLOCSAVE > MINALLOCSIZE * 32768)
416cb7545a9SGarrett Wollman #error "kmeminit: MAXALLOCSAVE too big"
417df8bae1dSRodney W. Grimes #endif
418f8845af0SPoul-Henning Kamp #if	(MAXALLOCSAVE < PAGE_SIZE)
419cb7545a9SGarrett Wollman #error "kmeminit: MAXALLOCSAVE too small"
420df8bae1dSRodney W. Grimes #endif
4218a58a9f6SJohn Dyson 
4228a58a9f6SJohn Dyson 	/*
4238a58a9f6SJohn Dyson 	 * Try to auto-tune the kernel memory size, so that it is
4248a58a9f6SJohn Dyson 	 * more applicable for a wider range of machine sizes.
4258a58a9f6SJohn Dyson 	 * On an X86, a VM_KMEM_SIZE_SCALE value of 4 is good, while
4268a58a9f6SJohn Dyson 	 * a VM_KMEM_SIZE of 12MB is a fair compromise.  The
4278a58a9f6SJohn Dyson 	 * VM_KMEM_SIZE_MAX is dependent on the maximum KVA space
4288a58a9f6SJohn Dyson 	 * available, and on an X86 with a total KVA space of 256MB,
4298a58a9f6SJohn Dyson 	 * try to keep VM_KMEM_SIZE_MAX at 80MB or below.
4308a58a9f6SJohn Dyson 	 *
4318a58a9f6SJohn Dyson 	 * Note that the kmem_map is also used by the zone allocator,
4328a58a9f6SJohn Dyson 	 * so make sure that there is enough space.
4338a58a9f6SJohn Dyson 	 */
434134c934cSMike Smith 	xvm_kmem_size = VM_KMEM_SIZE;
4358a58a9f6SJohn Dyson 	mem_size = cnt.v_page_count * PAGE_SIZE;
4368a58a9f6SJohn Dyson 
4378a58a9f6SJohn Dyson #if defined(VM_KMEM_SIZE_SCALE)
438134c934cSMike Smith 	if ((mem_size / VM_KMEM_SIZE_SCALE) > xvm_kmem_size)
439134c934cSMike Smith 		xvm_kmem_size = mem_size / VM_KMEM_SIZE_SCALE;
4408a58a9f6SJohn Dyson #endif
4418a58a9f6SJohn Dyson 
4428a58a9f6SJohn Dyson #if defined(VM_KMEM_SIZE_MAX)
443134c934cSMike Smith 	if (xvm_kmem_size >= VM_KMEM_SIZE_MAX)
444134c934cSMike Smith 		xvm_kmem_size = VM_KMEM_SIZE_MAX;
4458a58a9f6SJohn Dyson #endif
4468a58a9f6SJohn Dyson 
4478de6e8e1SMike Smith 	/* Allow final override from the kernel environment */
448134c934cSMike Smith 	TUNABLE_INT_FETCH("kern.vm.kmem.size", xvm_kmem_size, vm_kmem_size);
4498de6e8e1SMike Smith 
4508a58a9f6SJohn Dyson 	if (vm_kmem_size > 2 * (cnt.v_page_count * PAGE_SIZE))
4518a58a9f6SJohn Dyson 		vm_kmem_size = 2 * (cnt.v_page_count * PAGE_SIZE);
4528a58a9f6SJohn Dyson 
4538a58a9f6SJohn Dyson 	npg = (nmbufs * MSIZE + nmbclusters * MCLBYTES + vm_kmem_size)
454cb7545a9SGarrett Wollman 		/ PAGE_SIZE;
4550d94caffSDavid Greenman 
456df8bae1dSRodney W. Grimes 	kmemusage = (struct kmemusage *) kmem_alloc(kernel_map,
457df8bae1dSRodney W. Grimes 		(vm_size_t)(npg * sizeof(struct kmemusage)));
458df8bae1dSRodney W. Grimes 	kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase,
4592d8acc0fSJohn Dyson 		(vm_offset_t *)&kmemlimit, (vm_size_t)(npg * PAGE_SIZE));
4603075778bSJohn Dyson 	kmem_map->system_map = 1;
461df8bae1dSRodney W. Grimes 	for (indx = 0; indx < MINBUCKET + 16; indx++) {
462f8845af0SPoul-Henning Kamp 		if (1 << indx >= PAGE_SIZE)
463df8bae1dSRodney W. Grimes 			bucket[indx].kb_elmpercl = 1;
464df8bae1dSRodney W. Grimes 		else
465f8845af0SPoul-Henning Kamp 			bucket[indx].kb_elmpercl = PAGE_SIZE / (1 << indx);
466df8bae1dSRodney W. Grimes 		bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl;
467df8bae1dSRodney W. Grimes 	}
468254c6cb3SPoul-Henning Kamp }
469254c6cb3SPoul-Henning Kamp 
470db669378SPeter Wemm void
471db669378SPeter Wemm malloc_init(data)
472db669378SPeter Wemm 	void *data;
473254c6cb3SPoul-Henning Kamp {
474db669378SPeter Wemm 	struct malloc_type *type = (struct malloc_type *)data;
475254c6cb3SPoul-Henning Kamp 
476d1bbc7ecSPoul-Henning Kamp 	if (type->ks_magic != M_MAGIC)
477d1bbc7ecSPoul-Henning Kamp 		panic("malloc type lacks magic");
478d1bbc7ecSPoul-Henning Kamp 
479ce45b512SBruce Evans 	if (type->ks_limit != 0)
480db669378SPeter Wemm 		return;
481db669378SPeter Wemm 
482d4060a87SJohn Dyson 	if (cnt.v_page_count == 0)
483d4060a87SJohn Dyson 		panic("malloc_init not allowed before vm init");
484d4060a87SJohn Dyson 
48507bbd7f1SDavid Greenman 	/*
4868a58a9f6SJohn Dyson 	 * The default limits for each malloc region is 1/2 of the
4878a58a9f6SJohn Dyson 	 * malloc portion of the kmem map size.
48807bbd7f1SDavid Greenman 	 */
4898a58a9f6SJohn Dyson 	type->ks_limit = vm_kmem_size / 2;
490254c6cb3SPoul-Henning Kamp 	type->ks_next = kmemstatistics;
491254c6cb3SPoul-Henning Kamp 	kmemstatistics = type;
492df8bae1dSRodney W. Grimes }
493db669378SPeter Wemm 
494db669378SPeter Wemm void
495db669378SPeter Wemm malloc_uninit(data)
496db669378SPeter Wemm 	void *data;
497db669378SPeter Wemm {
498db669378SPeter Wemm 	struct malloc_type *type = (struct malloc_type *)data;
499db669378SPeter Wemm 	struct malloc_type *t;
500db669378SPeter Wemm 
501db669378SPeter Wemm 	if (type->ks_magic != M_MAGIC)
502db669378SPeter Wemm 		panic("malloc type lacks magic");
503db669378SPeter Wemm 
504db669378SPeter Wemm 	if (cnt.v_page_count == 0)
505db669378SPeter Wemm 		panic("malloc_uninit not allowed before vm init");
506db669378SPeter Wemm 
507ce45b512SBruce Evans 	if (type->ks_limit == 0)
508ce45b512SBruce Evans 		panic("malloc_uninit on uninitialized type");
509ce45b512SBruce Evans 
510db669378SPeter Wemm 	if (type == kmemstatistics)
511db669378SPeter Wemm 		kmemstatistics = type->ks_next;
512db669378SPeter Wemm 	else {
513db669378SPeter Wemm 		for (t = kmemstatistics; t->ks_next != NULL; t = t->ks_next) {
514db669378SPeter Wemm 			if (t->ks_next == type) {
515db669378SPeter Wemm 				t->ks_next = type->ks_next;
516db669378SPeter Wemm 				break;
517db669378SPeter Wemm 			}
518db669378SPeter Wemm 		}
519db669378SPeter Wemm 	}
520ce45b512SBruce Evans 	type->ks_next = NULL;
521ce45b512SBruce Evans 	type->ks_limit = 0;
522db669378SPeter Wemm }
523