Annotation of OSKit-Mach/vm/vm_pageout.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Mach Operating System
        !             3:  * Copyright (c) 1991,1990,1989,1988,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:   vm/vm_pageout.c
        !            31:  *     Author: Avadis Tevanian, Jr., Michael Wayne Young
        !            32:  *     Date:   1985
        !            33:  *
        !            34:  *     The proverbial page-out daemon.
        !            35:  */
        !            36: 
        !            37: #include <mach_pagemap.h>
        !            38: #include <norma_vm.h>
        !            39: 
        !            40: #include <mach/mach_types.h>
        !            41: #include <mach/memory_object.h>
        !            42: #include "memory_object_default.h"
        !            43: #include "memory_object_user.h"
        !            44: #include <mach/vm_param.h>
        !            45: #include <mach/vm_statistics.h>
        !            46: #include <kern/counters.h>
        !            47: #include <kern/thread.h>
        !            48: #include <vm/pmap.h>
        !            49: #include <vm/vm_map.h>
        !            50: #include <vm/vm_object.h>
        !            51: #include <vm/vm_page.h>
        !            52: #include <vm/vm_pageout.h>
        !            53: #include <machine/vm_tuning.h>
        !            54: 
        !            55: 
        !            56: 
        !            57: #ifndef        VM_PAGEOUT_BURST_MAX
        !            58: #define        VM_PAGEOUT_BURST_MAX    10              /* number of pages */
        !            59: #endif /* VM_PAGEOUT_BURST_MAX */
        !            60: 
        !            61: #ifndef        VM_PAGEOUT_BURST_MIN
        !            62: #define        VM_PAGEOUT_BURST_MIN    5               /* number of pages */
        !            63: #endif /* VM_PAGEOUT_BURST_MIN */
        !            64: 
        !            65: #ifndef        VM_PAGEOUT_BURST_WAIT
        !            66: #define        VM_PAGEOUT_BURST_WAIT   10              /* milliseconds per page */
        !            67: #endif /* VM_PAGEOUT_BURST_WAIT */
        !            68: 
        !            69: #ifndef        VM_PAGEOUT_EMPTY_WAIT
        !            70: #define VM_PAGEOUT_EMPTY_WAIT  75              /* milliseconds */
        !            71: #endif /* VM_PAGEOUT_EMPTY_WAIT */
        !            72: 
        !            73: #ifndef        VM_PAGEOUT_PAUSE_MAX
        !            74: #define        VM_PAGEOUT_PAUSE_MAX    10              /* number of pauses */
        !            75: #endif /* VM_PAGEOUT_PAUSE_MAX */
        !            76: 
        !            77: /*
        !            78:  *     To obtain a reasonable LRU approximation, the inactive queue
        !            79:  *     needs to be large enough to give pages on it a chance to be
        !            80:  *     referenced a second time.  This macro defines the fraction
        !            81:  *     of active+inactive pages that should be inactive.
        !            82:  *     The pageout daemon uses it to update vm_page_inactive_target.
        !            83:  *
        !            84:  *     If vm_page_free_count falls below vm_page_free_target and
        !            85:  *     vm_page_inactive_count is below vm_page_inactive_target,
        !            86:  *     then the pageout daemon starts running.
        !            87:  */
        !            88: 
        !            89: #ifndef        VM_PAGE_INACTIVE_TARGET
        !            90: #define        VM_PAGE_INACTIVE_TARGET(avail)  ((avail) * 2 / 3)
        !            91: #endif /* VM_PAGE_INACTIVE_TARGET */
        !            92: 
        !            93: /*
        !            94:  *     Once the pageout daemon starts running, it keeps going
        !            95:  *     until vm_page_free_count meets or exceeds vm_page_free_target.
        !            96:  */
        !            97: 
        !            98: #ifndef        VM_PAGE_FREE_TARGET
        !            99: #define        VM_PAGE_FREE_TARGET(free)       (15 + (free) / 80)
        !           100: #endif /* VM_PAGE_FREE_TARGET */
        !           101: 
        !           102: /*
        !           103:  *     The pageout daemon always starts running once vm_page_free_count
        !           104:  *     falls below vm_page_free_min.
        !           105:  */
        !           106: 
        !           107: #ifndef        VM_PAGE_FREE_MIN
        !           108: #define        VM_PAGE_FREE_MIN(free)  (10 + (free) / 100)
        !           109: #endif /* VM_PAGE_FREE_MIN */
        !           110: 
        !           111: /*      When vm_page_external_count exceeds vm_page_external_limit,
        !           112:  *     allocations of externally paged pages stops.
        !           113:  */
        !           114: 
        !           115: #ifndef VM_PAGE_EXTERNAL_LIMIT
        !           116: #define VM_PAGE_EXTERNAL_LIMIT(free)           ((free) / 2)
        !           117: #endif  /* VM_PAGE_EXTERNAL_LIMIT */
        !           118: 
        !           119: /*     Attempt to keep the number of externally paged pages less
        !           120:  *     than vm_pages_external_target.
        !           121:  */
        !           122: #ifndef VM_PAGE_EXTERNAL_TARGET
        !           123: #define VM_PAGE_EXTERNAL_TARGET(free)          ((free) / 4)
        !           124: #endif  /* VM_PAGE_EXTERNAL_TARGET */
        !           125: 
        !           126: /*
        !           127:  *     When vm_page_free_count falls below vm_page_free_reserved,
        !           128:  *     only vm-privileged threads can allocate pages.  vm-privilege
        !           129:  *     allows the pageout daemon and default pager (and any other
        !           130:  *     associated threads needed for default pageout) to continue
        !           131:  *     operation by dipping into the reserved pool of pages.  */
        !           132: 
        !           133: #ifndef        VM_PAGE_FREE_RESERVED
        !           134: #define        VM_PAGE_FREE_RESERVED                   50
        !           135: #endif /* VM_PAGE_FREE_RESERVED */
        !           136: 
        !           137: /*
        !           138:  *     When vm_page_free_count falls below vm_pageout_reserved_internal,
        !           139:  *     the pageout daemon no longer trusts external pagers to clean pages.
        !           140:  *     External pagers are probably all wedged waiting for a free page.
        !           141:  *     It forcibly double-pages dirty pages belonging to external objects,
        !           142:  *     getting the pages to the default pager to clean.
        !           143:  */
        !           144: 
        !           145: #ifndef        VM_PAGEOUT_RESERVED_INTERNAL
        !           146: #define        VM_PAGEOUT_RESERVED_INTERNAL(reserve)   ((reserve) - 25)
        !           147: #endif /* VM_PAGEOUT_RESERVED_INTERNAL */
        !           148: 
        !           149: /*
        !           150:  *     When vm_page_free_count falls below vm_pageout_reserved_really,
        !           151:  *     the pageout daemon stops work entirely to let the default pager
        !           152:  *     catch up (assuming the default pager has pages to clean).
        !           153:  *     Beyond this point, it is too dangerous to consume memory
        !           154:  *     even for memory_object_data_write messages to the default pager.
        !           155:  */
        !           156: 
        !           157: #ifndef        VM_PAGEOUT_RESERVED_REALLY
        !           158: #define        VM_PAGEOUT_RESERVED_REALLY(reserve)     ((reserve) - 40)
        !           159: #endif /* VM_PAGEOUT_RESERVED_REALLY */
        !           160: 
        !           161: extern void vm_pageout_continue();
        !           162: extern void vm_pageout_scan_continue();
        !           163: 
        !           164: unsigned int vm_pageout_reserved_internal = 0;
        !           165: unsigned int vm_pageout_reserved_really = 0;
        !           166: 
        !           167: unsigned int vm_page_external_target = 0;
        !           168: 
        !           169: unsigned int vm_pageout_burst_max = 0;
        !           170: unsigned int vm_pageout_burst_min = 0;
        !           171: unsigned int vm_pageout_burst_wait = 0;                /* milliseconds per page */
        !           172: unsigned int vm_pageout_empty_wait = 0;                /* milliseconds */
        !           173: unsigned int vm_pageout_pause_count = 0;
        !           174: unsigned int vm_pageout_pause_max = 0;
        !           175: 
        !           176: /*
        !           177:  *     These variables record the pageout daemon's actions:
        !           178:  *     how many pages it looks at and what happens to those pages.
        !           179:  *     No locking needed because only one thread modifies the variables.
        !           180:  */
        !           181: 
        !           182: unsigned int vm_pageout_active = 0;            /* debugging */
        !           183: unsigned int vm_pageout_inactive = 0;          /* debugging */
        !           184: unsigned int vm_pageout_inactive_nolock = 0;   /* debugging */
        !           185: unsigned int vm_pageout_inactive_busy = 0;     /* debugging */
        !           186: unsigned int vm_pageout_inactive_absent = 0;   /* debugging */
        !           187: unsigned int vm_pageout_inactive_used = 0;     /* debugging */
        !           188: unsigned int vm_pageout_inactive_clean = 0;    /* debugging */
        !           189: unsigned int vm_pageout_inactive_dirty = 0;    /* debugging */
        !           190: unsigned int vm_pageout_inactive_double = 0;   /* debugging */
        !           191: unsigned int vm_pageout_inactive_cleaned_external = 0;
        !           192: 
        !           193: #if    NORMA_VM
        !           194: /*
        !           195:  * Define them here, since they won't be defined by memory_object_user.h.
        !           196:  */
        !           197: extern kern_return_t memory_object_data_initialize();
        !           198: extern kern_return_t memory_object_data_write();
        !           199: #endif /* NORMA_VM */
        !           200: 
        !           201: /*
        !           202:  *     Routine:        vm_pageout_setup
        !           203:  *     Purpose:
        !           204:  *             Set up a page for pageout.
        !           205:  *
        !           206:  *             Move or copy the page to a new object, as part
        !           207:  *             of which it will be sent to its memory manager
        !           208:  *             in a memory_object_data_write or memory_object_initialize
        !           209:  *             message.
        !           210:  *
        !           211:  *             The "paging_offset" argument specifies the offset
        !           212:  *             of the page within its external memory object.
        !           213:  *
        !           214:  *             The "new_object" and "new_offset" arguments
        !           215:  *             indicate where the page should be moved.
        !           216:  *
        !           217:  *             The "flush" argument specifies whether the page
        !           218:  *             should be flushed from its object.  If not, a
        !           219:  *             copy of the page is moved to the new object.
        !           220:  *
        !           221:  *     In/Out conditions:
        !           222:  *             The page in question must not be on any pageout queues,
        !           223:  *             and must be busy.  The object to which it belongs
        !           224:  *             must be unlocked, and the caller must hold a paging
        !           225:  *             reference to it.  The new_object must not be locked.
        !           226:  *
        !           227:  *             If the page is flushed from its original object,
        !           228:  *             this routine returns a pointer to a place-holder page,
        !           229:  *             inserted at the same offset, to block out-of-order
        !           230:  *             requests for the page.  The place-holder page must
        !           231:  *             be freed after the data_write or initialize message
        !           232:  *             has been sent.  If the page is copied,
        !           233:  *             the holding page is VM_PAGE_NULL.
        !           234:  *
        !           235:  *             The original page is put on a paging queue and marked
        !           236:  *             not busy on exit.
        !           237:  */
        !           238: vm_page_t
        !           239: vm_pageout_setup(m, paging_offset, new_object, new_offset, flush)
        !           240:        register vm_page_t      m;
        !           241:        vm_offset_t             paging_offset;
        !           242:        register vm_object_t    new_object;
        !           243:        vm_offset_t             new_offset;
        !           244:        boolean_t               flush;
        !           245: {
        !           246:        register vm_object_t    old_object = m->object;
        !           247:        register vm_page_t      holding_page = 0; /*'=0'to quiet gcc warnings*/
        !           248:        register vm_page_t      new_m;
        !           249: 
        !           250:        assert(m->busy && !m->absent && !m->fictitious);
        !           251: 
        !           252:        /*
        !           253:         *      If we are not flushing the page, allocate a
        !           254:         *      page in the object.  If we cannot get the
        !           255:         *      page, flush instead.
        !           256:         */
        !           257:        if (!flush) {
        !           258:                vm_object_lock(new_object);
        !           259:                new_m = vm_page_alloc(new_object, new_offset);
        !           260:                if (new_m == VM_PAGE_NULL)
        !           261:                        flush = TRUE;
        !           262:                vm_object_unlock(new_object);
        !           263:        }
        !           264: 
        !           265:        if (flush) {
        !           266:                /*
        !           267:                 *      Create a place-holder page where the old one was,
        !           268:                 *      to prevent anyone from attempting to page in this
        !           269:                 *      page while we`re unlocked.
        !           270:                 */
        !           271:                while ((holding_page = vm_page_grab_fictitious())
        !           272:                                                        == VM_PAGE_NULL)
        !           273:                        vm_page_more_fictitious();
        !           274: 
        !           275:                vm_object_lock(old_object);
        !           276:                vm_page_lock_queues();
        !           277:                vm_page_remove(m);
        !           278:                vm_page_unlock_queues();
        !           279:                PAGE_WAKEUP_DONE(m);
        !           280: 
        !           281:                vm_page_lock_queues();
        !           282:                vm_page_insert(holding_page, old_object, m->offset);
        !           283:                vm_page_unlock_queues();
        !           284: 
        !           285:                /*
        !           286:                 *      Record that this page has been written out
        !           287:                 */
        !           288: #if    MACH_PAGEMAP
        !           289:                vm_external_state_set(old_object->existence_info,
        !           290:                                        paging_offset,
        !           291:                                        VM_EXTERNAL_STATE_EXISTS);
        !           292: #endif /* MACH_PAGEMAP */
        !           293: 
        !           294:                vm_object_unlock(old_object);
        !           295: 
        !           296:                vm_object_lock(new_object);
        !           297: 
        !           298:                /*
        !           299:                 *      Move this page into the new object
        !           300:                 */
        !           301: 
        !           302:                vm_page_lock_queues();
        !           303:                vm_page_insert(m, new_object, new_offset);
        !           304:                vm_page_unlock_queues();
        !           305: 
        !           306:                m->dirty = TRUE;
        !           307:                m->precious = FALSE;
        !           308:                m->page_lock = VM_PROT_NONE;
        !           309:                m->unlock_request = VM_PROT_NONE;
        !           310:        }
        !           311:        else {
        !           312:                /*
        !           313:                 *      Copy the data into the new page,
        !           314:                 *      and mark the new page as clean.
        !           315:                 */
        !           316:                vm_page_copy(m, new_m);
        !           317: 
        !           318:                vm_object_lock(old_object);
        !           319:                m->dirty = FALSE;
        !           320:                pmap_clear_modify(m->phys_addr);
        !           321: 
        !           322:                /*
        !           323:                 *      Deactivate old page.
        !           324:                 */
        !           325:                vm_page_lock_queues();
        !           326:                vm_page_deactivate(m);
        !           327:                vm_page_unlock_queues();
        !           328: 
        !           329:                PAGE_WAKEUP_DONE(m);
        !           330: 
        !           331:                /*
        !           332:                 *      Record that this page has been written out
        !           333:                 */
        !           334: 
        !           335: #if    MACH_PAGEMAP
        !           336:                vm_external_state_set(old_object->existence_info,
        !           337:                                        paging_offset,
        !           338:                                        VM_EXTERNAL_STATE_EXISTS);
        !           339: #endif /* MACH_PAGEMAP */
        !           340: 
        !           341:                vm_object_unlock(old_object);
        !           342: 
        !           343:                vm_object_lock(new_object);
        !           344: 
        !           345:                /*
        !           346:                 *      Use the new page below.
        !           347:                 */
        !           348:                m = new_m;
        !           349:                m->dirty = TRUE;
        !           350:                assert(!m->precious);
        !           351:                PAGE_WAKEUP_DONE(m);
        !           352:        }
        !           353: 
        !           354:        /*
        !           355:         *      Make the old page eligible for replacement again; if a
        !           356:         *      user-supplied memory manager fails to release the page,
        !           357:         *      it will be paged out again to the default memory manager.
        !           358:         *
        !           359:         *      Note that pages written to the default memory manager
        !           360:         *      must be wired down -- in return, it guarantees to free
        !           361:         *      this page, rather than reusing it.
        !           362:         */
        !           363: 
        !           364:        vm_page_lock_queues();
        !           365:        vm_stat.pageouts++;
        !           366:        if (m->laundry) {
        !           367:                /*
        !           368:                 *      vm_pageout_scan is telling us to put this page
        !           369:                 *      at the front of the inactive queue, so it will
        !           370:                 *      be immediately paged out to the default pager.
        !           371:                 */
        !           372: 
        !           373:                assert(!old_object->internal);
        !           374:                m->laundry = FALSE;
        !           375: 
        !           376:                queue_enter_first(&vm_page_queue_inactive, m,
        !           377:                                  vm_page_t, pageq);
        !           378:                m->inactive = TRUE;
        !           379:                vm_page_inactive_count++;
        !           380:        } else if (old_object->internal) {
        !           381:                m->laundry = TRUE;
        !           382:                vm_page_laundry_count++;
        !           383: 
        !           384:                vm_page_wire(m);
        !           385:        } else
        !           386:                vm_page_activate(m);
        !           387:        vm_page_unlock_queues();
        !           388: 
        !           389:        /*
        !           390:         *      Since IPC operations may block, we drop locks now.
        !           391:         *      [The placeholder page is busy, and we still have
        !           392:         *      paging_in_progress incremented.]
        !           393:         */
        !           394: 
        !           395:        vm_object_unlock(new_object);
        !           396: 
        !           397:        /*
        !           398:         *      Return the placeholder page to simplify cleanup.
        !           399:         */
        !           400:        return (flush ? holding_page : VM_PAGE_NULL);
        !           401: }
        !           402: 
        !           403: /*
        !           404:  *     Routine:        vm_pageout_page
        !           405:  *     Purpose:
        !           406:  *             Causes the specified page to be written back to
        !           407:  *             the appropriate memory object.
        !           408:  *
        !           409:  *             The "initial" argument specifies whether this
        !           410:  *             data is an initialization only, and should use
        !           411:  *             memory_object_data_initialize instead of
        !           412:  *             memory_object_data_write.
        !           413:  *
        !           414:  *             The "flush" argument specifies whether the page
        !           415:  *             should be flushed from the object.  If not, a
        !           416:  *             copy of the data is sent to the memory object.
        !           417:  *
        !           418:  *     In/out conditions:
        !           419:  *             The page in question must not be on any pageout queues.
        !           420:  *             The object to which it belongs must be locked.
        !           421:  *     Implementation:
        !           422:  *             Move this page to a completely new object, if flushing;
        !           423:  *             copy to a new page in a new object, if not.
        !           424:  */
        !           425: void
        !           426: vm_pageout_page(m, initial, flush)
        !           427:        register vm_page_t      m;
        !           428:        boolean_t               initial;
        !           429:        boolean_t               flush;
        !           430: {
        !           431:        vm_map_copy_t           copy;
        !           432:        register vm_object_t    old_object;
        !           433:        register vm_object_t    new_object;
        !           434:        register vm_page_t      holding_page;
        !           435:        vm_offset_t             paging_offset;
        !           436:        kern_return_t           rc;
        !           437:        boolean_t               precious_clean;
        !           438: 
        !           439:        assert(m->busy);
        !           440: 
        !           441:        /*
        !           442:         *      Cleaning but not flushing a clean precious page is a
        !           443:         *      no-op.  Remember whether page is clean and precious now
        !           444:         *      because vm_pageout_setup will mark it dirty and not precious.
        !           445:         *
        !           446:         * XXX Check if precious_clean && !flush can really happen.
        !           447:         */
        !           448:        precious_clean = (!m->dirty) && m->precious;
        !           449:        if (precious_clean && !flush) {
        !           450:                PAGE_WAKEUP_DONE(m);
        !           451:                return;
        !           452:        }
        !           453: 
        !           454:        /*
        !           455:         *      Verify that we really want to clean this page.
        !           456:         */
        !           457:        if (m->absent || m->error || (!m->dirty && !m->precious)) {
        !           458:                VM_PAGE_FREE(m);
        !           459:                return;
        !           460:        }
        !           461: 
        !           462:        /*
        !           463:         *      Create a paging reference to let us play with the object.
        !           464:         */
        !           465:        old_object = m->object;
        !           466:        paging_offset = m->offset + old_object->paging_offset;
        !           467:        vm_object_paging_begin(old_object);
        !           468:        vm_object_unlock(old_object);
        !           469: 
        !           470:        /*
        !           471:         *      Allocate a new object into which we can put the page.
        !           472:         */
        !           473:        new_object = vm_object_allocate(PAGE_SIZE);
        !           474: 
        !           475:        /*
        !           476:         *      Move the page into the new object.
        !           477:         */
        !           478:        holding_page = vm_pageout_setup(m,
        !           479:                                paging_offset,
        !           480:                                new_object,
        !           481:                                0,              /* new offset */
        !           482:                                flush);         /* flush */
        !           483: 
        !           484:        rc = vm_map_copyin_object(new_object, 0, PAGE_SIZE, &copy);
        !           485:        assert(rc == KERN_SUCCESS);
        !           486: 
        !           487:        if (initial || old_object->use_old_pageout) {
        !           488:                rc = (*(initial ? memory_object_data_initialize
        !           489:                             : memory_object_data_write))
        !           490:                        (old_object->pager,
        !           491:                         old_object->pager_request,
        !           492:                         paging_offset, (pointer_t) copy, PAGE_SIZE);
        !           493:        }
        !           494:        else {
        !           495:                rc = memory_object_data_return(
        !           496:                         old_object->pager,
        !           497:                         old_object->pager_request,
        !           498:                         paging_offset, (pointer_t) copy, PAGE_SIZE,
        !           499:                         !precious_clean, !flush);
        !           500:        }
        !           501: 
        !           502:        if (rc != KERN_SUCCESS)
        !           503:                vm_map_copy_discard(copy);
        !           504: 
        !           505:        /*
        !           506:         *      Clean up.
        !           507:         */
        !           508:        vm_object_lock(old_object);
        !           509:        if (holding_page != VM_PAGE_NULL)
        !           510:            VM_PAGE_FREE(holding_page);
        !           511:        vm_object_paging_end(old_object);
        !           512: }
        !           513: 
        !           514: /*
        !           515:  *     vm_pageout_scan does the dirty work for the pageout daemon.
        !           516:  *     It returns with vm_page_queue_free_lock held and
        !           517:  *     vm_page_free_wanted == 0.
        !           518:  */
        !           519: 
        !           520: void vm_pageout_scan()
        !           521: {
        !           522:        unsigned int burst_count;
        !           523:        unsigned int want_pages;
        !           524: 
        !           525:        /*
        !           526:         *      We want to gradually dribble pages from the active queue
        !           527:         *      to the inactive queue.  If we let the inactive queue get
        !           528:         *      very small, and then suddenly dump many pages into it,
        !           529:         *      those pages won't get a sufficient chance to be referenced
        !           530:         *      before we start taking them from the inactive queue.
        !           531:         *
        !           532:         *      We must limit the rate at which we send pages to the pagers.
        !           533:         *      data_write messages consume memory, for message buffers and
        !           534:         *      for map-copy objects.  If we get too far ahead of the pagers,
        !           535:         *      we can potentially run out of memory.
        !           536:         *
        !           537:         *      We can use the laundry count to limit directly the number
        !           538:         *      of pages outstanding to the default pager.  A similar
        !           539:         *      strategy for external pagers doesn't work, because
        !           540:         *      external pagers don't have to deallocate the pages sent them,
        !           541:         *      and because we might have to send pages to external pagers
        !           542:         *      even if they aren't processing writes.  So we also
        !           543:         *      use a burst count to limit writes to external pagers.
        !           544:         *
        !           545:         *      When memory is very tight, we can't rely on external pagers to
        !           546:         *      clean pages.  They probably aren't running, because they
        !           547:         *      aren't vm-privileged.  If we kept sending dirty pages to them,
        !           548:         *      we could exhaust the free list.  However, we can't just ignore
        !           549:         *      pages belonging to external objects, because there might be no
        !           550:         *      pages belonging to internal objects.  Hence, we get the page
        !           551:         *      into an internal object and then immediately double-page it,
        !           552:         *      sending it to the default pager.
        !           553:         *
        !           554:         *      consider_zone_gc should be last, because the other operations
        !           555:         *      might return memory to zones.  When we pause we use
        !           556:         *      vm_pageout_scan_continue as our continuation, so we will
        !           557:         *      reenter vm_pageout_scan periodically and attempt to reclaim
        !           558:         *      internal memory even if we never reach vm_page_free_target.
        !           559:         */
        !           560: 
        !           561:     Restart:
        !           562:        stack_collect();
        !           563:        net_kmsg_collect();
        !           564:        consider_lmm_collect();
        !           565:        consider_task_collect();
        !           566:        consider_thread_collect();
        !           567:        consider_zone_gc();
        !           568: 
        !           569:        for (burst_count = 0;;) {
        !           570:                register vm_page_t m;
        !           571:                register vm_object_t object;
        !           572:                unsigned int free_count;
        !           573: 
        !           574:                /*
        !           575:                 *      Recalculate vm_page_inactivate_target.
        !           576:                 */
        !           577: 
        !           578:                vm_page_lock_queues();
        !           579:                vm_page_inactive_target =
        !           580:                        VM_PAGE_INACTIVE_TARGET(vm_page_active_count +
        !           581:                                                vm_page_inactive_count);
        !           582: 
        !           583:                /*
        !           584:                 *      Move pages from active to inactive.
        !           585:                 */
        !           586: 
        !           587:                while ((vm_page_inactive_count < vm_page_inactive_target) &&
        !           588:                       !queue_empty(&vm_page_queue_active)) {
        !           589:                        register vm_object_t obj;
        !           590: 
        !           591:                        vm_pageout_active++;
        !           592:                        m = (vm_page_t) queue_first(&vm_page_queue_active);
        !           593:                        assert(m->active && !m->inactive);
        !           594: 
        !           595:                        obj = m->object;
        !           596:                        if (!vm_object_lock_try(obj)) {
        !           597:                                /*
        !           598:                                 *      Move page to end and continue.
        !           599:                                 */
        !           600: 
        !           601:                                queue_remove(&vm_page_queue_active, m,
        !           602:                                             vm_page_t, pageq);
        !           603:                                queue_enter(&vm_page_queue_active, m,
        !           604:                                            vm_page_t, pageq);
        !           605:                                vm_page_unlock_queues();
        !           606:                                vm_page_lock_queues();
        !           607:                                continue;
        !           608:                        }
        !           609: 
        !           610:                        /*
        !           611:                         *      If the page is busy, then we pull it
        !           612:                         *      off the active queue and leave it alone.
        !           613:                         */
        !           614: 
        !           615:                        if (m->busy) {
        !           616:                                vm_object_unlock(obj);
        !           617:                                queue_remove(&vm_page_queue_active, m,
        !           618:                                             vm_page_t, pageq);
        !           619:                                m->active = FALSE;
        !           620:                                vm_page_active_count--;
        !           621:                                continue;
        !           622:                        }
        !           623: 
        !           624:                        /*
        !           625:                         *      Deactivate the page while holding the object
        !           626:                         *      locked, so we know the page is still not busy.
        !           627:                         *      This should prevent races between pmap_enter
        !           628:                         *      and pmap_clear_reference.  The page might be
        !           629:                         *      absent or fictitious, but vm_page_deactivate
        !           630:                         *      can handle that.
        !           631:                         */
        !           632: 
        !           633:                        vm_page_deactivate(m);
        !           634:                        vm_object_unlock(obj);
        !           635:                }
        !           636: 
        !           637:                /*
        !           638:                 *      We are done if we have met our targets *and*
        !           639:                 *      nobody is still waiting for a page.
        !           640:                 */
        !           641: 
        !           642:                simple_lock(&vm_page_queue_free_lock);
        !           643:                free_count = vm_page_free_count;
        !           644:                if ((free_count >= vm_page_free_target) &&
        !           645:                    (vm_page_external_count <= vm_page_external_target) &&
        !           646:                    (vm_page_free_wanted == 0)) {
        !           647:                        vm_page_unlock_queues();
        !           648:                        break;
        !           649:                }
        !           650:                want_pages = ((free_count < vm_page_free_target) ||
        !           651:                              vm_page_free_wanted);
        !           652:                simple_unlock(&vm_page_queue_free_lock);
        !           653: 
        !           654:                /*
        !           655:                 * Sometimes we have to pause:
        !           656:                 *      1) No inactive pages - nothing to do.
        !           657:                 *      2) Flow control - wait for pagers to catch up.
        !           658:                 *      3) Extremely low memory - sending out dirty pages
        !           659:                 *      consumes memory.  We don't take the risk of doing
        !           660:                 *      this if the default pager already has work to do.
        !           661:                 */
        !           662:        pause:
        !           663:                if (queue_empty(&vm_page_queue_inactive) ||
        !           664:                    (burst_count >= vm_pageout_burst_max) ||
        !           665:                    (vm_page_laundry_count >= vm_pageout_burst_max) ||
        !           666:                    ((free_count < vm_pageout_reserved_really) &&
        !           667:                     (vm_page_laundry_count > 0))) {
        !           668:                        unsigned int pages, msecs;
        !           669: 
        !           670:                        /*
        !           671:                         *      vm_pageout_burst_wait is msecs/page.
        !           672:                         *      If there is nothing for us to do, we wait
        !           673:                         *      at least vm_pageout_empty_wait msecs.
        !           674:                         */
        !           675: 
        !           676:                        if (vm_page_laundry_count > burst_count)
        !           677:                                pages = vm_page_laundry_count;
        !           678:                        else
        !           679:                                pages = burst_count;
        !           680:                        msecs = pages * vm_pageout_burst_wait;
        !           681: 
        !           682:                        if (queue_empty(&vm_page_queue_inactive) &&
        !           683:                            (msecs < vm_pageout_empty_wait))
        !           684:                                msecs = vm_pageout_empty_wait;
        !           685:                        vm_page_unlock_queues();
        !           686: 
        !           687:                        thread_will_wait_with_timeout(current_thread(), msecs);
        !           688:                        counter(c_vm_pageout_scan_block++);
        !           689:                        thread_block(vm_pageout_scan_continue);
        !           690: #ifndef CONTINUATIONS
        !           691:                        /*
        !           692:                         *      Unfortunately, we don't have call_continuation
        !           693:                         *      so we can't rely on tail-recursion.
        !           694:                         */
        !           695: 
        !           696:                        vm_pageout_scan_continue();
        !           697:                        goto Restart;
        !           698: #else  /* CONTINUATIONS */
        !           699:                        call_continuation(vm_pageout_scan_continue);
        !           700:                        /*NOTREACHED*/
        !           701: #endif /* CONTINUATIONS */
        !           702:                }
        !           703: 
        !           704:                vm_pageout_inactive++;
        !           705: 
        !           706:                /* Find a page we are interested in paging out.  If we
        !           707:                   need pages, then we'll page anything out; otherwise
        !           708:                   we only page out external pages. */
        !           709:                m = (vm_page_t) queue_first (&vm_page_queue_inactive);
        !           710:                while (1)
        !           711:                  {
        !           712:                    assert (!m->active && m->inactive);
        !           713:                    if (want_pages || m->external)
        !           714:                      break;
        !           715: 
        !           716:                    m = (vm_page_t) queue_next (m);
        !           717:                    if (!m)
        !           718:                      goto pause;
        !           719:                  }
        !           720: 
        !           721:                object = m->object;
        !           722: 
        !           723:                /*
        !           724:                 *      Try to lock object; since we've got the
        !           725:                 *      page queues lock, we can only try for this one.
        !           726:                 */
        !           727: 
        !           728:                if (!vm_object_lock_try(object)) {
        !           729:                        /*
        !           730:                         *      Move page to end and continue.
        !           731:                         */
        !           732: 
        !           733:                        queue_remove(&vm_page_queue_inactive, m,
        !           734:                                     vm_page_t, pageq);
        !           735:                        queue_enter(&vm_page_queue_inactive, m,
        !           736:                                    vm_page_t, pageq);
        !           737:                        vm_page_unlock_queues();
        !           738:                        vm_pageout_inactive_nolock++;
        !           739:                        continue;
        !           740:                }
        !           741: 
        !           742:                /*
        !           743:                 *      Remove the page from the inactive list.
        !           744:                 */
        !           745: 
        !           746:                queue_remove(&vm_page_queue_inactive, m, vm_page_t, pageq);
        !           747:                vm_page_inactive_count--;
        !           748:                m->inactive = FALSE;
        !           749: 
        !           750:                if (m->busy || !object->alive) {
        !           751:                        /*
        !           752:                         *      Somebody is already playing with this page.
        !           753:                         *      Leave it off the pageout queues.
        !           754:                         */
        !           755: 
        !           756:                        vm_page_unlock_queues();
        !           757:                        vm_object_unlock(object);
        !           758:                        vm_pageout_inactive_busy++;
        !           759:                        continue;
        !           760:                }
        !           761: 
        !           762:                /*
        !           763:                 *      If it's absent, we can reclaim the page.
        !           764:                 */
        !           765: 
        !           766:                if (want_pages && m->absent) {
        !           767:                        vm_pageout_inactive_absent++;
        !           768:                    reclaim_page:
        !           769:                        vm_page_free(m);
        !           770:                        vm_page_unlock_queues();
        !           771:                        vm_object_unlock(object);
        !           772:                        continue;
        !           773:                }
        !           774: 
        !           775:                /*
        !           776:                 *      If it's being used, reactivate.
        !           777:                 *      (Fictitious pages are either busy or absent.)
        !           778:                 */
        !           779: 
        !           780:                assert(!m->fictitious);
        !           781:                if (m->reference || pmap_is_referenced(m->phys_addr)) {
        !           782:                        vm_object_unlock(object);
        !           783:                        vm_page_activate(m);
        !           784:                        vm_stat.reactivations++;
        !           785:                        vm_page_unlock_queues();
        !           786:                        vm_pageout_inactive_used++;
        !           787:                        continue;
        !           788:                }
        !           789: 
        !           790:                /*
        !           791:                 *      Eliminate all mappings.
        !           792:                 */
        !           793: 
        !           794:                m->busy = TRUE;
        !           795:                pmap_page_protect(m->phys_addr, VM_PROT_NONE);
        !           796:                if (!m->dirty)
        !           797:                        m->dirty = pmap_is_modified(m->phys_addr);
        !           798: 
        !           799:                if (m->external) {
        !           800:                        /* Figure out if we still care about this
        !           801:                        page in the limit of externally managed pages.
        !           802:                        Clean pages don't actually cause system hosage,
        !           803:                        so it's ok to stop considering them as
        !           804:                        "consumers" of memory. */
        !           805:                        if (m->dirty && !m->extcounted) {
        !           806:                                m->extcounted = TRUE;
        !           807:                                vm_page_external_count++;
        !           808:                        } else if (!m->dirty && m->extcounted) {
        !           809:                                m->extcounted = FALSE;
        !           810:                                vm_page_external_count--;
        !           811:                        }
        !           812:                }
        !           813: 
        !           814:                /* If we don't actually need more memory, and the page
        !           815:                   is not dirty, put it on the tail of the inactive queue
        !           816:                   and move on to the next page. */
        !           817:                if (!want_pages && !m->dirty) {
        !           818:                        queue_remove (&vm_page_queue_inactive, m,
        !           819:                                      vm_page_t, pageq);
        !           820:                        queue_enter (&vm_page_queue_inactive, m,
        !           821:                                     vm_page_t, pageq);
        !           822:                        vm_page_unlock_queues();
        !           823:                        vm_pageout_inactive_cleaned_external++;
        !           824:                        continue;
        !           825:                }
        !           826: 
        !           827:                /*
        !           828:                 *      If it's clean and not precious, we can free the page.
        !           829:                 */
        !           830: 
        !           831:                if (!m->dirty && !m->precious) {
        !           832:                        vm_pageout_inactive_clean++;
        !           833:                        goto reclaim_page;
        !           834:                }
        !           835: 
        !           836:                /*
        !           837:                 *      If we are very low on memory, then we can't
        !           838:                 *      rely on an external pager to clean a dirty page,
        !           839:                 *      because external pagers are not vm-privileged.
        !           840:                 *
        !           841:                 *      The laundry bit tells vm_pageout_setup to
        !           842:                 *      put the page back at the front of the inactive
        !           843:                 *      queue instead of activating the page.  Hence,
        !           844:                 *      we will pick the page up again immediately and
        !           845:                 *      resend it to the default pager.
        !           846:                 */
        !           847: 
        !           848:                assert(!m->laundry);
        !           849:                if ((free_count < vm_pageout_reserved_internal) &&
        !           850:                    !object->internal) {
        !           851:                        m->laundry = TRUE;
        !           852:                        vm_pageout_inactive_double++;
        !           853:                }
        !           854:                vm_page_unlock_queues();
        !           855: 
        !           856:                /*
        !           857:                 *      If there is no memory object for the page, create
        !           858:                 *      one and hand it to the default pager.
        !           859:                 *      [First try to collapse, so we don't create
        !           860:                 *      one unnecessarily.]
        !           861:                 */
        !           862: 
        !           863:                if (!object->pager_initialized)
        !           864:                        vm_object_collapse(object);
        !           865:                if (!object->pager_initialized)
        !           866:                        vm_object_pager_create(object);
        !           867:                if (!object->pager_initialized)
        !           868:                        panic("vm_pageout_scan");
        !           869: 
        !           870:                vm_pageout_inactive_dirty++;
        !           871:                vm_pageout_page(m, FALSE, TRUE);        /* flush it */
        !           872:                vm_object_unlock(object);
        !           873:                burst_count++;
        !           874:        }
        !           875: }
        !           876: 
        !           877: void vm_pageout_scan_continue()
        !           878: {
        !           879:        /*
        !           880:         *      We just paused to let the pagers catch up.
        !           881:         *      If vm_page_laundry_count is still high,
        !           882:         *      then we aren't waiting long enough.
        !           883:         *      If we have paused some vm_pageout_pause_max times without
        !           884:         *      adjusting vm_pageout_burst_wait, it might be too big,
        !           885:         *      so we decrease it.
        !           886:         */
        !           887: 
        !           888:        vm_page_lock_queues();
        !           889:        if (vm_page_laundry_count > vm_pageout_burst_min) {
        !           890:                vm_pageout_burst_wait++;
        !           891:                vm_pageout_pause_count = 0;
        !           892:        } else if (++vm_pageout_pause_count > vm_pageout_pause_max) {
        !           893:                vm_pageout_burst_wait = (vm_pageout_burst_wait * 3) / 4;
        !           894:                if (vm_pageout_burst_wait < 1)
        !           895:                        vm_pageout_burst_wait = 1;
        !           896:                vm_pageout_pause_count = 0;
        !           897:        }
        !           898:        vm_page_unlock_queues();
        !           899: 
        !           900: #ifdef CONTINUATIONS
        !           901:        vm_pageout_continue();
        !           902:        /*NOTREACHED*/
        !           903: #endif /* CONTINUATIONS */
        !           904: }
        !           905: 
        !           906: /*
        !           907:  *     vm_pageout is the high level pageout daemon.
        !           908:  */
        !           909: 
        !           910: void vm_pageout_continue()
        !           911: {
        !           912:        /*
        !           913:         *      The pageout daemon is never done, so loop forever.
        !           914:         *      We should call vm_pageout_scan at least once each
        !           915:         *      time we are woken, even if vm_page_free_wanted is
        !           916:         *      zero, to check vm_page_free_target and
        !           917:         *      vm_page_inactive_target.
        !           918:         */
        !           919: 
        !           920:        for (;;) {
        !           921:                vm_pageout_scan();
        !           922:                /* we hold vm_page_queue_free_lock now */
        !           923:                assert(vm_page_free_wanted == 0);
        !           924: 
        !           925:                assert_wait(&vm_page_free_wanted, FALSE);
        !           926:                simple_unlock(&vm_page_queue_free_lock);
        !           927:                counter(c_vm_pageout_block++);
        !           928:                thread_block(vm_pageout_continue);
        !           929:        }
        !           930: }
        !           931: 
        !           932: void vm_pageout()
        !           933: {
        !           934:        int             free_after_reserve;
        !           935: 
        !           936:        current_thread()->vm_privilege = TRUE;
        !           937:        stack_privilege(current_thread());
        !           938: 
        !           939:        /*
        !           940:         *      Initialize some paging parameters.
        !           941:         */
        !           942: 
        !           943:        if (vm_pageout_burst_max == 0)
        !           944:                vm_pageout_burst_max = VM_PAGEOUT_BURST_MAX;
        !           945: 
        !           946:        if (vm_pageout_burst_min == 0)
        !           947:                vm_pageout_burst_min = VM_PAGEOUT_BURST_MIN;
        !           948: 
        !           949:        if (vm_pageout_burst_wait == 0)
        !           950:                vm_pageout_burst_wait = VM_PAGEOUT_BURST_WAIT;
        !           951: 
        !           952:        if (vm_pageout_empty_wait == 0)
        !           953:                vm_pageout_empty_wait = VM_PAGEOUT_EMPTY_WAIT;
        !           954: 
        !           955:        if (vm_page_free_reserved == 0)
        !           956:                vm_page_free_reserved = VM_PAGE_FREE_RESERVED;
        !           957: 
        !           958:        if (vm_pageout_pause_max == 0)
        !           959:                vm_pageout_pause_max = VM_PAGEOUT_PAUSE_MAX;
        !           960: 
        !           961:        if (vm_pageout_reserved_internal == 0)
        !           962:                vm_pageout_reserved_internal =
        !           963:                        VM_PAGEOUT_RESERVED_INTERNAL(vm_page_free_reserved);
        !           964: 
        !           965:        if (vm_pageout_reserved_really == 0)
        !           966:                vm_pageout_reserved_really =
        !           967:                        VM_PAGEOUT_RESERVED_REALLY(vm_page_free_reserved);
        !           968: 
        !           969:        free_after_reserve = vm_page_free_count - vm_page_free_reserved;
        !           970: 
        !           971:        if (vm_page_external_limit == 0)
        !           972:                vm_page_external_limit =
        !           973:                        VM_PAGE_EXTERNAL_LIMIT (free_after_reserve);
        !           974: 
        !           975:        if (vm_page_external_target == 0)
        !           976:                vm_page_external_target =
        !           977:                        VM_PAGE_EXTERNAL_TARGET (free_after_reserve);
        !           978: 
        !           979:        if (vm_page_free_min == 0)
        !           980:                vm_page_free_min = vm_page_free_reserved +
        !           981:                        VM_PAGE_FREE_MIN(free_after_reserve);
        !           982: 
        !           983:        if (vm_page_free_target == 0)
        !           984:                vm_page_free_target = vm_page_free_reserved +
        !           985:                        VM_PAGE_FREE_TARGET(free_after_reserve);
        !           986: 
        !           987:        if (vm_page_free_target < vm_page_free_min + 5)
        !           988:                vm_page_free_target = vm_page_free_min + 5;
        !           989: 
        !           990:        /*
        !           991:         *      vm_pageout_scan will set vm_page_inactive_target.
        !           992:         */
        !           993: 
        !           994:        vm_pageout_continue();
        !           995:        /*NOTREACHED*/
        !           996: }

unix.superglobalmegacorp.com

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