Annotation of Gnu-Mach/vm/vm_pageout.c, revision 1.1.1.7

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: 
1.1.1.3   root       37: #include <device/net_io.h>
1.1       root       38: #include <mach/mach_types.h>
                     39: #include <mach/memory_object.h>
1.1.1.3   root       40: #include <vm/memory_object_default.user.h>
                     41: #include <vm/memory_object_user.user.h>
1.1       root       42: #include <mach/vm_param.h>
                     43: #include <mach/vm_statistics.h>
                     44: #include <kern/counters.h>
1.1.1.3   root       45: #include <kern/debug.h>
                     46: #include <kern/slab.h>
                     47: #include <kern/task.h>
1.1       root       48: #include <kern/thread.h>
1.1.1.7 ! root       49: #include <kern/printf.h>
1.1       root       50: #include <vm/pmap.h>
                     51: #include <vm/vm_map.h>
                     52: #include <vm/vm_object.h>
                     53: #include <vm/vm_page.h>
                     54: #include <vm/vm_pageout.h>
1.1.1.3   root       55: #include <machine/locore.h>
1.1       root       56: 
1.1.1.7 ! root       57: #define DEBUG 0
1.1       root       58: 
                     59: /*
1.1.1.7 ! root       60:  * Maximum delay, in milliseconds, between two pageout scans.
1.1.1.2   root       61:  */
1.1.1.7 ! root       62: #define VM_PAGEOUT_TIMEOUT 50
1.1       root       63: 
                     64: /*
1.1.1.7 ! root       65:  * Event placeholder for pageout requests, synchronized with
        !            66:  * the free page queue lock.
1.1       root       67:  */
1.1.1.7 ! root       68: static int vm_pageout_requested;
1.1       root       69: 
                     70: /*
1.1.1.7 ! root       71:  * Event placeholder for pageout throttling, synchronized with
        !            72:  * the free page queue lock.
1.1       root       73:  */
1.1.1.7 ! root       74: static int vm_pageout_continue;
1.1       root       75: 
                     76: /*
                     77:  *     Routine:        vm_pageout_setup
                     78:  *     Purpose:
                     79:  *             Set up a page for pageout.
                     80:  *
                     81:  *             Move or copy the page to a new object, as part
                     82:  *             of which it will be sent to its memory manager
1.1.1.7 ! root       83:  *             in a memory_object_data_return or memory_object_initialize
1.1       root       84:  *             message.
                     85:  *
                     86:  *             The "paging_offset" argument specifies the offset
                     87:  *             of the page within its external memory object.
                     88:  *
                     89:  *             The "new_object" and "new_offset" arguments
                     90:  *             indicate where the page should be moved.
                     91:  *
                     92:  *             The "flush" argument specifies whether the page
                     93:  *             should be flushed from its object.  If not, a
                     94:  *             copy of the page is moved to the new object.
                     95:  *
                     96:  *     In/Out conditions:
                     97:  *             The page in question must not be on any pageout queues,
                     98:  *             and must be busy.  The object to which it belongs
                     99:  *             must be unlocked, and the caller must hold a paging
                    100:  *             reference to it.  The new_object must not be locked.
                    101:  *
                    102:  *             If the page is flushed from its original object,
                    103:  *             this routine returns a pointer to a place-holder page,
                    104:  *             inserted at the same offset, to block out-of-order
                    105:  *             requests for the page.  The place-holder page must
1.1.1.7 ! root      106:  *             be freed after the data_return or initialize message
1.1       root      107:  *             has been sent.  If the page is copied,
                    108:  *             the holding page is VM_PAGE_NULL.
                    109:  *
                    110:  *             The original page is put on a paging queue and marked
                    111:  *             not busy on exit.
                    112:  */
                    113: vm_page_t
1.1.1.4   root      114: vm_pageout_setup(
                    115:        vm_page_t               m,
                    116:        vm_offset_t             paging_offset,
                    117:        vm_object_t             new_object,
                    118:        vm_offset_t             new_offset,
                    119:        boolean_t               flush)
