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