aboutsummaryrefslogtreecommitdiff
path: root/sys/vm/device_pager.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/vm/device_pager.c')
-rw-r--r--sys/vm/device_pager.c287
1 files changed, 195 insertions, 92 deletions
diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c
index a057e4e8726b..01ce7305d5a1 100644
--- a/sys/vm/device_pager.c
+++ b/sys/vm/device_pager.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 1990 University of Utah.
- * Copyright (c) 1991 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
@@ -35,30 +35,29 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * from: @(#)device_pager.c 7.2 (Berkeley) 4/20/91
- * $Id: device_pager.c,v 1.4 1993/10/16 16:20:10 rgrimes Exp $
+ * @(#)device_pager.c 8.1 (Berkeley) 6/11/93
*/
/*
* Page to/from special files.
*/
-#include "devpager.h"
-#if NDEVPAGER > 0
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/mman.h>
+#include <sys/malloc.h>
-#include "param.h"
-#include "conf.h"
-#include "mman.h"
-#include "malloc.h"
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/device_pager.h>
-#include "vm.h"
-#include "vm_page.h"
-#include "vm_kern.h"
-#include "device_pager.h"
#include "vnode.h"
#include "specdev.h"
-queue_head_t dev_pager_list; /* list of managed devices */
+queue_head_t dev_pager_list; /* list of managed devices */
+queue_head_t dev_pager_fakelist; /* list of available vm_page_t's */
#ifdef DEBUG
int dpagerdebug = 0;
@@ -68,7 +67,29 @@ int dpagerdebug = 0;
#define DDB_FAIL 0x08
#endif
-void
+static vm_pager_t dev_pager_alloc
+ __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t));
+static void dev_pager_dealloc __P((vm_pager_t));
+static int dev_pager_getpage
+ __P((vm_pager_t, vm_page_t, boolean_t));
+static boolean_t dev_pager_haspage __P((vm_pager_t, vm_offset_t));
+static void dev_pager_init __P((void));
+static int dev_pager_putpage
+ __P((vm_pager_t, vm_page_t, boolean_t));
+static vm_page_t dev_pager_getfake __P((vm_offset_t));
+static void dev_pager_putfake __P((vm_page_t));
+
+struct pagerops devicepagerops = {
+ dev_pager_init,
+ dev_pager_alloc,
+ dev_pager_dealloc,
+ dev_pager_getpage,
+ 0,
+ dev_pager_putpage,
+ dev_pager_haspage
+};
+
+static void
dev_pager_init()
{
#ifdef DEBUG
@@ -76,59 +97,68 @@ dev_pager_init()
printf("dev_pager_init()\n");
#endif
queue_init(&dev_pager_list);
+ queue_init(&dev_pager_fakelist);
}
-vm_pager_t
-dev_pager_alloc(handle, size, prot)
+static vm_pager_t
+dev_pager_alloc(handle, size, prot, foff)
caddr_t handle;
vm_size_t size;
vm_prot_t prot;
+ vm_offset_t foff;
{
dev_t dev;
vm_pager_t pager;
- int (*mapfunc)(), nprot;
- register vm_object_t object;
- register vm_page_t page;
- register dev_pager_t devp;
- register int npages, off;
- extern int nullop(), enodev();
-
+ int (*mapfunc)();
+ vm_object_t object;
+ dev_pager_t devp;
+ unsigned int npages, off;
#ifdef DEBUG
if (dpagerdebug & DDB_FOLLOW)
- printf("dev_pager_alloc(%x, %x, %x)\n", handle, size, prot);
+ printf("dev_pager_alloc(%x, %x, %x, %x)\n",
+ handle, size, prot, foff);
#endif
+#ifdef DIAGNOSTIC
/*
* Pageout to device, should never happen.
*/
if (handle == NULL)
panic("dev_pager_alloc called");
+#endif
+
+ /*
+ * Make sure this device can be mapped.
+ */
+ dev = (dev_t)(u_long)handle;
+ mapfunc = cdevsw[major(dev)].d_mmap;
+ if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
+ return(NULL);
+
+ /*
+ * Offset should be page aligned.
+ */
+ if (foff & (PAGE_SIZE-1))
+ return(NULL);
+
+ /*
+ * Check that the specified range of the device allows the
+ * desired protection.
+ *
+ * XXX assumes VM_PROT_* == PROT_*
+ */
+ npages = atop(round_page(size));
+ for (off = foff; npages--; off += PAGE_SIZE)
+ if ((*mapfunc)(dev, off, (int)prot) == -1)
+ return(NULL);
/*
- * Look it up, creating as necessary
+ * Look up pager, creating as necessary.
*/
+top:
pager = vm_pager_lookup(&dev_pager_list, handle);
if (pager == NULL) {
/*
- * Validation. Make sure this device can be mapped
- * and that range to map is acceptible to device.
- */
- dev = ((struct vnode *) handle)->v_rdev;
- mapfunc = cdevsw[major(dev)].d_mmap;
- if (!mapfunc || mapfunc == enodev || mapfunc == nullop)
- return(NULL);
- nprot = 0;
- if (prot & VM_PROT_READ)
- nprot |= PROT_READ;
- if (prot & VM_PROT_WRITE)
- nprot |= PROT_WRITE;
- if (prot & VM_PROT_EXECUTE)
- nprot |= PROT_EXEC;
- npages = atop(round_page(size));
- for (off = 0; npages--; off += PAGE_SIZE)
- if ((*mapfunc)(dev, off, nprot) == -1)
- return(NULL);
- /*
* Allocate and initialize pager structs
*/
pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK);
@@ -139,102 +169,141 @@ dev_pager_alloc(handle, size, prot)
free((caddr_t)pager, M_VMPAGER);
return(NULL);
}
- devp->devp_dev = dev;
- devp->devp_npages = atop(round_page(size));
pager->pg_handle = handle;
pager->pg_ops = &devicepagerops;
pager->pg_type = PG_DEVICE;
pager->pg_data = (caddr_t)devp;
+ queue_init(&devp->devp_pglist);
/*
- * Allocate object and vm_page structures to describe memory
+ * Allocate object and associate it with the pager.
*/
- npages = devp->devp_npages;
- object = devp->devp_object = vm_object_allocate(ptoa(npages));
+ object = devp->devp_object = vm_object_allocate(0);
vm_object_enter(object, pager);
- vm_object_setpager(object, pager, (vm_offset_t)0, FALSE);
- devp->devp_pages = (vm_page_t)
- kmem_alloc(kernel_map, npages*sizeof(struct vm_page));
- off = 0;
- for (page = devp->devp_pages;
- page < &devp->devp_pages[npages]; page++) {
- vm_object_lock(object);
- vm_page_init(page, object, off);
- page->phys_addr =
- pmap_phys_address((*mapfunc)(dev, off, nprot));
- page->wire_count = 1;
- page->fictitious = TRUE;
- PAGE_WAKEUP(page);
- vm_object_unlock(object);
- off += PAGE_SIZE;
- }
+ vm_object_setpager(object, pager, (vm_offset_t)foff, FALSE);
/*
* Finally, put it on the managed list so other can find it.
+ * First we re-lookup in case someone else beat us to this
+ * point (due to blocking in the various mallocs). If so,
+ * we free everything and start over.
*/
+ if (vm_pager_lookup(&dev_pager_list, handle)) {
+ free((caddr_t)devp, M_VMPGDATA);
+ free((caddr_t)pager, M_VMPAGER);
+ goto top;
+ }
queue_enter(&dev_pager_list, pager, vm_pager_t, pg_list);
#ifdef DEBUG
- if (dpagerdebug & DDB_ALLOC)
- printf("dev_pager_alloc: pages %d@%x\n",
- devp->devp_npages, devp->devp_pages);
+ if (dpagerdebug & DDB_ALLOC) {
+ printf("dev_pager_alloc: pager %x devp %x object %x\n",
+ pager, devp, object);
+ vm_object_print(object, FALSE);
+ }
#endif
} else {
/*
* vm_object_lookup() gains a reference and also
* removes the object from the cache.
*/
+ object = vm_object_lookup(pager);
+#ifdef DIAGNOSTIC
devp = (dev_pager_t)pager->pg_data;
- if (vm_object_lookup(pager) != devp->devp_object)
+ if (object != devp->devp_object)
panic("dev_pager_setup: bad object");
- }
-#ifdef DEBUG
- if (dpagerdebug & DDB_ALLOC) {
- printf("dev_pager_alloc: pager %x devp %x object %x\n",
- pager, devp, object);
- vm_object_print(object, FALSE);
- }
#endif
+ }
return(pager);
-
}
-void
+static void
dev_pager_dealloc(pager)
vm_pager_t pager;
{
- dev_pager_t devp = (dev_pager_t)pager->pg_data;
- register vm_object_t object;
+ dev_pager_t devp;
+ vm_object_t object;
+ vm_page_t m;
#ifdef DEBUG
if (dpagerdebug & DDB_FOLLOW)
printf("dev_pager_dealloc(%x)\n", pager);
#endif
queue_remove(&dev_pager_list, pager, vm_pager_t, pg_list);
+ /*
+ * Get the object.
+ * Note: cannot use vm_object_lookup since object has already
+ * been removed from the hash chain.
+ */
+ devp = (dev_pager_t)pager->pg_data;
object = devp->devp_object;
#ifdef DEBUG
if (dpagerdebug & DDB_ALLOC)
- printf("dev_pager_dealloc: devp %x object %x pages %d@%x\n",
- devp, object, devp->devp_npages, devp->devp_pages);
+ printf("dev_pager_dealloc: devp %x object %x\n", devp, object);
#endif
- while (!queue_empty(&object->memq))
- vm_page_remove((vm_page_t)queue_first(&object->memq));
- kmem_free(kernel_map, devp->devp_pages,
- devp->devp_npages * sizeof(struct vm_page));
+ /*
+ * Free up our fake pages.
+ */
+ while (!queue_empty(&devp->devp_pglist)) {
+ queue_remove_first(&devp->devp_pglist, m, vm_page_t, pageq);
+ dev_pager_putfake(m);
+ }
free((caddr_t)devp, M_VMPGDATA);
free((caddr_t)pager, M_VMPAGER);
- pager->pg_data = 0;
}
+static int
dev_pager_getpage(pager, m, sync)
vm_pager_t pager;
vm_page_t m;
boolean_t sync;
{
+ register vm_object_t object;
+ vm_offset_t offset, paddr;
+ vm_page_t page;
+ dev_t dev;
+ int s;
+ int (*mapfunc)(), prot;
+
#ifdef DEBUG
if (dpagerdebug & DDB_FOLLOW)
printf("dev_pager_getpage(%x, %x)\n", pager, m);
#endif
- return(VM_PAGER_BAD);
+
+ object = m->object;
+ dev = (dev_t)(u_long)pager->pg_handle;
+ offset = m->offset + object->paging_offset;
+ prot = PROT_READ; /* XXX should pass in? */
+ mapfunc = cdevsw[major(dev)].d_mmap;
+
+ if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
+ panic("dev_pager_getpage: no map function");
+
+ paddr = pmap_phys_address((*mapfunc)((dev_t)dev, (int)offset, prot));
+#ifdef DIAGNOSTIC
+ if (paddr == -1)
+ panic("dev_pager_getpage: map function returns error");
+#endif
+ /*
+ * Replace the passed in page with our own fake page and free
+ * up the original.
+ */
+ page = dev_pager_getfake(paddr);
+ queue_enter(&((dev_pager_t)pager->pg_data)->devp_pglist,
+ page, vm_page_t, pageq);
+ vm_object_lock(object);
+ vm_page_lock_queues();
+ vm_page_free(m);
+ vm_page_unlock_queues();
+ s = splhigh();
+ vm_page_insert(page, object, offset);
+ splx(s);
+ PAGE_WAKEUP(m);
+ if (offset + PAGE_SIZE > object->size)
+ object->size = offset + PAGE_SIZE; /* XXX anal */
+ vm_object_unlock(object);
+
+ return(VM_PAGER_OK);
}
+static int
dev_pager_putpage(pager, m, sync)
vm_pager_t pager;
vm_page_t m;
@@ -245,11 +314,11 @@ dev_pager_putpage(pager, m, sync)
printf("dev_pager_putpage(%x, %x)\n", pager, m);
#endif
if (pager == NULL)
- return;
+ return 0;
panic("dev_pager_putpage called");
}
-boolean_t
+static boolean_t
dev_pager_haspage(pager, offset)
vm_pager_t pager;
vm_offset_t offset;
@@ -260,4 +329,38 @@ dev_pager_haspage(pager, offset)
#endif
return(TRUE);
}
+
+static vm_page_t
+dev_pager_getfake(paddr)
+ vm_offset_t paddr;
+{
+ vm_page_t m;
+ int i;
+
+ if (queue_empty(&dev_pager_fakelist)) {
+ m = (vm_page_t)malloc(PAGE_SIZE, M_VMPGDATA, M_WAITOK);
+ for (i = PAGE_SIZE / sizeof(*m); i > 0; i--) {
+ queue_enter(&dev_pager_fakelist, m, vm_page_t, pageq);
+ m++;
+ }
+ }
+ queue_remove_first(&dev_pager_fakelist, m, vm_page_t, pageq);
+
+ m->flags = PG_BUSY | PG_CLEAN | PG_FAKE | PG_FICTITIOUS;
+
+ m->wire_count = 1;
+ m->phys_addr = paddr;
+
+ return(m);
+}
+
+static void
+dev_pager_putfake(m)
+ vm_page_t m;
+{
+#ifdef DIAGNOSTIC
+ if (!(m->flags & PG_FICTITIOUS))
+ panic("dev_pager_putfake: bad page");
#endif
+ queue_enter(&dev_pager_fakelist, m, vm_page_t, pageq);
+}