1.1       root      120: {
1.1.1.4   root      121:        vm_object_t     old_object = m->object;
                    122:        vm_page_t       holding_page = 0; /*'=0'to quiet gcc warnings*/
                    123:        vm_page_t       new_m;
1.1       root      124: 
                    125:        assert(m->busy && !m->absent && !m->fictitious);
                    126: 
                    127:        /*
                    128:         *      If we are not flushing the page, allocate a
1.1.1.7 ! root      129:         *      page in the object.
1.1       root      130:         */
                    131:        if (!flush) {
1.1.1.7 ! root      132:                for (;;) {
        !           133:                        vm_object_lock(new_object);
        !           134:                        new_m = vm_page_alloc(new_object, new_offset);
        !           135:                        vm_object_unlock(new_object);
        !           136: 
        !           137:                        if (new_m != VM_PAGE_NULL) {
        !           138:                                break;
        !           139:                        }
        !           140: 
        !           141:                        VM_PAGE_WAIT(NULL);
        !           142:                }
1.1       root      143:        }
                    144: 
                    145:        if (flush) {
                    146:                /*
                    147:                 *      Create a place-holder page where the old one was,
                    148:                 *      to prevent anyone from attempting to page in this
                    149:                 *      page while we`re unlocked.
                    150:                 */
                    151:                while ((holding_page = vm_page_grab_fictitious())
                    152:                                                        == VM_PAGE_NULL)
                    153:                        vm_page_more_fictitious();
                    154: 
                    155:                vm_object_lock(old_object);
                    156:                vm_page_lock_queues();
                    157:                vm_page_remove(m);
                    158:                vm_page_unlock_queues();
                    159:                PAGE_WAKEUP_DONE(m);
                    160: 
                    161:                vm_page_lock_queues();
                    162:                vm_page_insert(holding_page, old_object, m->offset);
                    163:                vm_page_unlock_queues();
                    164: 
                    165:                /*
                    166:                 *      Record that this page has been written out
                    167:                 */
                    168: #if    MACH_PAGEMAP
                    169:                vm_external_state_set(old_object->existence_info,
                    170:                                        paging_offset,
                    171:                                        VM_EXTERNAL_STATE_EXISTS);
1.1.1.2   root      172: #endif /* MACH_PAGEMAP */
1.1       root      173: 
                    174:                vm_object_unlock(old_object);
                    175: 
                    176:                vm_object_lock(new_object);
                    177: 
                    178:                /*
                    179:                 *      Move this page into the new object
                    180:                 */
                    181: 
                    182:                vm_page_lock_queues();
                    183:                vm_page_insert(m, new_object, new_offset);
                    184:                vm_page_unlock_queues();
                    185: 
                    186:                m->dirty = TRUE;
                    187:                m->precious = FALSE;
                    188:                m->page_lock = VM_PROT_NONE;
                    189:                m->unlock_request = VM_PROT_NONE;
                    190:        }
                    191:        else {
                    192:                /*
                    193:                 *      Copy the data into the new page,
                    194:                 *      and mark the new page as clean.
                    195:                 */
                    196:                vm_page_copy(m, new_m);
                    197: 
                    198:                vm_object_lock(old_object);
                    199:                m->dirty = FALSE;
                    200:                pmap_clear_modify(m->phys_addr);
                    201: 
                    202:                /*
                    203:                 *      Deactivate old page.
                    204:                 */
                    205:                vm_page_lock_queues();
                    206:                vm_page_deactivate(m);
                    207:                vm_page_unlock_queues();
                    208: 
                    209:                PAGE_WAKEUP_DONE(m);
                    210: 
                    211:                /*
                    212:                 *      Record that this page has been written out
                    213:                 */
                    214: 
                    215: #if    MACH_PAGEMAP
                    216:                vm_external_state_set(old_object->existence_info,
                    217:                                        paging_offset,
                    218:                                        VM_EXTERNAL_STATE_EXISTS);
1.1.1.2   root      219: #endif /* MACH_PAGEMAP */
1.1       root      220: 
                    221:                vm_object_unlock(old_object);
                    222: 
                    223:                vm_object_lock(new_object);
                    224: 
                    225:                /*
                    226:                 *      Use the new page below.
                    227:                 */
                    228:                m = new_m;
                    229:                m->dirty = TRUE;
                    230:                assert(!m->precious);
                    231:                PAGE_WAKEUP_DONE(m);
                    232:        }
                    233: 
                    234:        /*
                    235:         *      Make the old page eligible for replacement again; if a
                    236:         *      user-supplied memory manager fails to release the page,
                    237:         *      it will be paged out again to the default memory manager.
                    238:         *
                    239:         *      Note that pages written to the default memory manager
                    240:         *      must be wired down -- in return, it guarantees to free
                    241:         *      this page, rather than reusing it.
                    242:         */
                    243: 
                    244:        vm_page_lock_queues();
                    245:        vm_stat.pageouts++;
                    246:        if (m->laundry) {
1.1.1.7 ! root      247: 
1.1       root      248:                /*
1.1.1.7 ! root      249:                 *      The caller is telling us that it is going to
        !           250:                 *      immediately double page this page to the default
        !           251:                 *      pager.
1.1       root      252:                 */
                    253: 
                    254:                assert(!old_object->internal);
                    255:                m->laundry = FALSE;
                    256:        } else if (old_object->internal) {
                    257:                m->laundry = TRUE;
                    258:                vm_page_laundry_count++;
                    259: 
                    260:                vm_page_wire(m);
1.1.1.7 ! root      261:        } else {
        !           262:                m->external_laundry = TRUE;
        !           263: 
        !           264:                /*
        !           265:                 *      If vm_page_external_laundry_count is negative,
        !           266:                 *      the pageout daemon isn't expecting to be
        !           267:                 *      notified.
        !           268:                 */
        !           269: 
        !           270:                if (vm_page_external_laundry_count >= 0) {
        !           271:                        vm_page_external_laundry_count++;
        !           272:                }
        !           273: 
1.1       root      274:                vm_page_activate(m);
1.1.1.7 ! root      275:        }
1.1       root      276:        vm_page_unlock_queues();
                    277: 
                    278:        /*
                    279:         *      Since IPC operations may block, we drop locks now.
                    280:         *      [The placeholder page is busy, and we still have
                    281:         *      paging_in_progress incremented.]
                    282:         */
                    283: 
                    284:        vm_object_unlock(new_object);
                    285: 
                    286:        /*
                    287:         *      Return the placeholder page to simplify cleanup.
                    288:         */
                    289:        return (flush ? holding_page : VM_PAGE_NULL);
                    290: }
                    291: 
                    292: /*
                    293:  *     Routine:        vm_pageout_page
                    294:  *     Purpose:
                    295:  *             Causes the specified page to be written back to
                    296:  *             the appropriate memory object.
                    297:  *
                    298:  *             The "initial" argument specifies whether this
                    299:  *             data is an initialization only, and should use
                    300:  *             memory_object_data_initialize instead of
1.1.1.7 ! root      301:  *             memory_object_data_return.
1.1       root      302:  *
                    303:  *             The "flush" argument specifies whether the page
                    304:  *             should be flushed from the object.  If not, a
                    305:  *             copy of the data is sent to the memory object.
                    306:  *
                    307:  *     In/out conditions:
                    308:  *             The page in question must not be on any pageout queues.
                    309:  *             The object to which it belongs must be locked.
                    310:  *     Implementation:
                    311:  *             Move this page to a completely new object, if flushing;
                    312:  *             copy to a new page in a new object, if not.
                    313:  */
                    314: void
