Annotation of linux/lib/malloc.c, revision 1.1.1.2

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:        
1.1.1.2 ! root      102:        first = bdesc = (struct bucket_desc *) get_free_page(GFP_KERNEL);
1.1       root      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;
1.1.1.2 ! root      156:                bdesc->page = bdesc->freeptr = (void *) cp = get_free_page(GFP_KERNEL);
1.1       root      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: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.