2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 * Mach Operating System
32 * Copyright (c) 1987 Carnegie-Mellon University
33 * All rights reserved. The CMU software License Agreement specifies
34 * the terms and conditions for use and redistribution.
39 * "Swap" pager that pages to/from vnodes. Also
40 * handles demand paging from files.
44 #include <mach/boolean.h>
45 #include <sys/param.h>
46 #include <sys/systm.h>
49 #include <sys/kauth.h>
52 #include <sys/vnode_internal.h>
53 #include <sys/namei.h>
54 #include <sys/mount_internal.h> /* needs internal due to fhandle_t */
55 #include <sys/ubc_internal.h>
58 #include <mach/mach_types.h>
59 #include <mach/memory_object_types.h>
61 #include <vm/vm_map.h>
62 #include <vm/vm_kern.h>
63 #include <kern/zalloc.h>
64 #include <kern/kalloc.h>
65 #include <libkern/libkern.h>
67 #include <vm/vnode_pager.h>
68 #include <vm/vm_pageout.h>
70 #include <kern/assert.h>
71 #include <sys/kdebug.h>
72 #include <machine/spl.h>
74 #include <nfs/rpcv2.h>
75 #include <nfs/nfsproto.h>
78 #include <vm/vm_protos.h>
80 unsigned int vp_pagein
=0;
81 unsigned int vp_pgodirty
=0;
82 unsigned int vp_pgoclean
=0;
83 unsigned int dp_pgouts
=0; /* Default pager pageouts */
84 unsigned int dp_pgins
=0; /* Default pager pageins */
87 vnode_pager_get_filesize(struct vnode
*vp
)
90 return (vm_object_offset_t
) ubc_getsize(vp
);
94 vnode_pageout(struct vnode
*vp
,
96 vm_offset_t upl_offset
,
97 vm_object_offset_t f_offset
,
102 struct proc
*p
= current_proc();
103 int result
= PAGER_SUCCESS
;
112 struct vfs_context context
;
115 context
.vc_ucred
= kauth_cred_get();
120 result
= PAGER_ERROR
;
124 UBCINFOCHECK("vnode_pageout", vp
);
126 if (UBCINVALID(vp
)) {
127 result
= PAGER_ERROR
;
130 if (upl
&& !(flags
& UPL_NOCOMMIT
))
131 ubc_upl_abort_range(upl
, upl_offset
, size
, UPL_ABORT_FREE_ON_EMPTY
);
134 if ( !(flags
& UPL_VNODE_PAGER
)) {
136 * This is a pageout from the default pager,
137 * just go ahead and call vnop_pageout since
138 * it has already sorted out the dirty ranges
142 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_START
,
145 if ( (error_ret
= VNOP_PAGEOUT(vp
, upl
, upl_offset
, (off_t
)f_offset
,
146 (size_t)size
, flags
, &context
)) )
147 result
= PAGER_ERROR
;
149 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_END
,
155 * we come here for pageouts to 'real' files and
156 * for msyncs... the upl may not contain any
157 * dirty pages.. it's our responsibility to sort
158 * through it and find the 'runs' of dirty pages
159 * to call VNOP_PAGEOUT on...
161 pl
= ubc_upl_pageinfo(upl
);
163 if (ubc_getsize(vp
) == 0) {
165 * if the file has been effectively deleted, then
166 * we need to go through the UPL and invalidate any
167 * buffer headers we might have that reference any
170 for (offset
= upl_offset
; isize
; isize
-= PAGE_SIZE
, offset
+= PAGE_SIZE
) {
172 if (vp
->v_tag
== VT_NFS
)
173 /* check with nfs if page is OK to drop */
174 error
= nfs_buf_page_inval(vp
, (off_t
)f_offset
);
178 blkno
= ubc_offtoblk(vp
, (off_t
)f_offset
);
179 error
= buf_invalblkno(vp
, blkno
, 0);
182 if ( !(flags
& UPL_NOCOMMIT
))
183 ubc_upl_abort_range(upl
, offset
, PAGE_SIZE
, UPL_ABORT_FREE_ON_EMPTY
);
186 result
= PAGER_ERROR
;
188 } else if ( !(flags
& UPL_NOCOMMIT
)) {
189 ubc_upl_commit_range(upl
, offset
, PAGE_SIZE
, UPL_COMMIT_FREE_ON_EMPTY
);
191 f_offset
+= PAGE_SIZE
;
196 * Ignore any non-present pages at the end of the
197 * UPL so that we aren't looking at a upl that
198 * may already have been freed by the preceeding
199 * aborts/completions.
201 base_index
= upl_offset
/ PAGE_SIZE
;
203 for (pg_index
= (upl_offset
+ isize
) / PAGE_SIZE
; pg_index
> base_index
;) {
204 if (upl_page_present(pl
, --pg_index
))
206 if (pg_index
== base_index
) {
208 * no pages were returned, so release
209 * our hold on the upl and leave
211 if ( !(flags
& UPL_NOCOMMIT
))
212 ubc_upl_abort_range(upl
, upl_offset
, isize
, UPL_ABORT_FREE_ON_EMPTY
);
217 isize
= (pg_index
+ 1) * PAGE_SIZE
;
220 pg_index
= base_index
;
226 if ( !upl_page_present(pl
, pg_index
)) {
228 * we asked for RET_ONLY_DIRTY, so it's possible
229 * to get back empty slots in the UPL
230 * just skip over them
238 if ( !upl_dirty_page(pl
, pg_index
)) {
240 * if the page is not dirty and reached here it is
241 * marked precious or it is due to invalidation in
242 * memory_object_lock request as part of truncation
243 * We also get here from vm_object_terminate()
244 * So all you need to do in these
245 * cases is to invalidate incore buffer if it is there
246 * Note we must not sleep here if the buffer is busy - that is
247 * a lock inversion which causes deadlock.
252 if (vp
->v_tag
== VT_NFS
)
253 /* check with nfs if page is OK to drop */
254 error
= nfs_buf_page_inval(vp
, (off_t
)(f_offset
+ offset
));
258 blkno
= ubc_offtoblk(vp
, (off_t
)(f_offset
+ offset
));
259 error
= buf_invalblkno(vp
, blkno
, 0);
262 if ( !(flags
& UPL_NOCOMMIT
))
263 ubc_upl_abort_range(upl
, offset
, PAGE_SIZE
, UPL_ABORT_FREE_ON_EMPTY
);
266 result
= PAGER_ERROR
;
268 } else if ( !(flags
& UPL_NOCOMMIT
)) {
269 ubc_upl_commit_range(upl
, offset
, PAGE_SIZE
, UPL_COMMIT_FREE_ON_EMPTY
);
280 xsize
= isize
- PAGE_SIZE
;
283 if ( !upl_dirty_page(pl
, pg_index
+ num_of_pages
))
288 xsize
= num_of_pages
* PAGE_SIZE
;
290 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_START
,
291 xsize
, (int)(f_offset
+ offset
), 0, 0, 0);
293 if ( (error
= VNOP_PAGEOUT(vp
, upl
, (vm_offset_t
)offset
,
294 (off_t
)(f_offset
+ offset
), xsize
,
295 flags
, &context
)) ) {
298 result
= PAGER_ERROR
;
300 KERNEL_DEBUG_CONSTANT((MACHDBG_CODE(DBG_MACH_VM
, 1)) | DBG_FUNC_END
,
305 pg_index
+= num_of_pages
;
321 vm_offset_t upl_offset
,
322 vm_object_offset_t f_offset
,
327 struct proc
*p
= current_proc();
330 int result
= PAGER_SUCCESS
;
337 int abort_needed
= 1;
340 UBCINFOCHECK("vnode_pagein", vp
);
342 if (UBCINVALID(vp
)) {
343 result
= PAGER_ERROR
;
345 if (upl
&& !(flags
& UPL_NOCOMMIT
)) {
346 ubc_upl_abort_range(upl
, upl_offset
, size
, UPL_ABORT_FREE_ON_EMPTY
| UPL_ABORT_ERROR
);
350 if (upl
== (upl_t
)NULL
) {
351 if (size
> (MAX_UPL_TRANSFER
* PAGE_SIZE
)) {
352 result
= PAGER_ERROR
;
356 ubc_create_upl(vp
, f_offset
, size
, &upl
, &pl
, UPL_RET_ONLY_ABSENT
| UPL_SET_LITE
);
358 if (upl
== (upl_t
)NULL
) {
359 result
= PAGER_ABSENT
;
360 error
= PAGER_ABSENT
;
365 * if we get here, we've created the upl and
366 * are responsible for commiting/aborting it
367 * regardless of what the caller has passed in
369 flags
&= ~UPL_NOCOMMIT
;
373 pl
= ubc_upl_pageinfo(upl
);
377 pages_in_upl
= size
/ PAGE_SIZE
;
378 first_pg
= upl_offset
/ PAGE_SIZE
;
381 * before we start marching forward, we must make sure we end on
382 * a present page, otherwise we will be working with a freed
385 for (last_pg
= pages_in_upl
- 1; last_pg
>= first_pg
; last_pg
--) {
386 if (upl_page_present(pl
, last_pg
))
389 pages_in_upl
= last_pg
+ 1;
391 for (last_pg
= first_pg
; last_pg
< pages_in_upl
;) {
393 * scan the upl looking for the next
394 * page that is present.... if all of the
395 * pages are absent, we're done
397 for (start_pg
= last_pg
; last_pg
< pages_in_upl
; last_pg
++) {
398 if (upl_page_present(pl
, last_pg
))
401 if (last_pg
== pages_in_upl
)
405 * if we get here, we've sitting on a page
406 * that is present... we want to skip over
407 * any range of 'valid' pages... if this takes
408 * us to the end of the request, than we're done
410 for (start_pg
= last_pg
; last_pg
< pages_in_upl
; last_pg
++) {
411 if (!upl_valid_page(pl
, last_pg
) || !upl_page_present(pl
, last_pg
))
414 if (last_pg
> start_pg
) {
416 * we've found a range of valid pages
417 * if we've got COMMIT responsibility
418 * commit this range of pages back to the
421 xsize
= (last_pg
- start_pg
) * PAGE_SIZE
;
423 if (!(flags
& UPL_NOCOMMIT
))
424 ubc_upl_abort_range(upl
, start_pg
* PAGE_SIZE
, xsize
, UPL_ABORT_FREE_ON_EMPTY
);
428 if (last_pg
== pages_in_upl
)
431 if (!upl_page_present(pl
, last_pg
))
433 * if we found a range of valid pages
434 * terminated by a non-present page
440 * scan from the found invalid page looking for a valid
441 * or non-present page before the end of the upl is reached, if we
442 * find one, then it will be the last page of the request to
445 for (start_pg
= last_pg
; last_pg
< pages_in_upl
; last_pg
++) {
446 if (upl_valid_page(pl
, last_pg
) || !upl_page_present(pl
, last_pg
))
449 if (last_pg
> start_pg
) {
451 struct vfs_context context
;
454 context
.vc_ucred
= kauth_cred_get();
455 xsize
= (last_pg
- start_pg
) * PAGE_SIZE
;
456 xoff
= start_pg
* PAGE_SIZE
;
458 if ( (error
= VNOP_PAGEIN(vp
, upl
, (vm_offset_t
) xoff
,
459 (off_t
)f_offset
+ xoff
,
460 xsize
, flags
, &context
)) ) {
461 result
= PAGER_ERROR
;
468 if (!(flags
& UPL_NOCOMMIT
) && abort_needed
)
469 ubc_upl_abort_range(upl
, upl_offset
, size
, UPL_ABORT_FREE_ON_EMPTY
);
474 ut
= get_bsdthread_info(current_thread());
476 if (ut
->uu_lowpri_delay
) {
478 * task is marked as a low priority I/O type
479 * and the I/O we issued while in this system call
480 * collided with normal I/O operations... we'll
481 * delay in order to mitigate the impact of this
482 * task on the normal operation of the system
484 IOSleep(ut
->uu_lowpri_delay
);
485 ut
->uu_lowpri_delay
= 0;
491 vnode_pager_shutdown(void)
496 for(i
= 0; i
< MAX_BACKING_STORE
; i
++) {
497 vp
= (vnode_t
)(bs_port_table
[i
]).vp
;
499 (bs_port_table
[i
]).vp
= 0;
501 /* get rid of macx_swapon() reference */
509 upl_get_internal_page_list(upl_t upl
)
511 return(UPL_GET_INTERNAL_PAGE_LIST(upl
));