xref: /openbsd/sys/kern/kern_malloc.c (revision df930be7)
1 /*	$NetBSD: kern_malloc.c,v 1.11 1995/05/01 22:39:11 cgd Exp $	*/
2 
3 /*
4  * Copyright (c) 1987, 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	@(#)kern_malloc.c	8.3 (Berkeley) 1/4/94
36  */
37 
38 #include <sys/param.h>
39 #include <sys/proc.h>
40 #include <sys/map.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43 
44 #include <vm/vm.h>
45 #include <vm/vm_kern.h>
46 
47 struct kmembuckets bucket[MINBUCKET + 16];
48 struct kmemstats kmemstats[M_LAST];
49 struct kmemusage *kmemusage;
50 char *kmembase, *kmemlimit;
51 char *memname[] = INITKMEMNAMES;
52 
53 #ifdef DIAGNOSTIC
54 /*
55  * This structure provides a set of masks to catch unaligned frees.
56  */
57 long addrmask[] = { 0,
58 	0x00000001, 0x00000003, 0x00000007, 0x0000000f,
59 	0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
60 	0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
61 	0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
62 };
63 
64 /*
65  * The WEIRD_ADDR is used as known text to copy into free objects so
66  * that modifications after frees can be detected.
67  */
68 #define WEIRD_ADDR	0xdeadbeef
69 #define MAX_COPY	32
70 
71 /*
72  * Normally the freelist structure is used only to hold the list pointer
73  * for free objects.  However, when running with diagnostics, the first
74  * 8 bytes of the structure is unused except for diagnostic information,
75  * and the free list pointer is at offst 8 in the structure.  Since the
76  * first 8 bytes is the portion of the structure most often modified, this
77  * helps to detect memory reuse problems and avoid free list corruption.
78  */
79 struct freelist {
80 	int32_t	spare0;
81 	int16_t	type;
82 	int16_t	spare1;
83 	caddr_t	next;
84 };
85 #else /* !DIAGNOSTIC */
86 struct freelist {
87 	caddr_t	next;
88 };
89 #endif /* DIAGNOSTIC */
90 
91 /*
92  * Allocate a block of memory
93  */
94 void *
95 malloc(size, type, flags)
96 	unsigned long size;
97 	int type, flags;
98 {
99 	register struct kmembuckets *kbp;
100 	register struct kmemusage *kup;
101 	register struct freelist *freep;
102 	long indx, npg, allocsize;
103 	int s;
104 	caddr_t va, cp, savedlist;
105 #ifdef DIAGNOSTIC
106 	int32_t *end, *lp;
107 	int copysize;
108 	char *savedtype;
109 #endif
110 #ifdef KMEMSTATS
111 	register struct kmemstats *ksp = &kmemstats[type];
112 
113 	if (((unsigned long)type) > M_LAST)
114 		panic("malloc - bogus type");
115 #endif
116 	indx = BUCKETINDX(size);
117 	kbp = &bucket[indx];
118 	s = splimp();
119 #ifdef KMEMSTATS
120 	while (ksp->ks_memuse >= ksp->ks_limit) {
121 		if (flags & M_NOWAIT) {
122 			splx(s);
123 			return ((void *) NULL);
124 		}
125 		if (ksp->ks_limblocks < 65535)
126 			ksp->ks_limblocks++;
127 		tsleep((caddr_t)ksp, PSWP+2, memname[type], 0);
128 	}
129 	ksp->ks_size |= 1 << indx;
130 #endif
131 #ifdef DIAGNOSTIC
132 	copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY;
133 #endif
134 	if (kbp->kb_next == NULL) {
135 		kbp->kb_last = NULL;
136 		if (size > MAXALLOCSAVE)
137 			allocsize = roundup(size, CLBYTES);
138 		else
139 			allocsize = 1 << indx;
140 		npg = clrnd(btoc(allocsize));
141 		va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg),
142 					   !(flags & M_NOWAIT));
143 		if (va == NULL) {
144 			splx(s);
145 			return ((void *) NULL);
146 		}
147 #ifdef KMEMSTATS
148 		kbp->kb_total += kbp->kb_elmpercl;
149 #endif
150 		kup = btokup(va);
151 		kup->ku_indx = indx;
152 		if (allocsize > MAXALLOCSAVE) {
153 			if (npg > 65535)
154 				panic("malloc: allocation too large");
155 			kup->ku_pagecnt = npg;
156 #ifdef KMEMSTATS
157 			ksp->ks_memuse += allocsize;
158 #endif
159 			goto out;
160 		}
161 #ifdef KMEMSTATS
162 		kup->ku_freecnt = kbp->kb_elmpercl;
163 		kbp->kb_totalfree += kbp->kb_elmpercl;
164 #endif
165 		/*
166 		 * Just in case we blocked while allocating memory,
167 		 * and someone else also allocated memory for this
168 		 * bucket, don't assume the list is still empty.
169 		 */
170 		savedlist = kbp->kb_next;
171 		kbp->kb_next = cp = va + (npg * NBPG) - allocsize;
172 		for (;;) {
173 			freep = (struct freelist *)cp;
174 #ifdef DIAGNOSTIC
175 			/*
176 			 * Copy in known text to detect modification
177 			 * after freeing.
178 			 */
179 			end = (int32_t *)&cp[copysize];
180 			for (lp = (int32_t *)cp; lp < end; lp++)
181 				*lp = WEIRD_ADDR;
182 			freep->type = M_FREE;
183 #endif /* DIAGNOSTIC */
184 			if (cp <= va)
185 				break;
186 			cp -= allocsize;
187 			freep->next = cp;
188 		}
189 		freep->next = savedlist;
190 		if (kbp->kb_last == NULL)
191 			kbp->kb_last = (caddr_t)freep;
192 	}
193 	va = kbp->kb_next;
194 	kbp->kb_next = ((struct freelist *)va)->next;
195 #ifdef DIAGNOSTIC
196 	freep = (struct freelist *)va;
197 	savedtype = (unsigned)freep->type < M_LAST ?
198 		memname[freep->type] : "???";
199 	if (kbp->kb_next &&
200 	    !kernacc(kbp->kb_next, sizeof(struct freelist), 0)) {
201 		printf("%s %d of object %p size %d %s %s (invalid addr %p)\n",
202 			"Data modified on freelist: word",
203 			(int32_t *)&kbp->kb_next - (int32_t *)kbp, va, size,
204 			"previous type", savedtype, kbp->kb_next);
205 		kbp->kb_next = NULL;
206 	}
207 
208 	/* Fill the fields that we've used with WEIRD_ADDR */
209 #if BYTE_ORDER == BIG_ENDIAN
210 	freep->type = WEIRD_ADDR >> 16;
211 #endif
212 #if BYTE_ORDER == LITTLE_ENDIAN
213 	freep->type = (short)WEIRD_ADDR;
214 #endif
215 	end = (int32_t *)&freep->next +
216 	    (sizeof(freep->next) / sizeof(int32_t));
217 	for (lp = (int32_t *)&freep->next; lp < end; lp++)
218 		*lp = WEIRD_ADDR;
219 
220 	/* and check that the data hasn't been modified. */
221 	end = (int32_t *)&va[copysize];
222 	for (lp = (int32_t *)va; lp < end; lp++) {
223 		if (*lp == WEIRD_ADDR)
224 			continue;
225 		printf("%s %d of object %p size %d %s %s (%p != %p)\n",
226 			"Data modified on freelist: word", lp - (int32_t *)va,
227 			va, size, "previous type", savedtype, *lp, WEIRD_ADDR);
228 		break;
229 	}
230 
231 	freep->spare0 = 0;
232 #endif /* DIAGNOSTIC */
233 #ifdef KMEMSTATS
234 	kup = btokup(va);
235 	if (kup->ku_indx != indx)
236 		panic("malloc: wrong bucket");
237 	if (kup->ku_freecnt == 0)
238 		panic("malloc: lost data");
239 	kup->ku_freecnt--;
240 	kbp->kb_totalfree--;
241 	ksp->ks_memuse += 1 << indx;
242 out:
243 	kbp->kb_calls++;
244 	ksp->ks_inuse++;
245 	ksp->ks_calls++;
246 	if (ksp->ks_memuse > ksp->ks_maxused)
247 		ksp->ks_maxused = ksp->ks_memuse;
248 #else
249 out:
250 #endif
251 	splx(s);
252 	return ((void *) va);
253 }
254 
255 /*
256  * Free a block of memory allocated by malloc.
257  */
258 void
259 free(addr, type)
260 	void *addr;
261 	int type;
262 {
263 	register struct kmembuckets *kbp;
264 	register struct kmemusage *kup;
265 	register struct freelist *freep;
266 	long size;
267 	int s;
268 #ifdef DIAGNOSTIC
269 	caddr_t cp;
270 	int32_t *end, *lp;
271 	long alloc, copysize;
272 #endif
273 #ifdef KMEMSTATS
274 	register struct kmemstats *ksp = &kmemstats[type];
275 #endif
276 
277 	kup = btokup(addr);
278 	size = 1 << kup->ku_indx;
279 	kbp = &bucket[kup->ku_indx];
280 	s = splimp();
281 #ifdef DIAGNOSTIC
282 	/*
283 	 * Check for returns of data that do not point to the
284 	 * beginning of the allocation.
285 	 */
286 	if (size > NBPG * CLSIZE)
287 		alloc = addrmask[BUCKETINDX(NBPG * CLSIZE)];
288 	else
289 		alloc = addrmask[kup->ku_indx];
290 	if (((u_long)addr & alloc) != 0)
291 		panic("free: unaligned addr 0x%x, size %d, type %s, mask %d\n",
292 			addr, size, memname[type], alloc);
293 #endif /* DIAGNOSTIC */
294 	if (size > MAXALLOCSAVE) {
295 		kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt));
296 #ifdef KMEMSTATS
297 		size = kup->ku_pagecnt << PGSHIFT;
298 		ksp->ks_memuse -= size;
299 		kup->ku_indx = 0;
300 		kup->ku_pagecnt = 0;
301 		if (ksp->ks_memuse + size >= ksp->ks_limit &&
302 		    ksp->ks_memuse < ksp->ks_limit)
303 			wakeup((caddr_t)ksp);
304 		ksp->ks_inuse--;
305 		kbp->kb_total -= 1;
306 #endif
307 		splx(s);
308 		return;
309 	}
310 	freep = (struct freelist *)addr;
311 #ifdef DIAGNOSTIC
312 	/*
313 	 * Check for multiple frees. Use a quick check to see if
314 	 * it looks free before laboriously searching the freelist.
315 	 */
316 	if (freep->spare0 == WEIRD_ADDR) {
317 		for (cp = kbp->kb_next; cp; cp = *(caddr_t *)cp) {
318 			if (addr != cp)
319 				continue;
320 			printf("multiply freed item %p\n", addr);
321 			panic("free: duplicated free");
322 		}
323 	}
324 	/*
325 	 * Copy in known text to detect modification after freeing
326 	 * and to make it look free. Also, save the type being freed
327 	 * so we can list likely culprit if modification is detected
328 	 * when the object is reallocated.
329 	 */
330 	copysize = size < MAX_COPY ? size : MAX_COPY;
331 	end = (int32_t *)&((caddr_t)addr)[copysize];
332 	for (lp = (int32_t *)addr; lp < end; lp++)
333 		*lp = WEIRD_ADDR;
334 	freep->type = type;
335 #endif /* DIAGNOSTIC */
336 #ifdef KMEMSTATS
337 	kup->ku_freecnt++;
338 	if (kup->ku_freecnt >= kbp->kb_elmpercl)
339 		if (kup->ku_freecnt > kbp->kb_elmpercl)
340 			panic("free: multiple frees");
341 		else if (kbp->kb_totalfree > kbp->kb_highwat)
342 			kbp->kb_couldfree++;
343 	kbp->kb_totalfree++;
344 	ksp->ks_memuse -= size;
345 	if (ksp->ks_memuse + size >= ksp->ks_limit &&
346 	    ksp->ks_memuse < ksp->ks_limit)
347 		wakeup((caddr_t)ksp);
348 	ksp->ks_inuse--;
349 #endif
350 	if (kbp->kb_next == NULL)
351 		kbp->kb_next = addr;
352 	else
353 		((struct freelist *)kbp->kb_last)->next = addr;
354 	freep->next = NULL;
355 	kbp->kb_last = addr;
356 	splx(s);
357 }
358 
359 /*
360  * Initialize the kernel memory allocator
361  */
362 kmeminit()
363 {
364 	register long indx;
365 	int npg;
366 
367 #if	((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0)
368 		ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2
369 #endif
370 #if	(MAXALLOCSAVE > MINALLOCSIZE * 32768)
371 		ERROR!_kmeminit:_MAXALLOCSAVE_too_big
372 #endif
373 #if	(MAXALLOCSAVE < CLBYTES)
374 		ERROR!_kmeminit:_MAXALLOCSAVE_too_small
375 #endif
376 
377 	if (sizeof(struct freelist) > (1 << MINBUCKET))
378 		panic("minbucket too small/struct freelist too big");
379 
380 	npg = VM_KMEM_SIZE/ NBPG;
381 	kmemusage = (struct kmemusage *) kmem_alloc(kernel_map,
382 		(vm_size_t)(npg * sizeof(struct kmemusage)));
383 	kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase,
384 		(vm_offset_t *)&kmemlimit, (vm_size_t)(npg * NBPG), FALSE);
385 #ifdef KMEMSTATS
386 	for (indx = 0; indx < MINBUCKET + 16; indx++) {
387 		if (1 << indx >= CLBYTES)
388 			bucket[indx].kb_elmpercl = 1;
389 		else
390 			bucket[indx].kb_elmpercl = CLBYTES / (1 << indx);
391 		bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl;
392 	}
393 	for (indx = 0; indx < M_LAST; indx++)
394 		kmemstats[indx].ks_limit = npg * NBPG * 6 / 10;
395 #endif
396 }
397