2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Mach Operating System
30 * Copyright (c) 1987 Carnegie-Mellon University
31 * All rights reserved. The CMU software License Agreement specifies
32 * the terms and conditions for use and redistribution.
37 * "Swap" pager that pages to/from vnodes. Also
38 * handles demand paging from files.
42 #include <mach/boolean.h>
43 #include <sys/param.h>
44 #include <sys/systm.h>
47 #include <sys/kauth.h>
50 #include <sys/vnode_internal.h>
51 #include <sys/namei.h>
52 #include <sys/mount_internal.h> /* needs internal due to fhandle_t */
53 #include <sys/ubc_internal.h>
56 #include <mach/mach_types.h>
57 #include <mach/memory_object_types.h>
60 #include <vm/vm_map.h>
61 #include <vm/vm_kern.h>
62 #include <kern/zalloc.h>
63 #include <kern/kalloc.h>
64 #include <libkern/libkern.h>
66 #include <vm/vnode_pager.h>
67 #include <vm/vm_pageout.h>
69 #include <kern/assert.h>
70 #include <sys/kdebug.h>
71 #include <machine/spl.h>
73 #include <nfs/rpcv2.h>
74 #include <nfs/nfsproto.h>
77 #include <vm/vm_protos.h>
79 unsigned int vp_pagein
=0;
80 unsigned int vp_pgodirty
=0;
81 unsigned int vp_pgoclean
=0;
82 unsigned int dp_pgouts
=0; /* Default pager pageouts */
83 unsigned int dp_pgins
=0; /* Default pager pageins */
86 vnode_pager_get_filesize(struct vnode
*vp
)
89 return (vm_object_offset_t
) ubc_getsize(vp
);
93 vnode_pager_get_pathname(
100 len
= (int) *length_p
;
101 error
= vn_getpath(vp
, pathname
, &len
);
105 *length_p
= (vm_size_t
) len
;
110 vnode_pager_get_filename(
112 const char **filename
)
114 *filename
= vp
->v_name
;
119 vnode_pager_get_cs_blobs(
123 *blobs
= ubc_get_cs_blobs(vp
);
128 vnode_pageout(struct vnode
*vp
,
130 vm_offset_t upl_offset
,
131 vm_object_offset_t f_offset
,
136 int result
= PAGER_SUCCESS
;
145 vfs_context_t ctx
= vfs_context_current(); /* pager context */
150 result
= PAGER_ERROR
;
155 if (UBCINFOEXISTS(vp
) == 0) {
156 result
= PAGER_ERROR
;
159 if (upl
&& !(flags
& UPL_NOCOMMIT
))
160 ubc_upl_abort_range(upl
, upl_offset
, size
, UPL_ABORT_FREE_ON_EMPTY
);
163 if ( !(flags
& UPL_VNODE_PAGER
)) {
165 * This is a pageout from the default pager,
166 * just go ahead and call vnop_pageout since
167 * it has already sorted out the dirty ranges
171 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_START
,
174 if ( (error_ret
= VNOP_PAGEOUT(vp
, upl
, upl_offset
, (off_t
)f_offset
,
175 (size_t)size
, flags
, ctx
)) )
176 result
= PAGER_ERROR
;
178 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_END
,
184 * we come here for pageouts to 'real' files and
185 * for msyncs... the upl may not contain any
186 * dirty pages.. it's our responsibility to sort
187 * through it and find the 'runs' of dirty pages
188 * to call VNOP_PAGEOUT on...
190 pl
= ubc_upl_pageinfo(upl
);
192 if (ubc_getsize(vp
) == 0) {
194 * if the file has been effectively deleted, then
195 * we need to go through the UPL and invalidate any
196 * buffer headers we might have that reference any
199 for (offset
= upl_offset
; isize
; isize
-= PAGE_SIZE
, offset
+= PAGE_SIZE
) {
201 if (vp
->v_tag
== VT_NFS
)
202 /* check with nfs if page is OK to drop */
203 error
= nfs_buf_page_inval(vp
, (off_t
)f_offset
);
207 blkno
= ubc_offtoblk(vp
, (off_t
)f_offset
);
208 error
= buf_invalblkno(vp
, blkno
, 0);
211 if ( !(flags
& UPL_NOCOMMIT
))
212 ubc_upl_abort_range(upl
, offset
, PAGE_SIZE
, UPL_ABORT_FREE_ON_EMPTY
);
215 result
= PAGER_ERROR
;
217 } else if ( !(flags
& UPL_NOCOMMIT
)) {
218 ubc_upl_commit_range(upl
, offset
, PAGE_SIZE
, UPL_COMMIT_FREE_ON_EMPTY
);
220 f_offset
+= PAGE_SIZE
;
225 * Ignore any non-present pages at the end of the
226 * UPL so that we aren't looking at a upl that
227 * may already have been freed by the preceeding
228 * aborts/completions.
230 base_index
= upl_offset
/ PAGE_SIZE
;
232 for (pg_index
= (upl_offset
+ isize
) / PAGE_SIZE
; pg_index
> base_index
;) {
233 if (upl_page_present(pl
, --pg_index
))
235 if (pg_index
== base_index
) {
237 * no pages were returned, so release
238 * our hold on the upl and leave
240 if ( !(flags
& UPL_NOCOMMIT
))
241 ubc_upl_abort_range(upl
, upl_offset
, isize
, UPL_ABORT_FREE_ON_EMPTY
);
246 isize
= ((pg_index
+ 1) - base_index
) * PAGE_SIZE
;
249 pg_index
= base_index
;
255 if ( !upl_page_present(pl
, pg_index
)) {
257 * we asked for RET_ONLY_DIRTY, so it's possible
258 * to get back empty slots in the UPL
259 * just skip over them
261 f_offset
+= PAGE_SIZE
;
268 if ( !upl_dirty_page(pl
, pg_index
)) {
270 * if the page is not dirty and reached here it is
271 * marked precious or it is due to invalidation in
272 * memory_object_lock request as part of truncation
273 * We also get here from vm_object_terminate()
274 * So all you need to do in these
275 * cases is to invalidate incore buffer if it is there
276 * Note we must not sleep here if the buffer is busy - that is
277 * a lock inversion which causes deadlock.
282 if (vp
->v_tag
== VT_NFS
)
283 /* check with nfs if page is OK to drop */
284 error
= nfs_buf_page_inval(vp
, (off_t
)f_offset
);
288 blkno
= ubc_offtoblk(vp
, (off_t
)f_offset
);
289 error
= buf_invalblkno(vp
, blkno
, 0);
292 if ( !(flags
& UPL_NOCOMMIT
))
293 ubc_upl_abort_range(upl
, offset
, PAGE_SIZE
, UPL_ABORT_FREE_ON_EMPTY
);
296 result
= PAGER_ERROR
;
298 } else if ( !(flags
& UPL_NOCOMMIT
)) {
299 ubc_upl_commit_range(upl
, offset
, PAGE_SIZE
, UPL_COMMIT_FREE_ON_EMPTY
);
301 f_offset
+= PAGE_SIZE
;
311 xsize
= isize
- PAGE_SIZE
;
314 if ( !upl_dirty_page(pl
, pg_index
+ num_of_pages
))
319 xsize
= num_of_pages
* PAGE_SIZE
;
321 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_START
,
322 xsize
, (int)f_offset
, 0, 0, 0);
324 if ( (error
= VNOP_PAGEOUT(vp
, upl
, (vm_offset_t
)offset
, (off_t
)f_offset
,
325 xsize
, flags
, ctx
)) ) {
328 result
= PAGER_ERROR
;
330 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_END
,
336 pg_index
+= num_of_pages
;
346 extern void throttle_lowpri_io(int *lowpri_window
,mount_t v_mount
);
352 vm_offset_t upl_offset
,
353 vm_object_offset_t f_offset
,
360 int result
= PAGER_SUCCESS
;
369 if (flags
& UPL_NOCOMMIT
)
372 if (UBCINFOEXISTS(vp
) == 0) {
373 result
= PAGER_ERROR
;
376 if (upl
&& must_commit
)
377 ubc_upl_abort_range(upl
, upl_offset
, size
, UPL_ABORT_FREE_ON_EMPTY
| UPL_ABORT_ERROR
);
381 if (upl
== (upl_t
)NULL
) {
382 if (size
> (MAX_UPL_SIZE
* PAGE_SIZE
)) {
384 panic("vnode_pagein: size = %x\n", size
);
386 result
= PAGER_ERROR
;
390 ubc_create_upl(vp
, f_offset
, size
, &upl
, &pl
, UPL_NOBLOCK
| UPL_RET_ONLY_ABSENT
| UPL_SET_LITE
);
392 if (upl
== (upl_t
)NULL
) {
394 panic("vnode_pagein: ubc_create_upl failed\n");
396 result
= PAGER_ABSENT
;
397 error
= PAGER_ABSENT
;
404 * if we get here, we've created the upl and
405 * are responsible for commiting/aborting it
406 * regardless of what the caller has passed in
408 flags
&= ~UPL_NOCOMMIT
;
413 pl
= ubc_upl_pageinfo(upl
);
414 first_pg
= upl_offset
/ PAGE_SIZE
;
418 pages_in_upl
= size
/ PAGE_SIZE
;
419 DTRACE_VM2(pgpgin
, int, pages_in_upl
, (uint64_t *), NULL
);
422 * before we start marching forward, we must make sure we end on
423 * a present page, otherwise we will be working with a freed
426 for (last_pg
= pages_in_upl
- 1; last_pg
>= first_pg
; last_pg
--) {
427 if (upl_page_present(pl
, last_pg
))
429 if (last_pg
== first_pg
) {
431 * empty UPL, no pages are present
434 ubc_upl_abort_range(upl
, upl_offset
, size
, UPL_ABORT_FREE_ON_EMPTY
);
438 pages_in_upl
= last_pg
+ 1;
441 while (last_pg
< pages_in_upl
) {
443 * skip over missing pages...
445 for ( ; last_pg
< pages_in_upl
; last_pg
++) {
446 if (upl_page_present(pl
, last_pg
))
450 * skip over 'valid' pages... we don't want to issue I/O for these
452 for (start_pg
= last_pg
; last_pg
< pages_in_upl
; last_pg
++) {
453 if (!upl_valid_page(pl
, last_pg
))
456 if (last_pg
> start_pg
) {
458 * we've found a range of valid pages
459 * if we've got COMMIT responsibility
460 * commit this range of pages back to the
463 xsize
= (last_pg
- start_pg
) * PAGE_SIZE
;
466 ubc_upl_abort_range(upl
, start_pg
* PAGE_SIZE
, xsize
, UPL_ABORT_FREE_ON_EMPTY
);
468 if (last_pg
== pages_in_upl
)
470 * we're done... all pages that were present
471 * have either had I/O issued on them or
472 * were aborted unchanged...
476 if (!upl_page_present(pl
, last_pg
)) {
478 * we found a range of valid pages
479 * terminated by a missing page...
480 * bump index to the next page and continue on
486 * scan from the found invalid page looking for a valid
487 * or non-present page before the end of the upl is reached, if we
488 * find one, then it will be the last page of the request to
491 for (start_pg
= last_pg
; last_pg
< pages_in_upl
; last_pg
++) {
492 if (upl_valid_page(pl
, last_pg
) || !upl_page_present(pl
, last_pg
))
495 if (last_pg
> start_pg
) {
497 xsize
= (last_pg
- start_pg
) * PAGE_SIZE
;
498 xoff
= start_pg
* PAGE_SIZE
;
500 if ( (error
= VNOP_PAGEIN(vp
, upl
, (vm_offset_t
) xoff
,
501 (off_t
)f_offset
+ xoff
,
502 xsize
, flags
, vfs_context_current())) ) {
503 result
= PAGER_ERROR
;
513 ut
= get_bsdthread_info(current_thread());
515 if (ut
->uu_lowpri_window
&& ut
->v_mount
) {
517 * task is marked as a low priority I/O type
518 * and the I/O we issued while in this system call
519 * collided with normal I/O operations... we'll
520 * delay in order to mitigate the impact of this
521 * task on the normal operation of the system
523 throttle_lowpri_io(&ut
->uu_lowpri_window
,ut
->v_mount
);
529 vnode_pager_shutdown(void)
534 for(i
= 0; i
< MAX_BACKING_STORE
; i
++) {
535 vp
= (vnode_t
)(bs_port_table
[i
]).vp
;
537 (bs_port_table
[i
]).vp
= 0;
539 /* get rid of macx_swapon() reference */
547 upl_get_internal_page_list(upl_t upl
)
549 return(UPL_GET_INTERNAL_PAGE_LIST(upl
));