|
|
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: #include <vm/vm_kern.h> ! 35: ! 36: #define MACH_INCLUDE ! 37: #include <linux/sched.h> ! 38: #include <linux/malloc.h> ! 39: #include <linux/delay.h> ! 40: ! 41: #include <asm/system.h> ! 42: ! 43: extern void *alloc_contig_mem (unsigned, unsigned, unsigned, vm_page_t *); ! 44: extern int printf (const char *, ...); ! 45: ! 46: /* Amount of memory to reserve for Linux memory allocator. ! 47: We reserve 64K chunks to stay within DMA limits. ! 48: Increase MEM_CHUNKS if the kernel is running out of memory. */ ! 49: #define MEM_CHUNK_SIZE (64 * 1024) ! 50: #define MEM_CHUNKS 7 ! 51: ! 52: /* Mininum amount that linux_kmalloc will allocate. */ ! 53: #define MIN_ALLOC 12 ! 54: ! 55: #ifndef NBPW ! 56: #define NBPW 32 ! 57: #endif ! 58: ! 59: /* Memory block header. */ ! 60: struct blkhdr ! 61: { ! 62: unsigned short free; /* 1 if block is free */ ! 63: unsigned short size; /* size of block */ ! 64: }; ! 65: ! 66: /* This structure heads a page allocated by linux_kmalloc. */ ! 67: struct pagehdr ! 68: { ! 69: unsigned size; /* size (multiple of PAGE_SIZE) */ ! 70: struct pagehdr *next; /* next header in list */ ! 71: }; ! 72: ! 73: /* This structure describes a memory chunk. */ ! 74: struct chunkhdr ! 75: { ! 76: unsigned long start; /* start address */ ! 77: unsigned long end; /* end address */ ! 78: unsigned long bitmap; /* busy/free bitmap of pages */ ! 79: }; ! 80: ! 81: /* Chunks from which pages are allocated. */ ! 82: static struct chunkhdr pages_free[MEM_CHUNKS]; ! 83: ! 84: /* Memory list maintained by linux_kmalloc. */ ! 85: static struct pagehdr *memlist; ! 86: ! 87: /* Some statistics. */ ! 88: int num_block_coalesce = 0; ! 89: int num_page_collect = 0; ! 90: int linux_mem_avail; ! 91: ! 92: /* Initialize the Linux memory allocator. */ ! 93: void ! 94: linux_kmem_init () ! 95: { ! 96: int i, j; ! 97: vm_page_t p, pages; ! 98: ! 99: for (i = 0; i < MEM_CHUNKS; i++) ! 100: { ! 101: /* Allocate memory. */ ! 102: pages_free[i].start = (unsigned long) alloc_contig_mem (MEM_CHUNK_SIZE, ! 103: 16 * 1024 * 1024, ! 104: 0xffff, &pages); ! 105: ! 106: assert (pages_free[i].start); ! 107: assert ((pages_free[i].start & 0xffff) == 0); ! 108: ! 109: /* Sanity check: ensure pages are contiguous and within DMA limits. */ ! 110: for (p = pages, j = 0; j < MEM_CHUNK_SIZE - PAGE_SIZE; j += PAGE_SIZE) ! 111: { ! 112: assert (p->phys_addr < 16 * 1024 * 1024); ! 113: assert (p->phys_addr + PAGE_SIZE ! 114: == ((vm_page_t) p->pageq.next)->phys_addr); ! 115: ! 116: p = (vm_page_t) p->pageq.next; ! 117: } ! 118: ! 119: pages_free[i].end = pages_free[i].start + MEM_CHUNK_SIZE; ! 120: ! 121: /* Initialize free page bitmap. */ ! 122: pages_free[i].bitmap = 0; ! 123: j = MEM_CHUNK_SIZE >> PAGE_SHIFT; ! 124: while (--j >= 0) ! 125: pages_free[i].bitmap |= 1 << j; ! 126: } ! 127: ! 128: linux_mem_avail = (MEM_CHUNKS * MEM_CHUNK_SIZE) >> PAGE_SHIFT; ! 129: } ! 130: ! 131: /* Return the number by which the page size should be ! 132: shifted such that the resulting value is >= SIZE. */ ! 133: static unsigned long ! 134: get_page_order (int size) ! 135: { ! 136: unsigned long order; ! 137: ! 138: for (order = 0; (PAGE_SIZE << order) < size; order++) ! 139: ; ! 140: return order; ! 141: } ! 142: ! 143: #ifdef LINUX_DEV_DEBUG ! 144: static void ! 145: check_page_list (int line) ! 146: { ! 147: unsigned size; ! 148: struct pagehdr *ph; ! 149: struct blkhdr *bh; ! 150: ! 151: for (ph = memlist; ph; ph = ph->next) ! 152: { ! 153: if ((int) ph & PAGE_MASK) ! 154: panic ("%s:%d: page header not aligned", __FILE__, line); ! 155: ! 156: size = 0; ! 157: bh = (struct blkhdr *) (ph + 1); ! 158: while (bh < (struct blkhdr *) ((void *) ph + ph->size)) ! 159: { ! 160: size += bh->size + sizeof (struct blkhdr); ! 161: bh = (void *) (bh + 1) + bh->size; ! 162: } ! 163: ! 164: if (size + sizeof (struct pagehdr) != ph->size) ! 165: panic ("%s:%d: memory list destroyed", __FILE__, line); ! 166: } ! 167: } ! 168: #else ! 169: #define check_page_list(line) ! 170: #endif ! 171: ! 172: /* Merge adjacent free blocks in the memory list. */ ! 173: static void ! 174: coalesce_blocks () ! 175: { ! 176: struct pagehdr *ph; ! 177: struct blkhdr *bh, *bhp, *ebh; ! 178: ! 179: num_block_coalesce++; ! 180: ! 181: for (ph = memlist; ph; ph = ph->next) ! 182: { ! 183: bh = (struct blkhdr *) (ph + 1); ! 184: ebh = (struct blkhdr *) ((void *) ph + ph->size); ! 185: while (1) ! 186: { ! 187: /* Skip busy blocks. */ ! 188: while (bh < ebh && !bh->free) ! 189: bh = (struct blkhdr *) ((void *) (bh + 1) + bh->size); ! 190: if (bh == ebh) ! 191: break; ! 192: ! 193: /* Merge adjacent free blocks. */ ! 194: while (1) ! 195: { ! 196: bhp = (struct blkhdr *) ((void *) (bh + 1) + bh->size); ! 197: if (bhp == ebh) ! 198: { ! 199: bh = bhp; ! 200: break; ! 201: } ! 202: if (!bhp->free) ! 203: { ! 204: bh = (struct blkhdr *) ((void *) (bhp + 1) + bhp->size); ! 205: break; ! 206: } ! 207: bh->size += bhp->size + sizeof (struct blkhdr); ! 208: } ! 209: } ! 210: } ! 211: } ! 212: ! 213: /* Allocate SIZE bytes of memory. ! 214: The PRIORITY parameter specifies various flags ! 215: such as DMA, atomicity, etc. It is not used by Mach. */ ! 216: void * ! 217: linux_kmalloc (unsigned int size, int priority) ! 218: { ! 219: int order, coalesced = 0; ! 220: unsigned flags; ! 221: struct pagehdr *ph; ! 222: struct blkhdr *bh, *new_bh; ! 223: ! 224: if (size < MIN_ALLOC) ! 225: size = MIN_ALLOC; ! 226: else ! 227: size = (size + sizeof (int) - 1) & ~(sizeof (int) - 1); ! 228: ! 229: assert (size <= (MEM_CHUNK_SIZE ! 230: - sizeof (struct pagehdr) ! 231: - sizeof (struct blkhdr))); ! 232: ! 233: save_flags (flags); ! 234: cli (); ! 235: ! 236: again: ! 237: check_page_list (__LINE__); ! 238: ! 239: /* Walk the page list and find the first free block with size ! 240: greater than or equal to the one required. */ ! 241: for (ph = memlist; ph; ph = ph->next) ! 242: { ! 243: bh = (struct blkhdr *) (ph + 1); ! 244: while (bh < (struct blkhdr *) ((void *) ph + ph->size)) ! 245: { ! 246: if (bh->free && bh->size >= size) ! 247: { ! 248: bh->free = 0; ! 249: if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr)) ! 250: { ! 251: /* Split the current block and create a new free block. */ ! 252: new_bh = (void *) (bh + 1) + size; ! 253: new_bh->free = 1; ! 254: new_bh->size = bh->size - size - sizeof (struct blkhdr); ! 255: bh->size = size; ! 256: } ! 257: ! 258: check_page_list (__LINE__); ! 259: ! 260: restore_flags (flags); ! 261: return bh + 1; ! 262: } ! 263: bh = (void *) (bh + 1) + bh->size; ! 264: } ! 265: } ! 266: ! 267: check_page_list (__LINE__); ! 268: ! 269: /* Allocation failed; coalesce free blocks and try again. */ ! 270: if (!coalesced) ! 271: { ! 272: coalesce_blocks (); ! 273: coalesced = 1; ! 274: goto again; ! 275: } ! 276: ! 277: /* Allocate more pages. */ ! 278: order = get_page_order (size ! 279: + sizeof (struct pagehdr) ! 280: + sizeof (struct blkhdr)); ! 281: ph = (struct pagehdr *) __get_free_pages (GFP_KERNEL, order, ~0UL); ! 282: if (!ph) ! 283: { ! 284: restore_flags (flags); ! 285: return NULL; ! 286: } ! 287: ! 288: ph->size = PAGE_SIZE << order; ! 289: ph->next = memlist; ! 290: memlist = ph; ! 291: bh = (struct blkhdr *) (ph + 1); ! 292: bh->free = 0; ! 293: bh->size = ph->size - sizeof (struct pagehdr) - sizeof (struct blkhdr); ! 294: if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr)) ! 295: { ! 296: new_bh = (void *) (bh + 1) + size; ! 297: new_bh->free = 1; ! 298: new_bh->size = bh->size - size - sizeof (struct blkhdr); ! 299: bh->size = size; ! 300: } ! 301: ! 302: check_page_list (__LINE__); ! 303: ! 304: restore_flags (flags); ! 305: return bh + 1; ! 306: } ! 307: ! 308: /* Free memory P previously allocated by linux_kmalloc. */ ! 309: void ! 310: linux_kfree (void *p) ! 311: { ! 312: unsigned flags; ! 313: struct blkhdr *bh; ! 314: struct pagehdr *ph; ! 315: ! 316: assert (((int) p & (sizeof (int) - 1)) == 0); ! 317: ! 318: save_flags (flags); ! 319: cli (); ! 320: ! 321: check_page_list (__LINE__); ! 322: ! 323: for (ph = memlist; ph; ph = ph->next) ! 324: if (p >= (void *) ph && p < (void *) ph + ph->size) ! 325: break; ! 326: ! 327: assert (ph); ! 328: ! 329: bh = (struct blkhdr *) p - 1; ! 330: ! 331: assert (!bh->free); ! 332: assert (bh->size >= MIN_ALLOC); ! 333: assert ((bh->size & (sizeof (int) - 1)) == 0); ! 334: ! 335: bh->free = 1; ! 336: ! 337: check_page_list (__LINE__); ! 338: ! 339: restore_flags (flags); ! 340: } ! 341: ! 342: /* Free any pages that are not in use. ! 343: Called by __get_free_pages when pages are running low. */ ! 344: static void ! 345: collect_kmalloc_pages () ! 346: { ! 347: struct blkhdr *bh; ! 348: struct pagehdr *ph, **prev_ph; ! 349: ! 350: check_page_list (__LINE__); ! 351: ! 352: coalesce_blocks (); ! 353: ! 354: check_page_list (__LINE__); ! 355: ! 356: ph = memlist; ! 357: prev_ph = &memlist; ! 358: while (ph) ! 359: { ! 360: bh = (struct blkhdr *) (ph + 1); ! 361: if (bh->free && (void *) (bh + 1) + bh->size == (void *) ph + ph->size) ! 362: { ! 363: *prev_ph = ph->next; ! 364: free_pages ((unsigned long) ph, get_page_order (ph->size)); ! 365: ph = *prev_ph; ! 366: } ! 367: else ! 368: { ! 369: prev_ph = &ph->next; ! 370: ph = ph->next; ! 371: } ! 372: } ! 373: ! 374: check_page_list (__LINE__); ! 375: } ! 376: ! 377: /* Allocate ORDER + 1 number of physically contiguous pages. ! 378: PRIORITY and DMA are not used in Mach. ! 379: ! 380: XXX: This needs to be dynamic. To do that we need to make ! 381: the Mach page manipulation routines interrupt safe and they ! 382: must provide machine dependant hooks. */ ! 383: unsigned long ! 384: __get_free_pages (int priority, unsigned long order, int dma) ! 385: { ! 386: int i, pages_collected = 0; ! 387: unsigned flags, bits, off, j, len; ! 388: ! 389: assert ((PAGE_SIZE << order) <= MEM_CHUNK_SIZE); ! 390: ! 391: /* Construct bitmap of contiguous pages. */ ! 392: bits = 0; ! 393: j = 0; ! 394: len = 0; ! 395: while (len < (PAGE_SIZE << order)) ! 396: { ! 397: bits |= 1 << j++; ! 398: len += PAGE_SIZE; ! 399: } ! 400: ! 401: save_flags (flags); ! 402: cli (); ! 403: again: ! 404: ! 405: /* Search each chunk for the required number of contiguous pages. */ ! 406: for (i = 0; i < MEM_CHUNKS; i++) ! 407: { ! 408: off = 0; ! 409: j = bits; ! 410: while (MEM_CHUNK_SIZE - off >= (PAGE_SIZE << order)) ! 411: { ! 412: if ((pages_free[i].bitmap & j) == j) ! 413: { ! 414: pages_free[i].bitmap &= ~j; ! 415: linux_mem_avail -= order + 1; ! 416: restore_flags (flags); ! 417: return pages_free[i].start + off; ! 418: } ! 419: j <<= 1; ! 420: off += PAGE_SIZE; ! 421: } ! 422: } ! 423: ! 424: /* Allocation failed; collect kmalloc and buffer pages ! 425: and try again. */ ! 426: if (!pages_collected) ! 427: { ! 428: num_page_collect++; ! 429: collect_kmalloc_pages (); ! 430: pages_collected = 1; ! 431: goto again; ! 432: } ! 433: ! 434: printf ("%s:%d: __get_free_pages: ran out of pages\n", __FILE__, __LINE__); ! 435: ! 436: restore_flags (flags); ! 437: return 0; ! 438: } ! 439: ! 440: /* Free ORDER + 1 number of physically ! 441: contiguous pages starting at address ADDR. */ ! 442: void ! 443: free_pages (unsigned long addr, unsigned long order) ! 444: { ! 445: int i; ! 446: unsigned flags, bits, len, j; ! 447: ! 448: assert ((addr & PAGE_MASK) == 0); ! 449: ! 450: for (i = 0; i < MEM_CHUNKS; i++) ! 451: if (addr >= pages_free[i].start && addr < pages_free[i].end) ! 452: break; ! 453: ! 454: assert (i < MEM_CHUNKS); ! 455: ! 456: /* Contruct bitmap of contiguous pages. */ ! 457: len = 0; ! 458: j = 0; ! 459: bits = 0; ! 460: while (len < (PAGE_SIZE << order)) ! 461: { ! 462: bits |= 1 << j++; ! 463: len += PAGE_SIZE; ! 464: } ! 465: bits <<= (addr - pages_free[i].start) >> PAGE_SHIFT; ! 466: ! 467: save_flags (flags); ! 468: cli (); ! 469: ! 470: assert ((pages_free[i].bitmap & bits) == 0); ! 471: ! 472: pages_free[i].bitmap |= bits; ! 473: linux_mem_avail += order + 1; ! 474: restore_flags (flags); ! 475: } ! 476: ! 477: ! 478: /* vmalloc management routines. */ ! 479: struct vmalloc_struct ! 480: { ! 481: struct vmalloc_struct *prev; ! 482: struct vmalloc_struct *next; ! 483: vm_offset_t start; ! 484: vm_size_t size; ! 485: }; ! 486: ! 487: static struct vmalloc_struct ! 488: vmalloc_list = { &vmalloc_list, &vmalloc_list, 0, 0 }; ! 489: ! 490: static inline void ! 491: vmalloc_list_insert (vm_offset_t start, vm_size_t size) ! 492: { ! 493: struct vmalloc_struct *p; ! 494: ! 495: p = (struct vmalloc_struct *) kalloc (sizeof (struct vmalloc_struct)); ! 496: if (p == NULL) ! 497: panic ("kernel memory is exhausted"); ! 498: ! 499: p->prev = vmalloc_list.prev; ! 500: p->next = &vmalloc_list; ! 501: vmalloc_list.prev->next = p; ! 502: vmalloc_list.prev = p; ! 503: ! 504: p->start = start; ! 505: p->size = size; ! 506: } ! 507: ! 508: static struct vmalloc_struct * ! 509: vmalloc_list_lookup (vm_offset_t start) ! 510: { ! 511: struct vmalloc_struct *p; ! 512: ! 513: for (p = vmalloc_list.next; p != &vmalloc_list; p = p->next) ! 514: { ! 515: if (p->start == start) ! 516: return p; ! 517: } ! 518: ! 519: return NULL; ! 520: } ! 521: ! 522: static inline void ! 523: vmalloc_list_remove (struct vmalloc_struct *p) ! 524: { ! 525: p->next->prev = p->prev; ! 526: p->prev->next = p->next; ! 527: ! 528: kfree (p, sizeof (struct vmalloc_struct)); ! 529: } ! 530: ! 531: /* Allocate SIZE bytes of memory. The pages need not be contiguous. */ ! 532: void * ! 533: vmalloc (unsigned long size) ! 534: { ! 535: kern_return_t ret; ! 536: vm_offset_t addr; ! 537: ! 538: ret = kmem_alloc_wired (kernel_map, &addr, round_page (size)); ! 539: if (ret != KERN_SUCCESS) ! 540: return NULL; ! 541: ! 542: vmalloc_list_insert (addr, round_page (size)); ! 543: return (void *) addr; ! 544: } ! 545: ! 546: /* Free vmalloc'ed and vremap'ed virtual address space. */ ! 547: void ! 548: vfree (void *addr) ! 549: { ! 550: struct vmalloc_struct *p; ! 551: ! 552: p = vmalloc_list_lookup ((vm_offset_t) addr); ! 553: if (p) ! 554: panic ("vmalloc_list_lookup failure"); ! 555: ! 556: kmem_free (kernel_map, addr, p->size); ! 557: vmalloc_list_remove (p); ! 558: } ! 559: ! 560: /* XXX: Quick hacking. */ ! 561: /* Remap physical address into virtual address. */ ! 562: void * ! 563: vremap (unsigned long offset, unsigned long size) ! 564: { ! 565: extern vm_offset_t pmap_map_bd (register vm_offset_t virt, ! 566: register vm_offset_t start, ! 567: register vm_offset_t end, ! 568: vm_prot_t prot); ! 569: vm_offset_t addr; ! 570: kern_return_t ret; ! 571: ! 572: ret = kmem_alloc_wired (kernel_map, &addr, round_page (size)); ! 573: if (ret != KERN_SUCCESS) ! 574: return NULL; ! 575: ! 576: (void) pmap_map_bd (addr, offset, offset + round_page (size), ! 577: VM_PROT_READ | VM_PROT_WRITE); ! 578: ! 579: vmalloc_list_insert (addr, round_page (size)); ! 580: return (void *) addr; ! 581: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.