]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/vm/vm_swapfile_pager.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / osfmk / vm / vm_swapfile_pager.c
diff --git a/osfmk/vm/vm_swapfile_pager.c b/osfmk/vm/vm_swapfile_pager.c
new file mode 100644 (file)
index 0000000..db89433
--- /dev/null
@@ -0,0 +1,876 @@
+/*
+ * Copyright (c) 2008 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ * 
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <mach/kern_return.h>
+#include <mach/memory_object_control.h>
+#include <mach/upl.h>
+
+#include <kern/ipc_kobject.h>
+#include <kern/kalloc.h>
+#include <kern/queue.h>
+
+#include <vm/vm_kern.h>
+#include <vm/vm_map.h>
+#include <vm/vm_pageout.h>
+#include <vm/vm_protos.h>
+
+
+/* 
+ * APPLE SWAPFILE MEMORY PAGER 
+ *
+ * This external memory manager (EMM) handles mappings of the swap files.
+ * Swap files are not regular files and are used solely to store contents of
+ * anonymous memory mappings while not resident in memory.
+ * There's no valid reason to map a swap file.  This just puts extra burden
+ * on the system, is potentially a security issue and is not reliable since
+ * the contents can change at any time with pageout operations.
+ * Here are some of the issues with mapping a swap file.
+ * * PERFORMANCE:
+ *   Each page in the swap file belong to an anonymous memory object. Mapping
+ *   the swap file makes those pages also accessible via a vnode memory
+ *   object and each page can now be resident twice.
+ * * SECURITY:
+ *   Mapping a swap file allows access to other processes' memory.  Swap files
+ *   are only accessible by the "root" super-user, who can already access any
+ *   process's memory, so this is not a real issue but if permissions on the
+ *   swap file got changed, it could become one.
+ *   Swap files are not "zero-filled" on creation, so until their contents are
+ *   overwritten with pageout operations, they still contain whatever was on
+ *   the disk blocks they were allocated.  The "super-user" could see the
+ *   contents of free blocks anyway, so this is not a new security issue but
+ *   it may be perceive as one.
+ * * ENCRYPTED SWAP:
+ *   When swap is encrypted, one does not expect to find any clear contents
+ *   in the swap files.  Since unused blocks are not scrubbed, they could still
+ *   contain clear contents.  If these contents are visible through a mapping
+ *   of the swap file, it makes it look like swap is not really encrypted.
+ *   
+ * We can't legitimately prevent a user process with appropriate privileges
+ * from mapping a swap file, but we can prevent it from accessing its actual
+ * contents.
+ * This pager mostly handles page-in request (from memory_object_data_request())
+ * for swap file mappings and just returns bogus data.
+ * Pageouts are not handled, so mmap() has to make sure it does not allow
+ * writable (i.e. MAP_SHARED and PROT_WRITE) mappings of swap files.
+ */
+
+/* forward declarations */
+void swapfile_pager_reference(memory_object_t mem_obj);
+void swapfile_pager_deallocate(memory_object_t mem_obj);
+kern_return_t swapfile_pager_init(memory_object_t mem_obj,
+                                      memory_object_control_t control,
+                                      memory_object_cluster_size_t pg_size);
+kern_return_t swapfile_pager_terminate(memory_object_t mem_obj);
+kern_return_t swapfile_pager_data_request(memory_object_t mem_obj,
+                                              memory_object_offset_t offset,
+                                              memory_object_cluster_size_t length,
+                                              vm_prot_t protection_required,
+                                              memory_object_fault_info_t fault_info);
+kern_return_t swapfile_pager_data_return(memory_object_t mem_obj,
+                                             memory_object_offset_t offset,
+                                             memory_object_cluster_size_t      data_cnt,
+                                             memory_object_offset_t *resid_offset,
+                                             int *io_error,
+                                             boolean_t dirty,
+                                             boolean_t kernel_copy,
+                                             int upl_flags);
+kern_return_t swapfile_pager_data_initialize(memory_object_t mem_obj,
+                                                 memory_object_offset_t offset,
+                                                 memory_object_cluster_size_t data_cnt);
+kern_return_t swapfile_pager_data_unlock(memory_object_t mem_obj,
+                                             memory_object_offset_t offset,
+                                             memory_object_size_t size,
+                                             vm_prot_t desired_access);
+kern_return_t swapfile_pager_synchronize(memory_object_t mem_obj,
+                                             memory_object_offset_t offset,
+                                             memory_object_size_t length,
+                                             vm_sync_t sync_flags);
+kern_return_t swapfile_pager_map(memory_object_t mem_obj,
+                                     vm_prot_t prot);
+kern_return_t swapfile_pager_last_unmap(memory_object_t mem_obj);
+
+/*
+ * Vector of VM operations for this EMM.
+ * These routines are invoked by VM via the memory_object_*() interfaces.
+ */
+const struct memory_object_pager_ops swapfile_pager_ops = {
+       swapfile_pager_reference,
+       swapfile_pager_deallocate,
+       swapfile_pager_init,
+       swapfile_pager_terminate,
+       swapfile_pager_data_request,
+       swapfile_pager_data_return,
+       swapfile_pager_data_initialize,
+       swapfile_pager_data_unlock,
+       swapfile_pager_synchronize,
+       swapfile_pager_map,
+       swapfile_pager_last_unmap,
+       "swapfile pager"
+};
+
+/*
+ * The "swapfile_pager" describes a memory object backed by
+ * the "swapfile" EMM.
+ */
+typedef struct swapfile_pager {
+       struct ipc_object_header pager_header;  /* fake ip_kotype() */
+       memory_object_pager_ops_t pager_ops;    /* == &swapfile_pager_ops */
+       queue_chain_t           pager_queue;    /* next & prev pagers */
+       unsigned int            ref_count;      /* reference count */
+       boolean_t               is_ready;       /* is this pager ready ? */
+       boolean_t               is_mapped;      /* is this pager mapped ? */
+       memory_object_control_t pager_control;  /* mem object control handle */
+       struct vnode            *swapfile_vnode;/* the swapfile's vnode */
+} *swapfile_pager_t;
+#define        SWAPFILE_PAGER_NULL     ((swapfile_pager_t) NULL)
+#define pager_ikot pager_header.io_bits
+
+/*
+ * List of memory objects managed by this EMM.
+ * The list is protected by the "swapfile_pager_lock" lock.
+ */
+int swapfile_pager_count = 0;          /* number of pagers */
+queue_head_t swapfile_pager_queue;
+decl_lck_mtx_data(,swapfile_pager_lock)
+
+/*
+ * Statistics & counters.
+ */
+int swapfile_pager_count_max = 0;
+
+
+lck_grp_t              swapfile_pager_lck_grp;
+lck_grp_attr_t         swapfile_pager_lck_grp_attr;
+lck_attr_t             swapfile_pager_lck_attr;
+
+
+/* internal prototypes */
+swapfile_pager_t swapfile_pager_create(struct vnode *vp);
+swapfile_pager_t swapfile_pager_lookup(memory_object_t mem_obj);
+void swapfile_pager_dequeue(swapfile_pager_t pager);
+void swapfile_pager_deallocate_internal(swapfile_pager_t pager,
+                                       boolean_t locked);
+void swapfile_pager_terminate_internal(swapfile_pager_t pager);
+
+
+#if DEBUG
+int swapfile_pagerdebug = 0;
+#define PAGER_ALL              0xffffffff
+#define        PAGER_INIT              0x00000001
+#define        PAGER_PAGEIN            0x00000002
+
+#define PAGER_DEBUG(LEVEL, A)                                          \
+       MACRO_BEGIN                                                     \
+       if ((swapfile_pagerdebug & LEVEL)==LEVEL) {             \
+               printf A;                                               \
+       }                                                               \
+       MACRO_END
+#else
+#define PAGER_DEBUG(LEVEL, A)
+#endif
+
+
+void
+swapfile_pager_bootstrap(void)
+{
+       lck_grp_attr_setdefault(&swapfile_pager_lck_grp_attr);
+       lck_grp_init(&swapfile_pager_lck_grp, "swapfile pager", &swapfile_pager_lck_grp_attr);
+       lck_attr_setdefault(&swapfile_pager_lck_attr);
+       lck_mtx_init(&swapfile_pager_lock, &swapfile_pager_lck_grp, &swapfile_pager_lck_attr);
+       queue_init(&swapfile_pager_queue);
+}
+
+/*
+ * swapfile_pager_init()
+ *
+ * Initialize the memory object and makes it ready to be used and mapped.
+ */
+kern_return_t
+swapfile_pager_init(
+       memory_object_t         mem_obj, 
+       memory_object_control_t control, 
+#if !DEBUG
+       __unused
+#endif
+       memory_object_cluster_size_t pg_size)
+{
+       swapfile_pager_t        pager;
+       kern_return_t           kr;
+       memory_object_attr_info_data_t  attributes;
+
+       PAGER_DEBUG(PAGER_ALL,
+                   ("swapfile_pager_init: %p, %p, %x\n",
+                    mem_obj, control, pg_size));
+
+       if (control == MEMORY_OBJECT_CONTROL_NULL)
+               return KERN_INVALID_ARGUMENT;
+
+       pager = swapfile_pager_lookup(mem_obj);
+
+       memory_object_control_reference(control);
+
+       pager->pager_control = control;
+
+       attributes.copy_strategy = MEMORY_OBJECT_COPY_DELAY;
+       attributes.cluster_size = (1 << (PAGE_SHIFT));
+       attributes.may_cache_object = FALSE;
+       attributes.temporary = TRUE;
+
+       kr = memory_object_change_attributes(
+                                       control,
+                                       MEMORY_OBJECT_ATTRIBUTE_INFO,
+                                       (memory_object_info_t) &attributes,
+                                       MEMORY_OBJECT_ATTR_INFO_COUNT);
+       if (kr != KERN_SUCCESS)
+               panic("swapfile_pager_init: "
+                     "memory_object_change_attributes() failed");
+
+       return KERN_SUCCESS;
+}
+
+/*
+ * swapfile_data_return()
+ *
+ * Handles page-out requests from VM.  This should never happen since
+ * the pages provided by this EMM are not supposed to be dirty or dirtied
+ * and VM should simply discard the contents and reclaim the pages if it
+ * needs to.
+ */
+kern_return_t
+swapfile_pager_data_return(
+        __unused memory_object_t       mem_obj,
+        __unused memory_object_offset_t        offset,
+        __unused memory_object_cluster_size_t          data_cnt,
+        __unused memory_object_offset_t        *resid_offset,
+       __unused int                    *io_error,
+       __unused boolean_t              dirty,
+       __unused boolean_t              kernel_copy,
+       __unused int                    upl_flags)  
+{
+       panic("swapfile_pager_data_return: should never get called");
+       return KERN_FAILURE;
+}
+
+kern_return_t
+swapfile_pager_data_initialize(
+       __unused memory_object_t        mem_obj,
+       __unused memory_object_offset_t offset,
+       __unused memory_object_cluster_size_t           data_cnt)
+{
+       panic("swapfile_pager_data_initialize: should never get called");
+       return KERN_FAILURE;
+}
+
+kern_return_t
+swapfile_pager_data_unlock(
+       __unused memory_object_t        mem_obj,
+       __unused memory_object_offset_t offset,
+       __unused memory_object_size_t           size,
+       __unused vm_prot_t              desired_access)
+{
+       return KERN_FAILURE;
+}
+
+/*
+ * swapfile_pager_data_request()
+ *
+ * Handles page-in requests from VM.
+ */
+kern_return_t  
+swapfile_pager_data_request(
+       memory_object_t         mem_obj,
+       memory_object_offset_t  offset,
+       memory_object_cluster_size_t            length,
+#if !DEBUG
+       __unused
+#endif
+       vm_prot_t               protection_required,
+       __unused memory_object_fault_info_t mo_fault_info)
+{
+       swapfile_pager_t        pager;
+       memory_object_control_t mo_control;
+       upl_t                   upl;
+       int                     upl_flags;
+       upl_size_t              upl_size;
+       upl_page_info_t         *upl_pl = NULL;
+       unsigned int            pl_count;
+       vm_object_t             dst_object;
+       kern_return_t           kr, retval;
+       vm_map_offset_t         kernel_mapping;
+       vm_offset_t             dst_vaddr;
+       char                    *dst_ptr;
+       vm_offset_t             cur_offset;
+       vm_map_entry_t          map_entry;
+
+       PAGER_DEBUG(PAGER_ALL, ("swapfile_pager_data_request: %p, %llx, %x, %x\n", mem_obj, offset, length, protection_required));
+
+       kernel_mapping = 0;
+       upl = NULL;
+       upl_pl = NULL;
+
+       pager = swapfile_pager_lookup(mem_obj);
+       assert(pager->is_ready);
+       assert(pager->ref_count > 1); /* pager is alive and mapped */
+
+       PAGER_DEBUG(PAGER_PAGEIN, ("swapfile_pager_data_request: %p, %llx, %x, %x, pager %p\n", mem_obj, offset, length, protection_required, pager));
+
+       /*
+        * Gather in a UPL all the VM pages requested by VM.
+        */
+       mo_control = pager->pager_control;
+
+       upl_size = length;
+       upl_flags =
+               UPL_RET_ONLY_ABSENT |
+               UPL_SET_LITE |
+               UPL_NO_SYNC |
+               UPL_CLEAN_IN_PLACE |    /* triggers UPL_CLEAR_DIRTY */
+               UPL_SET_INTERNAL;
+       pl_count = 0;
+       kr = memory_object_upl_request(mo_control,
+                                      offset, upl_size,
+                                      &upl, NULL, NULL, upl_flags);
+       if (kr != KERN_SUCCESS) {
+               retval = kr;
+               goto done;
+       }
+       dst_object = mo_control->moc_object;
+       assert(dst_object != VM_OBJECT_NULL);
+
+
+       /*
+        * Reserve a virtual page in the kernel address space to map each
+        * destination physical page when it's its turn to be processed.
+        */
+       vm_object_reference(kernel_object);     /* ref. for mapping */
+       kr = vm_map_find_space(kernel_map,
+                              &kernel_mapping,
+                              PAGE_SIZE_64,
+                              0,
+                              0,
+                              &map_entry);
+       if (kr != KERN_SUCCESS) {
+               vm_object_deallocate(kernel_object);
+               retval = kr;
+               goto done;
+       }
+       map_entry->object.vm_object = kernel_object;
+       map_entry->offset = kernel_mapping - VM_MIN_KERNEL_ADDRESS;
+       vm_map_unlock(kernel_map);
+       dst_vaddr = CAST_DOWN(vm_offset_t, kernel_mapping);
+       dst_ptr = (char *) dst_vaddr;
+
+       /*
+        * Fill in the contents of the pages requested by VM.
+        */
+       upl_pl = UPL_GET_INTERNAL_PAGE_LIST(upl);
+       pl_count = length / PAGE_SIZE;
+       for (cur_offset = 0; cur_offset < length; cur_offset += PAGE_SIZE) {
+               ppnum_t dst_pnum;
+
+               if (!upl_page_present(upl_pl, (int)(cur_offset / PAGE_SIZE))) {
+                       /* this page is not in the UPL: skip it */
+                       continue;
+               }
+
+               /*
+                * Establish an explicit pmap mapping of the destination
+                * physical page.
+                * We can't do a regular VM mapping because the VM page
+                * is "busy".
+                */
+               dst_pnum = (ppnum_t)
+                       upl_phys_page(upl_pl, (int)(cur_offset / PAGE_SIZE));
+               assert(dst_pnum != 0);
+               pmap_enter(kernel_pmap,
+                          kernel_mapping,
+                          dst_pnum,
+                          VM_PROT_READ | VM_PROT_WRITE,
+                          dst_object->wimg_bits & VM_WIMG_MASK,
+                          TRUE);
+
+               memset(dst_ptr, '\0', PAGE_SIZE);
+               /* add an end-of-line to keep line counters happy */
+               dst_ptr[PAGE_SIZE-1] = '\n';
+               
+               /*
+                * Remove the pmap mapping of the destination page
+                * in the kernel.
+                */
+               pmap_remove(kernel_pmap,
+                           (addr64_t) kernel_mapping,
+                           (addr64_t) (kernel_mapping + PAGE_SIZE_64));
+
+       }
+
+       retval = KERN_SUCCESS;
+done:
+       if (upl != NULL) {
+               /* clean up the UPL */
+
+               /*
+                * The pages are currently dirty because we've just been
+                * writing on them, but as far as we're concerned, they're
+                * clean since they contain their "original" contents as
+                * provided by us, the pager.
+                * Tell the UPL to mark them "clean".
+                */
+               upl_clear_dirty(upl, TRUE);
+
+               /* abort or commit the UPL */
+               if (retval != KERN_SUCCESS) {
+                       upl_abort(upl, 0);
+               } else {
+                       boolean_t empty;
+                       upl_commit_range(upl, 0, upl->size, 
+                                        UPL_COMMIT_CS_VALIDATED,
+                                        upl_pl, pl_count, &empty);
+               }
+
+               /* and deallocate the UPL */
+               upl_deallocate(upl);
+               upl = NULL;
+       }
+       if (kernel_mapping != 0) {
+               /* clean up the mapping of the source and destination pages */
+               kr = vm_map_remove(kernel_map,
+                                  kernel_mapping,
+                                  kernel_mapping + PAGE_SIZE_64,
+                                  VM_MAP_NO_FLAGS);
+               assert(kr == KERN_SUCCESS);
+               kernel_mapping = 0;
+               dst_vaddr = 0;
+       }
+
+       return retval;
+}
+
+/*
+ * swapfile_pager_reference()
+ *
+ * Get a reference on this memory object.
+ * For external usage only.  Assumes that the initial reference count is not 0,
+ * i.e one should not "revive" a dead pager this way.
+ */
+void
+swapfile_pager_reference(
+       memory_object_t         mem_obj)
+{      
+       swapfile_pager_t        pager;
+
+       pager = swapfile_pager_lookup(mem_obj);
+
+       lck_mtx_lock(&swapfile_pager_lock);
+       assert(pager->ref_count > 0);
+       pager->ref_count++;
+       lck_mtx_unlock(&swapfile_pager_lock);
+}
+
+
+/*
+ * swapfile_pager_dequeue:
+ *
+ * Removes a pager from the list of pagers.
+ *
+ * The caller must hold "swapfile_pager_lock".
+ */
+void
+swapfile_pager_dequeue(
+       swapfile_pager_t pager)
+{
+       assert(!pager->is_mapped);
+
+       queue_remove(&swapfile_pager_queue,
+                    pager,
+                    swapfile_pager_t,
+                    pager_queue);
+       pager->pager_queue.next = NULL;
+       pager->pager_queue.prev = NULL;
+       
+       swapfile_pager_count--;
+}
+
+/*
+ * swapfile_pager_terminate_internal:
+ *
+ * Trigger the asynchronous termination of the memory object associated
+ * with this pager.
+ * When the memory object is terminated, there will be one more call
+ * to memory_object_deallocate() (i.e. swapfile_pager_deallocate())
+ * to finish the clean up.
+ *
+ * "swapfile_pager_lock" should not be held by the caller.
+ * We don't need the lock because the pager has already been removed from
+ * the pagers' list and is now ours exclusively.
+ */
+void
+swapfile_pager_terminate_internal(
+       swapfile_pager_t pager)
+{
+       assert(pager->is_ready);
+       assert(!pager->is_mapped);
+
+       if (pager->swapfile_vnode != NULL) {
+               pager->swapfile_vnode = NULL;
+       }
+
+       /* trigger the destruction of the memory object */
+       memory_object_destroy(pager->pager_control, 0);
+}
+
+/*
+ * swapfile_pager_deallocate_internal()
+ *
+ * Release a reference on this pager and free it when the last
+ * reference goes away.
+ * Can be called with swapfile_pager_lock held or not but always returns
+ * with it unlocked.
+ */
+void
+swapfile_pager_deallocate_internal(
+       swapfile_pager_t        pager,
+       boolean_t               locked)
+{
+       if (! locked) {
+               lck_mtx_lock(&swapfile_pager_lock);
+       }
+
+       /* drop a reference on this pager */
+       pager->ref_count--;
+
+       if (pager->ref_count == 1) {
+               /*
+                * Only the "named" reference is left, which means that
+                * no one is really holding on to this pager anymore.
+                * Terminate it.
+                */
+               swapfile_pager_dequeue(pager);
+               /* the pager is all ours: no need for the lock now */
+               lck_mtx_unlock(&swapfile_pager_lock);
+               swapfile_pager_terminate_internal(pager);
+       } else if (pager->ref_count == 0) {
+               /*
+                * Dropped the existence reference;  the memory object has
+                * been terminated.  Do some final cleanup and release the
+                * pager structure.
+                */
+               lck_mtx_unlock(&swapfile_pager_lock);
+               if (pager->pager_control != MEMORY_OBJECT_CONTROL_NULL) {
+                       memory_object_control_deallocate(pager->pager_control);
+                       pager->pager_control = MEMORY_OBJECT_CONTROL_NULL;
+               }
+               kfree(pager, sizeof (*pager));
+               pager = SWAPFILE_PAGER_NULL;
+       } else {
+               /* there are still plenty of references:  keep going... */
+               lck_mtx_unlock(&swapfile_pager_lock);
+       }
+
+       /* caution: lock is not held on return... */
+}
+
+/*
+ * swapfile_pager_deallocate()
+ *
+ * Release a reference on this pager and free it when the last
+ * reference goes away.
+ */
+void
+swapfile_pager_deallocate(
+       memory_object_t         mem_obj)
+{
+       swapfile_pager_t        pager;
+
+       PAGER_DEBUG(PAGER_ALL, ("swapfile_pager_deallocate: %p\n", mem_obj));
+       pager = swapfile_pager_lookup(mem_obj);
+       swapfile_pager_deallocate_internal(pager, FALSE);
+}
+
+/*
+ *
+ */
+kern_return_t
+swapfile_pager_terminate(
+#if !DEBUG
+       __unused
+#endif
+       memory_object_t mem_obj)
+{
+       PAGER_DEBUG(PAGER_ALL, ("swapfile_pager_terminate: %p\n", mem_obj));
+
+       return KERN_SUCCESS;
+}
+
+/*
+ *
+ */
+kern_return_t
+swapfile_pager_synchronize(
+       memory_object_t         mem_obj,
+       memory_object_offset_t  offset,
+       memory_object_size_t            length,
+       __unused vm_sync_t              sync_flags)
+{
+       swapfile_pager_t        pager;
+
+       PAGER_DEBUG(PAGER_ALL, ("swapfile_pager_synchronize: %p\n", mem_obj));
+
+       pager = swapfile_pager_lookup(mem_obj);
+
+       memory_object_synchronize_completed(pager->pager_control,
+                                           offset, length);
+
+       return KERN_SUCCESS;
+}
+
+/*
+ * swapfile_pager_map()
+ *
+ * This allows VM to let us, the EMM, know that this memory object
+ * is currently mapped one or more times.  This is called by VM each time
+ * the memory object gets mapped and we take one extra reference on the
+ * memory object to account for all its mappings.
+ */
+kern_return_t
+swapfile_pager_map(
+       memory_object_t         mem_obj,
+       __unused vm_prot_t      prot)
+{
+       swapfile_pager_t        pager;
+
+       PAGER_DEBUG(PAGER_ALL, ("swapfile_pager_map: %p\n", mem_obj));
+
+       pager = swapfile_pager_lookup(mem_obj);
+
+       lck_mtx_lock(&swapfile_pager_lock);
+       assert(pager->is_ready);
+       assert(pager->ref_count > 0); /* pager is alive */
+       if (pager->is_mapped == FALSE) {
+               /*
+                * First mapping of this pager:  take an extra reference
+                * that will remain until all the mappings of this pager
+                * are removed.
+                */
+               pager->is_mapped = TRUE;
+               pager->ref_count++;
+       }
+       lck_mtx_unlock(&swapfile_pager_lock);
+
+       return KERN_SUCCESS;
+}
+
+/*
+ * swapfile_pager_last_unmap()
+ *
+ * This is called by VM when this memory object is no longer mapped anywhere.
+ */
+kern_return_t
+swapfile_pager_last_unmap(
+       memory_object_t         mem_obj)
+{
+       swapfile_pager_t        pager;
+
+       PAGER_DEBUG(PAGER_ALL,
+                   ("swapfile_pager_last_unmap: %p\n", mem_obj));
+
+       pager = swapfile_pager_lookup(mem_obj);
+
+       lck_mtx_lock(&swapfile_pager_lock);
+       if (pager->is_mapped) {
+               /*
+                * All the mappings are gone, so let go of the one extra
+                * reference that represents all the mappings of this pager.
+                */
+               pager->is_mapped = FALSE;
+               swapfile_pager_deallocate_internal(pager, TRUE);
+               /* caution: deallocate_internal() released the lock ! */
+       } else {
+               lck_mtx_unlock(&swapfile_pager_lock);
+       }
+       
+       return KERN_SUCCESS;
+}
+
+
+/*
+ *
+ */
+swapfile_pager_t
+swapfile_pager_lookup(
+       memory_object_t  mem_obj)
+{
+       swapfile_pager_t        pager;
+
+       pager = (swapfile_pager_t) mem_obj;
+       assert(pager->pager_ops == &swapfile_pager_ops);
+       assert(pager->ref_count > 0);
+       return pager;
+}
+
+swapfile_pager_t
+swapfile_pager_create(
+       struct vnode            *vp)
+{
+       swapfile_pager_t        pager, pager2;
+       memory_object_control_t control;
+       kern_return_t           kr;
+
+       pager = (swapfile_pager_t) kalloc(sizeof (*pager));
+       if (pager == SWAPFILE_PAGER_NULL) {
+               return SWAPFILE_PAGER_NULL;
+       }
+
+       /*
+        * The vm_map call takes both named entry ports and raw memory
+        * objects in the same parameter.  We need to make sure that
+        * vm_map does not see this object as a named entry port.  So,
+        * we reserve the second word in the object for a fake ip_kotype
+        * setting - that will tell vm_map to use it as a memory object.
+        */
+       pager->pager_ops = &swapfile_pager_ops;
+       pager->pager_ikot = IKOT_MEMORY_OBJECT;
+       pager->is_ready = FALSE;/* not ready until it has a "name" */
+       pager->ref_count = 1;   /* setup reference */
+       pager->is_mapped = FALSE;
+       pager->pager_control = MEMORY_OBJECT_CONTROL_NULL;
+       pager->swapfile_vnode = vp;
+       
+       lck_mtx_lock(&swapfile_pager_lock);
+       /* see if anyone raced us to create a pager for the same object */
+       queue_iterate(&swapfile_pager_queue,
+                     pager2,
+                     swapfile_pager_t,
+                     pager_queue) {
+               if (pager2->swapfile_vnode == vp) {
+                       break;
+               }
+       }
+       if (! queue_end(&swapfile_pager_queue,
+                       (queue_entry_t) pager2)) {
+               /* while we hold the lock, transfer our setup ref to winner */
+               pager2->ref_count++;
+               /* we lost the race, down with the loser... */
+               lck_mtx_unlock(&swapfile_pager_lock);
+               pager->swapfile_vnode = NULL;
+               kfree(pager, sizeof (*pager));
+               /* ... and go with the winner */
+               pager = pager2;
+               /* let the winner make sure the pager gets ready */
+               return pager;
+       }
+
+       /* enter new pager at the head of our list of pagers */
+       queue_enter_first(&swapfile_pager_queue,
+                         pager,
+                         swapfile_pager_t,
+                         pager_queue);
+       swapfile_pager_count++;
+       if (swapfile_pager_count > swapfile_pager_count_max) {
+               swapfile_pager_count_max = swapfile_pager_count;
+       }
+       lck_mtx_unlock(&swapfile_pager_lock);
+
+       kr = memory_object_create_named((memory_object_t) pager,
+                                       0,
+                                       &control);
+       assert(kr == KERN_SUCCESS);
+
+       lck_mtx_lock(&swapfile_pager_lock);
+       /* the new pager is now ready to be used */
+       pager->is_ready = TRUE;
+       lck_mtx_unlock(&swapfile_pager_lock);
+
+       /* wakeup anyone waiting for this pager to be ready */
+       thread_wakeup(&pager->is_ready);
+
+       return pager;
+}
+
+/*
+ * swapfile_pager_setup()
+ *
+ * Provide the caller with a memory object backed by the provided
+ * "backing_object" VM object.  If such a memory object already exists,
+ * re-use it, otherwise create a new memory object.
+ */
+memory_object_t
+swapfile_pager_setup(
+       struct vnode *vp)
+{
+       swapfile_pager_t        pager;
+
+       lck_mtx_lock(&swapfile_pager_lock);
+
+       queue_iterate(&swapfile_pager_queue,
+                     pager,
+                     swapfile_pager_t,
+                     pager_queue) {
+               if (pager->swapfile_vnode == vp) {
+                       break;
+               }
+       }
+       if (queue_end(&swapfile_pager_queue,
+                     (queue_entry_t) pager)) {
+               /* no existing pager for this backing object */
+               pager = SWAPFILE_PAGER_NULL;
+       } else {
+               /* make sure pager doesn't disappear */
+               pager->ref_count++;
+       }
+
+       lck_mtx_unlock(&swapfile_pager_lock);
+
+       if (pager == SWAPFILE_PAGER_NULL) {
+               pager = swapfile_pager_create(vp);
+               if (pager == SWAPFILE_PAGER_NULL) {
+                       return MEMORY_OBJECT_NULL;
+               }
+       }
+
+       lck_mtx_lock(&swapfile_pager_lock);
+       while (!pager->is_ready) {
+               lck_mtx_sleep(&swapfile_pager_lock,
+                       LCK_SLEEP_DEFAULT,
+                       &pager->is_ready,
+                       THREAD_UNINT);
+       }
+       lck_mtx_unlock(&swapfile_pager_lock);
+
+       return (memory_object_t) pager;
+}      
+
+memory_object_control_t
+swapfile_pager_control(
+       memory_object_t mem_obj)
+{
+       swapfile_pager_t        pager;
+
+       pager = swapfile_pager_lookup(mem_obj);
+
+       return pager->pager_control;
+}