Annotation of OSKit-Mach/kern/zalloc.c, revision 1.1

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

unix.superglobalmegacorp.com

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