/* Paging interface for storeio devices Copyright (C) 1995,96,97,99,2002 Free Software Foundation, Inc. Written by Miles Bader 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 #include #include #include #include #include #include #include #include #include "dev.h" /* ---------------------------------------------------------------- */ /* Pager library callbacks; see 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 permissible 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; size_t read = 0; /* 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, (void **)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. */ memset ((char *)*buf + want, '\0', 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 permissible 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; size_t 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); munmap ((caddr_t) 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; } void pager_notify_evict (struct user_pager_info *pager, vm_offset_t page) { assert (!"unrequested notification on eviction"); } /* 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) { struct dev *dev = (struct dev *)upi; pthread_mutex_lock (&dev->pager_lock); dev->pager = 0; pthread_mutex_unlock (&dev->pager_lock); } static struct port_bucket *pager_port_bucket = 0; static struct pager_requests *pager_requests; /* Initialize paging for this device. */ static void init_dev_paging () { if (! pager_port_bucket) { static pthread_mutex_t pager_global_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock (&pager_global_lock); if (pager_port_bucket == NULL) { error_t err; pager_port_bucket = ports_create_bucket (); /* Start libpagers worker threads. */ err = pager_start_workers (pager_port_bucket, &pager_requests); if (err) { errno = err; error (0, err, "pager_start_workers"); } } pthread_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) { size_t num_pagers = (pager_port_bucket ? ports_count_bucket (pager_port_bucket) : 0); 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); } return num_pagers == 0; } /* 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, vm_prot_t prot, memory_object_t *memobj) { error_t err = store_map (dev->store, prot, memobj); if (err == EOPNOTSUPP && !dev->inhibit_cache) { int created = 0; init_dev_paging (); pthread_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, 0); if (dev->pager == NULL) { pthread_mutex_unlock (&dev->pager_lock); return errno; } created = 1; } *memobj = pager_get_port (dev->pager); if (*memobj == MACH_PORT_NULL) /* Pager is currently being destroyed, try again. */ { dev->pager = 0; pthread_mutex_unlock (&dev->pager_lock); return dev_get_memory_object (dev, prot, memobj); } else err = mach_port_insert_right (mach_task_self (), *memobj, *memobj, MACH_MSG_TYPE_MAKE_SEND); if (created) ports_port_deref (dev->pager); pthread_mutex_unlock (&dev->pager_lock); } return err; }