/* External pager. Copyright 1999-2002 Johan Rydberg, jrydberg@rtmk.org. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "tm.h" #include "vm-page.h" #include "vm-pager.h" #include "vm-object.h" #include "vm-map.h" #include "ipc-port.h" #include "thread.h" /* memory-pager-init: establish new mapping of PAGER */ extern void memory_pager_init (struct ipc_port *pager, struct ipc_port *pager_req); /* memory-pager-data-request: request data from PAGER */ extern void memory_pager_data_request (struct ipc_port *pager, struct ipc_port *pager_req, vm_offset_t offset, vm_size_t size, vm_prot_t access); /* memory-pager-data-request: request data from PAGER */ extern void memory_pager_write_data (struct ipc_port *pager, struct ipc_port *pager_req, vm_offset_t offset, vm_offset_t data_ptr, vm_size_t size, int dirty_p, int kernel_copy_p); /* Someone is requesting page at OFFSET in object COOKIE, from our PAGER. */ static kern_return_t extern_pager_request (void *cookie, struct vm_object *object, vm_offset_t offset, vm_prot_t desired_access, struct vm_page *page) { struct ipc_port *pager = cookie; memory_pager_data_request (pager, object->pager_request, offset, VM_PAGE_SIZE, desired_access); return KERN_SUCCESS; } /* Initialize new object OBJECT. Pager is provided through COOKIE. */ kern_return_t extern_pager_init_object (void *cookie, struct vm_object *object) { struct ipc_port *pager = cookie; memory_pager_init (pager, object->pager_request); return KERN_SUCCESS; } kern_return_t extern_pager_write_data (void *cookie, struct vm_object *old_object, vm_offset_t offset, struct vm_object *object, vm_size_t size, int dirty_p, int kernel_copy_p) { struct ipc_port *pager = cookie; struct vm_map_copy *copy; copy = vm_map_copyin_object (object, 0, size); if (! copy) return KERN_RESOURCE_SHORTAGE; memory_pager_write_data (pager, old_object->pager_request, offset, (vm_offset_t) copy, size, dirty_p, kernel_copy_p); return KERN_SUCCESS; } /* Pager structure for the external pager. */ struct vm_pager pager_extern = { extern_pager_request, extern_pager_init_object, extern_pager_write_data }; /* Supply the kernel with data for OBJECT. OFFSET is the offset into the object. ADDRESS points to the data in current tasks address space. SIZE is the amount of data that user supplys. If DEALLOC_P is true, the region [ADDRESS, ADDRESS+SIZE) is deallocated. LOCK_VALUE specifes locking of pages. If PRECIOUS_P is true, the page is flushed back to the pager before destroyed. */ void memory_object_data_supply (struct ipc_port *pager_request, vm_offset_t offset, vm_offset_t address, vm_size_t size, int dealloc_p, vm_prot_t lock_value, int precious_p) { struct vm_map_pagelist *pagelist; struct vm_object *object; struct vm_page *m, *data_m; kern_return_t kr; int i, absent_p = 0; if (! pager_request || ipc_port_kobject_type (pager_request) != IPC_KOBJECT_TYPE_PAGING_REQUEST) return; size = vm_round_page (size); kr = vm_map_copyin_pagelist (TASK_CURRENT()->map, address, size, dealloc_p, & pagelist); if (kr) return; object = (struct vm_object *) pager_request->kobject.port; offset = offset - object->pager_offset; vm_object_lock (object); for (i = 0; i < pagelist->n_pages; i++, offset += VM_PAGE_SIZE) { data_m = pagelist->pages [i]; /* Look up target page and check its state. */ retry_lookup: m = vm_page_lookup (object, offset); if (UNLIKELY (m)) { if (m->absent_p && m->busy_p) { /* Page was requested. Free the busy page waiting for it. Insertion of new page happens below. */ vm_page_free (m); absent_p = 1; } else { /* Have to wait for page that is busy and not absent. This is probably going to be an error, but go back and check. */ if (m->busy_p) { vm_object_unlock (object); VM_PAGE_WAIT (m); vm_object_lock (object); goto retry_lookup; } #if 0 /* Page already present; error. This is an error if data is precious. */ result = KERN_MEMORY_PRESENT; error_offset = offset + object->paging_offset; break; #endif } } else absent_p = 1; /* Ok to pagein page. Target object now has no page at offset. Set the page parameters, then drop in new page and set up pageout state. Object is still locked here. Must clear busy bit in page before inserting it. Ok to skip wakeup logic because nobody else can possibly know about this page. */ data_m->busy_p = 0; data_m->dirty_p = 0; data_m->precious_p = precious_p; data_m->prot_lock = lock_value; /* pmap_clear_modify(data_m->phys_addr); */ #if 0 data_m->unlock_request = VM_PROT_NONE; #endif vm_page_insert (data_m, object, offset); if (absent_p) vm_page_activate (data_m); else vm_page_deactivate (data_m); pagelist->pages [i] = 0; } vm_object_unlock (object); /* ??? free resources here */ } /* Make region [OFFSET, OFFSET+SIZE) of memory object PAGER_REQUEST unavailable. Unavailable pages will be zero-filled. */ void memory_object_data_unavail (struct ipc_port *pager_request, vm_offset_t offset, vm_size_t size) { struct vm_object *object; if (! pager_request || ipc_port_kobject_type (pager_request) != IPC_KOBJECT_TYPE_PAGING_REQUEST) return; object = (struct vm_object *) pager_request->kobject.port; offset = offset - object->pager_offset; vm_object_lock (object); while (size > 1) { struct vm_page *m; /* We're looking for pages that are both busy and absent (waiting to be filled), converting them to just absent. Pages that are just busy can be ignored entirely. */ m = vm_page_lookup (object, offset); if (m && m->busy_p && m->absent_p) { VM_PAGE_WAKEUP_DONE(m); /* vm_page_activate(m); */ } size -= VM_PAGE_SIZE; offset += VM_PAGE_SIZE; } vm_object_unlock (object); } /* Common function for setting attributes for OBJECT. */ static kern_return_t set_attributes_common (struct vm_object *object, int ready_p, int cache_p, int copy_strategy) { int wakeup_p = 0; if (object == 0) return KERN_INVALID_ARGUMENT; /* Verify the attributes of importance. */ switch(copy_strategy) { case MEMORY_OBJECT_COPY_NONE: case MEMORY_OBJECT_COPY_CALL: case MEMORY_OBJECT_COPY_DELAY: case MEMORY_OBJECT_COPY_TEMPORARY: break; default: return KERN_INVALID_ARGUMENT; } if (ready_p) ready_p = 1; if (cache_p) cache_p = 1; vm_object_lock(object); /* Wake up anyone waiting for the ready attribute to become asserted. */ if (ready_p && !object->pager_ready_p) wakeup_p = 1; /* Copy the attributes. */ object->can_persist_p = cache_p; object->pager_ready_p = ready_p; if (copy_strategy == MEMORY_OBJECT_COPY_TEMPORARY) object->temporary_p = 1; else object->copy_strategy = copy_strategy; vm_object_unlock(object); if (wakeup_p) vm_object_wakeup(object, VM_OBJECT_EVENT_PAGER_READY); return KERN_SUCCESS; } /* Set attributes for PAGER_REQUEST. Announce object as ready if READY_P is true. If CACHE_P is true, the object can persist. COPY_STRATEGY specifices copy strategy for object. */ kern_return_t memory_object_set_attributes (struct ipc_port *pager_request, int ready_p, int cache_p, int copy_strategy) { struct vm_object *object; if (! pager_request || ipc_port_kobject_type (pager_request) != IPC_KOBJECT_TYPE_PAGING_REQUEST) return KERN_INVALID_ARGUMENT; object = (struct vm_object *) pager_request->kobject.port; return set_attributes_common (object, ready_p, cache_p, copy_strategy); } /* Change attributes for PAGER_REQUEST. If CACHE_P is true, the object can persist. COPY_STRATEGY specifies copy strategy for object. ??? should we send reply? */ kern_return_t memory_object_change_attributes (struct ipc_port *pager_request, int cache_p, int copy_strategy) { struct vm_object *object; if (! pager_request || ipc_port_kobject_type (pager_request) != IPC_KOBJECT_TYPE_PAGING_REQUEST) return KERN_INVALID_ARGUMENT; object = (struct vm_object *) pager_request->kobject.port; return set_attributes_common (object, 1, cache_p, copy_strategy); }