diff options
Diffstat (limited to 'storeio/pager.c')
-rw-r--r-- | storeio/pager.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/storeio/pager.c b/storeio/pager.c new file mode 100644 index 00000000..e723f602 --- /dev/null +++ b/storeio/pager.c @@ -0,0 +1,264 @@ +/* Paging interface for storeio devices + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <hurd/pager.h> +#include <assert.h> +#include <strings.h> +#include <unistd.h> + +#include "dev.h" + +/* ---------------------------------------------------------------- */ +/* Pager library callbacks; see <hurd/pager.h> for more info. */ + +/* For pager PAGER, read one page from offset PAGE. Set *BUF to be the + address of the page, and set *WRITE_LOCK if the page must be provided + read-only. The only permissable error returns are EIO, EDQUOT, and + ENOSPC. */ +error_t +pager_read_page (struct user_pager_info *upi, + vm_offset_t page, vm_address_t *buf, int *writelock) +{ + error_t err; + int read; /* bytes actually read */ + int want = vm_page_size; /* bytes we want to read */ + struct dev *dev = (struct dev *)upi; + struct store *store = dev->store; + + if (page + want > store->size) + /* Read a partial page if necessary to avoid reading off the end. */ + want = store->size - page; + + err = dev_read (dev, page, want, (char **)buf, &read); + + if (!err && want < vm_page_size) + /* Zero anything we didn't read. Allocation only happens in page-size + multiples, so we know we can write there. */ + bzero ((char *)*buf + want, vm_page_size - want); + + *writelock = (store->flags & STORE_READONLY); + + if (err || read < want) + return EIO; + else + return 0; +} + +/* For pager PAGER, synchronously write one page from BUF to offset PAGE. In + addition, vm_deallocate (or equivalent) BUF. The only permissable error + returns are EIO, EDQUOT, and ENOSPC. */ +error_t +pager_write_page (struct user_pager_info *upi, + vm_offset_t page, vm_address_t buf) +{ + struct dev *dev = (struct dev *)upi; + struct store *store = dev->store; + + if (store->flags & STORE_READONLY) + return EROFS; + else + { + error_t err; + int written; + int want = vm_page_size; + + if (page + want > store->size) + /* Write a partial page if necessary to avoid reading off the end. */ + want = store->size - page; + + err = dev_write (dev, page, (char *)buf, want, &written); + + vm_deallocate (mach_task_self (), buf, vm_page_size); + + if (err || written < want) + return EIO; + else + return 0; + } +} + +/* A page should be made writable. */ +error_t +pager_unlock_page (struct user_pager_info *upi, vm_offset_t address) +{ + struct dev *dev = (struct dev *)upi; + + if (dev->store->flags & STORE_READONLY) + return EROFS; + else + return 0; +} + +/* The user must define this function. It should report back (in + *OFFSET and *SIZE the minimum valid address the pager will accept + and the size of the object. */ +error_t +pager_report_extent (struct user_pager_info *upi, + vm_address_t *offset, vm_size_t *size) +{ + *offset = 0; + *size = ((struct dev *)upi)->store->size; + return 0; +} + +/* This is called when a pager is being deallocated after all extant send + rights have been destroyed. */ +void +pager_clear_user_data (struct user_pager_info *upi) +{ +} + +static struct port_bucket *pager_port_bucket = 0; + +/* A top-level function for the paging thread that just services paging + requests. */ +static void +service_paging_requests (any_t arg) +{ + for (;;) + ports_manage_port_operations_multithread (pager_port_bucket, + pager_demuxer, + 1000 * 30, 1000 * 60 * 5, + 1, MACH_PORT_NULL); +} + +/* Initialize paging for this device. */ +static void +init_dev_paging () +{ + if (! pager_port_bucket) + { + static struct mutex pager_global_lock = MUTEX_INITIALIZER; + + mutex_lock (&pager_global_lock); + if (pager_port_bucket == NULL) + { + pager_port_bucket = ports_create_bucket (); + + /* Make a thread to service paging requests. */ + cthread_detach (cthread_fork ((cthread_fn_t)service_paging_requests, + (any_t)0)); + } + mutex_unlock (&pager_global_lock); + } +} + +void +pager_dropweak (struct user_pager_info *upi __attribute__ ((unused))) +{ +} + +/* Try to stop all paging activity on DEV, returning true if we were + successful. If NOSYNC is true, then we won't write back any (kernel) + cached pages to the device. */ +int +dev_stop_paging (struct dev *dev, int nosync) +{ + int success = 1; /* Initially assume success. */ + + mutex_lock (&dev->pager_lock); + + if (dev->pager != NULL) + { + int num_pagers = ports_count_bucket (pager_port_bucket); + if (num_pagers > 0 && !nosync) + { + error_t block_cache (void *arg) + { + struct pager *p = arg; + pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1); + return 0; + } + error_t enable_cache (void *arg) + { + struct pager *p = arg; + pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0); + return 0; + } + + /* Loop through the pagers and turn off caching one by one, + synchronously. That should cause termination of each pager. */ + ports_bucket_iterate (pager_port_bucket, block_cache); + + /* Give it a second; the kernel doesn't actually shutdown + immediately. XXX */ + sleep (1); + + num_pagers = ports_count_bucket (pager_port_bucket); + if (num_pagers > 0) + /* Darn, there are actual honest users. Turn caching back on, + and return failure. */ + { + ports_bucket_iterate (pager_port_bucket, enable_cache); + success = 0; + } + } + + if (success && !nosync) + /* shutdown the pager on DEV. If NOSYNC is set, we don't bother, for + fear that this may result in I/O. In this case we've disabled + rpcs on the pager's ports, so this will result in hanging... What + do we do??? XXXX */ + pager_shutdown (dev->pager); + } + + if (success) + dev->pager = NULL; + + mutex_unlock (&dev->pager_lock); + + return success; +} + +/* Returns in MEMOBJ the port for a memory object backed by the storage on + DEV. Returns 0 or the error code if an error occurred. */ +error_t +dev_get_memory_object (struct dev *dev, memory_object_t *memobj) +{ + error_t err = 0; + + init_dev_paging (); + + mutex_lock (&dev->pager_lock); + + if (dev->pager == NULL) + dev->pager = + pager_create ((struct user_pager_info *)dev, pager_port_bucket, + 1, MEMORY_OBJECT_COPY_DELAY); + else + ports_port_ref (dev->pager); + + if (dev->pager == NULL) + err = ENODEV; /* XXX ??? */ + else + { + *memobj = pager_get_port (dev->pager); + if (*memobj != MACH_PORT_NULL) + err = + mach_port_insert_right (mach_task_self (), + *memobj, *memobj, MACH_MSG_TYPE_MAKE_SEND); + ports_port_deref (dev->pager); /* Drop our original ref on PAGER. */ + } + + mutex_unlock (&dev->pager_lock); + + return err; +} |