1/* 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 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. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 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. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 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. 33 */ 34/* 35 * File: vnode_pager.c 36 * 37 * "Swap" pager that pages to/from vnodes. Also 38 * handles demand paging from files. 39 * 40 */ 41 42#include <mach/boolean.h> 43#include <sys/param.h> 44#include <sys/systm.h> 45#include <sys/user.h> 46#include <sys/proc.h> 47#include <sys/kauth.h> 48#include <sys/buf.h> 49#include <sys/uio.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> 54#include <sys/lock.h> 55#include <sys/disk.h> /* For DKIOC calls */ 56 57#include <mach/mach_types.h> 58#include <mach/memory_object_types.h> 59#include <mach/memory_object_control.h> 60#include <mach/vm_map.h> 61#include <mach/mach_vm.h> 62#include <mach/upl.h> 63#include <mach/sdt.h> 64 65#include <vm/vm_map.h> 66#include <vm/vm_kern.h> 67#include <kern/zalloc.h> 68#include <kern/kalloc.h> 69#include <libkern/libkern.h> 70 71#include <vm/vnode_pager.h> 72#include <vm/vm_pageout.h> 73 74#include <kern/assert.h> 75#include <sys/kdebug.h> 76#include <machine/spl.h> 77 78#include <nfs/rpcv2.h> 79#include <nfs/nfsproto.h> 80#include <nfs/nfs.h> 81 82#include <vm/vm_protos.h> 83 84 85void 86vnode_pager_throttle() 87{ 88 struct uthread *ut; 89 90 ut = get_bsdthread_info(current_thread()); 91 92 if (ut->uu_lowpri_window) 93 throttle_lowpri_io(TRUE); 94} 95 96 97boolean_t 98vnode_pager_isSSD(vnode_t vp) 99{ 100 if (vp->v_mount->mnt_kern_flag & MNTK_SSD) 101 return (TRUE); 102 return (FALSE); 103} 104 105 106uint32_t 107vnode_pager_isinuse(struct vnode *vp) 108{ 109 if (vp->v_usecount > vp->v_kusecount) 110 return (1); 111 return (0); 112} 113 114uint32_t 115vnode_pager_return_hard_throttle_limit(struct vnode *vp, uint32_t *limit, uint32_t hard_throttle) 116{ 117 return(cluster_hard_throttle_limit(vp, limit, hard_throttle)); 118} 119 120vm_object_offset_t 121vnode_pager_get_filesize(struct vnode *vp) 122{ 123 124 return (vm_object_offset_t) ubc_getsize(vp); 125} 126 127kern_return_t 128vnode_pager_get_pathname( 129 struct vnode *vp, 130 char *pathname, 131 vm_size_t *length_p) 132{ 133 int error, len; 134 135 len = (int) *length_p; 136 error = vn_getpath(vp, pathname, &len); 137 if (error != 0) { 138 return KERN_FAILURE; 139 } 140 *length_p = (vm_size_t) len; 141 return KERN_SUCCESS; 142} 143 144kern_return_t 145vnode_pager_get_filename( 146 struct vnode *vp, 147 const char **filename) 148{ 149 *filename = vp->v_name; 150 return KERN_SUCCESS; 151} 152 153kern_return_t 154vnode_pager_get_cs_blobs( 155 struct vnode *vp, 156 void **blobs) 157{ 158 *blobs = ubc_get_cs_blobs(vp); 159 return KERN_SUCCESS; 160} 161 162/* 163 * vnode_trim: 164 * Used to call the DKIOCUNMAP ioctl on the underlying disk device for the specified vnode. 165 * Trims the region at offset bytes into the file, for length bytes. 166 * 167 * Care must be taken to ensure that the vnode is sufficiently reference counted at the time this 168 * function is called; no iocounts or usecounts are taken on the vnode. 169 * This function is non-idempotent in error cases; We cannot un-discard the blocks if only some of them 170 * are successfully discarded. 171 */ 172u_int32_t vnode_trim ( 173 struct vnode *vp, 174 off_t offset, 175 size_t length) 176{ 177 daddr64_t io_blockno; /* Block number corresponding to the start of the extent */ 178 size_t io_bytecount; /* Number of bytes in current extent for the specified range */ 179 size_t trimmed = 0; 180 off_t current_offset = offset; 181 size_t remaining_length = length; 182 int error = 0; 183 u_int32_t blocksize = 0; 184 struct vnode *devvp; 185 dk_extent_t extent; 186 dk_unmap_t unmap; 187 188 189 /* Get the underlying device vnode */ 190 devvp = vp->v_mount->mnt_devvp; 191 192 /* Figure out the underlying device block size */ 193 error = VNOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&blocksize, 0, vfs_context_kernel()); 194 if (error) { 195 goto trim_exit; 196 } 197 198 /* 199 * We may not get the entire range from offset -> offset+length in a single 200 * extent from the blockmap call. Keep looping/going until we are sure we've hit 201 * the whole range or if we encounter an error. 202 */ 203 while (trimmed < length) { 204 /* 205 * VNOP_BLOCKMAP will tell us the logical to physical block number mapping for the 206 * specified offset. It returns blocks in contiguous chunks, so if the logical range is 207 * broken into multiple extents, it must be called multiple times, increasing the offset 208 * in each call to ensure that the entire range is covered. 209 */ 210 error = VNOP_BLOCKMAP (vp, current_offset, remaining_length, 211 &io_blockno, &io_bytecount, NULL, VNODE_READ, NULL); 212 213 if (error) { 214 goto trim_exit; 215 } 216 /* 217 * We have a contiguous run. Prepare & issue the ioctl for the device. 218 * the DKIOCUNMAP ioctl takes offset in bytes from the start of the device. 219 */ 220 memset (&extent, 0, sizeof(dk_extent_t)); 221 memset (&unmap, 0, sizeof(dk_unmap_t)); 222 extent.offset = (uint64_t) io_blockno * (u_int64_t) blocksize; 223 extent.length = io_bytecount; 224 unmap.extents = &extent; 225 unmap.extentsCount = 1; 226 error = VNOP_IOCTL(devvp, DKIOCUNMAP, (caddr_t)&unmap, 0, vfs_context_kernel()); 227 228 if (error) { 229 goto trim_exit; 230 } 231 remaining_length = remaining_length - io_bytecount; 232 trimmed = trimmed + io_bytecount; 233 current_offset = current_offset + io_bytecount; 234 } 235trim_exit: 236 237 return error; 238 239} 240 241pager_return_t 242vnode_pageout(struct vnode *vp, 243 upl_t upl, 244 upl_offset_t upl_offset, 245 vm_object_offset_t f_offset, 246 upl_size_t size, 247 int flags, 248 int *errorp) 249{ 250 int result = PAGER_SUCCESS; 251 int error = 0; 252 int error_ret = 0; 253 daddr64_t blkno; 254 int isize; 255 int pg_index; 256 int base_index; 257 upl_offset_t offset; 258 upl_page_info_t *pl; 259 vfs_context_t ctx = vfs_context_current(); /* pager context */ 260 261 isize = (int)size; 262 263 if (isize <= 0) { 264 result = PAGER_ERROR; 265 error_ret = EINVAL; 266 goto out; 267 } 268 269 if (UBCINFOEXISTS(vp) == 0) { 270 result = PAGER_ERROR; 271 error_ret = EINVAL; 272 273 if (upl && !(flags & UPL_NOCOMMIT)) 274 ubc_upl_abort_range(upl, upl_offset, size, UPL_ABORT_FREE_ON_EMPTY); 275 goto out; 276 } 277 if ( !(flags & UPL_VNODE_PAGER)) { 278 /* 279 * This is a pageout from the default pager, 280 * just go ahead and call vnop_pageout since 281 * it has already sorted out the dirty ranges 282 */ 283 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, 284 (MACHDBG_CODE(DBG_MACH_VM, 1)) | DBG_FUNC_START, 285 size, 1, 0, 0, 0); 286 287 if ( (error_ret = VNOP_PAGEOUT(vp, upl, upl_offset, (off_t)f_offset, 288 (size_t)size, flags, ctx)) ) 289 result = PAGER_ERROR; 290 291 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, 292 (MACHDBG_CODE(DBG_MACH_VM, 1)) | DBG_FUNC_END, 293 size, 1, 0, 0, 0); 294 295 goto out; 296 } 297 if (upl == NULL) { 298 int request_flags; 299 300 if (vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSVNOP_PAGEOUTV2) { 301 /* 302 * filesystem has requested the new form of VNOP_PAGEOUT for file 303 * backed objects... we will not grab the UPL befofe calling VNOP_PAGEOUT... 304 * it is the fileystem's responsibility to grab the range we're denoting 305 * via 'f_offset' and 'size' into a UPL... this allows the filesystem to first 306 * take any locks it needs, before effectively locking the pages into a UPL... 307 */ 308 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, 309 (MACHDBG_CODE(DBG_MACH_VM, 1)) | DBG_FUNC_START, 310 size, (int)f_offset, 0, 0, 0); 311 312 if ( (error_ret = VNOP_PAGEOUT(vp, NULL, upl_offset, (off_t)f_offset, 313 size, flags, ctx)) ) { 314 result = PAGER_ERROR; 315 } 316 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, 317 (MACHDBG_CODE(DBG_MACH_VM, 1)) | DBG_FUNC_END, 318 size, 0, 0, 0, 0); 319 320 goto out; 321 } 322 if (flags & UPL_MSYNC) 323 request_flags = UPL_UBC_MSYNC | UPL_RET_ONLY_DIRTY; 324 else 325 request_flags = UPL_UBC_PAGEOUT | UPL_RET_ONLY_DIRTY; 326 327 if (ubc_create_upl(vp, f_offset, size, &upl, &pl, request_flags) != KERN_SUCCESS) { 328 result = PAGER_ERROR; 329 error_ret = EINVAL; 330 goto out; 331 } 332 upl_offset = 0; 333 } else 334 pl = ubc_upl_pageinfo(upl); 335 336 /* 337 * we come here for pageouts to 'real' files and 338 * for msyncs... the upl may not contain any 339 * dirty pages.. it's our responsibility to sort 340 * through it and find the 'runs' of dirty pages 341 * to call VNOP_PAGEOUT on... 342 */ 343 if (ubc_getsize(vp) == 0) { 344 /* 345 * if the file has been effectively deleted, then 346 * we need to go through the UPL and invalidate any 347 * buffer headers we might have that reference any 348 * of it's pages 349 */ 350 for (offset = upl_offset; isize; isize -= PAGE_SIZE, offset += PAGE_SIZE) { 351#if NFSCLIENT 352 if (vp->v_tag == VT_NFS) 353 /* check with nfs if page is OK to drop */ 354 error = nfs_buf_page_inval(vp, (off_t)f_offset); 355 else 356#endif 357 { 358 blkno = ubc_offtoblk(vp, (off_t)f_offset); 359 error = buf_invalblkno(vp, blkno, 0); 360 } 361 if (error) { 362 if ( !(flags & UPL_NOCOMMIT)) 363 ubc_upl_abort_range(upl, offset, PAGE_SIZE, UPL_ABORT_FREE_ON_EMPTY); 364 if (error_ret == 0) 365 error_ret = error; 366 result = PAGER_ERROR; 367 368 } else if ( !(flags & UPL_NOCOMMIT)) { 369 ubc_upl_commit_range(upl, offset, PAGE_SIZE, UPL_COMMIT_FREE_ON_EMPTY); 370 } 371 f_offset += PAGE_SIZE; 372 } 373 goto out; 374 } 375 /* 376 * Ignore any non-present pages at the end of the 377 * UPL so that we aren't looking at a upl that 378 * may already have been freed by the preceeding 379 * aborts/completions. 380 */ 381 base_index = upl_offset / PAGE_SIZE; 382 383 for (pg_index = (upl_offset + isize) / PAGE_SIZE; pg_index > base_index;) { 384 if (upl_page_present(pl, --pg_index)) 385 break; 386 if (pg_index == base_index) { 387 /* 388 * no pages were returned, so release 389 * our hold on the upl and leave 390 */ 391 if ( !(flags & UPL_NOCOMMIT)) 392 ubc_upl_abort_range(upl, upl_offset, isize, UPL_ABORT_FREE_ON_EMPTY); 393 394 goto out; 395 } 396 } 397 isize = ((pg_index + 1) - base_index) * PAGE_SIZE; 398 399 offset = upl_offset; 400 pg_index = base_index; 401 402 while (isize) { 403 int xsize; 404 int num_of_pages; 405 406 if ( !upl_page_present(pl, pg_index)) { 407 /* 408 * we asked for RET_ONLY_DIRTY, so it's possible 409 * to get back empty slots in the UPL 410 * just skip over them 411 */ 412 f_offset += PAGE_SIZE; 413 offset += PAGE_SIZE; 414 isize -= PAGE_SIZE; 415 pg_index++; 416 417 continue; 418 } 419 if ( !upl_dirty_page(pl, pg_index)) { 420 /* 421 * if the page is not dirty and reached here it is 422 * marked precious or it is due to invalidation in 423 * memory_object_lock request as part of truncation 424 * We also get here from vm_object_terminate() 425 * So all you need to do in these 426 * cases is to invalidate incore buffer if it is there 427 * Note we must not sleep here if the buffer is busy - that is 428 * a lock inversion which causes deadlock. 429 */ 430#if NFSCLIENT 431 if (vp->v_tag == VT_NFS) 432 /* check with nfs if page is OK to drop */ 433 error = nfs_buf_page_inval(vp, (off_t)f_offset); 434 else 435#endif 436 { 437 blkno = ubc_offtoblk(vp, (off_t)f_offset); 438 error = buf_invalblkno(vp, blkno, 0); 439 } 440 if (error) { 441 if ( !(flags & UPL_NOCOMMIT)) 442 ubc_upl_abort_range(upl, offset, PAGE_SIZE, UPL_ABORT_FREE_ON_EMPTY); 443 if (error_ret == 0) 444 error_ret = error; 445 result = PAGER_ERROR; 446 447 } else if ( !(flags & UPL_NOCOMMIT)) { 448 ubc_upl_commit_range(upl, offset, PAGE_SIZE, UPL_COMMIT_FREE_ON_EMPTY); 449 } 450 f_offset += PAGE_SIZE; 451 offset += PAGE_SIZE; 452 isize -= PAGE_SIZE; 453 pg_index++; 454 455 continue; 456 } 457 num_of_pages = 1; 458 xsize = isize - PAGE_SIZE; 459 460 while (xsize) { 461 if ( !upl_dirty_page(pl, pg_index + num_of_pages)) 462 break; 463 num_of_pages++; 464 xsize -= PAGE_SIZE; 465 } 466 xsize = num_of_pages * PAGE_SIZE; 467 468 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, 469 (MACHDBG_CODE(DBG_MACH_VM, 1)) | DBG_FUNC_START, 470 xsize, (int)f_offset, 0, 0, 0); 471 472 if ( (error = VNOP_PAGEOUT(vp, upl, offset, (off_t)f_offset, 473 xsize, flags, ctx)) ) { 474 if (error_ret == 0) 475 error_ret = error; 476 result = PAGER_ERROR; 477 } 478 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, 479 (MACHDBG_CODE(DBG_MACH_VM, 1)) | DBG_FUNC_END, 480 xsize, 0, 0, 0, 0); 481 482 f_offset += xsize; 483 offset += xsize; 484 isize -= xsize; 485 pg_index += num_of_pages; 486 } 487out: 488 if (errorp) 489 *errorp = error_ret; 490 491 return (result); 492} 493 494 495pager_return_t 496vnode_pagein( 497 struct vnode *vp, 498 upl_t upl, 499 upl_offset_t upl_offset, 500 vm_object_offset_t f_offset, 501 upl_size_t size, 502 int flags, 503 int *errorp) 504{ 505 upl_page_info_t *pl; 506 int result = PAGER_SUCCESS; 507 int error = 0; 508 int pages_in_upl; 509 int start_pg; 510 int last_pg; 511 int first_pg; 512 int xsize; 513 int must_commit = 1; 514 515 if (flags & UPL_NOCOMMIT) 516 must_commit = 0; 517 518 if (UBCINFOEXISTS(vp) == 0) { 519 result = PAGER_ERROR; 520 error = PAGER_ERROR; 521 522 if (upl && must_commit) 523 ubc_upl_abort_range(upl, upl_offset, size, UPL_ABORT_FREE_ON_EMPTY | UPL_ABORT_ERROR); 524 525 goto out; 526 } 527 if (upl == (upl_t)NULL) { 528 flags &= ~UPL_NOCOMMIT; 529 530 if (size > (MAX_UPL_SIZE * PAGE_SIZE)) { 531 result = PAGER_ERROR; 532 error = PAGER_ERROR; 533 goto out; 534 } 535 if (vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSVNOP_PAGEINV2) { 536 /* 537 * filesystem has requested the new form of VNOP_PAGEIN for file 538 * backed objects... we will not grab the UPL befofe calling VNOP_PAGEIN... 539 * it is the fileystem's responsibility to grab the range we're denoting 540 * via 'f_offset' and 'size' into a UPL... this allows the filesystem to first 541 * take any locks it needs, before effectively locking the pages into a UPL... 542 * so we pass a NULL into the filesystem instead of a UPL pointer... the 'upl_offset' 543 * is used to identify the "must have" page in the extent... the filesystem is free 544 * to clip the extent to better fit the underlying FS blocksize if it desires as 545 * long as it continues to include the "must have" page... 'f_offset' + 'upl_offset' 546 * identifies that page 547 */ 548 if ( (error = VNOP_PAGEIN(vp, NULL, upl_offset, (off_t)f_offset, 549 size, flags, vfs_context_current())) ) { 550 result = PAGER_ERROR; 551 error = PAGER_ERROR; 552 } 553 goto out; 554 } 555 ubc_create_upl(vp, f_offset, size, &upl, &pl, UPL_UBC_PAGEIN | UPL_RET_ONLY_ABSENT); 556 557 if (upl == (upl_t)NULL) { 558 result = PAGER_ABSENT; 559 error = PAGER_ABSENT; 560 goto out; 561 } 562 ubc_upl_range_needed(upl, upl_offset / PAGE_SIZE, 1); 563 564 upl_offset = 0; 565 first_pg = 0; 566 567 /* 568 * if we get here, we've created the upl and 569 * are responsible for commiting/aborting it 570 * regardless of what the caller has passed in 571 */ 572 must_commit = 1; 573 } else { 574 pl = ubc_upl_pageinfo(upl); 575 first_pg = upl_offset / PAGE_SIZE; 576 } 577 pages_in_upl = size / PAGE_SIZE; 578 DTRACE_VM2(pgpgin, int, pages_in_upl, (uint64_t *), NULL); 579 580 /* 581 * before we start marching forward, we must make sure we end on 582 * a present page, otherwise we will be working with a freed 583 * upl 584 */ 585 for (last_pg = pages_in_upl - 1; last_pg >= first_pg; last_pg--) { 586 if (upl_page_present(pl, last_pg)) 587 break; 588 if (last_pg == first_pg) { 589 /* 590 * empty UPL, no pages are present 591 */ 592 if (must_commit) 593 ubc_upl_abort_range(upl, upl_offset, size, UPL_ABORT_FREE_ON_EMPTY); 594 goto out; 595 } 596 } 597 pages_in_upl = last_pg + 1; 598 last_pg = first_pg; 599 600 while (last_pg < pages_in_upl) { 601 /* 602 * skip over missing pages... 603 */ 604 for ( ; last_pg < pages_in_upl; last_pg++) { 605 if (upl_page_present(pl, last_pg)) 606 break; 607 } 608 /* 609 * skip over 'valid' pages... we don't want to issue I/O for these 610 */ 611 for (start_pg = last_pg; last_pg < pages_in_upl; last_pg++) { 612 if (!upl_valid_page(pl, last_pg)) 613 break; 614 } 615 if (last_pg > start_pg) { 616 /* 617 * we've found a range of valid pages 618 * if we've got COMMIT responsibility 619 * commit this range of pages back to the 620 * cache unchanged 621 */ 622 xsize = (last_pg - start_pg) * PAGE_SIZE; 623 624 if (must_commit) 625 ubc_upl_abort_range(upl, start_pg * PAGE_SIZE, xsize, UPL_ABORT_FREE_ON_EMPTY); 626 } 627 if (last_pg == pages_in_upl) 628 /* 629 * we're done... all pages that were present 630 * have either had I/O issued on them or 631 * were aborted unchanged... 632 */ 633 break; 634 635 if (!upl_page_present(pl, last_pg)) { 636 /* 637 * we found a range of valid pages 638 * terminated by a missing page... 639 * bump index to the next page and continue on 640 */ 641 last_pg++; 642 continue; 643 } 644 /* 645 * scan from the found invalid page looking for a valid 646 * or non-present page before the end of the upl is reached, if we 647 * find one, then it will be the last page of the request to 648 * 'cluster_io' 649 */ 650 for (start_pg = last_pg; last_pg < pages_in_upl; last_pg++) { 651 if (upl_valid_page(pl, last_pg) || !upl_page_present(pl, last_pg)) 652 break; 653 } 654 if (last_pg > start_pg) { 655 int xoff; 656 xsize = (last_pg - start_pg) * PAGE_SIZE; 657 xoff = start_pg * PAGE_SIZE; 658 659 if ( (error = VNOP_PAGEIN(vp, upl, (upl_offset_t) xoff, 660 (off_t)f_offset + xoff, 661 xsize, flags, vfs_context_current())) ) { 662 /* 663 * Usually this UPL will be aborted/committed by the lower cluster layer. 664 * 665 * a) In the case of decmpfs, however, we may return an error (EAGAIN) to avoid 666 * a deadlock with another thread already inflating the file. 667 * 668 * b) In the case of content protection, EPERM is a valid error and we should respect it. 669 * 670 * In those cases, we must take care of our UPL at this layer itself. 671 */ 672 if (must_commit) { 673 if(error == EAGAIN) { 674 ubc_upl_abort_range(upl, (upl_offset_t) xoff, xsize, UPL_ABORT_FREE_ON_EMPTY | UPL_ABORT_RESTART); 675 } 676#if CONFIG_PROTECT 677 if(error == EPERM) { 678 ubc_upl_abort_range(upl, (upl_offset_t) xoff, xsize, UPL_ABORT_FREE_ON_EMPTY | UPL_ABORT_ERROR); 679 } 680#endif 681 } 682 result = PAGER_ERROR; 683 error = PAGER_ERROR; 684 685 } 686 } 687 } 688out: 689 if (errorp) 690 *errorp = result; 691 692 return (error); 693} 694 695void 696vnode_pager_shutdown(void) 697{ 698 int i; 699 vnode_t vp; 700 701 for(i = 0; i < MAX_BACKING_STORE; i++) { 702 vp = (vnode_t)(bs_port_table[i]).vp; 703 if (vp) { 704 (bs_port_table[i]).vp = 0; 705 706 /* get rid of macx_swapon() reference */ 707 vnode_rele(vp); 708 } 709 } 710} 711 712 713void * 714upl_get_internal_page_list(upl_t upl) 715{ 716 return(UPL_GET_INTERNAL_PAGE_LIST(upl)); 717 718} 719