1.1.1.4   root      315: vm_pageout_page(
                    316:        vm_page_t               m,
                    317:        boolean_t               initial,
                    318:        boolean_t               flush)
1.1       root      319: {
                    320:        vm_map_copy_t           copy;
1.1.1.4   root      321:        vm_object_t             old_object;
                    322:        vm_object_t             new_object;
                    323:        vm_page_t               holding_page;
1.1       root      324:        vm_offset_t             paging_offset;
                    325:        kern_return_t           rc;
                    326:        boolean_t               precious_clean;
                    327: 
                    328:        assert(m->busy);
                    329: 
                    330:        /*
                    331:         *      Cleaning but not flushing a clean precious page is a
                    332:         *      no-op.  Remember whether page is clean and precious now
                    333:         *      because vm_pageout_setup will mark it dirty and not precious.
                    334:         *
                    335:         * XXX Check if precious_clean && !flush can really happen.
                    336:         */
                    337:        precious_clean = (!m->dirty) && m->precious;
                    338:        if (precious_clean && !flush) {
                    339:                PAGE_WAKEUP_DONE(m);
                    340:                return;
                    341:        }
                    342: 
                    343:        /*
                    344:         *      Verify that we really want to clean this page.
                    345:         */
                    346:        if (m->absent || m->error || (!m->dirty && !m->precious)) {
                    347:                VM_PAGE_FREE(m);
                    348:                return;
                    349:        }
                    350: 
                    351:        /*
                    352:         *      Create a paging reference to let us play with the object.
                    353:         */
                    354:        old_object = m->object;
                    355:        paging_offset = m->offset + old_object->paging_offset;
                    356:        vm_object_paging_begin(old_object);
                    357:        vm_object_unlock(old_object);
                    358: 
                    359:        /*
                    360:         *      Allocate a new object into which we can put the page.
                    361:         */
                    362:        new_object = vm_object_allocate(PAGE_SIZE);
1.1.1.6   root      363:        new_object->used_for_pageout = TRUE;
1.1       root      364: 
                    365:        /*
                    366:         *      Move the page into the new object.
                    367:         */
                    368:        holding_page = vm_pageout_setup(m,
                    369:                                paging_offset,
                    370:                                new_object,
                    371:                                0,              /* new offset */
                    372:                                flush);         /* flush */
                    373: 
                    374:        rc = vm_map_copyin_object(new_object, 0, PAGE_SIZE, &copy);
                    375:        assert(rc == KERN_SUCCESS);
                    376: 
1.1.1.7 ! root      377:        if (initial) {
        !           378:                rc = memory_object_data_initialize(
        !           379:                         old_object->pager,
1.1       root      380:                         old_object->pager_request,
                    381:                         paging_offset, (pointer_t) copy, PAGE_SIZE);
                    382:        }
                    383:        else {
                    384:                rc = memory_object_data_return(
                    385:                         old_object->pager,
                    386:                         old_object->pager_request,
                    387:                         paging_offset, (pointer_t) copy, PAGE_SIZE,
                    388:                         !precious_clean, !flush);
                    389:        }
                    390: 
                    391:        if (rc != KERN_SUCCESS)
                    392:                vm_map_copy_discard(copy);
                    393: 
                    394:        /*
                    395:         *      Clean up.
                    396:         */
                    397:        vm_object_lock(old_object);
                    398:        if (holding_page != VM_PAGE_NULL)
                    399:            VM_PAGE_FREE(holding_page);
                    400:        vm_object_paging_end(old_object);
                    401: }
                    402: 
                    403: /*
                    404:  *     vm_pageout_scan does the dirty work for the pageout daemon.
1.1.1.7 ! root      405:  *
        !           406:  *     Return TRUE if the pageout daemon is done for now, FALSE otherwise,
        !           407:  *     in which case should_wait indicates whether the pageout daemon
        !           408:  *     should wait to allow pagers to keep up.
        !           409:  *
        !           410:  *     It returns with vm_page_queue_free_lock held.
1.1       root      411:  */
                    412: 
