dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / psm / stand / boot / common / heap_kmem.c
blob0026258fba48ca315e37debd641443ce9fc6d37e
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
29 #if 1
30 #undef DEBUG
31 #endif
33 /* #define DEBUG ON */
36 * Conditions on use:
37 * kmem_alloc and kmem_free must not be called from interrupt level,
38 * except from software interrupt level. This is because they are
39 * not reentrant, and only block out software interrupts. They take
40 * too long to block any real devices. There is a routine
41 * kmem_free_intr that can be used to free blocks at interrupt level,
42 * but only up to splimp, not higher. This is because kmem_free_intr
43 * only spl's to splimp.
45 * Also, these routines are not that fast, so they should not be used
46 * in very frequent operations (e.g. operations that happen more often
47 * than, say, once every few seconds).
51 * description:
52 * Yet another memory allocator, this one based on a method
53 * described in C.J. Stephenson, "Fast Fits", IBM Sys. Journal
55 * The basic data structure is a "Cartesian" binary tree, in which
56 * nodes are ordered by ascending addresses (thus minimizing free
57 * list insertion time) and block sizes decrease with depth in the
58 * tree (thus minimizing search time for a block of a given size).
60 * In other words, for any node s, letting D(s) denote
61 * the set of descendents of s, we have:
63 * a. addr(D(left(s))) < addr(s) < addr(D(right(s)))
64 * b. len(D(left(s))) <= len(s) >= len(D(right(s)))
67 #include <sys/param.h>
68 #include <sys/sysmacros.h>
69 #include <sys/salib.h>
70 #include <sys/saio.h>
71 #include <sys/promif.h>
74 * The node header structure.
76 * To reduce storage consumption, a header block is associated with
77 * free blocks only, not allocated blocks.
78 * When a free block is allocated, its header block is put on
79 * a free header block list.
81 * This creates a header space and a free block space.
82 * The left pointer of a header blocks is used to chain free header
83 * blocks together.
86 typedef enum {false, true} bool;
87 typedef struct freehdr *Freehdr;
88 typedef struct dblk *Dblk;
91 * Description of a header for a free block
92 * Only free blocks have such headers.
94 struct freehdr {
95 Freehdr left; /* Left tree pointer */
96 Freehdr right; /* Right tree pointer */
97 Dblk block; /* Ptr to the data block */
98 size_t size; /* Size of the data block */
101 #define NIL ((Freehdr) 0)
102 #define WORDSIZE sizeof (int)
103 #define SMALLEST_BLK 1 /* Size of smallest block */
106 * Description of a data block.
108 struct dblk {
109 char data[1]; /* Addr returned to the caller */
113 * weight(x) is the size of a block, in bytes; or 0 if and only if x
114 * is a null pointer. It is the responsibility of kmem_alloc() and
115 * kmem_free() to keep zero-length blocks out of the arena.
118 #define weight(x) ((x) == NIL? 0: (x->size))
119 #define nextblk(p, size) ((Dblk) ((char *)(p) + (size)))
120 #define max(a, b) ((a) < (b)? (b): (a))
122 void *kmem_alloc(size_t, int);
123 void kmem_free(void *ptr, size_t nbytes);
124 Freehdr getfreehdr(void);
125 static bool morecore(size_t);
126 void insert(Dblk p, size_t len, Freehdr *tree);
127 void freehdr(Freehdr p);
128 void delete(Freehdr *p);
129 static void check_need_to_free(void);
130 extern caddr_t resalloc(enum RESOURCES, size_t, caddr_t, int);
131 extern int splnet(void);
132 extern int splimp(void);
133 extern void splx(int);
136 * Structure containing various info about allocated memory.
138 #define NEED_TO_FREE_SIZE 5
139 struct kmem_info {
140 Freehdr free_root;
141 Freehdr free_hdr_list;
142 struct map *map;
143 struct pte *pte;
144 caddr_t vaddr;
145 struct need_to_free {
146 caddr_t addr;
147 size_t nbytes;
148 } need_to_free_list, need_to_free[NEED_TO_FREE_SIZE];
149 } kmem_info;
152 struct map *kernelmap;
154 #ifdef DEBUG
155 static void prtree(Freehdr, char *);
156 #endif
159 * Initialize kernel memory allocator
162 void
163 kmem_init(void)
165 int i;
166 struct need_to_free *ntf;
168 #ifdef DEBUG
169 printf("kmem_init entered\n");
170 #endif
173 kmem_info.free_root = NIL;
174 kmem_info.free_hdr_list = NULL;
175 kmem_info.map = kernelmap;
176 kmem_info.need_to_free_list.addr = 0;
177 ntf = kmem_info.need_to_free;
178 for (i = 0; i < NEED_TO_FREE_SIZE; i++) {
179 ntf[i].addr = 0;
181 #ifdef DEBUG
182 printf("kmem_init returning\n");
183 prtree(kmem_info.free_root, "kmem_init");
184 #endif
188 * Insert a new node in a cartesian tree or subtree, placing it
189 * in the correct position with respect to the existing nodes.
191 * algorithm:
192 * Starting from the root, a binary search is made for the new
193 * node. If this search were allowed to continue, it would
194 * eventually fail (since there cannot already be a node at the
195 * given address); but in fact it stops when it reaches a node in
196 * the tree which has a length less than that of the new node (or
197 * when it reaches a null tree pointer). The new node is then
198 * inserted at the root of the subtree for which the shorter node
199 * forms the old root (or in place of the null pointer).
203 void
204 insert(Dblk p, /* Ptr to the block to insert */
205 size_t len, /* Length of new node */
206 Freehdr *tree) /* Address of ptr to root */
208 Freehdr x;
209 Freehdr *left_hook; /* Temp for insertion */
210 Freehdr *right_hook; /* Temp for insertion */
211 Freehdr newhdr;
213 x = *tree;
215 * Search for the first node which has a weight less
216 * than that of the new node; this will be the
217 * point at which we insert the new node.
220 while (weight(x) >= len) {
221 if (p < x->block)
222 tree = &x->left;
223 else
224 tree = &x->right;
225 x = *tree;
229 * Perform root insertion. The variable x traces a path through
230 * the tree, and with the help of left_hook and right_hook,
231 * rewrites all links that cross the territory occupied
232 * by p. Note that this improves performance under
233 * paging.
236 newhdr = getfreehdr();
237 *tree = newhdr;
238 left_hook = &newhdr->left;
239 right_hook = &newhdr->right;
241 newhdr->left = NIL;
242 newhdr->right = NIL;
243 newhdr->block = p;
244 newhdr->size = len;
246 while (x != NIL) {
248 * Remark:
249 * The name 'left_hook' is somewhat confusing, since
250 * it is always set to the address of a .right link
251 * field. However, its value is always an address
252 * below (i.e., to the left of) p. Similarly
253 * for right_hook. The values of left_hook and
254 * right_hook converge toward the value of p,
255 * as in a classical binary search.
257 if (x->block < p) {
259 * rewrite link crossing from the left
261 *left_hook = x;
262 left_hook = &x->right;
263 x = x->right;
264 } else {
266 * rewrite link crossing from the right
268 *right_hook = x;
269 right_hook = &x->left;
270 x = x->left;
271 } /* else */
272 } /* while */
274 *left_hook = *right_hook = NIL; /* clear remaining hooks */
276 } /* insert */
280 * Delete a node from a cartesian tree. p is the address of
281 * a pointer to the node which is to be deleted.
283 * algorithm:
284 * The left and right sons of the node to be deleted define two
285 * subtrees which are to be merged and attached in place of the
286 * deleted node. Each node on the inside edges of these two
287 * subtrees is examined and longer nodes are placed above the
288 * shorter ones.
290 * On entry:
291 * *p is assumed to be non-null.
294 void
295 delete(Freehdr *p)
297 Freehdr x;
298 Freehdr left_branch; /* left subtree of deleted node */
299 Freehdr right_branch; /* right subtree of deleted node */
301 x = *p;
302 left_branch = x->left;
303 right_branch = x->right;
305 while (left_branch != right_branch) {
307 * iterate until left branch and right branch are
308 * both NIL.
310 if (weight(left_branch) >= weight(right_branch)) {
312 * promote the left branch
314 *p = left_branch;
315 p = &left_branch->right;
316 left_branch = left_branch->right;
317 } else {
319 * promote the right branch
321 *p = right_branch;
322 p = &right_branch->left;
323 right_branch = right_branch->left;
324 } /* else */
325 } /* while */
326 *p = NIL;
327 freehdr(x);
328 } /* delete */
332 * Demote a node in a cartesian tree, if necessary, to establish
333 * the required vertical ordering.
335 * algorithm:
336 * The left and right subtrees of the node to be demoted are to
337 * be partially merged and attached in place of the demoted node.
338 * The nodes on the inside edges of these two subtrees are
339 * examined and the longer nodes are placed above the shorter
340 * ones, until a node is reached which has a length no greater
341 * than that of the node being demoted (or until a null pointer
342 * is reached). The node is then attached at this point, and
343 * the remaining subtrees (if any) become its descendants.
345 * on entry:
346 * a. All the nodes in the tree, including the one to be demoted,
347 * must be correctly ordered horizontally;
348 * b. All the nodes except the one to be demoted must also be
349 * correctly positioned vertically. The node to be demoted
350 * may be already correctly positioned vertically, or it may
351 * have a length which is less than that of one or both of
352 * its progeny.
353 * c. *p is non-null
357 static void
358 demote(Freehdr *p)
360 Freehdr x; /* addr of node to be demoted */
361 Freehdr left_branch;
362 Freehdr right_branch;
363 size_t wx;
365 x = *p;
366 left_branch = x->left;
367 right_branch = x->right;
368 wx = weight(x);
370 while (weight(left_branch) > wx || weight(right_branch) > wx) {
372 * select a descendant branch for promotion
374 if (weight(left_branch) >= weight(right_branch)) {
376 * promote the left branch
378 *p = left_branch;
379 p = &left_branch->right;
380 left_branch = *p;
381 } else {
383 * promote the right branch
385 *p = right_branch;
386 p = &right_branch->left;
387 right_branch = *p;
388 } /* else */
389 } /* while */
391 *p = x; /* attach demoted node here */
392 x->left = left_branch;
393 x->right = right_branch;
394 } /* demote */
397 * Allocate a block of storage
399 * algorithm:
400 * The freelist is searched by descending the tree from the root
401 * so that at each decision point the "better fitting" child node
402 * is chosen (i.e., the shorter one, if it is long enough, or
403 * the longer one, otherwise). The descent stops when both
404 * child nodes are too short.
406 * function result:
407 * kmem_alloc returns a pointer to the allocated block; a null
408 * pointer indicates storage could not be allocated.
411 * We need to return blocks that are on word boundaries so that callers
412 * that are putting int's into the area will work. Since we allow
413 * arbitrary free'ing, we need a weight function that considers
414 * free blocks starting on an odd boundary special. Allocation is
415 * aligned to 8 byte boundaries (ALIGN).
417 #define ALIGN 8 /* doubleword aligned .. */
418 #define ALIGNMASK (ALIGN-1)
419 #define ALIGNMORE(addr) (ALIGN - ((uintptr_t)(addr) & ALIGNMASK))
422 * If it is empty then weight == 0
423 * If it is aligned then weight == size
424 * If it is unaligned
425 * if not enough room to align then weight == 0
426 * else weight == aligned size
428 #define mweight(x) ((x) == NIL ? 0 : \
429 ((((uintptr_t)(x)->block) & ALIGNMASK) == 0 ? (x)->size : \
430 (((x)->size <= ALIGNMORE((x)->block)) ? 0 : \
431 (x)->size - ALIGNMORE((x)->block))))
433 /*ARGSUSED1*/
434 void *
435 kmem_alloc(size_t nbytes, int kmflag)
437 Freehdr a; /* ptr to node to be allocated */
438 Freehdr *p; /* address of ptr to node */
439 size_t left_weight;
440 size_t right_weight;
441 Freehdr left_son;
442 Freehdr right_son;
443 char *retblock; /* Address returned to the user */
444 int s;
445 #ifdef DEBUG
446 printf("kmem_alloc(nbytes 0x%lx)\n", nbytes);
447 #endif /* DEBUG */
449 if (nbytes == 0) {
450 return (NULL);
452 s = splnet();
454 if (nbytes < SMALLEST_BLK) {
455 printf("illegal kmem_alloc call for %lx bytes\n", nbytes);
456 prom_panic("kmem_alloc");
458 check_need_to_free();
461 * ensure that at least one block is big enough to satisfy
462 * the request.
465 if (mweight(kmem_info.free_root) <= nbytes) {
467 * the largest block is not enough.
469 if (!morecore(nbytes)) {
470 printf("kmem_alloc failed, nbytes %lx\n", nbytes);
471 prom_panic("kmem_alloc");
476 * search down through the tree until a suitable block is
477 * found. At each decision point, select the better
478 * fitting node.
481 p = (Freehdr *) &kmem_info.free_root;
482 a = *p;
483 left_son = a->left;
484 right_son = a->right;
485 left_weight = mweight(left_son);
486 right_weight = mweight(right_son);
488 while (left_weight >= nbytes || right_weight >= nbytes) {
489 if (left_weight <= right_weight) {
490 if (left_weight >= nbytes) {
491 p = &a->left;
492 a = left_son;
493 } else {
494 p = &a->right;
495 a = right_son;
497 } else {
498 if (right_weight >= nbytes) {
499 p = &a->right;
500 a = right_son;
501 } else {
502 p = &a->left;
503 a = left_son;
506 left_son = a->left;
507 right_son = a->right;
508 left_weight = mweight(left_son);
509 right_weight = mweight(right_son);
510 } /* while */
513 * allocate storage from the selected node.
516 if (a->size - nbytes < SMALLEST_BLK) {
518 * not big enough to split; must leave at least
519 * a dblk's worth of space.
521 retblock = a->block->data;
522 delete(p);
523 } else {
526 * split the node, allocating nbytes from the top.
527 * Remember we've already accounted for the
528 * allocated node's header space.
530 Freehdr x;
531 x = getfreehdr();
532 if ((uintptr_t)a->block->data & ALIGNMASK) {
533 size_t size;
534 if (a->size <= ALIGNMORE(a->block->data))
535 prom_panic("kmem_alloc: short block allocated");
536 size = nbytes + ALIGNMORE(a->block->data);
537 x->block = a->block;
538 x->size = ALIGNMORE(a->block->data);
539 x->left = a->left;
540 x->right = a->right;
542 * the node pointed to by *p has become smaller;
543 * move it down to its appropriate place in
544 * the tree.
546 *p = x;
547 demote(p);
548 retblock = a->block->data + ALIGNMORE(a->block->data);
549 if (a->size > size) {
550 kmem_free((caddr_t)nextblk(a->block, size),
551 (size_t)(a->size - size));
553 freehdr(a);
554 } else {
555 x->block = nextblk(a->block, nbytes);
556 x->size = a->size - nbytes;
557 x->left = a->left;
558 x->right = a->right;
560 * the node pointed to by *p has become smaller;
561 * move it down to its appropriate place in
562 * the tree.
564 *p = x;
565 demote(p);
566 retblock = a->block->data;
567 freehdr(a);
570 #ifdef DEBUG
571 prtree(kmem_info.free_root, "kmem_alloc");
572 #endif
574 splx(s);
575 bzero(retblock, nbytes);
576 #ifdef DEBUG
577 printf("kmem_alloc bzero complete - returning %p\n", retblock);
578 #endif
579 return (retblock);
581 } /* kmem_alloc */
584 * Return a block to the free space tree.
586 * algorithm:
587 * Starting at the root, search for and coalesce free blocks
588 * adjacent to one given. When the appropriate place in the
589 * tree is found, insert the given block.
591 * Do some sanity checks to avoid total confusion in the tree.
592 * If the block has already been freed, prom_panic.
593 * If the ptr is not from the arena, prom_panic.
595 void
596 kmem_free(void *ptr, size_t nbytes)
598 Freehdr *np; /* For deletion from free list */
599 Freehdr neighbor; /* Node to be coalesced */
600 char *neigh_block; /* Ptr to potential neighbor */
601 size_t neigh_size; /* Size of potential neighbor */
602 int s;
604 #ifdef DEBUG
605 printf("kmem_free (ptr %p nbytes %lx)\n", ptr, nbytes);
606 prtree(kmem_info.free_root, "kmem_free");
607 #endif
609 if (nbytes == 0) {
610 return;
613 if (ptr == 0) {
614 prom_panic("kmem_free of 0");
616 s = splnet();
619 * Search the tree for the correct insertion point for this
620 * node, coalescing adjacent free blocks along the way.
622 np = &kmem_info.free_root;
623 neighbor = *np;
624 while (neighbor != NIL) {
625 neigh_block = (char *)neighbor->block;
626 neigh_size = neighbor->size;
627 if ((char *)ptr < neigh_block) {
628 if ((char *)ptr + nbytes == neigh_block) {
630 * Absorb and delete right neighbor
632 nbytes += neigh_size;
633 delete(np);
634 } else if ((char *)ptr + nbytes > neigh_block) {
636 * The block being freed overlaps
637 * another block in the tree. This
638 * is bad news.
640 printf("kmem_free: free block overlap %p+%lx"
641 " over %p\n", (void *)ptr, nbytes,
642 (void *)neigh_block);
643 prom_panic("kmem_free: free block overlap");
644 } else {
646 * Search to the left
648 np = &neighbor->left;
650 } else if ((char *)ptr > neigh_block) {
651 if (neigh_block + neigh_size == ptr) {
653 * Absorb and delete left neighbor
655 ptr = neigh_block;
656 nbytes += neigh_size;
657 delete(np);
658 } else if (neigh_block + neigh_size > (char *)ptr) {
660 * This block has already been freed
662 prom_panic("kmem_free block already free");
663 } else {
665 * search to the right
667 np = &neighbor->right;
669 } else {
671 * This block has already been freed
672 * as "ptr == neigh_block"
674 prom_panic("kmem_free: block already free as neighbor");
675 return;
676 } /* else */
677 neighbor = *np;
678 } /* while */
681 * Insert the new node into the free space tree
683 insert((Dblk) ptr, nbytes, &kmem_info.free_root);
684 #ifdef DEBUG
685 printf("exiting kmem_free\n");
686 prtree(kmem_info.free_root, "kmem_free");
687 #endif
688 splx(s);
689 } /* kmem_free */
692 * Sigh. We include a header file which the kernel
693 * uses to declare (one of its many) kmem_free prototypes.
694 * In order not to use the kernel's namespace, then, we must
695 * define another name here for use by boot.
697 void *
698 bkmem_alloc(size_t size)
700 return (kmem_alloc(size, 0));
704 * Boot's kmem_alloc is really kmem_zalloc().
706 void *
707 bkmem_zalloc(size_t size)
709 return (kmem_alloc(size, 0));
712 void
713 bkmem_free(void *p, size_t bytes)
715 kmem_free(p, bytes);
718 static void
719 check_need_to_free(void)
721 int i;
722 struct need_to_free *ntf;
723 caddr_t addr;
724 size_t nbytes;
725 int s;
727 again:
728 s = splimp();
729 ntf = &kmem_info.need_to_free_list;
730 if (ntf->addr) {
731 addr = ntf->addr;
732 nbytes = ntf->nbytes;
733 *ntf = *(struct need_to_free *)ntf->addr;
734 splx(s);
735 kmem_free(addr, nbytes);
736 goto again;
738 ntf = kmem_info.need_to_free;
739 for (i = 0; i < NEED_TO_FREE_SIZE; i++) {
740 if (ntf[i].addr) {
741 addr = ntf[i].addr;
742 nbytes = ntf[i].nbytes;
743 ntf[i].addr = 0;
744 splx(s);
745 kmem_free(addr, nbytes);
746 goto again;
749 splx(s);
753 * Add a block of at least nbytes to the free space tree.
755 * return value:
756 * true if at least nbytes can be allocated
757 * false otherwise
759 * remark:
760 * free space (delimited by the static variable ubound) is
761 * extended by an amount determined by rounding nbytes up to
762 * a multiple of the system page size.
765 static bool
766 morecore(size_t nbytes)
768 enum RESOURCES type = RES_BOOTSCRATCH;
769 Dblk p;
770 #ifdef DEBUG
771 printf("morecore(nbytes 0x%lx)\n", nbytes);
772 #endif /* DEBUG */
775 nbytes = roundup(nbytes, PAGESIZE);
776 p = (Dblk) resalloc(type, nbytes, (caddr_t)0, 0);
777 if (p == 0) {
778 return (false);
780 kmem_free((caddr_t)p, nbytes);
781 #ifdef DEBUG
782 printf("morecore() returing, p = %p\n", p);
783 #endif
784 return (true);
786 } /* morecore */
789 * Get a free block header
790 * There is a list of available free block headers.
791 * When the list is empty, allocate another pagefull.
793 Freehdr
794 getfreehdr(void)
796 Freehdr r;
797 int n = 0;
798 #ifdef DEBUG
799 printf("getfreehdr()\n");
800 #endif /* DEBUG */
802 if (kmem_info.free_hdr_list != NIL) {
803 r = kmem_info.free_hdr_list;
804 kmem_info.free_hdr_list = kmem_info.free_hdr_list->left;
805 } else {
806 r = (Freehdr)resalloc(RES_BOOTSCRATCH, PAGESIZE, (caddr_t)0, 0);
807 if (r == 0) {
808 prom_panic("getfreehdr");
810 for (n = 1; n < PAGESIZE / sizeof (*r); n++) {
811 freehdr(&r[n]);
814 #ifdef DEBUG
815 printf("getfreehdr: freed %x headers\n", n);
816 printf("getfreehdr: returning %p\n", r);
817 #endif /* DEBUG */
818 return (r);
822 * Free a free block header
823 * Add it to the list of available headers.
826 void
827 freehdr(Freehdr p)
829 #ifdef DEBUG
830 printf("freehdr(%p)\n", p);
831 #endif /* DEBUG */
832 p->left = kmem_info.free_hdr_list;
833 p->right = NIL;
834 p->block = NULL;
835 kmem_info.free_hdr_list = p;
838 #ifdef DEBUG
840 * Diagnostic routines
842 static int depth = 0;
844 static void
845 prtree(Freehdr p, char *cp)
847 int n;
848 if (depth == 0) {
849 printf("prtree(p %p cp %s)\n", p, cp);
851 if (p != NIL) {
852 depth++;
853 prtree(p->left, NULL);
854 depth--;
856 for (n = 0; n < depth; n++) {
857 printf(" ");
859 printf(
860 "(%p): (left = %p, right = %p, block = %p, size = %lx)\n",
861 p, p->left, p->right, p->block, p->size);
863 depth++;
864 prtree(p->right, NULL);
865 depth--;
868 #endif /* DEBUG */