|
|
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, ©);
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.