1.1.1.7 ! root      413: boolean_t vm_pageout_scan(boolean_t *should_wait)
1.1       root      414: {
1.1.1.7 ! root      415:        boolean_t done;
1.1       root      416: 
                    417:        /*
1.1.1.7 ! root      418:         *      Try balancing pages among segments first, since this
        !           419:         *      may be enough to resume unprivileged allocations.
        !           420:         */
        !           421: 
        !           422:        /* This function returns with vm_page_queue_free_lock held */
        !           423:        done = vm_page_balance();
        !           424: 
        !           425:        if (done) {
        !           426:                return TRUE;
        !           427:        }
        !           428: 
        !           429:        simple_unlock(&vm_page_queue_free_lock);
        !           430: 
        !           431:        /*
        !           432:         *      Balancing is not enough. Shrink caches and scan pages
        !           433:         *      for eviction.
1.1       root      434:         */
                    435: 
                    436:        stack_collect();
                    437:        net_kmsg_collect();
                    438:        consider_task_collect();
1.1.1.4   root      439:        if (0)  /* XXX: pcb_collect doesn't do anything yet, so it is
                    440:                   pointless to call consider_thread_collect.  */
1.1       root      441:        consider_thread_collect();
                    442: 
1.1.1.7 ! root      443:        /*
        !           444:         *      slab_collect should be last, because the other operations
        !           445:         *      might return memory to caches.
        !           446:         */
        !           447:        slab_collect();
1.1       root      448: 
1.1.1.7 ! root      449:        vm_page_refill_inactive();
1.1.1.6   root      450: 
1.1.1.7 ! root      451:        /* This function returns with vm_page_queue_free_lock held */
        !           452:        return vm_page_evict(should_wait);
        !           453: }
