Annotation of linux/kernel/malloc.c, revision 1.1

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: 

unix.superglobalmegacorp.com

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