Copyright (c) 1988 The Regents of the University of California.
All rights reserved.

%sccs.include.redist.man%

@(#)appendix.t 5.1 (Berkeley) 04/16/91

.bp .H 1 "Appendix A - Implementation Details"

.vS
/*
 * Constants for setting the parameters of the kernel memory allocator.
 *
 * 2 ** MINBUCKET is the smallest unit of memory that will be
 * allocated. It must be at least large enough to hold a pointer.
 *
 * Units of memory less or equal to MAXALLOCSAVE will permanently
 * allocate physical memory; requests for these size pieces of memory
 * are quite fast. Allocations greater than MAXALLOCSAVE must
 * always allocate and free physical memory; requests for these size
 * allocations should be done infrequently as they will be slow.
 * Constraints: CLBYTES <= MAXALLOCSAVE <= 2 ** (MINBUCKET + 14)
 * and MAXALLOCSIZE must be a power of two.
 */
#define MINBUCKET 4 /* 4 => min allocation of 16 bytes */
#define MAXALLOCSAVE (2 * CLBYTES)

/*
 * Maximum amount of kernel dynamic memory.
 * Constraints: must be a multiple of the pagesize.
 */
#define MAXKMEM (1024 * PAGESIZE)

/*
 * Arena for all kernel dynamic memory allocation.
 * This arena is known to start on a page boundary.
 */
extern char kmembase[MAXKMEM];

/*
 * Array of descriptors that describe the contents of each page
 */
struct kmemsizes {
 short ks_indx; /* bucket index, size of small allocations */
 u_short ks_pagecnt; /* for large allocations, pages allocated */
} kmemsizes[MAXKMEM / PAGESIZE];

/*
 * Set of buckets for each size of memory block that is retained
 */
struct kmembuckets {
 caddr_t kb_next; /* list of free blocks */
} bucket[MINBUCKET + 16];
.bp
/*
 * Macro to convert a size to a bucket index. If the size is constant,
 * this macro reduces to a compile time constant.
 */
#define MINALLOCSIZE (1 << MINBUCKET)
#define BUCKETINDX(size) \
 (size) <= (MINALLOCSIZE * 128) \
 ? (size) <= (MINALLOCSIZE * 8) \
 ? (size) <= (MINALLOCSIZE * 2) \
 ? (size) <= (MINALLOCSIZE * 1) \
 ? (MINBUCKET + 0) \
 : (MINBUCKET + 1) \
 : (size) <= (MINALLOCSIZE * 4) \
 ? (MINBUCKET + 2) \
 : (MINBUCKET + 3) \
 : (size) <= (MINALLOCSIZE* 32) \
 ? (size) <= (MINALLOCSIZE * 16) \
 ? (MINBUCKET + 4) \
 : (MINBUCKET + 5) \
 : (size) <= (MINALLOCSIZE * 64) \
 ? (MINBUCKET + 6) \
 : (MINBUCKET + 7) \
 : (size) <= (MINALLOCSIZE * 2048) \
 /* etc ... */

/*
 * Macro versions for the usual cases of malloc/free
 */
#define MALLOC(space, cast, size, flags) { \
 register struct kmembuckets *kbp = &bucket[BUCKETINDX(size)]; \
 long s = splimp(); \
 if (kbp->kb_next == NULL) { \
 (space) = (cast)malloc(size, flags); \
 } else { \
 (space) = (cast)kbp->kb_next; \
 kbp->kb_next = *(caddr_t *)(space); \
 } \
 splx(s); \
}

#define FREE(addr) { \
 register struct kmembuckets *kbp; \
 register struct kmemsizes *ksp = \
 &kmemsizes[((addr) - kmembase) / PAGESIZE]; \
 long s = splimp(); \
 if (1 << ksp->ks_indx > MAXALLOCSAVE) { \
 free(addr); \
 } else { \
 kbp = &bucket[ksp->ks_indx]; \
 *(caddr_t *)(addr) = kbp->kb_next; \
 kbp->kb_next = (caddr_t)(addr); \
 } \
 splx(s); \
}
.vE