1.1       root      454: 
1.1.1.7 ! root      455: void vm_pageout(void)
        !           456: {
        !           457:        boolean_t done, should_wait;
1.1       root      458: 
1.1.1.7 ! root      459:        current_thread()->vm_privilege = 1;
        !           460:        stack_privilege(current_thread());
        !           461:        thread_set_own_priority(0);
1.1       root      462: 
1.1.1.7 ! root      463:        for (;;) {
        !           464:                done = vm_pageout_scan(&should_wait);
        !           465:                /* we hold vm_page_queue_free_lock now */
1.1       root      466: 
1.1.1.7 ! root      467:                if (done) {
        !           468:                        thread_sleep(&vm_pageout_requested,
        !           469:                                     simple_lock_addr(vm_page_queue_free_lock),
        !           470:                                     FALSE);
        !           471:                } else if (should_wait) {
        !           472:                        assert_wait(&vm_pageout_continue, FALSE);
        !           473:                        thread_set_timeout(VM_PAGEOUT_TIMEOUT);
        !           474:                        simple_unlock(&vm_page_queue_free_lock);
        !           475:                        thread_block(NULL);
        !           476: 
        !           477: #if DEBUG
        !           478:                        if (current_thread()->wait_result != THREAD_AWAKENED) {
        !           479:                                printf("vm_pageout: timeout,"
        !           480:                                       " vm_page_laundry_count:%d"
        !           481:                                       " vm_page_external_laundry_count:%d\n",
        !           482:                                       vm_page_laundry_count,
        !           483:                                       vm_page_external_laundry_count);
1.1.1.2   root      484:                        }
1.1.1.7 ! root      485: #endif
        !           486:                } else {
        !           487:                        simple_unlock(&vm_page_queue_free_lock);
1.1.1.2   root      488:                }
1.1       root      489:        }
                    490: }
                    491: 
                    492: /*
1.1.1.7 ! root      493:  *     Start pageout
        !           494:  *
        !           495:  *     The free page queue lock must be held before calling this function.
1.1       root      496:  */
1.1.1.7 ! root      497: void vm_pageout_start(void)
1.1       root      498: {
1.1.1.7 ! root      499:        if (!current_thread())
        !           500:                return;
1.1       root      501: 
1.1.1.7 ! root      502:        thread_wakeup_one(&vm_pageout_requested);
1.1       root      503: }
                    504: 
1.1.1.7 ! root      505: /*
        !           506:  *     Resume pageout
        !           507:  *
        !           508:  *     The free page queue lock must be held before calling this function.
        !           509:  */
        !           510: void vm_pageout_resume(void)
1.1       root      511: {
1.1.1.7 ! root      512:        thread_wakeup_one(&vm_pageout_continue);
1.1       root      513: }

unix.superglobalmegacorp.com

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