|
|
1.1 ! root 1: /* ! 2: * Mach Operating System ! 3: * Copyright (c) 1993-1987 Carnegie Mellon University. ! 4: * Copyright (c) 1993,1994 The University of Utah and ! 5: * the Computer Systems Laboratory (CSL). ! 6: * All rights reserved. ! 7: * ! 8: * Permission to use, copy, modify and distribute this software and its ! 9: * documentation is hereby granted, provided that both the copyright ! 10: * notice and this permission notice appear in all copies of the ! 11: * software, derivative works or modified versions, and any portions ! 12: * thereof, and that both notices appear in supporting documentation. ! 13: * ! 14: * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF ! 15: * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY ! 16: * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF ! 17: * THIS SOFTWARE. ! 18: * ! 19: * Carnegie Mellon requests users of this software to return to ! 20: * ! 21: * Software Distribution Coordinator or [email protected] ! 22: * School of Computer Science ! 23: * Carnegie Mellon University ! 24: * Pittsburgh PA 15213-3890 ! 25: * ! 26: * any improvements or extensions that they make and grant Carnegie Mellon ! 27: * the rights to redistribute these changes. ! 28: */ ! 29: /* ! 30: * File: kern/zalloc.c ! 31: * Author: Avadis Tevanian, Jr. ! 32: * ! 33: * Zone-based memory allocator. A zone is a collection of fixed size ! 34: * data blocks for which quick allocation/deallocation is possible. ! 35: */ ! 36: ! 37: #include <kern/macro_help.h> ! 38: #include <kern/sched.h> ! 39: #include <kern/time_out.h> ! 40: #include <kern/zalloc.h> ! 41: #include <mach/vm_param.h> ! 42: #include <vm/vm_kern.h> ! 43: #include <machine/machspl.h> ! 44: ! 45: #include <mach_debug.h> ! 46: #if MACH_DEBUG ! 47: #include <mach/kern_return.h> ! 48: #include <mach/machine/vm_types.h> ! 49: #include <mach_debug/zone_info.h> ! 50: #include <kern/host.h> ! 51: #include <vm/vm_map.h> ! 52: #include <vm/vm_user.h> ! 53: #include <vm/vm_kern.h> ! 54: #endif ! 55: ! 56: #define ADD_TO_ZONE(zone, element) \ ! 57: MACRO_BEGIN \ ! 58: *((vm_offset_t *)(element)) = (zone)->free_elements; \ ! 59: (zone)->free_elements = (vm_offset_t) (element); \ ! 60: zone_count_down(zone); \ ! 61: MACRO_END ! 62: ! 63: #define REMOVE_FROM_ZONE(zone, ret, type) \ ! 64: MACRO_BEGIN \ ! 65: (ret) = (type) (zone)->free_elements; \ ! 66: if ((ret) != (type) 0) { \ ! 67: zone_count_up(zone); \ ! 68: (zone)->free_elements = *((vm_offset_t *)(ret)); \ ! 69: } \ ! 70: MACRO_END ! 71: ! 72: /* ! 73: * Support for garbage collection of unused zone pages: ! 74: */ ! 75: ! 76: struct zone_page_table_entry { ! 77: struct zone_page_table_entry *next; ! 78: short in_free_list; ! 79: short alloc_count; ! 80: }; ! 81: ! 82: extern struct zone_page_table_entry * zone_page_table; ! 83: extern vm_offset_t zone_map_min_address; ! 84: ! 85: #define lock_zone_page_table() simple_lock(&zone_page_table_lock) ! 86: #define unlock_zone_page_table() simple_unlock(&zone_page_table_lock) ! 87: ! 88: #define zone_page(addr) \ ! 89: (&(zone_page_table[(atop(((vm_offset_t)addr) - zone_map_min_address))])) ! 90: ! 91: ! 92: extern void zone_page_alloc(); ! 93: extern void zone_page_dealloc(); ! 94: extern void zone_page_in_use(); ! 95: extern void zone_page_free(); ! 96: ! 97: zone_t zone_zone; /* this is the zone containing other zones */ ! 98: ! 99: boolean_t zone_ignore_overflow = TRUE; ! 100: ! 101: vm_map_t zone_map = VM_MAP_NULL; ! 102: vm_size_t zone_map_size = 12 * 1024 * 1024; ! 103: ! 104: /* ! 105: * The VM system gives us an initial chunk of memory. ! 106: * It has to be big enough to allocate the zone_zone ! 107: * and some initial kernel data structures, like kernel maps. ! 108: * It is advantageous to make it bigger than really necessary, ! 109: * because this memory is more efficient than normal kernel ! 110: * virtual memory. (It doesn't have vm_page structures backing it ! 111: * and it may have other machine-dependent advantages.) ! 112: * So for best performance, zdata_size should approximate ! 113: * the amount of memory you expect the zone system to consume. ! 114: */ ! 115: ! 116: vm_offset_t zdata; ! 117: vm_size_t zdata_size = 420 * 1024; ! 118: ! 119: #define zone_lock(zone) \ ! 120: MACRO_BEGIN \ ! 121: if (zone->type & ZONE_PAGEABLE) { \ ! 122: lock_write(&zone->complex_lock); \ ! 123: } else { \ ! 124: simple_lock(&zone->lock); \ ! 125: } \ ! 126: MACRO_END ! 127: ! 128: #define zone_unlock(zone) \ ! 129: MACRO_BEGIN \ ! 130: if (zone->type & ZONE_PAGEABLE) { \ ! 131: lock_done(&zone->complex_lock); \ ! 132: } else { \ ! 133: simple_unlock(&zone->lock); \ ! 134: } \ ! 135: MACRO_END ! 136: ! 137: #define zone_lock_init(zone) \ ! 138: MACRO_BEGIN \ ! 139: if (zone->type & ZONE_PAGEABLE) { \ ! 140: lock_init(&zone->complex_lock, TRUE); \ ! 141: } else { \ ! 142: simple_lock_init(&zone->lock); \ ! 143: } \ ! 144: MACRO_END ! 145: ! 146: static vm_offset_t zget_space(); ! 147: ! 148: decl_simple_lock_data(,zget_space_lock) ! 149: vm_offset_t zalloc_next_space; ! 150: vm_offset_t zalloc_end_of_space; ! 151: vm_size_t zalloc_wasted_space; ! 152: ! 153: /* ! 154: * Garbage collection map information ! 155: */ ! 156: decl_simple_lock_data(,zone_page_table_lock) ! 157: struct zone_page_table_entry * zone_page_table; ! 158: vm_offset_t zone_map_min_address; ! 159: vm_offset_t zone_map_max_address; ! 160: int zone_pages; ! 161: ! 162: extern void zone_page_init(); ! 163: ! 164: #define ZONE_PAGE_USED 0 ! 165: #define ZONE_PAGE_UNUSED -1 ! 166: ! 167: ! 168: /* ! 169: * Protects first_zone, last_zone, num_zones, ! 170: * and the next_zone field of zones. ! 171: */ ! 172: decl_simple_lock_data(,all_zones_lock) ! 173: zone_t first_zone; ! 174: zone_t *last_zone; ! 175: int num_zones; ! 176: ! 177: /* ! 178: * zinit initializes a new zone. The zone data structures themselves ! 179: * are stored in a zone, which is initially a static structure that ! 180: * is initialized by zone_init. ! 181: */ ! 182: zone_t zinit(size, max, alloc, memtype, name) ! 183: vm_size_t size; /* the size of an element */ ! 184: vm_size_t max; /* maximum memory to use */ ! 185: vm_size_t alloc; /* allocation size */ ! 186: unsigned int memtype; /* flags specifying type of memory */ ! 187: char *name; /* a name for the zone */ ! 188: { ! 189: register zone_t z; ! 190: ! 191: if (zone_zone == ZONE_NULL) ! 192: z = (zone_t) zget_space(sizeof(struct zone)); ! 193: else ! 194: z = (zone_t) zalloc(zone_zone); ! 195: if (z == ZONE_NULL) ! 196: panic("zinit"); ! 197: ! 198: if (alloc == 0) ! 199: alloc = PAGE_SIZE; ! 200: ! 201: if (size == 0) ! 202: size = sizeof(z->free_elements); ! 203: /* ! 204: * Round off all the parameters appropriately. ! 205: */ ! 206: ! 207: if ((max = round_page(max)) < (alloc = round_page(alloc))) ! 208: max = alloc; ! 209: ! 210: z->free_elements = 0; ! 211: z->cur_size = 0; ! 212: z->max_size = max; ! 213: z->elem_size = ((size-1) + sizeof(z->free_elements)) - ! 214: ((size-1) % sizeof(z->free_elements)); ! 215: ! 216: z->alloc_size = alloc; ! 217: z->type = memtype; ! 218: z->zone_name = name; ! 219: #ifdef ZONE_COUNT ! 220: z->count = 0; ! 221: #endif ! 222: z->doing_alloc = FALSE; ! 223: zone_lock_init(z); ! 224: ! 225: /* ! 226: * Add the zone to the all-zones list. ! 227: */ ! 228: ! 229: z->next_zone = ZONE_NULL; ! 230: simple_lock(&all_zones_lock); ! 231: *last_zone = z; ! 232: last_zone = &z->next_zone; ! 233: num_zones++; ! 234: simple_unlock(&all_zones_lock); ! 235: ! 236: return(z); ! 237: } ! 238: ! 239: /* ! 240: * Cram the given memory into the specified zone. ! 241: */ ! 242: void zcram(zone_t zone, vm_offset_t newmem, vm_size_t size) ! 243: { ! 244: register vm_size_t elem_size; ! 245: ! 246: if (newmem == (vm_offset_t) 0) { ! 247: panic("zcram - memory at zero"); ! 248: } ! 249: elem_size = zone->elem_size; ! 250: ! 251: zone_lock(zone); ! 252: while (size >= elem_size) { ! 253: ADD_TO_ZONE(zone, newmem); ! 254: zone_page_alloc(newmem, elem_size); ! 255: zone_count_up(zone); /* compensate for ADD_TO_ZONE */ ! 256: size -= elem_size; ! 257: newmem += elem_size; ! 258: zone->cur_size += elem_size; ! 259: } ! 260: zone_unlock(zone); ! 261: } ! 262: ! 263: /* ! 264: * Contiguous space allocator for non-paged zones. Allocates "size" amount ! 265: * of memory from zone_map. ! 266: */ ! 267: ! 268: static vm_offset_t zget_space(vm_offset_t size) ! 269: { ! 270: vm_offset_t new_space = 0; ! 271: vm_offset_t result; ! 272: vm_size_t space_to_add = 0; /*'=0' to quiet gcc warnings */ ! 273: ! 274: simple_lock(&zget_space_lock); ! 275: while ((zalloc_next_space + size) > zalloc_end_of_space) { ! 276: /* ! 277: * Add at least one page to allocation area. ! 278: */ ! 279: ! 280: space_to_add = round_page(size); ! 281: ! 282: if (new_space == 0) { ! 283: /* ! 284: * Memory cannot be wired down while holding ! 285: * any locks that the pageout daemon might ! 286: * need to free up pages. [Making the zget_space ! 287: * lock a complex lock does not help in this ! 288: * regard.] ! 289: * ! 290: * Unlock and allocate memory. Because several ! 291: * threads might try to do this at once, don't ! 292: * use the memory before checking for available ! 293: * space again. ! 294: */ ! 295: ! 296: simple_unlock(&zget_space_lock); ! 297: ! 298: if (kmem_alloc_wired(zone_map, ! 299: &new_space, space_to_add) ! 300: != KERN_SUCCESS) ! 301: return(0); ! 302: zone_page_init(new_space, space_to_add, ! 303: ZONE_PAGE_USED); ! 304: simple_lock(&zget_space_lock); ! 305: continue; ! 306: } ! 307: ! 308: ! 309: /* ! 310: * Memory was allocated in a previous iteration. ! 311: * ! 312: * Check whether the new region is contiguous ! 313: * with the old one. ! 314: */ ! 315: ! 316: if (new_space != zalloc_end_of_space) { ! 317: /* ! 318: * Throw away the remainder of the ! 319: * old space, and start a new one. ! 320: */ ! 321: zalloc_wasted_space += ! 322: zalloc_end_of_space - zalloc_next_space; ! 323: zalloc_next_space = new_space; ! 324: } ! 325: ! 326: zalloc_end_of_space = new_space + space_to_add; ! 327: ! 328: new_space = 0; ! 329: } ! 330: result = zalloc_next_space; ! 331: zalloc_next_space += size; ! 332: simple_unlock(&zget_space_lock); ! 333: ! 334: if (new_space != 0) ! 335: kmem_free(zone_map, new_space, space_to_add); ! 336: ! 337: return(result); ! 338: } ! 339: ! 340: ! 341: /* ! 342: * Initialize the "zone of zones" which uses fixed memory allocated ! 343: * earlier in memory initialization. zone_bootstrap is called ! 344: * before zone_init. ! 345: */ ! 346: void zone_bootstrap() ! 347: { ! 348: simple_lock_init(&all_zones_lock); ! 349: first_zone = ZONE_NULL; ! 350: last_zone = &first_zone; ! 351: num_zones = 0; ! 352: ! 353: simple_lock_init(&zget_space_lock); ! 354: zalloc_next_space = zdata; ! 355: zalloc_end_of_space = zdata + zdata_size; ! 356: zalloc_wasted_space = 0; ! 357: ! 358: zone_zone = ZONE_NULL; ! 359: zone_zone = zinit(sizeof(struct zone), 128 * sizeof(struct zone), ! 360: sizeof(struct zone), 0, "zones"); ! 361: } ! 362: ! 363: void zone_init() ! 364: { ! 365: vm_offset_t zone_min; ! 366: vm_offset_t zone_max; ! 367: ! 368: vm_size_t zone_table_size; ! 369: ! 370: zone_map = kmem_suballoc(kernel_map, &zone_min, &zone_max, ! 371: zone_map_size, FALSE); ! 372: ! 373: /* ! 374: * Setup garbage collection information: ! 375: */ ! 376: ! 377: zone_table_size = atop(zone_max - zone_min) * ! 378: sizeof(struct zone_page_table_entry); ! 379: if (kmem_alloc_wired(zone_map, (vm_offset_t *) &zone_page_table, ! 380: zone_table_size) != KERN_SUCCESS) ! 381: panic("zone_init"); ! 382: zone_min = (vm_offset_t)zone_page_table + round_page(zone_table_size); ! 383: zone_pages = atop(zone_max - zone_min); ! 384: zone_map_min_address = zone_min; ! 385: zone_map_max_address = zone_max; ! 386: simple_lock_init(&zone_page_table_lock); ! 387: zone_page_init(zone_min, zone_max - zone_min, ZONE_PAGE_UNUSED); ! 388: } ! 389: ! 390: ! 391: /* ! 392: * zalloc returns an element from the specified zone. ! 393: */ ! 394: vm_offset_t zalloc(zone_t zone) ! 395: { ! 396: vm_offset_t addr; ! 397: ! 398: if (zone == ZONE_NULL) ! 399: panic ("zalloc: null zone"); ! 400: ! 401: check_simple_locks(); ! 402: ! 403: zone_lock(zone); ! 404: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 405: while (addr == 0) { ! 406: /* ! 407: * If nothing was there, try to get more ! 408: */ ! 409: if (zone->doing_alloc) { ! 410: /* ! 411: * Someone is allocating memory for this zone. ! 412: * Wait for it to show up, then try again. ! 413: */ ! 414: assert_wait((event_t)&zone->doing_alloc, TRUE); ! 415: /* XXX say wakeup needed */ ! 416: zone_unlock(zone); ! 417: thread_block((void (*)()) 0); ! 418: zone_lock(zone); ! 419: } ! 420: else { ! 421: if ((zone->cur_size + (zone->type & ZONE_PAGEABLE ? ! 422: zone->alloc_size : zone->elem_size)) > ! 423: zone->max_size) { ! 424: if (zone->type & ZONE_EXHAUSTIBLE) ! 425: break; ! 426: /* ! 427: * Printf calls logwakeup, which calls ! 428: * select_wakeup which will do a zfree ! 429: * (which tries to take the select_zone ! 430: * lock... Hang. Release the lock now ! 431: * so it can be taken again later. ! 432: * NOTE: this used to be specific to ! 433: * the select_zone, but for ! 434: * cleanliness, we just unlock all ! 435: * zones before this. ! 436: */ ! 437: if (!(zone->type & ZONE_FIXED)) { ! 438: /* ! 439: * We're willing to overflow certain ! 440: * zones, but not without complaining. ! 441: * ! 442: * This is best used in conjunction ! 443: * with the collecatable flag. What we ! 444: * want is an assurance we can get the ! 445: * memory back, assuming there's no ! 446: * leak. ! 447: */ ! 448: zone->max_size += (zone->max_size >> 1); ! 449: } else if (!zone_ignore_overflow) { ! 450: zone_unlock(zone); ! 451: printf("zone \"%s\" empty.\n", ! 452: zone->zone_name); ! 453: panic("zalloc"); ! 454: } ! 455: } ! 456: ! 457: if (zone->type & ZONE_PAGEABLE) ! 458: zone->doing_alloc = TRUE; ! 459: zone_unlock(zone); ! 460: ! 461: if (zone->type & ZONE_PAGEABLE) { ! 462: if (kmem_alloc_pageable(zone_map, &addr, ! 463: zone->alloc_size) ! 464: != KERN_SUCCESS) ! 465: panic("zalloc"); ! 466: zcram(zone, addr, zone->alloc_size); ! 467: zone_lock(zone); ! 468: zone->doing_alloc = FALSE; ! 469: /* XXX check before doing this */ ! 470: thread_wakeup((event_t)&zone->doing_alloc); ! 471: ! 472: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 473: } else if (zone->type & ZONE_COLLECTABLE) { ! 474: if (kmem_alloc_wired(zone_map, ! 475: &addr, zone->alloc_size) ! 476: != KERN_SUCCESS) ! 477: panic("zalloc"); ! 478: zone_page_init(addr, zone->alloc_size, ! 479: ZONE_PAGE_USED); ! 480: zcram(zone, addr, zone->alloc_size); ! 481: zone_lock(zone); ! 482: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 483: } else { ! 484: addr = zget_space(zone->elem_size); ! 485: if (addr == 0) ! 486: panic("zalloc"); ! 487: ! 488: zone_lock(zone); ! 489: zone_count_up(zone); ! 490: zone->cur_size += zone->elem_size; ! 491: zone_unlock(zone); ! 492: zone_page_alloc(addr, zone->elem_size); ! 493: return(addr); ! 494: } ! 495: } ! 496: } ! 497: ! 498: zone_unlock(zone); ! 499: return(addr); ! 500: } ! 501: ! 502: ! 503: /* ! 504: * zget returns an element from the specified zone ! 505: * and immediately returns nothing if there is nothing there. ! 506: * ! 507: * This form should be used when you can not block (like when ! 508: * processing an interrupt). ! 509: */ ! 510: vm_offset_t zget(zone_t zone) ! 511: { ! 512: register vm_offset_t addr; ! 513: ! 514: if (zone == ZONE_NULL) ! 515: panic ("zalloc: null zone"); ! 516: ! 517: zone_lock(zone); ! 518: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 519: zone_unlock(zone); ! 520: ! 521: return(addr); ! 522: } ! 523: ! 524: boolean_t zone_check = FALSE; ! 525: ! 526: void zfree(zone_t zone, vm_offset_t elem) ! 527: { ! 528: zone_lock(zone); ! 529: if (zone_check) { ! 530: vm_offset_t this; ! 531: ! 532: /* check the zone's consistency */ ! 533: ! 534: for (this = zone->free_elements; ! 535: this != 0; ! 536: this = * (vm_offset_t *) this) ! 537: if (this == elem) ! 538: panic("zfree"); ! 539: } ! 540: ADD_TO_ZONE(zone, elem); ! 541: zone_unlock(zone); ! 542: } ! 543: ! 544: /* ! 545: * Zone garbage collection subroutines ! 546: * ! 547: * These routines have in common the modification of entries in the ! 548: * zone_page_table. The latter contains one entry for every page ! 549: * in the zone_map. ! 550: * ! 551: * For each page table entry in the given range: ! 552: * ! 553: * zone_page_in_use - decrements in_free_list ! 554: * zone_page_free - increments in_free_list ! 555: * zone_page_init - initializes in_free_list and alloc_count ! 556: * zone_page_alloc - increments alloc_count ! 557: * zone_page_dealloc - decrements alloc_count ! 558: * zone_add_free_page_list - adds the page to the free list ! 559: * ! 560: * Two counts are maintained for each page, the in_free_list count and ! 561: * alloc_count. The alloc_count is how many zone elements have been ! 562: * allocated from a page. (Note that the page could contain elements ! 563: * that span page boundaries. The count includes these elements so ! 564: * one element may be counted in two pages.) In_free_list is a count ! 565: * of how many zone elements are currently free. If in_free_list is ! 566: * equal to alloc_count then the page is eligible for garbage ! 567: * collection. ! 568: * ! 569: * Alloc_count and in_free_list are initialized to the correct values ! 570: * for a particular zone when a page is zcram'ed into a zone. Subsequent ! 571: * gets and frees of zone elements will call zone_page_in_use and ! 572: * zone_page_free which modify the in_free_list count. When the zones ! 573: * garbage collector runs it will walk through a zones free element list, ! 574: * remove the elements that reside on collectable pages, and use ! 575: * zone_add_free_page_list to create a list of pages to be collected. ! 576: */ ! 577: ! 578: void zone_page_in_use(addr, size) ! 579: vm_offset_t addr; ! 580: vm_size_t size; ! 581: { ! 582: int i, j; ! 583: if ((addr < zone_map_min_address) || ! 584: (addr+size > zone_map_max_address)) return; ! 585: i = atop(addr-zone_map_min_address); ! 586: j = atop((addr+size-1) - zone_map_min_address); ! 587: lock_zone_page_table(); ! 588: for (; i <= j; i++) { ! 589: zone_page_table[i].in_free_list--; ! 590: } ! 591: unlock_zone_page_table(); ! 592: } ! 593: ! 594: void zone_page_free(addr, size) ! 595: vm_offset_t addr; ! 596: vm_size_t size; ! 597: { ! 598: int i, j; ! 599: if ((addr < zone_map_min_address) || ! 600: (addr+size > zone_map_max_address)) return; ! 601: i = atop(addr-zone_map_min_address); ! 602: j = atop((addr+size-1) - zone_map_min_address); ! 603: lock_zone_page_table(); ! 604: for (; i <= j; i++) { ! 605: /* Set in_free_list to (ZONE_PAGE_USED + 1) if ! 606: * it was previously set to ZONE_PAGE_UNUSED. ! 607: */ ! 608: if (zone_page_table[i].in_free_list == ZONE_PAGE_UNUSED) { ! 609: zone_page_table[i].in_free_list = 1; ! 610: } else { ! 611: zone_page_table[i].in_free_list++; ! 612: } ! 613: } ! 614: unlock_zone_page_table(); ! 615: } ! 616: ! 617: void zone_page_init(addr, size, value) ! 618: ! 619: vm_offset_t addr; ! 620: vm_size_t size; ! 621: int value; ! 622: { ! 623: int i, j; ! 624: if ((addr < zone_map_min_address) || ! 625: (addr+size > zone_map_max_address)) return; ! 626: i = atop(addr-zone_map_min_address); ! 627: j = atop((addr+size-1) - zone_map_min_address); ! 628: lock_zone_page_table(); ! 629: for (; i <= j; i++) { ! 630: zone_page_table[i].alloc_count = value; ! 631: zone_page_table[i].in_free_list = 0; ! 632: } ! 633: unlock_zone_page_table(); ! 634: } ! 635: ! 636: void zone_page_alloc(addr, size) ! 637: vm_offset_t addr; ! 638: vm_size_t size; ! 639: { ! 640: int i, j; ! 641: if ((addr < zone_map_min_address) || ! 642: (addr+size > zone_map_max_address)) return; ! 643: i = atop(addr-zone_map_min_address); ! 644: j = atop((addr+size-1) - zone_map_min_address); ! 645: lock_zone_page_table(); ! 646: for (; i <= j; i++) { ! 647: /* Set alloc_count to (ZONE_PAGE_USED + 1) if ! 648: * it was previously set to ZONE_PAGE_UNUSED. ! 649: */ ! 650: if (zone_page_table[i].alloc_count == ZONE_PAGE_UNUSED) { ! 651: zone_page_table[i].alloc_count = 1; ! 652: } else { ! 653: zone_page_table[i].alloc_count++; ! 654: } ! 655: } ! 656: unlock_zone_page_table(); ! 657: } ! 658: ! 659: void zone_page_dealloc(addr, size) ! 660: vm_offset_t addr; ! 661: vm_size_t size; ! 662: { ! 663: int i, j; ! 664: if ((addr < zone_map_min_address) || ! 665: (addr+size > zone_map_max_address)) return; ! 666: i = atop(addr-zone_map_min_address); ! 667: j = atop((addr+size-1) - zone_map_min_address); ! 668: lock_zone_page_table(); ! 669: for (; i <= j; i++) { ! 670: zone_page_table[i].alloc_count--; ! 671: } ! 672: unlock_zone_page_table(); ! 673: } ! 674: ! 675: void ! 676: zone_add_free_page_list(free_list, addr, size) ! 677: struct zone_page_table_entry **free_list; ! 678: vm_offset_t addr; ! 679: vm_size_t size; ! 680: { ! 681: int i, j; ! 682: if ((addr < zone_map_min_address) || ! 683: (addr+size > zone_map_max_address)) return; ! 684: i = atop(addr-zone_map_min_address); ! 685: j = atop((addr+size-1) - zone_map_min_address); ! 686: lock_zone_page_table(); ! 687: for (; i <= j; i++) { ! 688: if (zone_page_table[i].alloc_count == 0) { ! 689: zone_page_table[i].next = *free_list; ! 690: *free_list = &zone_page_table[i]; ! 691: zone_page_table[i].alloc_count = ZONE_PAGE_UNUSED; ! 692: zone_page_table[i].in_free_list = 0; ! 693: } ! 694: } ! 695: unlock_zone_page_table(); ! 696: } ! 697: ! 698: ! 699: /* This is used for walking through a zone's free element list. ! 700: */ ! 701: struct zone_free_entry { ! 702: struct zone_free_entry * next; ! 703: }; ! 704: ! 705: ! 706: /* Zone garbage collection ! 707: * ! 708: * zone_gc will walk through all the free elements in all the ! 709: * zones that are marked collectable looking for reclaimable ! 710: * pages. zone_gc is called by consider_zone_gc when the system ! 711: * begins to run out of memory. ! 712: */ ! 713: static void zone_gc() ! 714: { ! 715: int max_zones; ! 716: zone_t z; ! 717: int i; ! 718: register spl_t s; ! 719: struct zone_page_table_entry *freep; ! 720: struct zone_page_table_entry *zone_free_page_list; ! 721: ! 722: simple_lock(&all_zones_lock); ! 723: max_zones = num_zones; ! 724: z = first_zone; ! 725: simple_unlock(&all_zones_lock); ! 726: ! 727: zone_free_page_list = (struct zone_page_table_entry *) 0; ! 728: ! 729: for (i = 0; i < max_zones; i++) { ! 730: struct zone_free_entry * last; ! 731: struct zone_free_entry * elt; ! 732: assert(z != ZONE_NULL); ! 733: /* run this at splhigh so that interupt routines that use zones ! 734: can not interupt while their zone is locked */ ! 735: s=splhigh(); ! 736: zone_lock(z); ! 737: ! 738: if ((z->type & (ZONE_PAGEABLE|ZONE_COLLECTABLE)) == ZONE_COLLECTABLE) { ! 739: ! 740: /* Count the free elements in each page. This loop ! 741: * requires that all in_free_list entries are zero. ! 742: */ ! 743: elt = (struct zone_free_entry *)(z->free_elements); ! 744: while ((elt != (struct zone_free_entry *)0)) { ! 745: zone_page_free((vm_offset_t)elt, z->elem_size); ! 746: elt = elt->next; ! 747: } ! 748: ! 749: /* Now determine which elements should be removed ! 750: * from the free list and, after all the elements ! 751: * on a page have been removed, add the element's ! 752: * page to a list of pages to be freed. ! 753: */ ! 754: elt = (struct zone_free_entry *)(z->free_elements); ! 755: last = elt; ! 756: while ((elt != (struct zone_free_entry *)0)) { ! 757: if (((vm_offset_t)elt>=zone_map_min_address)&& ! 758: ((vm_offset_t)elt<=zone_map_max_address)&& ! 759: (zone_page(elt)->in_free_list == ! 760: zone_page(elt)->alloc_count)) { ! 761: ! 762: z->cur_size -= z->elem_size; ! 763: zone_page_in_use((vm_offset_t)elt, z->elem_size); ! 764: zone_page_dealloc((vm_offset_t)elt, z->elem_size); ! 765: if (zone_page(elt)->alloc_count == 0 || ! 766: zone_page(elt+(z->elem_size-1))->alloc_count==0) { ! 767: zone_add_free_page_list( ! 768: &zone_free_page_list, ! 769: (vm_offset_t)elt, z->elem_size); ! 770: } ! 771: ! 772: ! 773: if (elt == last) { ! 774: elt = elt->next; ! 775: z->free_elements =(vm_offset_t)elt; ! 776: last = elt; ! 777: } else { ! 778: last->next = elt->next; ! 779: elt = elt->next; ! 780: } ! 781: } else { ! 782: /* This element is not eligible for collection ! 783: * so clear in_free_list in preparation for a ! 784: * subsequent garbage collection pass. ! 785: */ ! 786: if (((vm_offset_t)elt>=zone_map_min_address)&& ! 787: ((vm_offset_t)elt<=zone_map_max_address)) { ! 788: zone_page(elt)->in_free_list = 0; ! 789: } ! 790: last = elt; ! 791: elt = elt->next; ! 792: } ! 793: } ! 794: } ! 795: zone_unlock(z); ! 796: splx(s); ! 797: simple_lock(&all_zones_lock); ! 798: z = z->next_zone; ! 799: simple_unlock(&all_zones_lock); ! 800: } ! 801: ! 802: for (freep = zone_free_page_list; freep != 0; freep = freep->next) { ! 803: vm_offset_t free_addr; ! 804: ! 805: free_addr = zone_map_min_address + ! 806: PAGE_SIZE * (freep - zone_page_table); ! 807: kmem_free(zone_map, free_addr, PAGE_SIZE); ! 808: } ! 809: } ! 810: ! 811: boolean_t zone_gc_allowed = TRUE; ! 812: unsigned zone_gc_last_tick = 0; ! 813: unsigned zone_gc_max_rate = 0; /* in ticks */ ! 814: ! 815: /* ! 816: * consider_zone_gc: ! 817: * ! 818: * Called by the pageout daemon when the system needs more free pages. ! 819: */ ! 820: ! 821: void ! 822: consider_zone_gc() ! 823: { ! 824: /* ! 825: * By default, don't attempt zone GC more frequently ! 826: * than once a second. ! 827: */ ! 828: ! 829: if (zone_gc_max_rate == 0) ! 830: zone_gc_max_rate = hz; ! 831: ! 832: if (zone_gc_allowed && ! 833: (sched_tick > (zone_gc_last_tick + zone_gc_max_rate))) { ! 834: zone_gc_last_tick = sched_tick; ! 835: zone_gc(); ! 836: } ! 837: } ! 838: ! 839: #if MACH_DEBUG ! 840: kern_return_t host_zone_info(host, namesp, namesCntp, infop, infoCntp) ! 841: host_t host; ! 842: zone_name_array_t *namesp; ! 843: unsigned int *namesCntp; ! 844: zone_info_array_t *infop; ! 845: unsigned int *infoCntp; ! 846: { ! 847: zone_name_t *names; ! 848: vm_offset_t names_addr; ! 849: vm_size_t names_size = 0; /*'=0' to quiet gcc warnings */ ! 850: zone_info_t *info; ! 851: vm_offset_t info_addr; ! 852: vm_size_t info_size = 0; /*'=0' to quiet gcc warnings */ ! 853: unsigned int max_zones, i; ! 854: zone_t z; ! 855: kern_return_t kr; ! 856: ! 857: if (host == HOST_NULL) ! 858: return KERN_INVALID_HOST; ! 859: ! 860: /* ! 861: * We assume that zones aren't freed once allocated. ! 862: * We won't pick up any zones that are allocated later. ! 863: */ ! 864: ! 865: simple_lock(&all_zones_lock); ! 866: max_zones = num_zones; ! 867: z = first_zone; ! 868: simple_unlock(&all_zones_lock); ! 869: ! 870: if (max_zones <= *namesCntp) { ! 871: /* use in-line memory */ ! 872: ! 873: names = *namesp; ! 874: } else { ! 875: names_size = round_page(max_zones * sizeof *names); ! 876: kr = kmem_alloc_pageable(ipc_kernel_map, ! 877: &names_addr, names_size); ! 878: if (kr != KERN_SUCCESS) ! 879: return kr; ! 880: ! 881: names = (zone_name_t *) names_addr; ! 882: } ! 883: ! 884: if (max_zones <= *infoCntp) { ! 885: /* use in-line memory */ ! 886: ! 887: info = *infop; ! 888: } else { ! 889: info_size = round_page(max_zones * sizeof *info); ! 890: kr = kmem_alloc_pageable(ipc_kernel_map, ! 891: &info_addr, info_size); ! 892: if (kr != KERN_SUCCESS) { ! 893: if (names != *namesp) ! 894: kmem_free(ipc_kernel_map, ! 895: names_addr, names_size); ! 896: return kr; ! 897: } ! 898: ! 899: info = (zone_info_t *) info_addr; ! 900: } ! 901: ! 902: for (i = 0; i < max_zones; i++) { ! 903: zone_name_t *zn = &names[i]; ! 904: zone_info_t *zi = &info[i]; ! 905: struct zone zcopy; ! 906: ! 907: assert(z != ZONE_NULL); ! 908: ! 909: zone_lock(z); ! 910: zcopy = *z; ! 911: zone_unlock(z); ! 912: ! 913: simple_lock(&all_zones_lock); ! 914: z = z->next_zone; ! 915: simple_unlock(&all_zones_lock); ! 916: ! 917: /* assuming here the name data is static */ ! 918: (void) strncpy(zn->zn_name, zcopy.zone_name, ! 919: sizeof zn->zn_name); ! 920: ! 921: #ifdef ZONE_COUNT ! 922: zi->zi_count = zcopy.count; ! 923: #else ! 924: zi->zi_count = 0; ! 925: #endif ! 926: zi->zi_cur_size = zcopy.cur_size; ! 927: zi->zi_max_size = zcopy.max_size; ! 928: zi->zi_elem_size = zcopy.elem_size; ! 929: zi->zi_alloc_size = zcopy.alloc_size; ! 930: zi->zi_pageable = (zcopy.type & ZONE_PAGEABLE) != 0; ! 931: zi->zi_exhaustible = (zcopy.type & ZONE_EXHAUSTIBLE) != 0; ! 932: zi->zi_collectable = (zcopy.type & ZONE_COLLECTABLE) != 0; ! 933: } ! 934: ! 935: if (names != *namesp) { ! 936: vm_size_t used; ! 937: vm_map_copy_t copy; ! 938: ! 939: used = max_zones * sizeof *names; ! 940: ! 941: if (used != names_size) ! 942: bzero((char *) (names_addr + used), names_size - used); ! 943: ! 944: kr = vm_map_copyin(ipc_kernel_map, names_addr, names_size, ! 945: TRUE, ©); ! 946: assert(kr == KERN_SUCCESS); ! 947: ! 948: *namesp = (zone_name_t *) copy; ! 949: } ! 950: *namesCntp = max_zones; ! 951: ! 952: if (info != *infop) { ! 953: vm_size_t used; ! 954: vm_map_copy_t copy; ! 955: ! 956: used = max_zones * sizeof *info; ! 957: ! 958: if (used != info_size) ! 959: bzero((char *) (info_addr + used), info_size - used); ! 960: ! 961: kr = vm_map_copyin(ipc_kernel_map, info_addr, info_size, ! 962: TRUE, ©); ! 963: assert(kr == KERN_SUCCESS); ! 964: ! 965: *infop = (zone_info_t *) copy; ! 966: } ! 967: *infoCntp = max_zones; ! 968: ! 969: return KERN_SUCCESS; ! 970: } ! 971: #endif MACH_DEBUG
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.