Annotation of XNU/osfmk/kern/zalloc.c, revision 1.1.1.1

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, &copy);
                   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, &copy);
                   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 */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.