ref: bf645afaac246967b9590ae7463f243c11d97480
dir: /sys/src/cmd/diff/test/diff-t8.expected/
--- diff-t8.1 +++ diff-t8.2 @@ -1,4 +1,5 @@ -/* $NetBSD: kern_malloc.c,v 1.11 1995/05/01 22:39:11 cgd Exp $ */ +/* $OpenBSD: t8.2,v 1.1 2003/07/17 21:04:04 otto Exp $ */ +/* $NetBSD: kern_malloc.c,v 1.15.4.2 1996/06/13 17:10:56 cgd Exp $ */ /* * Copyright (c) 1987, 1991, 1993 @@ -33,24 +34,59 @@ #include <sys/param.h> #include <sys/proc.h> -#include <sys/map.h> #include <sys/kernel.h> #include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/sysctl.h> -#include <vm/vm.h> -#include <vm/vm_kern.h> +#include <uvm/uvm_extern.h> + +static struct vm_map_intrsafe kmem_map_store; +struct vm_map *kmem_map = NULL; + +#ifdef NKMEMCLUSTERS +#error NKMEMCLUSTERS is obsolete; remove it from your kernel config file and use NKMEMPAGES instead or let the kernel auto-size +#endif + +/* + * Default number of pages in kmem_map. We attempt to calculate this + * at run-time, but allow it to be either patched or set in the kernel + * config file. + */ +#ifndef NKMEMPAGES +#define NKMEMPAGES 0 +#endif +int nkmempages = NKMEMPAGES; + +/* + * Defaults for lower- and upper-bounds for the kmem_map page count. + * Can be overridden by kernel config options. + */ +#ifndef NKMEMPAGES_MIN +#define NKMEMPAGES_MIN NKMEMPAGES_MIN_DEFAULT +#endif + +#ifndef NKMEMPAGES_MAX +#define NKMEMPAGES_MAX NKMEMPAGES_MAX_DEFAULT +#endif struct kmembuckets bucket[MINBUCKET + 16]; struct kmemstats kmemstats[M_LAST]; struct kmemusage *kmemusage; char *kmembase, *kmemlimit; +char buckstring[16 * sizeof("123456,")]; +int buckstring_init = 0; +#if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES) char *memname[] = INITKMEMNAMES; +char *memall = NULL; +extern struct lock sysctl_kmemlock; +#endif #ifdef DIAGNOSTIC /* * This structure provides a set of masks to catch unaligned frees. */ -long addrmask[] = { 0, +const long addrmask[] = { 0, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, @@ -61,14 +97,14 @@ * The WEIRD_ADDR is used as known text to copy into free objects so * that modifications after frees can be detected. */ -#define WEIRD_ADDR 0xdeadbeef +#define WEIRD_ADDR ((unsigned) 0xdeadbeef) #define MAX_COPY 32 /* * Normally the freelist structure is used only to hold the list pointer * for free objects. However, when running with diagnostics, the first * 8 bytes of the structure is unused except for diagnostic information, - * and the free list pointer is at offst 8 in the structure. Since the + * and the free list pointer is at offset 8 in the structure. Since the * first 8 bytes is the portion of the structure most often modified, this * helps to detect memory reuse problems and avoid free list corruption. */ @@ -106,12 +142,18 @@ #ifdef KMEMSTATS register struct kmemstats *ksp = &kmemstats[type]; - if (((unsigned long)type) > M_LAST) + if (((unsigned long)type) >= M_LAST) panic("malloc - bogus type"); #endif + +#ifdef MALLOC_DEBUG + if (debug_malloc(size, type, flags, (void **)&va)) + return ((void *) va); +#endif + indx = BUCKETINDX(size); kbp = &bucket[indx]; - s = splimp(); + s = splvm(); #ifdef KMEMSTATS while (ksp->ks_memuse >= ksp->ks_limit) { if (flags & M_NOWAIT) { @@ -130,13 +172,24 @@ if (kbp->kb_next == NULL) { kbp->kb_last = NULL; if (size > MAXALLOCSAVE) - allocsize = roundup(size, CLBYTES); + allocsize = round_page(size); else allocsize = 1 << indx; - npg = clrnd(btoc(allocsize)); - va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg), - !(flags & M_NOWAIT)); + npg = btoc(allocsize); + va = (caddr_t) uvm_km_kmemalloc(kmem_map, uvmexp.kmem_object, + (vsize_t)ctob(npg), + (flags & M_NOWAIT) ? UVM_KMF_NOWAIT : 0); if (va == NULL) { + /* + * Kmem_malloc() can return NULL, even if it can + * wait, if there is no map space available, because + * it can't fix that problem. Neither can we, + * right now. (We should release pages which + * are completely free and which are in buckets + * with too many free elements.) + */ + if ((flags & M_NOWAIT) == 0) + panic("malloc: out of space in kmem_map"); splx(s); return ((void *) NULL); } @@ -164,7 +217,7 @@ * bucket, don't assume the list is still empty. */ savedlist = kbp->kb_next; - kbp->kb_next = cp = va + (npg * NBPG) - allocsize; + kbp->kb_next = cp = va + (npg * PAGE_SIZE) - allocsize; for (;;) { freep = (struct freelist *)cp; #ifdef DIAGNOSTIC @@ -192,13 +245,22 @@ freep = (struct freelist *)va; savedtype = (unsigned)freep->type < M_LAST ? memname[freep->type] : "???"; - if (kbp->kb_next && - !kernacc(kbp->kb_next, sizeof(struct freelist), 0)) { - printf("%s %d of object %p size %d %s %s (invalid addr %p)\n", + if (kbp->kb_next) { + int rv; + vaddr_t addr = (vaddr_t)kbp->kb_next; + + vm_map_lock(kmem_map); + rv = uvm_map_checkprot(kmem_map, addr, + addr + sizeof(struct freelist), VM_PROT_WRITE); + vm_map_unlock(kmem_map); + + if (!rv) { + printf("%s %d of object %p size 0x%lx %s %s (invalid addr %p)\n", "Data modified on freelist: word", (int32_t *)&kbp->kb_next - (int32_t *)kbp, va, size, "previous type", savedtype, kbp->kb_next); kbp->kb_next = NULL; + } } /* Fill the fields that we've used with WEIRD_ADDR */ @@ -218,7 +280,7 @@ for (lp = (int32_t *)va; lp < end; lp++) { if (*lp == WEIRD_ADDR) continue; - printf("%s %d of object %p size %d %s %s (%p != %p)\n", + printf("%s %d of object %p size 0x%lx %s %s (0x%x != 0x%x)\n", "Data modified on freelist: word", lp - (int32_t *)va, va, size, "previous type", savedtype, *lp, WEIRD_ADDR); break; @@ -270,25 +332,36 @@ register struct kmemstats *ksp = &kmemstats[type]; #endif +#ifdef MALLOC_DEBUG + if (debug_free(addr, type)) + return; +#endif + +#ifdef DIAGNOSTIC + if (addr < (void *)kmembase || addr >= (void *)kmemlimit) + panic("free: non-malloced addr %p type %s", addr, + memname[type]); +#endif + kup = btokup(addr); size = 1 << kup->ku_indx; kbp = &bucket[kup->ku_indx]; - s = splimp(); + s = splvm(); #ifdef DIAGNOSTIC /* * Check for returns of data that do not point to the * beginning of the allocation. */ - if (size > NBPG * CLSIZE) - alloc = addrmask[BUCKETINDX(NBPG * CLSIZE)]; + if (size > PAGE_SIZE) + alloc = addrmask[BUCKETINDX(PAGE_SIZE)]; else alloc = addrmask[kup->ku_indx]; if (((u_long)addr & alloc) != 0) - panic("free: unaligned addr 0x%x, size %d, type %s, mask %d\n", + panic("free: unaligned addr %p, size %ld, type %s, mask %ld", addr, size, memname[type], alloc); #endif /* DIAGNOSTIC */ if (size > MAXALLOCSAVE) { - kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt)); + uvm_km_free(kmem_map, (vaddr_t)addr, ctob(kup->ku_pagecnt)); #ifdef KMEMSTATS size = kup->ku_pagecnt << PGSHIFT; ksp->ks_memuse -= size; @@ -310,7 +383,8 @@ * it looks free before laboriously searching the freelist. */ if (freep->spare0 == WEIRD_ADDR) { - for (cp = kbp->kb_next; cp; cp = *(caddr_t *)cp) { + for (cp = kbp->kb_next; cp; + cp = ((struct freelist *)cp)->next) { if (addr != cp) continue; printf("multiply freed item %p\n", addr); @@ -331,11 +405,12 @@ #endif /* DIAGNOSTIC */ #ifdef KMEMSTATS kup->ku_freecnt++; - if (kup->ku_freecnt >= kbp->kb_elmpercl) + if (kup->ku_freecnt >= kbp->kb_elmpercl) { if (kup->ku_freecnt > kbp->kb_elmpercl) panic("free: multiple frees"); else if (kbp->kb_totalfree > kbp->kb_highwat) kbp->kb_couldfree++; + } kbp->kb_totalfree++; ksp->ks_memuse -= size; if (ksp->ks_memuse + size >= ksp->ks_limit && @@ -353,40 +428,189 @@ } /* + * Compute the number of pages that kmem_map will map, that is, + * the size of the kernel malloc arena. + */ +void +kmeminit_nkmempages() +{ + int npages; + + if (nkmempages != 0) { + /* + * It's already been set (by us being here before, or + * by patching or kernel config options), bail out now. + */ + return; + } + + /* + * We use the following (simple) formula: + * + * - Starting point is physical memory / 4. + * + * - Clamp it down to NKMEMPAGES_MAX. + * + * - Round it up to NKMEMPAGES_MIN. + */ + npages = physmem / 4; + + if (npages > NKMEMPAGES_MAX) + npages = NKMEMPAGES_MAX; + + if (npages < NKMEMPAGES_MIN) + npages = NKMEMPAGES_MIN; + + nkmempages = npages; +} + +/* * Initialize the kernel memory allocator */ +void kmeminit() { - register long indx; - int npg; - -#if ((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0) - ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2 -#endif -#if (MAXALLOCSAVE > MINALLOCSIZE * 32768) - ERROR!_kmeminit:_MAXALLOCSAVE_too_big -#endif -#if (MAXALLOCSAVE < CLBYTES) - ERROR!_kmeminit:_MAXALLOCSAVE_too_small + vaddr_t base, limit; +#ifdef KMEMSTATS + long indx; #endif +#ifdef DIAGNOSTIC if (sizeof(struct freelist) > (1 << MINBUCKET)) - panic("minbucket too small/struct freelist too big"); + panic("kmeminit: minbucket too small/struct freelist too big"); +#endif - npg = VM_KMEM_SIZE/ NBPG; - kmemusage = (struct kmemusage *) kmem_alloc(kernel_map, - (vm_size_t)(npg * sizeof(struct kmemusage))); - kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase, - (vm_offset_t *)&kmemlimit, (vm_size_t)(npg * NBPG), FALSE); + /* + * Compute the number of kmem_map pages, if we have not + * done so already. + */ + kmeminit_nkmempages(); + base = vm_map_min(kernel_map); + kmem_map = uvm_km_suballoc(kernel_map, &base, &limit, + (vsize_t)(nkmempages * PAGE_SIZE), VM_MAP_INTRSAFE, FALSE, + &kmem_map_store.vmi_map); + kmembase = (char *)base; + kmemlimit = (char *)limit; + kmemusage = (struct kmemusage *) uvm_km_zalloc(kernel_map, + (vsize_t)(nkmempages * sizeof(struct kmemusage))); #ifdef KMEMSTATS for (indx = 0; indx < MINBUCKET + 16; indx++) { - if (1 << indx >= CLBYTES) + if (1 << indx >= PAGE_SIZE) bucket[indx].kb_elmpercl = 1; else - bucket[indx].kb_elmpercl = CLBYTES / (1 << indx); + bucket[indx].kb_elmpercl = PAGE_SIZE / (1 << indx); bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl; } for (indx = 0; indx < M_LAST; indx++) - kmemstats[indx].ks_limit = npg * NBPG * 6 / 10; + kmemstats[indx].ks_limit = nkmempages * PAGE_SIZE * 6 / 10; +#endif +#ifdef MALLOC_DEBUG + debug_malloc_init(); +#endif +} + +/* + * Return kernel malloc statistics information. + */ +int +sysctl_malloc(name, namelen, oldp, oldlenp, newp, newlen, p) + int *name; + u_int namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; + struct proc *p; +{ + struct kmembuckets kb; + int i, siz; + + if (namelen != 2 && name[0] != KERN_MALLOC_BUCKETS && + name[0] != KERN_MALLOC_KMEMNAMES) + return (ENOTDIR); /* overloaded */ + + switch (name[0]) { + case KERN_MALLOC_BUCKETS: + /* Initialize the first time */ + if (buckstring_init == 0) { + buckstring_init = 1; + bzero(buckstring, sizeof(buckstring)); + for (siz = 0, i = MINBUCKET; i < MINBUCKET + 16; i++) { + snprintf(buckstring + siz, + sizeof buckstring - siz, + "%d,", (u_int)(1<<i)); + siz += strlen(buckstring + siz); + } + /* Remove trailing comma */ + if (siz) + buckstring[siz - 1] = '\0'; + } + return (sysctl_rdstring(oldp, oldlenp, newp, buckstring)); + + case KERN_MALLOC_BUCKET: + bcopy(&bucket[BUCKETINDX(name[1])], &kb, sizeof(kb)); + kb.kb_next = kb.kb_last = 0; + return (sysctl_rdstruct(oldp, oldlenp, newp, &kb, sizeof(kb))); + case KERN_MALLOC_KMEMSTATS: +#ifdef KMEMSTATS + if ((name[1] < 0) || (name[1] >= M_LAST)) + return (EINVAL); + return (sysctl_rdstruct(oldp, oldlenp, newp, + &kmemstats[name[1]], sizeof(struct kmemstats))); +#else + return (EOPNOTSUPP); #endif + case KERN_MALLOC_KMEMNAMES: +#if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES) + if (memall == NULL) { + int totlen; + + i = lockmgr(&sysctl_kmemlock, LK_EXCLUSIVE, NULL, p); + if (i) + return (i); + + /* Figure out how large a buffer we need */ + for (totlen = 0, i = 0; i < M_LAST; i++) { + if (memname[i]) + totlen += strlen(memname[i]); + totlen++; + } + memall = malloc(totlen + M_LAST, M_SYSCTL, M_WAITOK); + bzero(memall, totlen + M_LAST); + for (siz = 0, i = 0; i < M_LAST; i++) { + snprintf(memall + siz, + totlen + M_LAST - siz, + "%s,", memname[i] ? memname[i] : ""); + siz += strlen(memall + siz); + } + /* Remove trailing comma */ + if (siz) + memall[siz - 1] = '\0'; + + /* Now, convert all spaces to underscores */ + for (i = 0; i < totlen; i++) + if (memall[i] == ' ') + memall[i] = '_'; + lockmgr(&sysctl_kmemlock, LK_RELEASE, NULL, p); + } + return (sysctl_rdstring(oldp, oldlenp, newp, memall)); +#else + return (EOPNOTSUPP); +#endif + default: + return (EOPNOTSUPP); + } + /* NOTREACHED */ +} + +/* + * Round up a size to how much malloc would actually allocate. + */ +size_t +malloc_roundup(size_t sz) +{ + if (sz > MAXALLOCSAVE) + return round_page(sz); + + return (1 << BUCKETINDX(sz)); }