[BACK]Return to kmem.c CVS log [TXT][DIR] Up to [local] / prex-old / sys / mem

Annotation of prex-old/sys/mem/kmem.c, Revision 1.1.1.1

1.1       nbrk        1: /*-
                      2:  * Copyright (c) 2005-2006, Kohsuke Ohtani
                      3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  * 3. Neither the name of the author nor the names of any co-contributors
                     14:  *    may be used to endorse or promote products derived from this software
                     15:  *    without specific prior written permission.
                     16:  *
                     17:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
                     18:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     19:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     20:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
                     21:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     22:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     23:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     24:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     25:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     26:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     27:  * SUCH DAMAGE.
                     28:  */
                     29:
                     30: /*
                     31:  * kmem.c - kernel memory allocator
                     32:  */
                     33:
                     34: /*
                     35:  * This is a memory allocator optimized for the low foot print kernel.
                     36:  * It works on top of the underlying page allocator, and manages more
                     37:  * smaller memory than page size. It will divide one page into two or
                     38:  * more blocks, and each page is linked as a kernel page.
                     39:  *
                     40:  * There are following 3 linked lists to manage used/free blocks.
                     41:  *  1) All pages allocated for the kernel memory are linked.
                     42:  *  2) All blocks divided in the same page are linked.
                     43:  *  3) All free blocks of the same size are linked.
                     44:  *
                     45:  * Currently, it can not handle the memory size exceeding one page.
                     46:  * Instead, a driver can use page_alloc() to allocate larger memory.
                     47:  *
                     48:  * The kmem functions are used by not only the kernel core but also
                     49:  * by the buggy drivers. If such kernel code illegally writes data in
                     50:  * exceeding the allocated area, the system will crash easily. In order
                     51:  * to detect the memory over run, each free block has a magic ID.
                     52:  */
                     53:
                     54: #include <kernel.h>
                     55: #include <page.h>
                     56: #include <sched.h>
                     57: #include <vm.h>
                     58:
                     59: /*
                     60:  * Block header
                     61:  *
                     62:  * All free blocks that have same size are linked each other.
                     63:  * In addition, all free blocks within same page are also linked.
                     64:  */
                     65: struct block_hdr {
                     66:        u_short magic;                  /* magic number */
                     67:        u_short size;                   /* size of this block */
                     68:        struct  list link;              /* link to the free list */
                     69:        struct  block_hdr *pg_next;     /* next block in same page */
                     70: };
                     71:
                     72: /*
                     73:  * Page header
                     74:  *
                     75:  * The page header is placed at the top of each page.  This header is
                     76:  * used in order to free the page when there are no used block left in
                     77:  * the page. If nr_alloc value becomes zero, that page can be removed
                     78:  * from kernel page.
                     79:  */
                     80: struct page_hdr {
                     81:        u_short magic;                  /* magic number */
                     82:        u_short nallocs;                /* number of allocated blocks */
                     83:        struct  block_hdr first_blk;    /* first block in this page */
                     84: };
                     85:
                     86: #define ALIGN_SIZE     16
                     87: #define ALIGN_MASK     (ALIGN_SIZE - 1)
                     88: #define ALLOC_ALIGN(n) (((n) + ALIGN_MASK) & ~ALIGN_MASK)
                     89:
                     90: #define BLOCK_MAGIC    0xdead
                     91: #define PAGE_MAGIC     0xbeef
                     92:
                     93: #define BLKHDR_SIZE    (sizeof(struct block_hdr))
                     94: #define PGHDR_SIZE     (sizeof(struct page_hdr))
                     95: #define MAX_ALLOC_SIZE (PAGE_SIZE - PGHDR_SIZE)
                     96:
                     97: #define MIN_BLOCK_SIZE (BLKHDR_SIZE + 16)
                     98: #define MAX_BLOCK_SIZE (u_short)(PAGE_SIZE - (PGHDR_SIZE - BLKHDR_SIZE))
                     99:
                    100: /* macro to point the page header from specific address */
                    101: #define PAGE_TOP(n)    (struct page_hdr *) \
                    102:                                    ((u_long)(n) & ~(PAGE_SIZE - 1))
                    103:
                    104: /* index of free block list */
                    105: #define BLKIDX(b)      ((int)((b)->size) >> 4)
                    106:
                    107: /* number of free block list */
                    108: #define NR_BLOCK_LIST  (PAGE_SIZE / ALIGN_SIZE)
                    109:
                    110: /**
                    111:  * Array of the head block of free block list.
                    112:  *
                    113:  * The index of array is decided by the size of each block.
                    114:  * All block has the size of the multiple of 16.
                    115:  *
                    116:  * ie. free_blocks[0] = list for 16 byte block
                    117:  *     free_blocks[1] = list for 32 byte block
                    118:  *     free_blocks[2] = list for 48 byte block
                    119:  *         .
                    120:  *         .
                    121:  *     free_blocks[255] = list for 4096 byte block
                    122:  *
                    123:  * Generally, only one list is used to search the free block with
                    124:  * a first fit algorithm. Basically, this allocator also uses a first
                    125:  * fit method. However it uses multiple lists corresponding to each
                    126:  * block size.
                    127:  * A search is started from the list of the requested size. So, it is
                    128:  * not necessary to search smaller block's list wastefully.
                    129:  *
                    130:  * Most of kernel memory allocator is using 2^n as block size. But,
                    131:  * these implementation will throw away much memory that the block
                    132:  * size is not fit. This is not suitable for the embedded system with
                    133:  * low foot print.
                    134:  */
                    135: static struct list free_blocks[NR_BLOCK_LIST];
                    136:
                    137: static int kmem_bytes;         /* number of bytes currently allocated */
                    138:
                    139: #ifdef DEBUG
                    140: /*
                    141:  * profiling data
                    142:  */
                    143: static int nr_pages;                   /* number of pages currently used */
                    144: static int nr_blocks[NR_BLOCK_LIST];   /* number of blocks currently used */
                    145:
                    146: #endif /* DEBUG */
                    147:
                    148: /*
                    149:  * Find the free block for the specified size.
                    150:  * Returns pointer to free block, or NULL on failure.
                    151:  *
                    152:  * First, it searches from the list of same size. If it does not
                    153:  * exists, then it will search the list of larger one.
                    154:  * It will use the block of smallest size that satisfies the
                    155:  * specified size.
                    156:  */
                    157: static struct block_hdr *
                    158: block_find(size_t size)
                    159: {
                    160:        int i;
                    161:        list_t n;
                    162:
                    163:        for (i = (int)size >> 4; i < NR_BLOCK_LIST; i++) {
                    164:                if (!list_empty(&free_blocks[i]))
                    165:                        break;
                    166:        }
                    167:        if (i >= NR_BLOCK_LIST)
                    168:                return NULL;
                    169:
                    170:        n = list_first(&free_blocks[i]);
                    171:        return list_entry(n, struct block_hdr, link);
                    172: }
                    173:
                    174: /*
                    175:  * Allocate memory block for kernel
                    176:  *
                    177:  * This function does not fill the allocated block by 0 for performance.
                    178:  * kmem_alloc() returns NULL on failure.
                    179:  */
                    180: void *
                    181: kmem_alloc(size_t size)
                    182: {
                    183:        struct block_hdr *blk, *new_blk;
                    184:        struct page_hdr *pg;
                    185:        void *p;
                    186:
                    187:        ASSERT(irq_level == 0);
                    188:
                    189:        sched_lock();           /* Lock scheduler */
                    190:        /*
                    191:         * First, the free block of enough size is searched
                    192:         * from the page already used. If it does not exist,
                    193:         * new page is allocated for free block.
                    194:         */
                    195:        size = ALLOC_ALIGN(size + BLKHDR_SIZE);
                    196:
                    197:        ASSERT(size && size <= MAX_ALLOC_SIZE);
                    198:
                    199:        blk = block_find(size);
                    200:        if (blk) {
                    201:                /* Block found */
                    202:                list_remove(&blk->link); /* Remove from free list */
                    203:                pg = PAGE_TOP(blk);      /* Get the page address */
                    204:        } else {
                    205:                /* No block found. Allocate new page */
                    206:                if ((pg = page_alloc(PAGE_SIZE)) == NULL) {
                    207:                        sched_unlock();
                    208:                        return NULL;
                    209:                }
                    210:                pg = phys_to_virt(pg);
                    211:                pg->nallocs = 0;
                    212:                pg->magic = PAGE_MAGIC;
                    213:
                    214:                /* Setup first block */
                    215:                blk = &(pg->first_blk);
                    216:                blk->magic = BLOCK_MAGIC;
                    217:                blk->size = MAX_BLOCK_SIZE;
                    218:                blk->pg_next = NULL;
                    219: #ifdef DEBUG
                    220:                nr_pages++;
                    221: #endif
                    222:        }
                    223:        /* Sanity check */
                    224:        if (pg->magic != PAGE_MAGIC || blk->magic != BLOCK_MAGIC)
                    225:                panic("kmem overrun: addr=%x", blk);
                    226:        /*
                    227:         * If the found block is large enough, split it.
                    228:         */
                    229:        if (blk->size - size >= MIN_BLOCK_SIZE) {
                    230:                /* Make new block */
                    231:                new_blk = (struct block_hdr *)((u_long)blk + size);
                    232:                new_blk->magic = BLOCK_MAGIC;
                    233:                new_blk->size = (u_short)(blk->size - size);
                    234:                list_insert(&free_blocks[BLKIDX(new_blk)], &new_blk->link);
                    235:
                    236:                /* Update page list */
                    237:                new_blk->pg_next = blk->pg_next;
                    238:                blk->pg_next = new_blk;
                    239:
                    240:                blk->size = (u_short)size;
                    241:        }
                    242:        /* Increment allocation count of this page */
                    243:        pg->nallocs++;
                    244:        kmem_bytes += blk->size;
                    245: #ifdef DEBUG
                    246:        nr_blocks[BLKIDX(blk)]++;
                    247: #endif
                    248:        p = (void *)((u_long)blk + BLKHDR_SIZE);
                    249:        sched_unlock();
                    250:        return p;
                    251: }
                    252:
                    253: /*
                    254:  * Free allocated memory block.
                    255:  *
                    256:  * Some kernel does not release the free page for the kernel memory
                    257:  * because it is needed to allocate immediately later. For example,
                    258:  * it is efficient here if the free page is just linked to the list
                    259:  * of the biggest size. However, consider the case where a driver
                    260:  * requires many small memories temporarily. After these pages are
                    261:  * freed, they can not be reused for an application.
                    262:  */
                    263: void
                    264: kmem_free(void *ptr)
                    265: {
                    266:        struct block_hdr *blk;
                    267:        struct page_hdr *pg;
                    268:
                    269:        ASSERT(irq_level == 0);
                    270:        ASSERT(ptr);
                    271:
                    272:        /* Lock scheduler */
                    273:        sched_lock();
                    274:
                    275:        /* Get the block header */
                    276:        blk = (struct block_hdr *)((u_long)ptr - BLKHDR_SIZE);
                    277:        if (blk->magic != BLOCK_MAGIC)
                    278:                panic("kmem_free");
                    279:
                    280:        kmem_bytes -= blk->size;
                    281:
                    282: #ifdef DEBUG
                    283:        nr_blocks[BLKIDX(blk)]--;
                    284: #endif
                    285:        /*
                    286:         * Return the block to free list.
                    287:         * Since kernel code will request fixed size of memory block,
                    288:         * we don't merge the blocks to use it as cache.
                    289:         */
                    290:        list_insert(&free_blocks[BLKIDX(blk)], &blk->link);
                    291:
                    292:        /* Decrement allocation count of this page */
                    293:        pg = PAGE_TOP(blk);
                    294:        if (--pg->nallocs <= 0) {
                    295:                /*
                    296:                 * No allocated block in this page.
                    297:                 * Remove all blocks and deallocate this page.
                    298:                 */
                    299:                for (blk = &(pg->first_blk); blk != NULL; blk = blk->pg_next) {
                    300:                        list_remove(&blk->link); /* Remove from free list */
                    301: #ifdef DEBUG
                    302:                        nr_blocks[BLKIDX(blk)]--;
                    303: #endif
                    304:                }
                    305:                pg->magic = 0;
                    306:                page_free(virt_to_phys(pg), PAGE_SIZE);
                    307: #ifdef DEBUG
                    308:                nr_pages--;
                    309: #endif
                    310:        }
                    311:        sched_unlock();
                    312: }
                    313:
                    314: /*
                    315:  * Map specified virtual address to the kernel address
                    316:  * Returns kernel address on success, or NULL if no mapped memory.
                    317:  */
                    318: void *
                    319: kmem_map(void *addr, size_t size)
                    320: {
                    321:        void *phys;
                    322:
                    323:        phys = vm_translate(addr, size);
                    324:        if (phys == NULL)
                    325:                return NULL;
                    326:        return phys_to_virt(phys);
                    327: }
                    328:
                    329: void
                    330: kmem_info(size_t *size)
                    331: {
                    332:
                    333:        *size = (size_t)kmem_bytes;
                    334: }
                    335:
                    336: #if defined(DEBUG) && defined(CONFIG_KDUMP)
                    337: void
                    338: kmem_dump(void)
                    339: {
                    340:        list_t head, n;
                    341:        int i, cnt;
                    342:        struct block_hdr *blk;
                    343:
                    344:        printk("\nKernel memory dump:\n");
                    345:
                    346:        printk(" allocated blocks:\n");
                    347:        printk(" block size count\n");
                    348:        printk(" ---------- --------\n");
                    349:
                    350:        for (i = 0; i < NR_BLOCK_LIST; i++) {
                    351:                if (nr_blocks[i])
                    352:                        printk("       %4d %8d\n", i << 4, nr_blocks[i]);
                    353:        }
                    354:        printk("\n free blocks:\n");
                    355:        printk(" block size count\n");
                    356:        printk(" ---------- --------\n");
                    357:
                    358:        for (i = 0; i < NR_BLOCK_LIST; i++) {
                    359:                cnt = 0;
                    360:                head = &free_blocks[i];
                    361:                for (n = list_first(head); n != head; n = list_next(n)) {
                    362:                        cnt++;
                    363:
                    364:                        blk = list_entry(n, struct block_hdr, link);
                    365:                }
                    366:                if (cnt > 0)
                    367:                        printk("       %4d %8d\n", i << 4, cnt);
                    368:        }
                    369:        printk(" Total: page=%d (%dKbyte) alloc=%dbyte unused=%dbyte\n",
                    370:             nr_pages, nr_pages * 4, kmem_bytes,
                    371:             nr_pages * PAGE_SIZE - kmem_bytes);
                    372: }
                    373: #endif
                    374:
                    375: void
                    376: kmem_init(void)
                    377: {
                    378:        int i;
                    379:
                    380:        for (i = 0; i < NR_BLOCK_LIST; i++)
                    381:                list_init(&free_blocks[i]);
                    382: }

CVSweb