|
|
1.1 ! root 1: /* ! 2: * malloc.c --- a general purpose kernel memory allocator for Linux. ! 3: * ! 4: * Written by Theodore Ts'o ([email protected]), 11/29/91 ! 5: * ! 6: * This routine is written to be as fast as possible, so that it ! 7: * can be called from the interrupt level. ! 8: * ! 9: * Limitations: maximum size of memory we can allocate using this routine ! 10: * is 4k, the size of a page in Linux. ! 11: * ! 12: * The general game plan is that each page (called a bucket) will only hold ! 13: * objects of a given size. When all of the object on a page are released, ! 14: * the page can be returned to the general free pool. When malloc() is ! 15: * called, it looks for the smallest bucket size which will fulfill its ! 16: * request, and allocate a piece of memory from that bucket pool. ! 17: * ! 18: * Each bucket has as its control block a bucket descriptor which keeps ! 19: * track of how many objects are in use on that page, and the free list ! 20: * for that page. Like the buckets themselves, bucket descriptors are ! 21: * stored on pages requested from get_free_page(). However, unlike buckets, ! 22: * pages devoted to bucket descriptor pages are never released back to the ! 23: * system. Fortunately, a system should probably only need 1 or 2 bucket ! 24: * descriptor pages, since a page can hold 256 bucket descriptors (which ! 25: * corresponds to 1 megabyte worth of bucket pages.) If the kernel is using ! 26: * that much allocated memory, it's probably doing something wrong. :-) ! 27: * ! 28: * Note: malloc() and free() both call get_free_page() and free_page() ! 29: * in sections of code where interrupts are turned off, to allow ! 30: * malloc() and free() to be safely called from an interrupt routine. ! 31: * (We will probably need this functionality when networking code, ! 32: * particularily things like NFS, is added to Linux.) However, this ! 33: * presumes that get_free_page() and free_page() are interrupt-level ! 34: * safe, which they may not be once paging is added. If this is the ! 35: * case, we will need to modify malloc() to keep a few unused pages ! 36: * "pre-allocated" so that it can safely draw upon those pages if ! 37: * it is called from an interrupt routine. ! 38: * ! 39: * Another concern is that get_free_page() should not sleep; if it ! 40: * does, the code is carefully ordered so as to avoid any race ! 41: * conditions. The catch is that if malloc() is called re-entrantly, ! 42: * there is a chance that unecessary pages will be grabbed from the ! 43: * system. Except for the pages for the bucket descriptor page, the ! 44: * extra pages will eventually get released back to the system, though, ! 45: * so it isn't all that bad. ! 46: */ ! 47: ! 48: #include <linux/kernel.h> ! 49: #include <linux/mm.h> ! 50: #include <asm/system.h> ! 51: ! 52: struct bucket_desc { /* 16 bytes */ ! 53: void *page; ! 54: struct bucket_desc *next; ! 55: void *freeptr; ! 56: unsigned short refcnt; ! 57: unsigned short bucket_size; ! 58: }; ! 59: ! 60: struct _bucket_dir { /* 8 bytes */ ! 61: int size; ! 62: struct bucket_desc *chain; ! 63: }; ! 64: ! 65: /* ! 66: * The following is the where we store a pointer to the first bucket ! 67: * descriptor for a given size. ! 68: * ! 69: * If it turns out that the Linux kernel allocates a lot of objects of a ! 70: * specific size, then we may want to add that specific size to this list, ! 71: * since that will allow the memory to be allocated more efficiently. ! 72: * However, since an entire page must be dedicated to each specific size ! 73: * on this list, some amount of temperance must be exercised here. ! 74: * ! 75: * Note that this list *must* be kept in order. ! 76: */ ! 77: struct _bucket_dir bucket_dir[] = { ! 78: { 16, (struct bucket_desc *) 0}, ! 79: { 32, (struct bucket_desc *) 0}, ! 80: { 64, (struct bucket_desc *) 0}, ! 81: { 128, (struct bucket_desc *) 0}, ! 82: { 256, (struct bucket_desc *) 0}, ! 83: { 512, (struct bucket_desc *) 0}, ! 84: { 1024, (struct bucket_desc *) 0}, ! 85: { 2048, (struct bucket_desc *) 0}, ! 86: { 4096, (struct bucket_desc *) 0}, ! 87: { 0, (struct bucket_desc *) 0}}; /* End of list marker */ ! 88: ! 89: /* ! 90: * This contains a linked list of free bucket descriptor blocks ! 91: */ ! 92: struct bucket_desc *free_bucket_desc = (struct bucket_desc *) 0; ! 93: ! 94: /* ! 95: * This routine initializes a bucket description page. ! 96: */ ! 97: static inline void init_bucket_desc() ! 98: { ! 99: struct bucket_desc *bdesc, *first; ! 100: int i; ! 101: ! 102: first = bdesc = (struct bucket_desc *) get_free_page(); ! 103: if (!bdesc) ! 104: panic("Out of memory in init_bucket_desc()"); ! 105: for (i = PAGE_SIZE/sizeof(struct bucket_desc); i > 1; i--) { ! 106: bdesc->next = bdesc+1; ! 107: bdesc++; ! 108: } ! 109: /* ! 110: * This is done last, to avoid race conditions in case ! 111: * get_free_page() sleeps and this routine gets called again.... ! 112: */ ! 113: bdesc->next = free_bucket_desc; ! 114: free_bucket_desc = first; ! 115: } ! 116: ! 117: void *malloc(unsigned int len) ! 118: { ! 119: struct _bucket_dir *bdir; ! 120: struct bucket_desc *bdesc; ! 121: void *retval; ! 122: ! 123: /* ! 124: * First we search the bucket_dir to find the right bucket change ! 125: * for this request. ! 126: */ ! 127: for (bdir = bucket_dir; bdir->size; bdir++) ! 128: if (bdir->size >= len) ! 129: break; ! 130: if (!bdir->size) { ! 131: printk("malloc called with impossibly large argument (%d)\n", ! 132: len); ! 133: panic("malloc: bad arg"); ! 134: } ! 135: /* ! 136: * Now we search for a bucket descriptor which has free space ! 137: */ ! 138: cli(); /* Avoid race conditions */ ! 139: for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) ! 140: if (bdesc->freeptr) ! 141: break; ! 142: /* ! 143: * If we didn't find a bucket with free space, then we'll ! 144: * allocate a new one. ! 145: */ ! 146: if (!bdesc) { ! 147: char *cp; ! 148: int i; ! 149: ! 150: if (!free_bucket_desc) ! 151: init_bucket_desc(); ! 152: bdesc = free_bucket_desc; ! 153: free_bucket_desc = bdesc->next; ! 154: bdesc->refcnt = 0; ! 155: bdesc->bucket_size = bdir->size; ! 156: bdesc->page = bdesc->freeptr = (void *) cp = get_free_page(); ! 157: if (!cp) ! 158: panic("Out of memory in kernel malloc()"); ! 159: /* Set up the chain of free objects */ ! 160: for (i=PAGE_SIZE/bdir->size; i > 1; i--) { ! 161: *((char **) cp) = cp + bdir->size; ! 162: cp += bdir->size; ! 163: } ! 164: *((char **) cp) = 0; ! 165: bdesc->next = bdir->chain; /* OK, link it in! */ ! 166: bdir->chain = bdesc; ! 167: } ! 168: retval = (void *) bdesc->freeptr; ! 169: bdesc->freeptr = *((void **) retval); ! 170: bdesc->refcnt++; ! 171: sti(); /* OK, we're safe again */ ! 172: return(retval); ! 173: } ! 174: ! 175: /* ! 176: * Here is the free routine. If you know the size of the object that you ! 177: * are freeing, then free_s() will use that information to speed up the ! 178: * search for the bucket descriptor. ! 179: * ! 180: * We will #define a macro so that "free(x)" is becomes "free_s(x, 0)" ! 181: */ ! 182: void free_s(void *obj, int size) ! 183: { ! 184: void *page; ! 185: struct _bucket_dir *bdir; ! 186: struct bucket_desc *bdesc, *prev; ! 187: ! 188: /* Calculate what page this object lives in */ ! 189: page = (void *) ((unsigned long) obj & 0xfffff000); ! 190: /* Now search the buckets looking for that page */ ! 191: for (bdir = bucket_dir; bdir->size; bdir++) { ! 192: prev = 0; ! 193: /* If size is zero then this conditional is always false */ ! 194: if (bdir->size < size) ! 195: continue; ! 196: for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) { ! 197: if (bdesc->page == page) ! 198: goto found; ! 199: prev = bdesc; ! 200: } ! 201: } ! 202: panic("Bad address passed to kernel free_s()"); ! 203: found: ! 204: cli(); /* To avoid race conditions */ ! 205: *((void **)obj) = bdesc->freeptr; ! 206: bdesc->freeptr = obj; ! 207: bdesc->refcnt--; ! 208: if (bdesc->refcnt == 0) { ! 209: /* ! 210: * We need to make sure that prev is still accurate. It ! 211: * may not be, if someone rudely interrupted us.... ! 212: */ ! 213: if ((prev && (prev->next != bdesc)) || ! 214: (!prev && (bdir->chain != bdesc))) ! 215: for (prev = bdir->chain; prev; prev = prev->next) ! 216: if (prev->next == bdesc) ! 217: break; ! 218: if (prev) ! 219: prev->next = bdesc->next; ! 220: else { ! 221: if (bdir->chain != bdesc) ! 222: panic("malloc bucket chains corrupted"); ! 223: bdir->chain = bdesc->next; ! 224: } ! 225: free_page((unsigned long) bdesc->page); ! 226: bdesc->next = free_bucket_desc; ! 227: free_bucket_desc = bdesc; ! 228: } ! 229: sti(); ! 230: return; ! 231: } ! 232:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.