kern_malloc.c revision 1.17 1 /* $NetBSD: kern_malloc.c,v 1.17 1996/06/13 16:53:34 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 #include <sys/systm.h>
44
45 #include <vm/vm.h>
46 #include <vm/vm_kern.h>
47
48 struct kmembuckets bucket[MINBUCKET + 16];
49 struct kmemstats kmemstats[M_LAST];
50 struct kmemusage *kmemusage;
51 char *kmembase, *kmemlimit;
52 char *memname[] = INITKMEMNAMES;
53
54 #ifdef DIAGNOSTIC
55 /*
56 * This structure provides a set of masks to catch unaligned frees.
57 */
58 long addrmask[] = { 0,
59 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
60 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
61 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
62 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
63 };
64
65 /*
66 * The WEIRD_ADDR is used as known text to copy into free objects so
67 * that modifications after frees can be detected.
68 */
69 #define WEIRD_ADDR ((unsigned) 0xdeadbeef)
70 #define MAX_COPY 32
71
72 /*
73 * Normally the freelist structure is used only to hold the list pointer
74 * for free objects. However, when running with diagnostics, the first
75 * 8 bytes of the structure is unused except for diagnostic information,
76 * and the free list pointer is at offst 8 in the structure. Since the
77 * first 8 bytes is the portion of the structure most often modified, this
78 * helps to detect memory reuse problems and avoid free list corruption.
79 */
80 struct freelist {
81 int32_t spare0;
82 int16_t type;
83 int16_t spare1;
84 caddr_t next;
85 };
86 #else /* !DIAGNOSTIC */
87 struct freelist {
88 caddr_t next;
89 };
90 #endif /* DIAGNOSTIC */
91
92 /*
93 * Allocate a block of memory
94 */
95 void *
96 malloc(size, type, flags)
97 unsigned long size;
98 int type, flags;
99 {
100 register struct kmembuckets *kbp;
101 register struct kmemusage *kup;
102 register struct freelist *freep;
103 long indx, npg, allocsize;
104 int s;
105 caddr_t va, cp, savedlist;
106 #ifdef DIAGNOSTIC
107 int32_t *end, *lp;
108 int copysize;
109 char *savedtype;
110 #endif
111 #ifdef KMEMSTATS
112 register struct kmemstats *ksp = &kmemstats[type];
113
114 if (((unsigned long)type) > M_LAST)
115 panic("malloc - bogus type");
116 #endif
117 indx = BUCKETINDX(size);
118 kbp = &bucket[indx];
119 s = splimp();
120 #ifdef KMEMSTATS
121 while (ksp->ks_memuse >= ksp->ks_limit) {
122 if (flags & M_NOWAIT) {
123 splx(s);
124 return ((void *) NULL);
125 }
126 if (ksp->ks_limblocks < 65535)
127 ksp->ks_limblocks++;
128 tsleep((caddr_t)ksp, PSWP+2, memname[type], 0);
129 }
130 ksp->ks_size |= 1 << indx;
131 #endif
132 #ifdef DIAGNOSTIC
133 copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY;
134 #endif
135 if (kbp->kb_next == NULL) {
136 kbp->kb_last = NULL;
137 if (size > MAXALLOCSAVE)
138 allocsize = roundup(size, CLBYTES);
139 else
140 allocsize = 1 << indx;
141 npg = clrnd(btoc(allocsize));
142 va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg),
143 !(flags & M_NOWAIT));
144 if (va == NULL) {
145 /*
146 * Kmem_malloc() can return NULL, even if it can
147 * wait, if there is no map space avaiable, because
148 * it can't fix that problem. Neither can we,
149 * right now. (We should release pages which
150 * are completely free and which are in buckets
151 * with too many free elements.)
152 */
153 if ((flags & M_NOWAIT) == 0)
154 panic("malloc: out of space in kmem_map");
155 splx(s);
156 return ((void *) NULL);
157 }
158 #ifdef KMEMSTATS
159 kbp->kb_total += kbp->kb_elmpercl;
160 #endif
161 kup = btokup(va);
162 kup->ku_indx = indx;
163 if (allocsize > MAXALLOCSAVE) {
164 if (npg > 65535)
165 panic("malloc: allocation too large");
166 kup->ku_pagecnt = npg;
167 #ifdef KMEMSTATS
168 ksp->ks_memuse += allocsize;
169 #endif
170 goto out;
171 }
172 #ifdef KMEMSTATS
173 kup->ku_freecnt = kbp->kb_elmpercl;
174 kbp->kb_totalfree += kbp->kb_elmpercl;
175 #endif
176 /*
177 * Just in case we blocked while allocating memory,
178 * and someone else also allocated memory for this
179 * bucket, don't assume the list is still empty.
180 */
181 savedlist = kbp->kb_next;
182 kbp->kb_next = cp = va + (npg * NBPG) - allocsize;
183 for (;;) {
184 freep = (struct freelist *)cp;
185 #ifdef DIAGNOSTIC
186 /*
187 * Copy in known text to detect modification
188 * after freeing.
189 */
190 end = (int32_t *)&cp[copysize];
191 for (lp = (int32_t *)cp; lp < end; lp++)
192 *lp = WEIRD_ADDR;
193 freep->type = M_FREE;
194 #endif /* DIAGNOSTIC */
195 if (cp <= va)
196 break;
197 cp -= allocsize;
198 freep->next = cp;
199 }
200 freep->next = savedlist;
201 if (kbp->kb_last == NULL)
202 kbp->kb_last = (caddr_t)freep;
203 }
204 va = kbp->kb_next;
205 kbp->kb_next = ((struct freelist *)va)->next;
206 #ifdef DIAGNOSTIC
207 freep = (struct freelist *)va;
208 savedtype = (unsigned)freep->type < M_LAST ?
209 memname[freep->type] : "???";
210 if (kbp->kb_next &&
211 !kernacc(kbp->kb_next, sizeof(struct freelist), 0)) {
212 printf("%s %d of object %p size %ld %s %s (invalid addr %p)\n",
213 "Data modified on freelist: word",
214 (int32_t *)&kbp->kb_next - (int32_t *)kbp, va, size,
215 "previous type", savedtype, kbp->kb_next);
216 kbp->kb_next = NULL;
217 }
218
219 /* Fill the fields that we've used with WEIRD_ADDR */
220 #if BYTE_ORDER == BIG_ENDIAN
221 freep->type = WEIRD_ADDR >> 16;
222 #endif
223 #if BYTE_ORDER == LITTLE_ENDIAN
224 freep->type = (short)WEIRD_ADDR;
225 #endif
226 end = (int32_t *)&freep->next +
227 (sizeof(freep->next) / sizeof(int32_t));
228 for (lp = (int32_t *)&freep->next; lp < end; lp++)
229 *lp = WEIRD_ADDR;
230
231 /* and check that the data hasn't been modified. */
232 end = (int32_t *)&va[copysize];
233 for (lp = (int32_t *)va; lp < end; lp++) {
234 if (*lp == WEIRD_ADDR)
235 continue;
236 printf("%s %d of object %p size %ld %s %s (0x%x != 0x%x)\n",
237 "Data modified on freelist: word", lp - (int32_t *)va,
238 va, size, "previous type", savedtype, *lp, WEIRD_ADDR);
239 break;
240 }
241
242 freep->spare0 = 0;
243 #endif /* DIAGNOSTIC */
244 #ifdef KMEMSTATS
245 kup = btokup(va);
246 if (kup->ku_indx != indx)
247 panic("malloc: wrong bucket");
248 if (kup->ku_freecnt == 0)
249 panic("malloc: lost data");
250 kup->ku_freecnt--;
251 kbp->kb_totalfree--;
252 ksp->ks_memuse += 1 << indx;
253 out:
254 kbp->kb_calls++;
255 ksp->ks_inuse++;
256 ksp->ks_calls++;
257 if (ksp->ks_memuse > ksp->ks_maxused)
258 ksp->ks_maxused = ksp->ks_memuse;
259 #else
260 out:
261 #endif
262 splx(s);
263 return ((void *) va);
264 }
265
266 /*
267 * Free a block of memory allocated by malloc.
268 */
269 void
270 free(addr, type)
271 void *addr;
272 int type;
273 {
274 register struct kmembuckets *kbp;
275 register struct kmemusage *kup;
276 register struct freelist *freep;
277 long size;
278 int s;
279 #ifdef DIAGNOSTIC
280 caddr_t cp;
281 int32_t *end, *lp;
282 long alloc, copysize;
283 #endif
284 #ifdef KMEMSTATS
285 register struct kmemstats *ksp = &kmemstats[type];
286 #endif
287
288 kup = btokup(addr);
289 size = 1 << kup->ku_indx;
290 kbp = &bucket[kup->ku_indx];
291 s = splimp();
292 #ifdef DIAGNOSTIC
293 /*
294 * Check for returns of data that do not point to the
295 * beginning of the allocation.
296 */
297 if (size > NBPG * CLSIZE)
298 alloc = addrmask[BUCKETINDX(NBPG * CLSIZE)];
299 else
300 alloc = addrmask[kup->ku_indx];
301 if (((u_long)addr & alloc) != 0)
302 panic("free: unaligned addr %p, size %ld, type %s, mask %ld\n",
303 addr, size, memname[type], alloc);
304 #endif /* DIAGNOSTIC */
305 if (size > MAXALLOCSAVE) {
306 kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt));
307 #ifdef KMEMSTATS
308 size = kup->ku_pagecnt << PGSHIFT;
309 ksp->ks_memuse -= size;
310 kup->ku_indx = 0;
311 kup->ku_pagecnt = 0;
312 if (ksp->ks_memuse + size >= ksp->ks_limit &&
313 ksp->ks_memuse < ksp->ks_limit)
314 wakeup((caddr_t)ksp);
315 ksp->ks_inuse--;
316 kbp->kb_total -= 1;
317 #endif
318 splx(s);
319 return;
320 }
321 freep = (struct freelist *)addr;
322 #ifdef DIAGNOSTIC
323 /*
324 * Check for multiple frees. Use a quick check to see if
325 * it looks free before laboriously searching the freelist.
326 */
327 if (freep->spare0 == WEIRD_ADDR) {
328 for (cp = kbp->kb_next; cp;
329 cp = ((struct freelist *)cp)->next) {
330 if (addr != cp)
331 continue;
332 printf("multiply freed item %p\n", addr);
333 panic("free: duplicated free");
334 }
335 }
336 /*
337 * Copy in known text to detect modification after freeing
338 * and to make it look free. Also, save the type being freed
339 * so we can list likely culprit if modification is detected
340 * when the object is reallocated.
341 */
342 copysize = size < MAX_COPY ? size : MAX_COPY;
343 end = (int32_t *)&((caddr_t)addr)[copysize];
344 for (lp = (int32_t *)addr; lp < end; lp++)
345 *lp = WEIRD_ADDR;
346 freep->type = type;
347 #endif /* DIAGNOSTIC */
348 #ifdef KMEMSTATS
349 kup->ku_freecnt++;
350 if (kup->ku_freecnt >= kbp->kb_elmpercl)
351 if (kup->ku_freecnt > kbp->kb_elmpercl)
352 panic("free: multiple frees");
353 else if (kbp->kb_totalfree > kbp->kb_highwat)
354 kbp->kb_couldfree++;
355 kbp->kb_totalfree++;
356 ksp->ks_memuse -= size;
357 if (ksp->ks_memuse + size >= ksp->ks_limit &&
358 ksp->ks_memuse < ksp->ks_limit)
359 wakeup((caddr_t)ksp);
360 ksp->ks_inuse--;
361 #endif
362 if (kbp->kb_next == NULL)
363 kbp->kb_next = addr;
364 else
365 ((struct freelist *)kbp->kb_last)->next = addr;
366 freep->next = NULL;
367 kbp->kb_last = addr;
368 splx(s);
369 }
370
371 /*
372 * Initialize the kernel memory allocator
373 */
374 void
375 kmeminit()
376 {
377 register long indx;
378 int npg;
379
380 #if ((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0)
381 ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2
382 #endif
383 #if (MAXALLOCSAVE > MINALLOCSIZE * 32768)
384 ERROR!_kmeminit:_MAXALLOCSAVE_too_big
385 #endif
386 #if (MAXALLOCSAVE < CLBYTES)
387 ERROR!_kmeminit:_MAXALLOCSAVE_too_small
388 #endif
389
390 if (sizeof(struct freelist) > (1 << MINBUCKET))
391 panic("minbucket too small/struct freelist too big");
392
393 npg = VM_KMEM_SIZE/ NBPG;
394 kmemusage = (struct kmemusage *) kmem_alloc(kernel_map,
395 (vm_size_t)(npg * sizeof(struct kmemusage)));
396 kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase,
397 (vm_offset_t *)&kmemlimit, (vm_size_t)(npg * NBPG), FALSE);
398 #ifdef KMEMSTATS
399 for (indx = 0; indx < MINBUCKET + 16; indx++) {
400 if (1 << indx >= CLBYTES)
401 bucket[indx].kb_elmpercl = 1;
402 else
403 bucket[indx].kb_elmpercl = CLBYTES / (1 << indx);
404 bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl;
405 }
406 for (indx = 0; indx < M_LAST; indx++)
407 kmemstats[indx].ks_limit = npg * NBPG * 6 / 10;
408 #endif
409 }
410