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