|
|
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: zone %s exhausted", ! 454: zone->zone_name); ! 455: } ! 456: } ! 457: ! 458: if (zone->type & ZONE_PAGEABLE) ! 459: zone->doing_alloc = TRUE; ! 460: zone_unlock(zone); ! 461: ! 462: if (zone->type & ZONE_PAGEABLE) { ! 463: if (kmem_alloc_pageable(zone_map, &addr, ! 464: zone->alloc_size) ! 465: != KERN_SUCCESS) ! 466: panic("zalloc: zone %s exhausted", ! 467: zone->zone_name); ! 468: zcram(zone, addr, zone->alloc_size); ! 469: zone_lock(zone); ! 470: zone->doing_alloc = FALSE; ! 471: /* XXX check before doing this */ ! 472: thread_wakeup((event_t)&zone->doing_alloc); ! 473: ! 474: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 475: } else if (zone->type & ZONE_COLLECTABLE) { ! 476: if (kmem_alloc_wired(zone_map, ! 477: &addr, zone->alloc_size) ! 478: != KERN_SUCCESS) ! 479: panic("zalloc: zone %s exhausted", ! 480: zone->zone_name); ! 481: zone_page_init(addr, zone->alloc_size, ! 482: ZONE_PAGE_USED); ! 483: zcram(zone, addr, zone->alloc_size); ! 484: zone_lock(zone); ! 485: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 486: } else { ! 487: addr = zget_space(zone->elem_size); ! 488: if (addr == 0) ! 489: panic("zalloc: zone %s exhausted", ! 490: zone->zone_name); ! 491: ! 492: zone_lock(zone); ! 493: zone_count_up(zone); ! 494: zone->cur_size += zone->elem_size; ! 495: zone_unlock(zone); ! 496: zone_page_alloc(addr, zone->elem_size); ! 497: return(addr); ! 498: } ! 499: } ! 500: } ! 501: ! 502: zone_unlock(zone); ! 503: return(addr); ! 504: } ! 505: ! 506: ! 507: /* ! 508: * zget returns an element from the specified zone ! 509: * and immediately returns nothing if there is nothing there. ! 510: * ! 511: * This form should be used when you can not block (like when ! 512: * processing an interrupt). ! 513: */ ! 514: vm_offset_t zget(zone_t zone) ! 515: { ! 516: register vm_offset_t addr; ! 517: ! 518: if (zone == ZONE_NULL) ! 519: panic ("zalloc: null zone"); ! 520: ! 521: zone_lock(zone); ! 522: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 523: zone_unlock(zone); ! 524: ! 525: return(addr); ! 526: } ! 527: ! 528: boolean_t zone_check = FALSE; ! 529: ! 530: void zfree(zone_t zone, vm_offset_t elem) ! 531: { ! 532: zone_lock(zone); ! 533: if (zone_check) { ! 534: vm_offset_t this; ! 535: ! 536: /* check the zone's consistency */ ! 537: ! 538: for (this = zone->free_elements; ! 539: this != 0; ! 540: this = * (vm_offset_t *) this) ! 541: if (this == elem) ! 542: panic("zfree"); ! 543: } ! 544: ADD_TO_ZONE(zone, elem); ! 545: zone_unlock(zone); ! 546: } ! 547: ! 548: /* ! 549: * Zone garbage collection subroutines ! 550: * ! 551: * These routines have in common the modification of entries in the ! 552: * zone_page_table. The latter contains one entry for every page ! 553: * in the zone_map. ! 554: * ! 555: * For each page table entry in the given range: ! 556: * ! 557: * zone_page_in_use - decrements in_free_list ! 558: * zone_page_free - increments in_free_list ! 559: * zone_page_init - initializes in_free_list and alloc_count ! 560: * zone_page_alloc - increments alloc_count ! 561: * zone_page_dealloc - decrements alloc_count ! 562: * zone_add_free_page_list - adds the page to the free list ! 563: * ! 564: * Two counts are maintained for each page, the in_free_list count and ! 565: * alloc_count. The alloc_count is how many zone elements have been ! 566: * allocated from a page. (Note that the page could contain elements ! 567: * that span page boundaries. The count includes these elements so ! 568: * one element may be counted in two pages.) In_free_list is a count ! 569: * of how many zone elements are currently free. If in_free_list is ! 570: * equal to alloc_count then the page is eligible for garbage ! 571: * collection. ! 572: * ! 573: * Alloc_count and in_free_list are initialized to the correct values ! 574: * for a particular zone when a page is zcram'ed into a zone. Subsequent ! 575: * gets and frees of zone elements will call zone_page_in_use and ! 576: * zone_page_free which modify the in_free_list count. When the zones ! 577: * garbage collector runs it will walk through a zones free element list, ! 578: * remove the elements that reside on collectable pages, and use ! 579: * zone_add_free_page_list to create a list of pages to be collected. ! 580: */ ! 581: ! 582: void zone_page_in_use(addr, size) ! 583: vm_offset_t addr; ! 584: vm_size_t size; ! 585: { ! 586: int i, j; ! 587: if ((addr < zone_map_min_address) || ! 588: (addr+size > zone_map_max_address)) return; ! 589: i = atop(addr-zone_map_min_address); ! 590: j = atop((addr+size-1) - zone_map_min_address); ! 591: lock_zone_page_table(); ! 592: for (; i <= j; i++) { ! 593: zone_page_table[i].in_free_list--; ! 594: } ! 595: unlock_zone_page_table(); ! 596: } ! 597: ! 598: void zone_page_free(addr, size) ! 599: vm_offset_t addr; ! 600: vm_size_t size; ! 601: { ! 602: int i, j; ! 603: if ((addr < zone_map_min_address) || ! 604: (addr+size > zone_map_max_address)) return; ! 605: i = atop(addr-zone_map_min_address); ! 606: j = atop((addr+size-1) - zone_map_min_address); ! 607: lock_zone_page_table(); ! 608: for (; i <= j; i++) { ! 609: /* Set in_free_list to (ZONE_PAGE_USED + 1) if ! 610: * it was previously set to ZONE_PAGE_UNUSED. ! 611: */ ! 612: if (zone_page_table[i].in_free_list == ZONE_PAGE_UNUSED) { ! 613: zone_page_table[i].in_free_list = 1; ! 614: } else { ! 615: zone_page_table[i].in_free_list++; ! 616: } ! 617: } ! 618: unlock_zone_page_table(); ! 619: } ! 620: ! 621: void zone_page_init(addr, size, value) ! 622: ! 623: vm_offset_t addr; ! 624: vm_size_t size; ! 625: int value; ! 626: { ! 627: int i, j; ! 628: if ((addr < zone_map_min_address) || ! 629: (addr+size > zone_map_max_address)) return; ! 630: i = atop(addr-zone_map_min_address); ! 631: j = atop((addr+size-1) - zone_map_min_address); ! 632: lock_zone_page_table(); ! 633: for (; i <= j; i++) { ! 634: zone_page_table[i].alloc_count = value; ! 635: zone_page_table[i].in_free_list = 0; ! 636: } ! 637: unlock_zone_page_table(); ! 638: } ! 639: ! 640: void zone_page_alloc(addr, size) ! 641: vm_offset_t addr; ! 642: vm_size_t size; ! 643: { ! 644: int i, j; ! 645: if ((addr < zone_map_min_address) || ! 646: (addr+size > zone_map_max_address)) return; ! 647: i = atop(addr-zone_map_min_address); ! 648: j = atop((addr+size-1) - zone_map_min_address); ! 649: lock_zone_page_table(); ! 650: for (; i <= j; i++) { ! 651: /* Set alloc_count to (ZONE_PAGE_USED + 1) if ! 652: * it was previously set to ZONE_PAGE_UNUSED. ! 653: */ ! 654: if (zone_page_table[i].alloc_count == ZONE_PAGE_UNUSED) { ! 655: zone_page_table[i].alloc_count = 1; ! 656: } else { ! 657: zone_page_table[i].alloc_count++; ! 658: } ! 659: } ! 660: unlock_zone_page_table(); ! 661: } ! 662: ! 663: void zone_page_dealloc(addr, size) ! 664: vm_offset_t addr; ! 665: vm_size_t size; ! 666: { ! 667: int i, j; ! 668: if ((addr < zone_map_min_address) || ! 669: (addr+size > zone_map_max_address)) return; ! 670: i = atop(addr-zone_map_min_address); ! 671: j = atop((addr+size-1) - zone_map_min_address); ! 672: lock_zone_page_table(); ! 673: for (; i <= j; i++) { ! 674: zone_page_table[i].alloc_count--; ! 675: } ! 676: unlock_zone_page_table(); ! 677: } ! 678: ! 679: void ! 680: zone_add_free_page_list(free_list, addr, size) ! 681: struct zone_page_table_entry **free_list; ! 682: vm_offset_t addr; ! 683: vm_size_t size; ! 684: { ! 685: int i, j; ! 686: if ((addr < zone_map_min_address) || ! 687: (addr+size > zone_map_max_address)) return; ! 688: i = atop(addr-zone_map_min_address); ! 689: j = atop((addr+size-1) - zone_map_min_address); ! 690: lock_zone_page_table(); ! 691: for (; i <= j; i++) { ! 692: if (zone_page_table[i].alloc_count == 0) { ! 693: zone_page_table[i].next = *free_list; ! 694: *free_list = &zone_page_table[i]; ! 695: zone_page_table[i].alloc_count = ZONE_PAGE_UNUSED; ! 696: zone_page_table[i].in_free_list = 0; ! 697: } ! 698: } ! 699: unlock_zone_page_table(); ! 700: } ! 701: ! 702: ! 703: /* This is used for walking through a zone's free element list. ! 704: */ ! 705: struct zone_free_entry { ! 706: struct zone_free_entry * next; ! 707: }; ! 708: ! 709: ! 710: /* Zone garbage collection ! 711: * ! 712: * zone_gc will walk through all the free elements in all the ! 713: * zones that are marked collectable looking for reclaimable ! 714: * pages. zone_gc is called by consider_zone_gc when the system ! 715: * begins to run out of memory. ! 716: */ ! 717: static void zone_gc() ! 718: { ! 719: int max_zones; ! 720: zone_t z; ! 721: int i; ! 722: register spl_t s; ! 723: struct zone_page_table_entry *freep; ! 724: struct zone_page_table_entry *zone_free_page_list; ! 725: ! 726: simple_lock(&all_zones_lock); ! 727: max_zones = num_zones; ! 728: z = first_zone; ! 729: simple_unlock(&all_zones_lock); ! 730: ! 731: zone_free_page_list = (struct zone_page_table_entry *) 0; ! 732: ! 733: for (i = 0; i < max_zones; i++) { ! 734: struct zone_free_entry * last; ! 735: struct zone_free_entry * elt; ! 736: assert(z != ZONE_NULL); ! 737: /* run this at splhigh so that interupt routines that use zones ! 738: can not interupt while their zone is locked */ ! 739: s=splhigh(); ! 740: zone_lock(z); ! 741: ! 742: if ((z->type & (ZONE_PAGEABLE|ZONE_COLLECTABLE)) == ZONE_COLLECTABLE) { ! 743: ! 744: /* Count the free elements in each page. This loop ! 745: * requires that all in_free_list entries are zero. ! 746: */ ! 747: elt = (struct zone_free_entry *)(z->free_elements); ! 748: while ((elt != (struct zone_free_entry *)0)) { ! 749: zone_page_free((vm_offset_t)elt, z->elem_size); ! 750: elt = elt->next; ! 751: } ! 752: ! 753: /* Now determine which elements should be removed ! 754: * from the free list and, after all the elements ! 755: * on a page have been removed, add the element's ! 756: * page to a list of pages to be freed. ! 757: */ ! 758: elt = (struct zone_free_entry *)(z->free_elements); ! 759: last = elt; ! 760: while ((elt != (struct zone_free_entry *)0)) { ! 761: if (((vm_offset_t)elt>=zone_map_min_address)&& ! 762: ((vm_offset_t)elt<=zone_map_max_address)&& ! 763: (zone_page(elt)->in_free_list == ! 764: zone_page(elt)->alloc_count)) { ! 765: ! 766: z->cur_size -= z->elem_size; ! 767: zone_page_in_use((vm_offset_t)elt, z->elem_size); ! 768: zone_page_dealloc((vm_offset_t)elt, z->elem_size); ! 769: if (zone_page(elt)->alloc_count == 0 || ! 770: zone_page(elt+(z->elem_size-1))->alloc_count==0) { ! 771: zone_add_free_page_list( ! 772: &zone_free_page_list, ! 773: (vm_offset_t)elt, z->elem_size); ! 774: } ! 775: ! 776: ! 777: if (elt == last) { ! 778: elt = elt->next; ! 779: z->free_elements =(vm_offset_t)elt; ! 780: last = elt; ! 781: } else { ! 782: last->next = elt->next; ! 783: elt = elt->next; ! 784: } ! 785: } else { ! 786: /* This element is not eligible for collection ! 787: * so clear in_free_list in preparation for a ! 788: * subsequent garbage collection pass. ! 789: */ ! 790: if (((vm_offset_t)elt>=zone_map_min_address)&& ! 791: ((vm_offset_t)elt<=zone_map_max_address)) { ! 792: zone_page(elt)->in_free_list = 0; ! 793: } ! 794: last = elt; ! 795: elt = elt->next; ! 796: } ! 797: } ! 798: } ! 799: zone_unlock(z); ! 800: splx(s); ! 801: simple_lock(&all_zones_lock); ! 802: z = z->next_zone; ! 803: simple_unlock(&all_zones_lock); ! 804: } ! 805: ! 806: for (freep = zone_free_page_list; freep != 0; freep = freep->next) { ! 807: vm_offset_t free_addr; ! 808: ! 809: free_addr = zone_map_min_address + ! 810: PAGE_SIZE * (freep - zone_page_table); ! 811: kmem_free(zone_map, free_addr, PAGE_SIZE); ! 812: } ! 813: } ! 814: ! 815: boolean_t zone_gc_allowed = TRUE; ! 816: unsigned zone_gc_last_tick = 0; ! 817: unsigned zone_gc_max_rate = 0; /* in ticks */ ! 818: ! 819: /* ! 820: * consider_zone_gc: ! 821: * ! 822: * Called by the pageout daemon when the system needs more free pages. ! 823: */ ! 824: ! 825: void ! 826: consider_zone_gc() ! 827: { ! 828: /* ! 829: * By default, don't attempt zone GC more frequently ! 830: * than once a second. ! 831: */ ! 832: ! 833: if (zone_gc_max_rate == 0) ! 834: zone_gc_max_rate = hz; ! 835: ! 836: if (zone_gc_allowed && ! 837: (sched_tick > (zone_gc_last_tick + zone_gc_max_rate))) { ! 838: zone_gc_last_tick = sched_tick; ! 839: zone_gc(); ! 840: } ! 841: } ! 842: ! 843: #if MACH_DEBUG ! 844: kern_return_t host_zone_info(host, namesp, namesCntp, infop, infoCntp) ! 845: host_t host; ! 846: zone_name_array_t *namesp; ! 847: unsigned int *namesCntp; ! 848: zone_info_array_t *infop; ! 849: unsigned int *infoCntp; ! 850: { ! 851: zone_name_t *names; ! 852: vm_offset_t names_addr; ! 853: vm_size_t names_size = 0; /*'=0' to quiet gcc warnings */ ! 854: zone_info_t *info; ! 855: vm_offset_t info_addr; ! 856: vm_size_t info_size = 0; /*'=0' to quiet gcc warnings */ ! 857: unsigned int max_zones, i; ! 858: zone_t z; ! 859: kern_return_t kr; ! 860: ! 861: if (host == HOST_NULL) ! 862: return KERN_INVALID_HOST; ! 863: ! 864: /* ! 865: * We assume that zones aren't freed once allocated. ! 866: * We won't pick up any zones that are allocated later. ! 867: */ ! 868: ! 869: simple_lock(&all_zones_lock); ! 870: max_zones = num_zones; ! 871: z = first_zone; ! 872: simple_unlock(&all_zones_lock); ! 873: ! 874: if (max_zones <= *namesCntp) { ! 875: /* use in-line memory */ ! 876: ! 877: names = *namesp; ! 878: } else { ! 879: names_size = round_page(max_zones * sizeof *names); ! 880: kr = kmem_alloc_pageable(ipc_kernel_map, ! 881: &names_addr, names_size); ! 882: if (kr != KERN_SUCCESS) ! 883: return kr; ! 884: ! 885: names = (zone_name_t *) names_addr; ! 886: } ! 887: ! 888: if (max_zones <= *infoCntp) { ! 889: /* use in-line memory */ ! 890: ! 891: info = *infop; ! 892: } else { ! 893: info_size = round_page(max_zones * sizeof *info); ! 894: kr = kmem_alloc_pageable(ipc_kernel_map, ! 895: &info_addr, info_size); ! 896: if (kr != KERN_SUCCESS) { ! 897: if (names != *namesp) ! 898: kmem_free(ipc_kernel_map, ! 899: names_addr, names_size); ! 900: return kr; ! 901: } ! 902: ! 903: info = (zone_info_t *) info_addr; ! 904: } ! 905: ! 906: for (i = 0; i < max_zones; i++) { ! 907: zone_name_t *zn = &names[i]; ! 908: zone_info_t *zi = &info[i]; ! 909: struct zone zcopy; ! 910: ! 911: assert(z != ZONE_NULL); ! 912: ! 913: zone_lock(z); ! 914: zcopy = *z; ! 915: zone_unlock(z); ! 916: ! 917: simple_lock(&all_zones_lock); ! 918: z = z->next_zone; ! 919: simple_unlock(&all_zones_lock); ! 920: ! 921: /* assuming here the name data is static */ ! 922: (void) strncpy(zn->zn_name, zcopy.zone_name, ! 923: sizeof zn->zn_name); ! 924: ! 925: #ifdef ZONE_COUNT ! 926: zi->zi_count = zcopy.count; ! 927: #else ! 928: zi->zi_count = 0; ! 929: #endif ! 930: zi->zi_cur_size = zcopy.cur_size; ! 931: zi->zi_max_size = zcopy.max_size; ! 932: zi->zi_elem_size = zcopy.elem_size; ! 933: zi->zi_alloc_size = zcopy.alloc_size; ! 934: zi->zi_pageable = (zcopy.type & ZONE_PAGEABLE) != 0; ! 935: zi->zi_exhaustible = (zcopy.type & ZONE_EXHAUSTIBLE) != 0; ! 936: zi->zi_collectable = (zcopy.type & ZONE_COLLECTABLE) != 0; ! 937: } ! 938: ! 939: if (names != *namesp) { ! 940: vm_size_t used; ! 941: vm_map_copy_t copy; ! 942: ! 943: used = max_zones * sizeof *names; ! 944: ! 945: if (used != names_size) ! 946: bzero((char *) (names_addr + used), names_size - used); ! 947: ! 948: kr = vm_map_copyin(ipc_kernel_map, names_addr, names_size, ! 949: TRUE, ©); ! 950: assert(kr == KERN_SUCCESS); ! 951: ! 952: *namesp = (zone_name_t *) copy; ! 953: } ! 954: *namesCntp = max_zones; ! 955: ! 956: if (info != *infop) { ! 957: vm_size_t used; ! 958: vm_map_copy_t copy; ! 959: ! 960: used = max_zones * sizeof *info; ! 961: ! 962: if (used != info_size) ! 963: bzero((char *) (info_addr + used), info_size - used); ! 964: ! 965: kr = vm_map_copyin(ipc_kernel_map, info_addr, info_size, ! 966: TRUE, ©); ! 967: assert(kr == KERN_SUCCESS); ! 968: ! 969: *infop = (zone_info_t *) copy; ! 970: } ! 971: *infoCntp = max_zones; ! 972: ! 973: return KERN_SUCCESS; ! 974: } ! 975: #endif /* MACH_DEBUG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.