|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: * @OSF_COPYRIGHT@ ! 24: */ ! 25: /* ! 26: * Mach Operating System ! 27: * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University ! 28: * All Rights Reserved. ! 29: * ! 30: * Permission to use, copy, modify and distribute this software and its ! 31: * documentation is hereby granted, provided that both the copyright ! 32: * notice and this permission notice appear in all copies of the ! 33: * software, derivative works or modified versions, and any portions ! 34: * thereof, and that both notices appear in supporting documentation. ! 35: * ! 36: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" ! 37: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR ! 38: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 39: * ! 40: * Carnegie Mellon requests users of this software to return to ! 41: * ! 42: * Software Distribution Coordinator or [email protected] ! 43: * School of Computer Science ! 44: * Carnegie Mellon University ! 45: * Pittsburgh PA 15213-3890 ! 46: * ! 47: * any improvements or extensions that they make and grant Carnegie Mellon ! 48: * the rights to redistribute these changes. ! 49: */ ! 50: /* ! 51: */ ! 52: /* ! 53: * File: kern/zalloc.c ! 54: * Author: Avadis Tevanian, Jr. ! 55: * ! 56: * Zone-based memory allocator. A zone is a collection of fixed size ! 57: * data blocks for which quick allocation/deallocation is possible. ! 58: */ ! 59: ! 60: #include <zone_debug.h> ! 61: #include <norma_vm.h> ! 62: #include <mach_kdb.h> ! 63: #include <kern/assert.h> ! 64: #include <kern/macro_help.h> ! 65: #include <kern/sched.h> ! 66: #include <kern/lock.h> ! 67: #include <kern/sched_prim.h> ! 68: #include <kern/misc_protos.h> ! 69: #include <kern/zalloc.h> ! 70: #include <kern/rtmalloc.h> ! 71: #include <mach/vm_param.h> ! 72: #include <vm/vm_kern.h> ! 73: #include <machine/machparam.h> ! 74: ! 75: ! 76: #if MACH_ASSERT ! 77: ! 78: /* Detect use of zone elt after freeing it by two methods: ! 79: * (1) Range-check the free-list "next" ptr for sanity. ! 80: * (2) Store the ptr in two different words, and compare them against ! 81: * each other when re-using the zone elt, to detect modifications; ! 82: */ ! 83: ! 84: #if defined(__alpha) ! 85: ! 86: #define is_kernel_data_addr(a) \ ! 87: (!(a) || IS_SYS_VA(a) && !((a) & (sizeof(long)-1))) ! 88: ! 89: #else /* !defined(__alpha) */ ! 90: ! 91: #define is_kernel_data_addr(a) \ ! 92: (!(a) || (a) >= VM_MIN_KERNEL_ADDRESS && !((a) & 0x3)) ! 93: ! 94: #endif /* defined(__alpha) */ ! 95: ! 96: /* Should we set all words of the zone element to an illegal address ! 97: * when it is freed, to help catch usage after freeing? The down-side ! 98: * is that this obscures the identity of the freed element. ! 99: */ ! 100: boolean_t zfree_clear = FALSE; ! 101: ! 102: #define ADD_TO_ZONE(zone, element) \ ! 103: MACRO_BEGIN \ ! 104: if (zfree_clear) \ ! 105: { int i; \ ! 106: for (i=1; \ ! 107: i < zone->elem_size/sizeof(vm_offset_t) - 1; \ ! 108: i++) \ ! 109: ((vm_offset_t *)(element))[i] = 0xdeadbeef; \ ! 110: } \ ! 111: ((vm_offset_t *)(element))[0] = (zone)->free_elements; \ ! 112: (zone)->free_elements = (vm_offset_t) (element); \ ! 113: (zone)->count--; \ ! 114: MACRO_END ! 115: ! 116: #define REMOVE_FROM_ZONE(zone, ret, type) \ ! 117: MACRO_BEGIN \ ! 118: (ret) = (type) (zone)->free_elements; \ ! 119: if ((ret) != (type) 0) { \ ! 120: if (!is_kernel_data_addr(((vm_offset_t *)(ret))[0])) { \ ! 121: panic("A freed zone element has been modified.\n"); \ ! 122: } \ ! 123: (zone)->count++; \ ! 124: (zone)->free_elements = *((vm_offset_t *)(ret)); \ ! 125: } \ ! 126: MACRO_END ! 127: #else /* MACH_ASSERT */ ! 128: ! 129: #define ADD_TO_ZONE(zone, element) \ ! 130: MACRO_BEGIN \ ! 131: *((vm_offset_t *)(element)) = (zone)->free_elements; \ ! 132: (zone)->free_elements = (vm_offset_t) (element); \ ! 133: (zone)->count--; \ ! 134: MACRO_END ! 135: ! 136: #define REMOVE_FROM_ZONE(zone, ret, type) \ ! 137: MACRO_BEGIN \ ! 138: (ret) = (type) (zone)->free_elements; \ ! 139: if ((ret) != (type) 0) { \ ! 140: (zone)->count++; \ ! 141: (zone)->free_elements = *((vm_offset_t *)(ret)); \ ! 142: } \ ! 143: MACRO_END ! 144: ! 145: #endif /* MACH_ASSERT */ ! 146: ! 147: #if ZONE_DEBUG ! 148: #define zone_debug_enabled(z) z->active_zones.next ! 149: #endif /* ZONE_DEBUG */ ! 150: ! 151: /* ! 152: * Support for garbage collection of unused zone pages: ! 153: */ ! 154: ! 155: struct zone_page_table_entry { ! 156: struct zone_page_table_entry *next; ! 157: short in_free_list; ! 158: short alloc_count; ! 159: }; ! 160: ! 161: extern struct zone_page_table_entry * zone_page_table; ! 162: ! 163: #define lock_zone_page_table() simple_lock(&zone_page_table_lock) ! 164: #define unlock_zone_page_table() simple_unlock(&zone_page_table_lock) ! 165: ! 166: #define zone_page(addr) \ ! 167: (&(zone_page_table[(atop(((vm_offset_t)addr) - zone_map_min_address))])) ! 168: ! 169: /* Forwards */ ! 170: void zone_page_init( ! 171: vm_offset_t addr, ! 172: vm_size_t size, ! 173: int value); ! 174: ! 175: void zone_page_alloc( ! 176: vm_offset_t addr, ! 177: vm_size_t size); ! 178: ! 179: void zone_add_free_page_list( ! 180: struct zone_page_table_entry **free_list, ! 181: vm_offset_t addr, ! 182: vm_size_t size); ! 183: void zone_page_dealloc( ! 184: vm_offset_t addr, ! 185: vm_size_t size); ! 186: ! 187: void zone_page_in_use( ! 188: vm_offset_t addr, ! 189: vm_size_t size); ! 190: ! 191: void zone_page_free( ! 192: vm_offset_t addr, ! 193: vm_size_t size); ! 194: ! 195: boolean_t zone_page_collectable( ! 196: vm_offset_t addr, ! 197: vm_size_t size); ! 198: ! 199: void zone_page_keep( ! 200: vm_offset_t addr, ! 201: vm_size_t size); ! 202: ! 203: #if ZONE_DEBUG && MACH_KDB ! 204: int zone_count( ! 205: zone_t z, ! 206: int tail); ! 207: #endif /* ZONE_DEBUG && MACH_KDB */ ! 208: ! 209: vm_map_t zone_map = VM_MAP_NULL; ! 210: ! 211: zone_t zone_zone = ZONE_NULL; /* the zone containing other zones */ ! 212: ! 213: /* ! 214: * The VM system gives us an initial chunk of memory. ! 215: * It has to be big enough to allocate the zone_zone ! 216: */ ! 217: ! 218: vm_offset_t zdata; ! 219: vm_size_t zdata_size; ! 220: ! 221: #define lock_zone(zone) \ ! 222: MACRO_BEGIN \ ! 223: simple_lock(&zone->lock); \ ! 224: MACRO_END ! 225: ! 226: #define unlock_zone(zone) \ ! 227: MACRO_BEGIN \ ! 228: simple_unlock(&zone->lock); \ ! 229: MACRO_END ! 230: ! 231: #define lock_zone_init(zone) \ ! 232: MACRO_BEGIN \ ! 233: simple_lock_init(&zone->lock, ETAP_MISC_ZONE); \ ! 234: MACRO_END ! 235: ! 236: #define lock_try_zone(zone) simple_lock_try(&zone->lock) ! 237: ! 238: kern_return_t zget_space( ! 239: vm_offset_t size, ! 240: vm_offset_t *result); ! 241: ! 242: decl_simple_lock_data(,zget_space_lock) ! 243: vm_offset_t zalloc_next_space; ! 244: vm_offset_t zalloc_end_of_space; ! 245: vm_size_t zalloc_wasted_space; ! 246: ! 247: /* ! 248: * Garbage collection map information ! 249: */ ! 250: decl_simple_lock_data(, zone_page_table_lock) ! 251: struct zone_page_table_entry * zone_page_table; ! 252: vm_offset_t zone_map_min_address; ! 253: vm_offset_t zone_map_max_address; ! 254: integer_t zone_pages; ! 255: ! 256: /* ! 257: * Exclude more than one concurrent garbage collection ! 258: */ ! 259: decl_mutex_data(, zone_gc_lock) ! 260: ! 261: #define from_zone_map(addr) \ ! 262: ((vm_offset_t)(addr) >= zone_map_min_address && \ ! 263: (vm_offset_t)(addr) < zone_map_max_address) ! 264: ! 265: #define ZONE_PAGE_USED 0 ! 266: #define ZONE_PAGE_UNUSED -1 ! 267: ! 268: ! 269: /* ! 270: * Protects first_zone, last_zone, num_zones, ! 271: * and the next_zone field of zones. ! 272: */ ! 273: decl_simple_lock_data(, all_zones_lock) ! 274: zone_t first_zone; ! 275: zone_t *last_zone; ! 276: int num_zones; ! 277: ! 278: /* ! 279: * zinit initializes a new zone. The zone data structures themselves ! 280: * are stored in a zone, which is initially a static structure that ! 281: * is initialized by zone_init. ! 282: */ ! 283: zone_t ! 284: zinit( ! 285: vm_size_t size, /* the size of an element */ ! 286: vm_size_t max, /* maximum memory to use */ ! 287: vm_size_t alloc, /* allocation size */ ! 288: char *name) /* a name for the zone */ ! 289: { ! 290: zone_t z; ! 291: ! 292: if (zone_zone == ZONE_NULL) { ! 293: if (zget_space(sizeof(struct zone), (vm_offset_t *)&z) ! 294: != KERN_SUCCESS) ! 295: return(ZONE_NULL); ! 296: } else ! 297: z = (zone_t) zalloc(zone_zone); ! 298: if (z == ZONE_NULL) ! 299: return(ZONE_NULL); ! 300: ! 301: /* ! 302: * Round off all the parameters appropriately. ! 303: */ ! 304: if (size < sizeof(z->free_elements)) ! 305: size = sizeof(z->free_elements); ! 306: size = ((size-1) + sizeof(z->free_elements)) - ! 307: ((size-1) % sizeof(z->free_elements)); ! 308: if (alloc == 0) ! 309: alloc = PAGE_SIZE; ! 310: alloc = round_page(alloc); ! 311: max = round_page(max); ! 312: /* ! 313: * We look for an allocation size with least fragmentation ! 314: * in the range of 1 - 5 pages. This size will be used unless ! 315: * the user suggestion is larger AND has less fragmentation ! 316: */ ! 317: { vm_size_t best, waste; unsigned int i; ! 318: best = PAGE_SIZE; ! 319: waste = best % size; ! 320: for (i = 2; i <= 5; i++){ vm_size_t tsize, twaste; ! 321: tsize = i * PAGE_SIZE; ! 322: twaste = tsize % size; ! 323: if (twaste < waste) ! 324: best = tsize, waste = twaste; ! 325: } ! 326: if (alloc <= best || (alloc % size >= waste)) ! 327: alloc = best; ! 328: } ! 329: if (max && (max < alloc)) ! 330: max = alloc; ! 331: ! 332: z->free_elements = 0; ! 333: z->cur_size = 0; ! 334: z->max_size = max; ! 335: z->elem_size = size; ! 336: z->alloc_size = alloc; ! 337: z->zone_name = name; ! 338: z->count = 0; ! 339: z->doing_alloc = FALSE; ! 340: z->exhaustible = FALSE; ! 341: z->collectable = TRUE; ! 342: z->allows_foreign = FALSE; ! 343: z->expandable = TRUE; ! 344: z->waiting = FALSE; ! 345: ! 346: #if ZONE_DEBUG ! 347: z->active_zones.next = z->active_zones.prev = 0; ! 348: zone_debug_enable(z); ! 349: #endif /* ZONE_DEBUG */ ! 350: lock_zone_init(z); ! 351: ! 352: /* ! 353: * Add the zone to the all-zones list. ! 354: */ ! 355: ! 356: z->next_zone = ZONE_NULL; ! 357: simple_lock(&all_zones_lock); ! 358: *last_zone = z; ! 359: last_zone = &z->next_zone; ! 360: num_zones++; ! 361: simple_unlock(&all_zones_lock); ! 362: ! 363: return(z); ! 364: } ! 365: ! 366: /* ! 367: * Cram the given memory into the specified zone. ! 368: */ ! 369: void ! 370: zcram( ! 371: register zone_t zone, ! 372: vm_offset_t newmem, ! 373: vm_size_t size) ! 374: { ! 375: register vm_size_t elem_size; ! 376: ! 377: /* Basic sanity checks */ ! 378: assert(zone != ZONE_NULL && newmem != (vm_offset_t)0); ! 379: assert(!zone->collectable || zone->allows_foreign ! 380: || (from_zone_map(newmem) && from_zone_map(newmem+size-1))); ! 381: ! 382: elem_size = zone->elem_size; ! 383: ! 384: lock_zone(zone); ! 385: while (size >= elem_size) { ! 386: ADD_TO_ZONE(zone, newmem); ! 387: if (from_zone_map(newmem)) ! 388: zone_page_alloc(newmem, elem_size); ! 389: zone->count++; /* compensate for ADD_TO_ZONE */ ! 390: size -= elem_size; ! 391: newmem += elem_size; ! 392: zone->cur_size += elem_size; ! 393: } ! 394: unlock_zone(zone); ! 395: } ! 396: ! 397: /* ! 398: * Contiguous space allocator for non-paged zones. Allocates "size" amount ! 399: * of memory from zone_map. ! 400: */ ! 401: ! 402: kern_return_t ! 403: zget_space( ! 404: vm_offset_t size, ! 405: vm_offset_t *result) ! 406: { ! 407: vm_offset_t new_space = 0; ! 408: vm_size_t space_to_add; ! 409: ! 410: simple_lock(&zget_space_lock); ! 411: while ((zalloc_next_space + size) > zalloc_end_of_space) { ! 412: /* ! 413: * Add at least one page to allocation area. ! 414: */ ! 415: ! 416: space_to_add = round_page(size); ! 417: ! 418: if (new_space == 0) { ! 419: kern_return_t retval; ! 420: /* ! 421: * Memory cannot be wired down while holding ! 422: * any locks that the pageout daemon might ! 423: * need to free up pages. [Making the zget_space ! 424: * lock a complex lock does not help in this ! 425: * regard.] ! 426: * ! 427: * Unlock and allocate memory. Because several ! 428: * threads might try to do this at once, don't ! 429: * use the memory before checking for available ! 430: * space again. ! 431: */ ! 432: ! 433: simple_unlock(&zget_space_lock); ! 434: ! 435: retval = kernel_memory_allocate(zone_map, &new_space, ! 436: space_to_add, 0, KMA_KOBJECT|KMA_NOPAGEWAIT); ! 437: if (retval != KERN_SUCCESS) ! 438: return(retval); ! 439: zone_page_init(new_space, space_to_add, ! 440: ZONE_PAGE_USED); ! 441: simple_lock(&zget_space_lock); ! 442: continue; ! 443: } ! 444: ! 445: ! 446: /* ! 447: * Memory was allocated in a previous iteration. ! 448: * ! 449: * Check whether the new region is contiguous ! 450: * with the old one. ! 451: */ ! 452: ! 453: if (new_space != zalloc_end_of_space) { ! 454: /* ! 455: * Throw away the remainder of the ! 456: * old space, and start a new one. ! 457: */ ! 458: zalloc_wasted_space += ! 459: zalloc_end_of_space - zalloc_next_space; ! 460: zalloc_next_space = new_space; ! 461: } ! 462: ! 463: zalloc_end_of_space = new_space + space_to_add; ! 464: ! 465: new_space = 0; ! 466: } ! 467: *result = zalloc_next_space; ! 468: zalloc_next_space += size; ! 469: simple_unlock(&zget_space_lock); ! 470: ! 471: if (new_space != 0) ! 472: kmem_free(zone_map, new_space, space_to_add); ! 473: ! 474: return(KERN_SUCCESS); ! 475: } ! 476: ! 477: ! 478: /* ! 479: * Steal memory for the zone package. Called from ! 480: * vm_page_bootstrap(). ! 481: */ ! 482: void ! 483: zone_steal_memory(void) ! 484: { ! 485: zdata_size = round_page(128*sizeof(struct zone)); ! 486: zdata = pmap_steal_memory(zdata_size); ! 487: } ! 488: ! 489: ! 490: /* ! 491: * Fill a zone with enough memory to contain at least nelem elements. ! 492: * Memory is obtained with kmem_alloc_wired from the kernel_map. ! 493: * Return the number of elements actually put into the zone, which may ! 494: * be more than the caller asked for since the memory allocation is ! 495: * rounded up to a full page. ! 496: */ ! 497: int ! 498: zfill( ! 499: zone_t zone, ! 500: int nelem) ! 501: { ! 502: kern_return_t kr; ! 503: vm_size_t size; ! 504: vm_offset_t memory; ! 505: int nalloc; ! 506: ! 507: assert(nelem > 0); ! 508: if (nelem <= 0) ! 509: return 0; ! 510: ! 511: size = nelem * zone->elem_size; ! 512: size = round_page(size); ! 513: kr = kmem_alloc_wired(kernel_map, &memory, size); ! 514: if (kr != KERN_SUCCESS) ! 515: return 0; ! 516: ! 517: zone_change(zone, Z_FOREIGN, TRUE); ! 518: zcram(zone, memory, size); ! 519: nalloc = size / zone->elem_size; ! 520: assert(nalloc >= nelem); ! 521: ! 522: return nalloc; ! 523: } ! 524: ! 525: /* ! 526: * Initialize the "zone of zones" which uses fixed memory allocated ! 527: * earlier in memory initialization. zone_bootstrap is called ! 528: * before zone_init. ! 529: */ ! 530: void ! 531: zone_bootstrap(void) ! 532: { ! 533: vm_size_t zone_zone_size; ! 534: vm_offset_t zone_zone_space; ! 535: ! 536: simple_lock_init(&all_zones_lock, ETAP_MISC_ZONE_ALL); ! 537: ! 538: first_zone = ZONE_NULL; ! 539: last_zone = &first_zone; ! 540: num_zones = 0; ! 541: ! 542: simple_lock_init(&zget_space_lock, ETAP_MISC_ZONE_GET); ! 543: zalloc_next_space = zdata; ! 544: zalloc_end_of_space = zdata + zdata_size; ! 545: zalloc_wasted_space = 0; ! 546: ! 547: /* assertion: nobody else called zinit before us */ ! 548: assert(zone_zone == ZONE_NULL); ! 549: zone_zone = zinit(sizeof(struct zone), 128 * sizeof(struct zone), ! 550: sizeof(struct zone), "zones"); ! 551: zone_change(zone_zone, Z_COLLECT, FALSE); ! 552: zone_zone_size = zalloc_end_of_space - zalloc_next_space; ! 553: zget_space(zone_zone_size, &zone_zone_space); ! 554: zcram(zone_zone, zone_zone_space, zone_zone_size); ! 555: } ! 556: ! 557: void ! 558: zone_init( ! 559: vm_size_t max_zonemap_size) ! 560: { ! 561: kern_return_t retval; ! 562: vm_offset_t zone_min; ! 563: vm_offset_t zone_max; ! 564: vm_size_t zone_table_size; ! 565: ! 566: retval = kmem_suballoc(kernel_map, &zone_min, max_zonemap_size, ! 567: FALSE, TRUE, &zone_map); ! 568: if (retval != KERN_SUCCESS) ! 569: panic("zone_init: kmem_suballoc failed"); ! 570: zone_max = zone_min + round_page(max_zonemap_size); ! 571: /* ! 572: * Setup garbage collection information: ! 573: */ ! 574: zone_table_size = atop(zone_max - zone_min) * ! 575: sizeof(struct zone_page_table_entry); ! 576: if (kmem_alloc_wired(zone_map, (vm_offset_t *) &zone_page_table, ! 577: zone_table_size) != KERN_SUCCESS) ! 578: panic("zone_init"); ! 579: zone_min = (vm_offset_t)zone_page_table + round_page(zone_table_size); ! 580: zone_pages = atop(zone_max - zone_min); ! 581: zone_map_min_address = zone_min; ! 582: zone_map_max_address = zone_max; ! 583: simple_lock_init(&zone_page_table_lock, ETAP_MISC_ZONE_PTABLE); ! 584: mutex_init(&zone_gc_lock, ETAP_NO_TRACE); ! 585: zone_page_init(zone_min, zone_max - zone_min, ZONE_PAGE_UNUSED); ! 586: } ! 587: ! 588: ! 589: /* ! 590: * zalloc returns an element from the specified zone. ! 591: */ ! 592: vm_offset_t ! 593: zalloc( ! 594: register zone_t zone) ! 595: { ! 596: vm_offset_t addr; ! 597: kern_return_t retval; ! 598: ! 599: assert(zone != ZONE_NULL); ! 600: check_simple_locks(); ! 601: ! 602: lock_zone(zone); ! 603: ! 604: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 605: while (addr == 0) { ! 606: /* ! 607: * If nothing was there, try to get more ! 608: */ ! 609: if (zone->doing_alloc) { ! 610: /* ! 611: * Someone is allocating memory for this zone. ! 612: * Wait for it to show up, then try again. ! 613: */ ! 614: assert_wait((event_t)zone, THREAD_INTERRUPTIBLE); ! 615: zone->waiting = TRUE; ! 616: unlock_zone(zone); ! 617: thread_block((void (*)(void)) 0); ! 618: lock_zone(zone); ! 619: } ! 620: else { ! 621: if ((zone->cur_size + zone->elem_size) > ! 622: zone->max_size) { ! 623: if (zone->exhaustible) ! 624: break; ! 625: if (zone->expandable) { ! 626: /* ! 627: * We're willing to overflow certain ! 628: * zones, but not without complaining. ! 629: * ! 630: * This is best used in conjunction ! 631: * with the collectable flag. What we ! 632: * want is an assurance we can get the ! 633: * memory back, assuming there's no ! 634: * leak. ! 635: */ ! 636: zone->max_size += (zone->max_size >> 1); ! 637: } else { ! 638: unlock_zone(zone); ! 639: panic("zalloc: zone \"%s\" empty.", zone->zone_name); ! 640: } ! 641: } ! 642: zone->doing_alloc = TRUE; ! 643: unlock_zone(zone); ! 644: ! 645: if (zone->collectable) { ! 646: vm_offset_t space; ! 647: vm_size_t alloc_size; ! 648: ! 649: if (vm_pool_low()) ! 650: alloc_size = ! 651: round_page(zone->elem_size); ! 652: else ! 653: alloc_size = zone->alloc_size; ! 654: ! 655: retval = kernel_memory_allocate(zone_map, ! 656: &space, alloc_size, 0, ! 657: KMA_KOBJECT|KMA_NOPAGEWAIT); ! 658: if (retval == KERN_SUCCESS) { ! 659: zone_page_init(space, alloc_size, ! 660: ZONE_PAGE_USED); ! 661: zcram(zone, space, alloc_size); ! 662: } else if (retval != KERN_RESOURCE_SHORTAGE) { ! 663: /* would like to cause a zone_gc() */ ! 664: panic("zalloc"); ! 665: } ! 666: lock_zone(zone); ! 667: zone->doing_alloc = FALSE; ! 668: if (zone->waiting) { ! 669: zone->waiting = FALSE; ! 670: thread_wakeup((event_t)zone); ! 671: } ! 672: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 673: if (addr == 0 && ! 674: retval == KERN_RESOURCE_SHORTAGE) { ! 675: unlock_zone(zone); ! 676: VM_PAGE_WAIT(); ! 677: lock_zone(zone); ! 678: } ! 679: } else { ! 680: vm_offset_t space; ! 681: retval = zget_space(zone->elem_size, &space); ! 682: ! 683: lock_zone(zone); ! 684: zone->doing_alloc = FALSE; ! 685: if (zone->waiting) { ! 686: zone->waiting = FALSE; ! 687: thread_wakeup((event_t)zone); ! 688: } ! 689: if (retval == KERN_SUCCESS) { ! 690: zone->count++; ! 691: zone->cur_size += zone->elem_size; ! 692: #if ZONE_DEBUG ! 693: if (zone_debug_enabled(zone)) { ! 694: enqueue_tail(&zone->active_zones, (queue_entry_t)space); ! 695: } ! 696: #endif ! 697: unlock_zone(zone); ! 698: zone_page_alloc(space, zone->elem_size); ! 699: #if ZONE_DEBUG ! 700: if (zone_debug_enabled(zone)) ! 701: space += sizeof(queue_chain_t); ! 702: #endif ! 703: return(space); ! 704: } ! 705: if (retval == KERN_RESOURCE_SHORTAGE) { ! 706: unlock_zone(zone); ! 707: VM_PAGE_WAIT(); ! 708: lock_zone(zone); ! 709: } else { ! 710: panic("zalloc"); ! 711: } ! 712: } ! 713: } ! 714: if (addr == 0) ! 715: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 716: } ! 717: ! 718: #if ZONE_DEBUG ! 719: if (addr && zone_debug_enabled(zone)) { ! 720: enqueue_tail(&zone->active_zones, (queue_entry_t)addr); ! 721: addr += sizeof(queue_chain_t); ! 722: } ! 723: #endif ! 724: ! 725: unlock_zone(zone); ! 726: return(addr); ! 727: } ! 728: ! 729: ! 730: /* ! 731: * zget returns an element from the specified zone ! 732: * and immediately returns nothing if there is nothing there. ! 733: * ! 734: * This form should be used when you can not block (like when ! 735: * processing an interrupt). ! 736: */ ! 737: vm_offset_t ! 738: zget( ! 739: register zone_t zone) ! 740: { ! 741: register vm_offset_t addr; ! 742: ! 743: assert( zone != ZONE_NULL ); ! 744: ! 745: if (!lock_try_zone(zone)) ! 746: return ((vm_offset_t)0); ! 747: ! 748: REMOVE_FROM_ZONE(zone, addr, vm_offset_t); ! 749: #if ZONE_DEBUG ! 750: if (addr && zone_debug_enabled(zone)) { ! 751: enqueue_tail(&zone->active_zones, (queue_entry_t)addr); ! 752: addr += sizeof(queue_chain_t); ! 753: } ! 754: #endif /* ZONE_DEBUG */ ! 755: unlock_zone(zone); ! 756: ! 757: return(addr); ! 758: } ! 759: ! 760: #if ZONE_DEBUG ! 761: boolean_t zone_check = TRUE; ! 762: #else /* ZONE_DEBUG */ ! 763: boolean_t zone_check = FALSE; ! 764: #endif /* ZONE_DEBUG */ ! 765: ! 766: void ! 767: zfree( ! 768: register zone_t zone, ! 769: vm_offset_t elem) ! 770: { ! 771: ! 772: #if MACH_ASSERT ! 773: /* Basic sanity checks */ ! 774: if (zone == ZONE_NULL || elem == (vm_offset_t)0) ! 775: panic("zfree: NULL"); ! 776: /* zone_gc assumes zones are never freed */ ! 777: if (zone == zone_zone) ! 778: panic("zfree: freeing to zone_zone breaks zone_gc!"); ! 779: if (zone->collectable && !zone->allows_foreign && ! 780: (!from_zone_map(elem) || !from_zone_map(elem+zone->elem_size-1))) ! 781: panic("zfree: non-allocated memory in collectable zone!"); ! 782: #endif ! 783: ! 784: lock_zone(zone); ! 785: #if ZONE_DEBUG ! 786: if (zone_debug_enabled(zone)) { ! 787: queue_t tmp_elem; ! 788: ! 789: elem -= sizeof(queue_chain_t); ! 790: if (zone_check) { ! 791: /* check the zone's consistency */ ! 792: ! 793: for (tmp_elem = queue_first(&zone->active_zones); ! 794: !queue_end(tmp_elem, &zone->active_zones); ! 795: tmp_elem = queue_next(tmp_elem)) ! 796: if (elem == (vm_offset_t)tmp_elem) ! 797: break; ! 798: if (elem != (vm_offset_t)tmp_elem) ! 799: panic("zfree()ing element from wrong zone"); ! 800: } ! 801: remqueue(&zone->active_zones, (queue_t) elem); ! 802: } ! 803: #endif /* ZONE_DEBUG */ ! 804: if (zone_check) { ! 805: vm_offset_t this; ! 806: ! 807: /* check the zone's consistency */ ! 808: ! 809: for (this = zone->free_elements; ! 810: this != 0; ! 811: this = * (vm_offset_t *) this) ! 812: if (!pmap_kernel_va(this) || this == elem) ! 813: panic("zfree"); ! 814: } ! 815: /* ! 816: * If elements have one or more pages, and memory is low, ! 817: * put it directly back into circulation rather than ! 818: * back into a zone, where a non-vm_privileged task can grab it. ! 819: * This lessens the impact of a privileged task cycling reserved ! 820: * memory into a publicly accessible zone. ! 821: */ ! 822: if (zone->elem_size >= PAGE_SIZE && ! 823: vm_pool_low()){ ! 824: assert( !(zone->elem_size & (zone->alloc_size-1)) ); ! 825: zone->count--; ! 826: zone->cur_size -= zone->elem_size; ! 827: zone_page_init(elem, zone->elem_size, ZONE_PAGE_UNUSED); ! 828: unlock_zone(zone); ! 829: kmem_free(zone_map, elem, zone->elem_size); ! 830: return; ! 831: } ! 832: ADD_TO_ZONE(zone, elem); ! 833: unlock_zone(zone); ! 834: } ! 835: ! 836: ! 837: /* Change a zone's flags. ! 838: * This routine must be called immediately after zinit. ! 839: */ ! 840: void ! 841: zone_change( ! 842: zone_t zone, ! 843: unsigned int item, ! 844: boolean_t value) ! 845: { ! 846: assert( zone != ZONE_NULL ); ! 847: assert( value == TRUE || value == FALSE ); ! 848: ! 849: switch(item){ ! 850: case Z_EXHAUST: ! 851: zone->exhaustible = value; ! 852: break; ! 853: case Z_COLLECT: ! 854: zone->collectable = value; ! 855: break; ! 856: case Z_EXPAND: ! 857: zone->expandable = value; ! 858: break; ! 859: case Z_FOREIGN: ! 860: zone->allows_foreign = value; ! 861: break; ! 862: #if MACH_ASSERT ! 863: default: ! 864: panic("Zone_change: Wrong Item Type!"); ! 865: /* break; */ ! 866: #endif ! 867: } ! 868: lock_zone_init(zone); ! 869: } ! 870: ! 871: /* ! 872: * Return the expected number of free elements in the zone. ! 873: * This calculation will be incorrect if items are zfree'd that ! 874: * were never zalloc'd/zget'd. The correct way to stuff memory ! 875: * into a zone is by zcram. ! 876: */ ! 877: ! 878: integer_t ! 879: zone_free_count(zone_t zone) ! 880: { ! 881: integer_t free_count; ! 882: ! 883: lock_zone(zone); ! 884: free_count = zone->cur_size/zone->elem_size - zone->count; ! 885: unlock_zone(zone); ! 886: ! 887: assert(free_count >= 0); ! 888: ! 889: return(free_count); ! 890: } ! 891: ! 892: /* ! 893: * zprealloc preallocates wired memory, exanding the specified ! 894: * zone to the specified size ! 895: */ ! 896: void ! 897: zprealloc( ! 898: zone_t zone, ! 899: vm_size_t size) ! 900: { ! 901: vm_offset_t addr; ! 902: ! 903: if (size != 0) { ! 904: if (kmem_alloc_wired(zone_map, &addr, size) != KERN_SUCCESS) ! 905: panic("zprealloc"); ! 906: zone_page_init(addr, size, ZONE_PAGE_USED); ! 907: zcram(zone, addr, size); ! 908: } ! 909: } ! 910: ! 911: /* ! 912: * Zone garbage collection subroutines ! 913: * ! 914: * These routines have in common the modification of entries in the ! 915: * zone_page_table. The latter contains one entry for every page ! 916: * in the zone_map. ! 917: * ! 918: * For each page table entry in the given range: ! 919: * ! 920: * zone_page_collectable - test if one (in_free_list == alloc_count) ! 921: * zone_page_keep - reset in_free_list ! 922: * zone_page_in_use - decrements in_free_list ! 923: * zone_page_free - increments in_free_list ! 924: * zone_page_init - initializes in_free_list and alloc_count ! 925: * zone_page_alloc - increments alloc_count ! 926: * zone_page_dealloc - decrements alloc_count ! 927: * zone_add_free_page_list - adds the page to the free list ! 928: * ! 929: * Two counts are maintained for each page, the in_free_list count and ! 930: * alloc_count. The alloc_count is how many zone elements have been ! 931: * allocated from a page. (Note that the page could contain elements ! 932: * that span page boundaries. The count includes these elements so ! 933: * one element may be counted in two pages.) In_free_list is a count ! 934: * of how many zone elements are currently free. If in_free_list is ! 935: * equal to alloc_count then the page is eligible for garbage ! 936: * collection. ! 937: * ! 938: * Alloc_count and in_free_list are initialized to the correct values ! 939: * for a particular zone when a page is zcram'ed into a zone. Subsequent ! 940: * gets and frees of zone elements will call zone_page_in_use and ! 941: * zone_page_free which modify the in_free_list count. When the zones ! 942: * garbage collector runs it will walk through a zones free element list, ! 943: * remove the elements that reside on collectable pages, and use ! 944: * zone_add_free_page_list to create a list of pages to be collected. ! 945: */ ! 946: ! 947: boolean_t ! 948: zone_page_collectable( ! 949: vm_offset_t addr, ! 950: vm_size_t size) ! 951: { ! 952: natural_t i, j; ! 953: ! 954: #if MACH_ASSERT ! 955: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 956: panic("zone_page_collectable"); ! 957: #endif ! 958: ! 959: i = atop(addr-zone_map_min_address); ! 960: j = atop((addr+size-1) - zone_map_min_address); ! 961: lock_zone_page_table(); ! 962: for (; i <= j; i++) { ! 963: if (zone_page_table[i].in_free_list == ! 964: zone_page_table[i].alloc_count) { ! 965: unlock_zone_page_table(); ! 966: return (TRUE); ! 967: } ! 968: } ! 969: unlock_zone_page_table(); ! 970: return (FALSE); ! 971: } ! 972: ! 973: void ! 974: zone_page_keep( ! 975: vm_offset_t addr, ! 976: vm_size_t size) ! 977: { ! 978: natural_t i, j; ! 979: ! 980: #if MACH_ASSERT ! 981: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 982: panic("zone_page_keep"); ! 983: #endif ! 984: ! 985: i = atop(addr-zone_map_min_address); ! 986: j = atop((addr+size-1) - zone_map_min_address); ! 987: lock_zone_page_table(); ! 988: for (; i <= j; i++) { ! 989: zone_page_table[i].in_free_list = 0; ! 990: } ! 991: unlock_zone_page_table(); ! 992: } ! 993: ! 994: void ! 995: zone_page_in_use( ! 996: vm_offset_t addr, ! 997: vm_size_t size) ! 998: { ! 999: natural_t i, j; ! 1000: ! 1001: #if MACH_ASSERT ! 1002: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 1003: panic("zone_page_in_use"); ! 1004: #endif ! 1005: ! 1006: i = atop(addr-zone_map_min_address); ! 1007: j = atop((addr+size-1) - zone_map_min_address); ! 1008: lock_zone_page_table(); ! 1009: for (; i <= j; i++) { ! 1010: if (zone_page_table[i].in_free_list > 0) ! 1011: zone_page_table[i].in_free_list--; ! 1012: } ! 1013: unlock_zone_page_table(); ! 1014: } ! 1015: ! 1016: void ! 1017: zone_page_free( ! 1018: vm_offset_t addr, ! 1019: vm_size_t size) ! 1020: { ! 1021: natural_t i, j; ! 1022: ! 1023: #if MACH_ASSERT ! 1024: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 1025: panic("zone_page_free"); ! 1026: #endif ! 1027: ! 1028: i = atop(addr-zone_map_min_address); ! 1029: j = atop((addr+size-1) - zone_map_min_address); ! 1030: lock_zone_page_table(); ! 1031: for (; i <= j; i++) { ! 1032: assert(zone_page_table[i].in_free_list >= 0); ! 1033: zone_page_table[i].in_free_list++; ! 1034: } ! 1035: unlock_zone_page_table(); ! 1036: } ! 1037: ! 1038: void ! 1039: zone_page_init( ! 1040: vm_offset_t addr, ! 1041: vm_size_t size, ! 1042: int value) ! 1043: { ! 1044: natural_t i, j; ! 1045: ! 1046: #if MACH_ASSERT ! 1047: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 1048: panic("zone_page_init"); ! 1049: #endif ! 1050: ! 1051: i = atop(addr-zone_map_min_address); ! 1052: j = atop((addr+size-1) - zone_map_min_address); ! 1053: lock_zone_page_table(); ! 1054: for (; i <= j; i++) { ! 1055: zone_page_table[i].alloc_count = value; ! 1056: zone_page_table[i].in_free_list = 0; ! 1057: } ! 1058: unlock_zone_page_table(); ! 1059: } ! 1060: ! 1061: void ! 1062: zone_page_alloc( ! 1063: vm_offset_t addr, ! 1064: vm_size_t size) ! 1065: { ! 1066: natural_t i, j; ! 1067: ! 1068: #if MACH_ASSERT ! 1069: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 1070: panic("zone_page_alloc"); ! 1071: #endif ! 1072: ! 1073: i = atop(addr-zone_map_min_address); ! 1074: j = atop((addr+size-1) - zone_map_min_address); ! 1075: lock_zone_page_table(); ! 1076: for (; i <= j; i++) { ! 1077: /* Set alloc_count to (ZONE_PAGE_USED + 1) if ! 1078: * it was previously set to ZONE_PAGE_UNUSED. ! 1079: */ ! 1080: if (zone_page_table[i].alloc_count == ZONE_PAGE_UNUSED) { ! 1081: zone_page_table[i].alloc_count = 1; ! 1082: } else { ! 1083: zone_page_table[i].alloc_count++; ! 1084: } ! 1085: } ! 1086: unlock_zone_page_table(); ! 1087: } ! 1088: ! 1089: void ! 1090: zone_page_dealloc( ! 1091: vm_offset_t addr, ! 1092: vm_size_t size) ! 1093: { ! 1094: natural_t i, j; ! 1095: ! 1096: #if MACH_ASSERT ! 1097: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 1098: panic("zone_page_dealloc"); ! 1099: #endif ! 1100: ! 1101: i = atop(addr-zone_map_min_address); ! 1102: j = atop((addr+size-1) - zone_map_min_address); ! 1103: lock_zone_page_table(); ! 1104: for (; i <= j; i++) { ! 1105: zone_page_table[i].alloc_count--; ! 1106: } ! 1107: unlock_zone_page_table(); ! 1108: } ! 1109: ! 1110: void ! 1111: zone_add_free_page_list( ! 1112: struct zone_page_table_entry **free_list, ! 1113: vm_offset_t addr, ! 1114: vm_size_t size) ! 1115: { ! 1116: natural_t i, j; ! 1117: ! 1118: #if MACH_ASSERT ! 1119: if (!from_zone_map(addr) || !from_zone_map(addr+size-1)) ! 1120: panic("zone_add_free_page_list"); ! 1121: #endif ! 1122: ! 1123: i = atop(addr-zone_map_min_address); ! 1124: j = atop((addr+size-1) - zone_map_min_address); ! 1125: lock_zone_page_table(); ! 1126: for (; i <= j; i++) { ! 1127: if (zone_page_table[i].alloc_count == 0) { ! 1128: zone_page_table[i].next = *free_list; ! 1129: *free_list = &zone_page_table[i]; ! 1130: zone_page_table[i].alloc_count = ZONE_PAGE_UNUSED; ! 1131: zone_page_table[i].in_free_list = 0; ! 1132: } ! 1133: } ! 1134: unlock_zone_page_table(); ! 1135: } ! 1136: ! 1137: ! 1138: /* This is used for walking through a zone's free element list. ! 1139: */ ! 1140: struct zone_free_entry { ! 1141: struct zone_free_entry * next; ! 1142: }; ! 1143: ! 1144: int reclaim_page_count = 0; ! 1145: ! 1146: /* Zone garbage collection ! 1147: * ! 1148: * zone_gc will walk through all the free elements in all the ! 1149: * zones that are marked collectable looking for reclaimable ! 1150: * pages. zone_gc is called by consider_zone_gc when the system ! 1151: * begins to run out of memory. ! 1152: */ ! 1153: void ! 1154: zone_gc(void) ! 1155: { ! 1156: unsigned int max_zones; ! 1157: zone_t z; ! 1158: unsigned int i; ! 1159: struct zone_page_table_entry *freep; ! 1160: struct zone_page_table_entry *zone_free_page_list; ! 1161: ! 1162: mutex_lock(&zone_gc_lock); ! 1163: ! 1164: simple_lock(&all_zones_lock); ! 1165: max_zones = num_zones; ! 1166: z = first_zone; ! 1167: simple_unlock(&all_zones_lock); ! 1168: ! 1169: #if MACH_ASSERT ! 1170: lock_zone_page_table(); ! 1171: for (i = 0; i < zone_pages; i++) ! 1172: assert(zone_page_table[i].in_free_list == 0); ! 1173: unlock_zone_page_table(); ! 1174: #endif /* MACH_ASSERT */ ! 1175: ! 1176: zone_free_page_list = (struct zone_page_table_entry *) 0; ! 1177: ! 1178: for (i = 0; i < max_zones; i++) { ! 1179: struct zone_free_entry * last; ! 1180: struct zone_free_entry * elt; ! 1181: assert(z != ZONE_NULL); ! 1182: ! 1183: if (!z->collectable) { ! 1184: simple_lock(&all_zones_lock); ! 1185: z = z->next_zone; ! 1186: simple_unlock(&all_zones_lock); ! 1187: continue; ! 1188: } ! 1189: lock_zone(z); ! 1190: ! 1191: /* ! 1192: * Do a quick feasability check before we scan the zone: ! 1193: * skip unless there is likelihood of getting 1+ pages back. ! 1194: */ ! 1195: if ((z->cur_size - z->count * z->elem_size) <= (2*PAGE_SIZE)){ ! 1196: unlock_zone(z); ! 1197: simple_lock(&all_zones_lock); ! 1198: z = z->next_zone; ! 1199: simple_unlock(&all_zones_lock); ! 1200: continue; ! 1201: } ! 1202: ! 1203: /* Count the free elements in each page. This loop ! 1204: * requires that all in_free_list entries are zero. ! 1205: */ ! 1206: for (elt = (struct zone_free_entry *)(z->free_elements); ! 1207: elt != (struct zone_free_entry *)0; elt = elt->next) { ! 1208: ! 1209: if (!from_zone_map(elt)) continue; ! 1210: ! 1211: zone_page_free((vm_offset_t)elt, z->elem_size); ! 1212: } ! 1213: ! 1214: /* Now determine which elements should be removed ! 1215: * from the free list and, after all the elements ! 1216: * on a page have been removed, add the element's ! 1217: * page to a list of pages to be freed. ! 1218: */ ! 1219: elt = (struct zone_free_entry *)(z->free_elements); ! 1220: last = elt; ! 1221: while ((elt != (struct zone_free_entry *)0)) { ! 1222: if (!from_zone_map(elt)) { ! 1223: last = elt; ! 1224: elt = elt->next; ! 1225: continue; ! 1226: } ! 1227: if (zone_page_collectable((vm_offset_t)elt, ! 1228: z->elem_size)) { ! 1229: z->cur_size -= z->elem_size; ! 1230: zone_page_in_use((vm_offset_t)elt, ! 1231: z->elem_size); ! 1232: zone_page_dealloc((vm_offset_t)elt, ! 1233: z->elem_size); ! 1234: zone_add_free_page_list(&zone_free_page_list, ! 1235: (vm_offset_t)elt, ! 1236: z->elem_size); ! 1237: if (elt == last) { ! 1238: elt = elt->next; ! 1239: z->free_elements =(vm_offset_t)elt; ! 1240: last = elt; ! 1241: } else { ! 1242: last->next = elt->next; ! 1243: elt = elt->next; ! 1244: } ! 1245: } else { ! 1246: /* This element is not eligible for collection ! 1247: * so clear in_free_list in preparation for a ! 1248: * subsequent garbage collection pass. ! 1249: */ ! 1250: zone_page_keep((vm_offset_t)elt, z->elem_size); ! 1251: last = elt; ! 1252: elt = elt->next; ! 1253: } ! 1254: } /* end while(elt) */ ! 1255: ! 1256: unlock_zone(z); ! 1257: simple_lock(&all_zones_lock); ! 1258: /* ! 1259: * Note that this scheme of locking only to walk the zone list ! 1260: * assumes that zones are never freed (checked by zfree) ! 1261: */ ! 1262: z = z->next_zone; ! 1263: simple_unlock(&all_zones_lock); ! 1264: } ! 1265: ! 1266: for (freep = zone_free_page_list; freep != 0; freep = freep->next) { ! 1267: vm_offset_t free_addr; ! 1268: ! 1269: free_addr = zone_map_min_address + ! 1270: PAGE_SIZE * (freep - zone_page_table); ! 1271: kmem_free(zone_map, free_addr, PAGE_SIZE); ! 1272: reclaim_page_count++; ! 1273: } ! 1274: mutex_unlock(&zone_gc_lock); ! 1275: } ! 1276: ! 1277: boolean_t zone_gc_allowed = TRUE; /* XXX */ ! 1278: unsigned zone_gc_last_tick = 0; ! 1279: unsigned zone_gc_max_rate = 0; /* in ticks */ ! 1280: ! 1281: /* ! 1282: * consider_zone_gc: ! 1283: * ! 1284: * Called by the pageout daemon when the system needs more free pages. ! 1285: */ ! 1286: ! 1287: void ! 1288: consider_zone_gc(void) ! 1289: { ! 1290: /* ! 1291: * By default, don't attempt zone GC more frequently ! 1292: * than once a second (which is one scheduler tick). ! 1293: */ ! 1294: ! 1295: if (zone_gc_max_rate == 0) ! 1296: zone_gc_max_rate = 2; /* sched_tick is a 1 second resolution 2 here insures at least 1 second interval */ ! 1297: ! 1298: if (zone_gc_allowed && ! 1299: (sched_tick > (zone_gc_last_tick + zone_gc_max_rate))) { ! 1300: zone_gc_last_tick = sched_tick; ! 1301: zone_gc(); ! 1302: } ! 1303: } ! 1304: ! 1305: #include <mach/kern_return.h> ! 1306: #include <mach/machine/vm_types.h> ! 1307: #include <mach_debug/zone_info.h> ! 1308: #include <kern/host.h> ! 1309: #include <vm/vm_map.h> ! 1310: #include <vm/vm_user.h> ! 1311: #include <vm/vm_kern.h> ! 1312: ! 1313: #include <mach/mach_host_server.h> ! 1314: ! 1315: kern_return_t ! 1316: host_zone_info( ! 1317: host_t host, ! 1318: zone_name_array_t *namesp, ! 1319: mach_msg_type_number_t *namesCntp, ! 1320: zone_info_array_t *infop, ! 1321: mach_msg_type_number_t *infoCntp) ! 1322: { ! 1323: zone_name_t *names; ! 1324: vm_offset_t names_addr; ! 1325: vm_size_t names_size; ! 1326: zone_info_t *info; ! 1327: vm_offset_t info_addr; ! 1328: vm_size_t info_size; ! 1329: unsigned int max_zones, i; ! 1330: zone_t z; ! 1331: kern_return_t kr; ! 1332: ! 1333: if (host == HOST_NULL) ! 1334: return KERN_INVALID_HOST; ! 1335: ! 1336: /* ! 1337: * We assume that zones aren't freed once allocated. ! 1338: * We won't pick up any zones that are allocated later. ! 1339: */ ! 1340: ! 1341: simple_lock(&all_zones_lock); ! 1342: max_zones = num_zones; ! 1343: z = first_zone; ! 1344: simple_unlock(&all_zones_lock); ! 1345: ! 1346: if (max_zones <= *namesCntp) { ! 1347: /* use in-line memory */ ! 1348: ! 1349: names = *namesp; ! 1350: } else { ! 1351: names_size = round_page(max_zones * sizeof *names); ! 1352: kr = kmem_alloc_pageable(ipc_kernel_map, ! 1353: &names_addr, names_size); ! 1354: if (kr != KERN_SUCCESS) ! 1355: return kr; ! 1356: ! 1357: names = (zone_name_t *) names_addr; ! 1358: } ! 1359: ! 1360: if (max_zones <= *infoCntp) { ! 1361: /* use in-line memory */ ! 1362: ! 1363: info = *infop; ! 1364: } else { ! 1365: info_size = round_page(max_zones * sizeof *info); ! 1366: kr = kmem_alloc_pageable(ipc_kernel_map, ! 1367: &info_addr, info_size); ! 1368: if (kr != KERN_SUCCESS) { ! 1369: if (names != *namesp) ! 1370: kmem_free(ipc_kernel_map, ! 1371: names_addr, names_size); ! 1372: return kr; ! 1373: } ! 1374: ! 1375: info = (zone_info_t *) info_addr; ! 1376: } ! 1377: ! 1378: for (i = 0; i < max_zones; i++) { ! 1379: zone_name_t *zn = &names[i]; ! 1380: zone_info_t *zi = &info[i]; ! 1381: struct zone zcopy; ! 1382: ! 1383: assert(z != ZONE_NULL); ! 1384: ! 1385: lock_zone(z); ! 1386: zcopy = *z; ! 1387: unlock_zone(z); ! 1388: ! 1389: simple_lock(&all_zones_lock); ! 1390: z = z->next_zone; ! 1391: simple_unlock(&all_zones_lock); ! 1392: ! 1393: /* assuming here the name data is static */ ! 1394: (void) strncpy(zn->zn_name, zcopy.zone_name, ! 1395: sizeof zn->zn_name); ! 1396: ! 1397: zi->zi_count = zcopy.count; ! 1398: zi->zi_cur_size = zcopy.cur_size; ! 1399: zi->zi_max_size = zcopy.max_size; ! 1400: zi->zi_elem_size = zcopy.elem_size; ! 1401: zi->zi_alloc_size = zcopy.alloc_size; ! 1402: zi->zi_exhaustible = zcopy.exhaustible; ! 1403: zi->zi_collectable = zcopy.collectable; ! 1404: } ! 1405: ! 1406: if (names != *namesp) { ! 1407: vm_size_t used; ! 1408: vm_map_copy_t copy; ! 1409: ! 1410: used = max_zones * sizeof *names; ! 1411: ! 1412: if (used != names_size) ! 1413: bzero((char *) (names_addr + used), names_size - used); ! 1414: ! 1415: kr = vm_map_copyin(ipc_kernel_map, names_addr, names_size, ! 1416: TRUE, ©); ! 1417: assert(kr == KERN_SUCCESS); ! 1418: ! 1419: *namesp = (zone_name_t *) copy; ! 1420: } ! 1421: *namesCntp = max_zones; ! 1422: ! 1423: if (info != *infop) { ! 1424: vm_size_t used; ! 1425: vm_map_copy_t copy; ! 1426: ! 1427: used = max_zones * sizeof *info; ! 1428: ! 1429: if (used != info_size) ! 1430: bzero((char *) (info_addr + used), info_size - used); ! 1431: ! 1432: kr = vm_map_copyin(ipc_kernel_map, info_addr, info_size, ! 1433: TRUE, ©); ! 1434: assert(kr == KERN_SUCCESS); ! 1435: ! 1436: *infop = (zone_info_t *) copy; ! 1437: } ! 1438: *infoCntp = max_zones; ! 1439: ! 1440: return KERN_SUCCESS; ! 1441: } ! 1442: ! 1443: #if MACH_KDB ! 1444: #include <ddb/db_command.h> ! 1445: #include <ddb/db_output.h> ! 1446: #include <kern/kern_print.h> ! 1447: ! 1448: const char *zone_labels = ! 1449: "ENTRY COUNT TOT_SZ MAX_SZ ELT_SZ ALLOC_SZ NAME"; ! 1450: ! 1451: /* Forwards */ ! 1452: void db_print_zone( ! 1453: zone_t addr); ! 1454: ! 1455: #if ZONE_DEBUG ! 1456: void db_zone_check_active( ! 1457: zone_t zone); ! 1458: void db_zone_print_active( ! 1459: zone_t zone); ! 1460: #endif /* ZONE_DEBUG */ ! 1461: void db_zone_print_free( ! 1462: zone_t zone); ! 1463: void ! 1464: db_print_zone( ! 1465: zone_t addr) ! 1466: { ! 1467: struct zone zcopy; ! 1468: ! 1469: zcopy = *addr; ! 1470: ! 1471: db_printf("%8x %8x %8x %8x %6x %8x %s ", ! 1472: addr, zcopy.count, zcopy.cur_size, ! 1473: zcopy.max_size, zcopy.elem_size, ! 1474: zcopy.alloc_size, zcopy.zone_name); ! 1475: if (zcopy.exhaustible) ! 1476: db_printf("H"); ! 1477: if (zcopy.collectable) ! 1478: db_printf("C"); ! 1479: if (zcopy.expandable) ! 1480: db_printf("X"); ! 1481: db_printf("\n"); ! 1482: } ! 1483: ! 1484: /*ARGSUSED*/ ! 1485: void ! 1486: db_show_one_zone( ! 1487: db_expr_t addr, ! 1488: int have_addr, ! 1489: db_expr_t count, ! 1490: char * modif) ! 1491: { ! 1492: struct zone *z = (zone_t)addr; ! 1493: ! 1494: if (z == ZONE_NULL || !have_addr){ ! 1495: db_error("No Zone\n"); ! 1496: /*NOTREACHED*/ ! 1497: } ! 1498: ! 1499: db_printf("%s\n", zone_labels); ! 1500: db_print_zone(z); ! 1501: } ! 1502: ! 1503: /*ARGSUSED*/ ! 1504: void ! 1505: db_show_all_zones( ! 1506: db_expr_t addr, ! 1507: int have_addr, ! 1508: db_expr_t count, ! 1509: char * modif) ! 1510: { ! 1511: zone_t z; ! 1512: unsigned total = 0; ! 1513: ! 1514: /* ! 1515: * Don't risk hanging by unconditionally locking, ! 1516: * risk of incoherent data is small (zones aren't freed). ! 1517: */ ! 1518: have_addr = simple_lock_try(&all_zones_lock); ! 1519: count = num_zones; ! 1520: z = first_zone; ! 1521: if (have_addr) { ! 1522: simple_unlock(&all_zones_lock); ! 1523: } ! 1524: ! 1525: db_printf("%s\n", zone_labels); ! 1526: for ( ; count > 0; count--) { ! 1527: if (!z) { ! 1528: db_error("Mangled Zone List\n"); ! 1529: /*NOTREACHED*/ ! 1530: } ! 1531: db_print_zone(z); ! 1532: total += z->cur_size, ! 1533: ! 1534: have_addr = simple_lock_try(&all_zones_lock); ! 1535: z = z->next_zone; ! 1536: if (have_addr) { ! 1537: simple_unlock(&all_zones_lock); ! 1538: } ! 1539: } ! 1540: db_printf("\nTotal %8x", total); ! 1541: db_printf("\n\nzone_gc() has reclaimed %d pages\n", ! 1542: reclaim_page_count); ! 1543: } ! 1544: ! 1545: #if ZONE_DEBUG ! 1546: void ! 1547: db_zone_check_active( ! 1548: zone_t zone) ! 1549: { ! 1550: int count = 0; ! 1551: queue_t tmp_elem; ! 1552: ! 1553: if (!zone_debug_enabled(zone) || !zone_check) ! 1554: return; ! 1555: tmp_elem = queue_first(&zone->active_zones); ! 1556: while (count < zone->count) { ! 1557: count++; ! 1558: if (tmp_elem == 0) { ! 1559: printf("unexpected zero element, zone=0x%x, count=%d\n", ! 1560: zone, count); ! 1561: assert(FALSE); ! 1562: break; ! 1563: } ! 1564: if (queue_end(tmp_elem, &zone->active_zones)) { ! 1565: printf("unexpected queue_end, zone=0x%x, count=%d\n", ! 1566: zone, count); ! 1567: assert(FALSE); ! 1568: break; ! 1569: } ! 1570: tmp_elem = queue_next(tmp_elem); ! 1571: } ! 1572: if (!queue_end(tmp_elem, &zone->active_zones)) { ! 1573: printf("not at queue_end, zone=0x%x, tmp_elem=0x%x\n", ! 1574: zone, tmp_elem); ! 1575: assert(FALSE); ! 1576: } ! 1577: } ! 1578: ! 1579: void ! 1580: db_zone_print_active( ! 1581: zone_t zone) ! 1582: { ! 1583: int count = 0; ! 1584: queue_t tmp_elem; ! 1585: ! 1586: if (!zone_debug_enabled(zone)) { ! 1587: printf("zone 0x%x debug not enabled\n", zone); ! 1588: return; ! 1589: } ! 1590: if (!zone_check) { ! 1591: printf("zone_check FALSE\n"); ! 1592: return; ! 1593: } ! 1594: ! 1595: printf("zone 0x%x, active elements %d\n", zone, zone->count); ! 1596: printf("active list:\n"); ! 1597: tmp_elem = queue_first(&zone->active_zones); ! 1598: while (count < zone->count) { ! 1599: printf(" 0x%x", tmp_elem); ! 1600: count++; ! 1601: if ((count % 6) == 0) ! 1602: printf("\n"); ! 1603: if (tmp_elem == 0) { ! 1604: printf("\nunexpected zero element, count=%d\n", count); ! 1605: break; ! 1606: } ! 1607: if (queue_end(tmp_elem, &zone->active_zones)) { ! 1608: printf("\nunexpected queue_end, count=%d\n", count); ! 1609: break; ! 1610: } ! 1611: tmp_elem = queue_next(tmp_elem); ! 1612: } ! 1613: if (!queue_end(tmp_elem, &zone->active_zones)) ! 1614: printf("\nnot at queue_end, tmp_elem=0x%x\n", tmp_elem); ! 1615: else ! 1616: printf("\n"); ! 1617: } ! 1618: #endif /* ZONE_DEBUG */ ! 1619: ! 1620: void ! 1621: db_zone_print_free( ! 1622: zone_t zone) ! 1623: { ! 1624: int count = 0; ! 1625: int freecount; ! 1626: vm_offset_t elem; ! 1627: ! 1628: freecount = zone_free_count(zone); ! 1629: printf("zone 0x%x, free elements %d\n", zone, freecount); ! 1630: printf("free list:\n"); ! 1631: elem = zone->free_elements; ! 1632: while (count < freecount) { ! 1633: printf(" 0x%x", elem); ! 1634: count++; ! 1635: if ((count % 6) == 0) ! 1636: printf("\n"); ! 1637: if (elem == 0) { ! 1638: printf("\nunexpected zero element, count=%d\n", count); ! 1639: break; ! 1640: } ! 1641: elem = *((vm_offset_t *)elem); ! 1642: } ! 1643: if (elem != 0) ! 1644: printf("\nnot at end of free list, elem=0x%x\n", elem); ! 1645: else ! 1646: printf("\n"); ! 1647: } ! 1648: ! 1649: #endif /* MACH_KDB */ ! 1650: ! 1651: ! 1652: #if ZONE_DEBUG ! 1653: ! 1654: /* should we care about locks here ? */ ! 1655: ! 1656: #if MACH_KDB ! 1657: vm_offset_t ! 1658: next_element( ! 1659: zone_t z, ! 1660: vm_offset_t elt) ! 1661: { ! 1662: if (!zone_debug_enabled(z)) ! 1663: return(0); ! 1664: elt -= sizeof(queue_chain_t); ! 1665: elt = (vm_offset_t) queue_next((queue_t) elt); ! 1666: if ((queue_t) elt == &z->active_zones) ! 1667: return(0); ! 1668: elt += sizeof(queue_chain_t); ! 1669: return(elt); ! 1670: } ! 1671: ! 1672: vm_offset_t ! 1673: first_element( ! 1674: zone_t z) ! 1675: { ! 1676: vm_offset_t elt; ! 1677: ! 1678: if (!zone_debug_enabled(z)) ! 1679: return(0); ! 1680: if (queue_empty(&z->active_zones)) ! 1681: return(0); ! 1682: elt = (vm_offset_t) queue_first(&z->active_zones); ! 1683: elt += sizeof(queue_chain_t); ! 1684: return(elt); ! 1685: } ! 1686: ! 1687: /* ! 1688: * Second arg controls how many zone elements are printed: ! 1689: * 0 => none ! 1690: * n, n < 0 => all ! 1691: * n, n > 0 => last n on active list ! 1692: */ ! 1693: int ! 1694: zone_count( ! 1695: zone_t z, ! 1696: int tail) ! 1697: { ! 1698: vm_offset_t elt; ! 1699: int count = 0; ! 1700: boolean_t print = (tail != 0); ! 1701: ! 1702: if (tail < 0) ! 1703: tail = z->count; ! 1704: if (z->count < tail) ! 1705: tail = 0; ! 1706: tail = z->count - tail; ! 1707: for (elt = first_element(z); elt; elt = next_element(z, elt)) { ! 1708: if (print && tail <= count) ! 1709: db_printf("%8x\n", elt); ! 1710: count++; ! 1711: } ! 1712: assert(count == z->count); ! 1713: return(count); ! 1714: } ! 1715: #endif /* MACH_KDB */ ! 1716: ! 1717: #define zone_in_use(z) ( z->count || z->free_elements ) ! 1718: ! 1719: void ! 1720: zone_debug_enable( ! 1721: zone_t z) ! 1722: { ! 1723: if (zone_debug_enabled(z) || zone_in_use(z) || ! 1724: z->alloc_size < (z->elem_size + sizeof(queue_chain_t))) ! 1725: return; ! 1726: queue_init(&z->active_zones); ! 1727: z->elem_size += sizeof(queue_chain_t); ! 1728: } ! 1729: ! 1730: void ! 1731: zone_debug_disable( ! 1732: zone_t z) ! 1733: { ! 1734: if (!zone_debug_enabled(z) || zone_in_use(z)) ! 1735: return; ! 1736: z->elem_size -= sizeof(queue_chain_t); ! 1737: z->active_zones.next = z->active_zones.prev = 0; ! 1738: } ! 1739: #endif /* ZONE_DEBUG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.