Uncomment some code that John previously commented out. It turns out to
[unix-history] / sys / vm / device_pager.c
index 404bf96..01ce730 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 1990 University of Utah.
 /*
  * 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
  *
  * This code is derived from software contributed to Berkeley by
  * the Systems Programming Group of the University of Utah Computer
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     @(#)device_pager.c      7.2 (Berkeley) 4/20/91
+ *     @(#)device_pager.c      8.1 (Berkeley) 6/11/93
  */
 
 /*
  * Page to/from special files.
  */
 
  */
 
 /*
  * 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;
 
 #ifdef DEBUG
 int    dpagerdebug = 0;
@@ -65,7 +67,29 @@ int  dpagerdebug = 0;
 #define DDB_FAIL       0x08
 #endif
 
 #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
 dev_pager_init()
 {
 #ifdef DEBUG
@@ -73,58 +97,67 @@ dev_pager_init()
                printf("dev_pager_init()\n");
 #endif
        queue_init(&dev_pager_list);
                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;
        caddr_t handle;
        vm_size_t size;
        vm_prot_t prot;
+       vm_offset_t foff;
 {
        dev_t dev;
        vm_pager_t pager;
 {
        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)
 
 #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
 #endif
+#ifdef DIAGNOSTIC
        /*
         * Pageout to device, should never happen.
         */
        if (handle == NULL)
                panic("dev_pager_alloc called");
        /*
         * 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) {
        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 = (dev_t)handle;
-               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
                 */
                /*
                 * Allocate and initialize pager structs
                 */
@@ -136,102 +169,141 @@ dev_pager_alloc(handle, size, prot)
                        free((caddr_t)pager, M_VMPAGER);
                        return(NULL);
                }
                        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;
                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_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.
                /*
                 * 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
                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.
                 */
 #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;
                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");
                        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
 #endif
+       }
        return(pager);
        return(pager);
-
 }
 
 }
 
-void
+static void
 dev_pager_dealloc(pager)
        vm_pager_t pager;
 {
 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);
 
 #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)
        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
 #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);
        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;
 {
 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
 #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;
 dev_pager_putpage(pager, m, sync)
        vm_pager_t pager;
        vm_page_t m;
@@ -242,11 +314,11 @@ dev_pager_putpage(pager, m, sync)
                printf("dev_pager_putpage(%x, %x)\n", pager, m);
 #endif
        if (pager == NULL)
                printf("dev_pager_putpage(%x, %x)\n", pager, m);
 #endif
        if (pager == NULL)
-               return;
+               return 0;
        panic("dev_pager_putpage called");
 }
 
        panic("dev_pager_putpage called");
 }
 
-boolean_t
+static boolean_t
 dev_pager_haspage(pager, offset)
        vm_pager_t pager;
        vm_offset_t offset;
 dev_pager_haspage(pager, offset)
        vm_pager_t pager;
        vm_offset_t offset;
@@ -257,4 +329,38 @@ dev_pager_haspage(pager, offset)
 #endif
        return(TRUE);
 }
 #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
 #endif
+       queue_enter(&dev_pager_fakelist, m, vm_page_t, pageq);
+}