|
|
1.1 root 1: /*
2: * Linux memory allocation.
3: *
4: * Copyright (C) 1996 The University of Utah and the Computer Systems
5: * Laboratory at the University of Utah (CSL)
6: *
7: * This program is free software; you can redistribute it and/or modify
8: * it under the terms of the GNU General Public License as published by
9: * the Free Software Foundation; either version 2, or (at your option)
10: * any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15: * GNU General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public License
18: * along with this program; if not, write to the Free Software
19: * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20: *
21: * Author: Shantanu Goel, University of Utah CSL
22: *
23: */
24:
25: #include <sys/types.h>
26:
27: #include <mach/mach_types.h>
28: #include <mach/vm_param.h>
29:
30: #include <kern/assert.h>
31: #include <kern/kalloc.h>
32:
33: #include <vm/vm_page.h>
34:
35: #include <i386at/gpl/linux/linux_emul.h>
36:
37: #define MACH_INCLUDE
38: #include <linux/sched.h>
39: #include <linux/malloc.h>
40: #include <linux/delay.h>
41:
42: #include <asm/system.h>
43:
44: /* Amount of memory to reserve for Linux memory allocator.
45: We reserve 64K chunks to stay within DMA limits.
46: Increase MEM_CHUNKS if the kernel is running out of memory. */
47: #define MEM_CHUNK_SIZE (64 * 1024)
48: #define MEM_CHUNKS 3
49:
50: /* Mininum amount that linux_kmalloc will allocate. */
51: #define MIN_ALLOC 12
52:
53: #ifndef NBPW
54: #define NBPW 32
55: #endif
56:
57: /* Memory block header. */
58: struct blkhdr
59: {
60: unsigned short free; /* 1 if block is free */
61: unsigned short size; /* size of block */
62: };
63:
64: /* This structure heads a page allocated by linux_kmalloc. */
65: struct pagehdr
66: {
67: unsigned size; /* size (multiple of PAGE_SIZE) */
68: struct pagehdr *next; /* next header in list */
69: };
70:
71: /* This structure describes a memory chunk. */
72: struct chunkhdr
73: {
74: unsigned long start; /* start address */
75: unsigned long end; /* end address */
76: unsigned long bitmap; /* busy/free bitmap of pages */
77: };
78:
79: /* Chunks from which pages are allocated. */
80: static struct chunkhdr pages_free[MEM_CHUNKS];
81:
82: /* Memory list maintained by linux_kmalloc. */
83: static struct pagehdr *memlist;
84:
85: /* Some statistics. */
86: int num_block_coalesce = 0;
87: int num_page_collect = 0;
88: int linux_mem_avail;
89:
90: /* Initialize the Linux memory allocator. */
91: void
92: linux_kmem_init ()
93: {
94: int i, j;
95: vm_page_t p, pages;
96:
97: for (i = 0; i < MEM_CHUNKS; i++)
98: {
99: /* Allocate memory. */
100: pages_free[i].start = (unsigned long) alloc_contig_mem (MEM_CHUNK_SIZE,
101: 16 * 1024 * 1024,
102: 0xffff, &pages);
103:
104: assert (pages_free[i].start);
105: assert ((pages_free[i].start & 0xffff) == 0);
106:
107: /* Sanity check: ensure pages are contiguous and within DMA limits. */
108: for (p = pages, j = 0; j < MEM_CHUNK_SIZE - PAGE_SIZE; j += PAGE_SIZE)
109: {
110: assert (p->phys_addr < 16 * 1024 * 1024);
111: assert (p->phys_addr + PAGE_SIZE
112: == ((vm_page_t) p->pageq.next)->phys_addr);
113:
114: p = (vm_page_t) p->pageq.next;
115: }
116:
117: pages_free[i].end = pages_free[i].start + MEM_CHUNK_SIZE;
118:
119: /* Initialize free page bitmap. */
120: pages_free[i].bitmap = 0;
121: j = MEM_CHUNK_SIZE >> PAGE_SHIFT;
122: while (--j >= 0)
123: pages_free[i].bitmap |= 1 << j;
124: }
125:
126: linux_mem_avail = (MEM_CHUNKS * MEM_CHUNK_SIZE) >> PAGE_SHIFT;
127: }
128:
129: /* Return the number by which the page size should be
130: shifted such that the resulting value is >= SIZE. */
131: static unsigned long
132: get_page_order (int size)
133: {
134: unsigned long order;
135:
136: for (order = 0; (PAGE_SIZE << order) < size; order++)
137: ;
138: return order;
139: }
140:
141: #ifdef LINUX_DEV_DEBUG
142: static void
143: check_page_list (int line)
144: {
145: unsigned size;
146: struct pagehdr *ph;
147: struct blkhdr *bh;
148:
149: for (ph = memlist; ph; ph = ph->next)
150: {
151: if ((int) ph & PAGE_MASK)
152: panic ("%s:%d: page header not aligned", __FILE__, line);
153:
154: size = 0;
155: bh = (struct blkhdr *) (ph + 1);
156: while (bh < (struct blkhdr *) ((void *) ph + ph->size))
157: {
158: size += bh->size + sizeof (struct blkhdr);
159: bh = (void *) (bh + 1) + bh->size;
160: }
161:
162: if (size + sizeof (struct pagehdr) != ph->size)
163: panic ("%s:%d: memory list destroyed", __FILE__, line);
164: }
165: }
166: #else
167: #define check_page_list(line)
168: #endif
169:
170: /* Merge adjacent free blocks in the memory list. */
171: static void
172: coalesce_blocks ()
173: {
174: struct pagehdr *ph;
175: struct blkhdr *bh, *bhp, *ebh;
176:
177: num_block_coalesce++;
178:
179: for (ph = memlist; ph; ph = ph->next)
180: {
181: bh = (struct blkhdr *) (ph + 1);
182: ebh = (struct blkhdr *) ((void *) ph + ph->size);
183: while (1)
184: {
185: /* Skip busy blocks. */
186: while (bh < ebh && ! bh->free)
187: bh = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
188: if (bh == ebh)
189: break;
190:
191: /* Merge adjacent free blocks. */
192: while (1)
193: {
194: bhp = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
195: if (bhp == ebh)
196: {
197: bh = bhp;
198: break;
199: }
200: if (! bhp->free)
201: {
202: bh = (struct blkhdr *) ((void *) (bhp + 1) + bhp->size);
203: break;
204: }
205: bh->size += bhp->size + sizeof (struct blkhdr);
206: }
207: }
208: }
209: }
210:
211: /* Allocate SIZE bytes of memory.
212: The PRIORITY parameter specifies various flags
213: such as DMA, atomicity, etc. It is not used by Mach. */
214: void *
215: linux_kmalloc (unsigned int size, int priority)
216: {
217: int order, coalesced = 0;
218: unsigned flags;
219: struct pagehdr *ph;
220: struct blkhdr *bh, *new_bh;
221:
222: if (size < MIN_ALLOC)
223: size = MIN_ALLOC;
224: else
225: size = (size + sizeof (int) - 1) & ~(sizeof (int) - 1);
226:
227: assert (size <= (MEM_CHUNK_SIZE
228: - sizeof (struct pagehdr)
229: - sizeof (struct blkhdr)));
230:
231: save_flags (flags);
232: cli ();
233:
234: again:
235: check_page_list (__LINE__);
236:
237: /* Walk the page list and find the first free block with size
238: greater than or equal to the one required. */
239: for (ph = memlist; ph; ph = ph->next)
240: {
241: bh = (struct blkhdr *) (ph + 1);
242: while (bh < (struct blkhdr *) ((void *) ph + ph->size))
243: {
244: if (bh->free && bh->size >= size)
245: {
246: bh->free = 0;
247: if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
248: {
249: /* Split the current block and create a new free block. */
250: new_bh = (void *) (bh + 1) + size;
251: new_bh->free = 1;
252: new_bh->size = bh->size - size - sizeof (struct blkhdr);
253: bh->size = size;
254: }
255:
256: check_page_list (__LINE__);
257:
258: restore_flags (flags);
259: return bh + 1;
260: }
261: bh = (void *) (bh + 1) + bh->size;
262: }
263: }
264:
265: check_page_list (__LINE__);
266:
267: /* Allocation failed; coalesce free blocks and try again. */
268: if (! coalesced)
269: {
270: coalesce_blocks ();
271: coalesced = 1;
272: goto again;
273: }
274:
275: /* Allocate more pages. */
276: order = get_page_order (size
277: + sizeof (struct pagehdr)
278: + sizeof (struct blkhdr));
279: ph = (struct pagehdr *) __get_free_pages (GFP_KERNEL, order, ~0UL);
280: if (! ph)
281: {
282: restore_flags (flags);
283: return NULL;
284: }
285:
286: ph->size = PAGE_SIZE << order;
287: ph->next = memlist;
288: memlist = ph;
289: bh = (struct blkhdr *) (ph + 1);
290: bh->free = 0;
291: bh->size = ph->size - sizeof (struct pagehdr) - sizeof (struct blkhdr);
292: if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
293: {
294: new_bh = (void *) (bh + 1) + size;
295: new_bh->free = 1;
296: new_bh->size = bh->size - size - sizeof (struct blkhdr);
297: bh->size = size;
298: }
299:
300: check_page_list (__LINE__);
301:
302: restore_flags (flags);
303: return bh + 1;
304: }
305:
306: /* Free memory P previously allocated by linux_kmalloc. */
307: void
308: linux_kfree (void *p)
309: {
310: unsigned flags;
311: struct blkhdr *bh, *bhp;
312: struct pagehdr *ph;
313:
314: assert (((int) p & (sizeof (int) - 1)) == 0);
315:
316: save_flags (flags);
317: cli ();
318:
319: check_page_list (__LINE__);
320:
321: for (ph = memlist; ph; ph = ph->next)
322: if (p >= (void *) ph && p < (void *) ph + ph->size)
323: break;
324:
325: assert (ph);
326:
327: bh = (struct blkhdr *) p - 1;
328:
329: assert (! bh->free);
330: assert (bh->size >= MIN_ALLOC);
331: assert ((bh->size & (sizeof (int) - 1)) == 0);
332:
333: bh->free = 1;
334:
335: check_page_list (__LINE__);
336:
337: restore_flags (flags);
338: }
339:
340: /* Free any pages that are not in use.
341: Called by __get_free_pages when pages are running low. */
342: static void
343: collect_kmalloc_pages ()
344: {
345: struct blkhdr *bh;
346: struct pagehdr *ph, **prev_ph;
347:
348: check_page_list (__LINE__);
349:
350: coalesce_blocks ();
351:
352: check_page_list (__LINE__);
353:
354: ph = memlist;
355: prev_ph = &memlist;
356: while (ph)
357: {
358: bh = (struct blkhdr *) (ph + 1);
359: if (bh->free && (void *) (bh + 1) + bh->size == (void *) ph + ph->size)
360: {
361: *prev_ph = ph->next;
362: free_pages ((unsigned long) ph, get_page_order (ph->size));
363: ph = *prev_ph;
364: }
365: else
366: {
367: prev_ph = &ph->next;
368: ph = ph->next;
369: }
370: }
371:
372: check_page_list (__LINE__);
373: }
374:
375: /* Allocate ORDER + 1 number of physically contiguous pages.
376: PRIORITY and MAX_ADDR are not used in Mach.
377:
378: XXX: This needs to be dynamic. To do that we need to make
379: the Mach page manipulation routines interrupt safe and they
380: must provide machine dependant hooks. */
381: unsigned long
382: __get_free_pages (int priority, unsigned long order, unsigned long max_addr)
383: {
384: int i, pages_collected = 0;
385: unsigned flags, bits, off, j, len;
386:
387: assert ((PAGE_SIZE << order) <= MEM_CHUNK_SIZE);
388:
389: /* Construct bitmap of contiguous pages. */
390: bits = 0;
391: j = 0;
392: len = 0;
393: while (len < (PAGE_SIZE << order))
394: {
395: bits |= 1 << j++;
396: len += PAGE_SIZE;
397: }
398:
399: again:
400: save_flags (flags);
401: cli ();
402:
403: /* Search each chunk for the required number of contiguous pages. */
404: for (i = 0; i < MEM_CHUNKS; i++)
405: {
406: off = 0;
407: j = bits;
408: while (MEM_CHUNK_SIZE - off >= (PAGE_SIZE << order))
409: {
410: if ((pages_free[i].bitmap & j) == j)
411: {
412: pages_free[i].bitmap &= ~j;
413: linux_mem_avail -= order + 1;
414: restore_flags (flags);
415: return pages_free[i].start + off;
416: }
417: j <<= 1;
418: off += PAGE_SIZE;
419: }
420: }
421:
422: /* Allocation failed; collect kmalloc and buffer pages
423: and try again. */
424: if (! pages_collected)
425: {
426: num_page_collect++;
427: collect_kmalloc_pages ();
428: collect_buffer_pages ();
429: pages_collected = 1;
430: goto again;
431: }
432:
433: printf ("%s:%d: __get_free_pages: ran out of pages\n", __FILE__, __LINE__);
434:
435: restore_flags (flags);
436: return 0;
437: }
438:
439: /* Free ORDER + 1 number of physically
440: contiguous pages starting at address ADDR. */
441: void
442: free_pages (unsigned long addr, unsigned long order)
443: {
444: int i;
445: unsigned flags, bits, len, j;
446:
447: assert ((addr & PAGE_MASK) == 0);
448:
449: for (i = 0; i < MEM_CHUNKS; i++)
450: if (addr >= pages_free[i].start && addr < pages_free[i].end)
451: break;
452:
453: assert (i < MEM_CHUNKS);
454:
455: /* Contruct bitmap of contiguous pages. */
456: len = 0;
457: j = 0;
458: bits = 0;
459: while (len < (PAGE_SIZE << order))
460: {
461: bits |= 1 << j++;
462: len += PAGE_SIZE;
463: }
464: bits <<= (addr - pages_free[i].start) >> PAGE_SHIFT;
465:
466: save_flags (flags);
467: cli ();
468:
469: assert ((pages_free[i].bitmap & bits) == 0);
470:
471: pages_free[i].bitmap |= bits;
472: linux_mem_avail += order + 1;
473: restore_flags (flags);
474: }
475:
476: /* Allocate SIZE bytes of memory. The pages need not be contiguous. */
477: void *
478: vmalloc (unsigned long size)
479: {
480: return (void *) __get_free_pages (GFP_KERNEL, get_page_order (size), ~0UL);
481: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.