device_pager.c revision 9507
11541Srgrimes/*
21541Srgrimes * Copyright (c) 1990 University of Utah.
31541Srgrimes * Copyright (c) 1991, 1993
41541Srgrimes *	The Regents of the University of California.  All rights reserved.
51541Srgrimes *
61541Srgrimes * This code is derived from software contributed to Berkeley by
71541Srgrimes * the Systems Programming Group of the University of Utah Computer
81541Srgrimes * Science Department.
91541Srgrimes *
101541Srgrimes * Redistribution and use in source and binary forms, with or without
111541Srgrimes * modification, are permitted provided that the following conditions
121541Srgrimes * are met:
131541Srgrimes * 1. Redistributions of source code must retain the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer.
151541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161541Srgrimes *    notice, this list of conditions and the following disclaimer in the
171541Srgrimes *    documentation and/or other materials provided with the distribution.
181541Srgrimes * 3. All advertising materials mentioning features or use of this software
191541Srgrimes *    must display the following acknowledgement:
201541Srgrimes *	This product includes software developed by the University of
211541Srgrimes *	California, Berkeley and its contributors.
221541Srgrimes * 4. Neither the name of the University nor the names of its contributors
231541Srgrimes *    may be used to endorse or promote products derived from this software
241541Srgrimes *    without specific prior written permission.
251541Srgrimes *
261541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
271541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
281541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
291541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
301541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
311541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
321541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
331541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
341541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
351541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
361541Srgrimes * SUCH DAMAGE.
371541Srgrimes *
381549Srgrimes *	@(#)device_pager.c	8.1 (Berkeley) 6/11/93
399507Sdg * $Id: device_pager.c,v 1.11 1995/05/30 08:15:46 rgrimes Exp $
401541Srgrimes */
411541Srgrimes
421541Srgrimes#include <sys/param.h>
431541Srgrimes#include <sys/systm.h>
441541Srgrimes#include <sys/conf.h>
451541Srgrimes#include <sys/mman.h>
461541Srgrimes#include <sys/malloc.h>
473311Sphk#include <sys/proc.h>
481541Srgrimes
491541Srgrimes#include <vm/vm.h>
501541Srgrimes#include <vm/vm_kern.h>
511541Srgrimes#include <vm/vm_page.h>
529507Sdg#include <vm/vm_pager.h>
531541Srgrimes#include <vm/device_pager.h>
541541Srgrimes
559507Sdgstruct pagerlst dev_pager_object_list;	/* list of device pager objects */
569507SdgTAILQ_HEAD(, vm_page) dev_pager_fakelist;	/* list of available vm_page_t's */
571541Srgrimes
585455Sdgstatic vm_page_t dev_pager_getfake __P((vm_offset_t));
595455Sdgstatic void dev_pager_putfake __P((vm_page_t));
601541Srgrimes
619507Sdgstatic int dev_pager_alloc_lock, dev_pager_alloc_lock_want;
629507Sdg
631541Srgrimesstruct pagerops devicepagerops = {
641541Srgrimes	dev_pager_init,
651541Srgrimes	dev_pager_alloc,
661541Srgrimes	dev_pager_dealloc,
679507Sdg	dev_pager_getpages,
689507Sdg	dev_pager_putpages,
699507Sdg	dev_pager_haspage,
709507Sdg	NULL
711541Srgrimes};
721541Srgrimes
739507Sdgvoid
741541Srgrimesdev_pager_init()
751541Srgrimes{
769507Sdg	TAILQ_INIT(&dev_pager_object_list);
771541Srgrimes	TAILQ_INIT(&dev_pager_fakelist);
781541Srgrimes}
791541Srgrimes
809507Sdgvm_object_t
811541Srgrimesdev_pager_alloc(handle, size, prot, foff)
828416Sdg	void *handle;
831541Srgrimes	vm_size_t size;
841541Srgrimes	vm_prot_t prot;
851541Srgrimes	vm_offset_t foff;
861541Srgrimes{
871541Srgrimes	dev_t dev;
885455Sdg	int (*mapfunc) ();
891541Srgrimes	vm_object_t object;
901549Srgrimes	unsigned int npages, off;
911541Srgrimes
921541Srgrimes	/*
931541Srgrimes	 * Make sure this device can be mapped.
941541Srgrimes	 */
955455Sdg	dev = (dev_t) (u_long) handle;
961541Srgrimes	mapfunc = cdevsw[major(dev)].d_mmap;
971541Srgrimes	if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
985455Sdg		return (NULL);
991541Srgrimes
1001541Srgrimes	/*
1011541Srgrimes	 * Offset should be page aligned.
1021541Srgrimes	 */
1035455Sdg	if (foff & (PAGE_SIZE - 1))
1045455Sdg		return (NULL);
1051541Srgrimes
1061541Srgrimes	/*
1075455Sdg	 * Check that the specified range of the device allows the desired
1085455Sdg	 * protection.
1098876Srgrimes	 *
1101541Srgrimes	 * XXX assumes VM_PROT_* == PROT_*
1111541Srgrimes	 */
1121541Srgrimes	npages = atop(round_page(size));
1131541Srgrimes	for (off = foff; npages--; off += PAGE_SIZE)
1145455Sdg		if ((*mapfunc) (dev, off, (int) prot) == -1)
1155455Sdg			return (NULL);
1161541Srgrimes
1171541Srgrimes	/*
1189507Sdg	 * Lock to prevent object creation race contion.
1199507Sdg	 */
1209507Sdg	while (dev_pager_alloc_lock) {
1219507Sdg		dev_pager_alloc_lock_want++;
1229507Sdg		tsleep(&dev_pager_alloc_lock, PVM, "dvpall", 0);
1239507Sdg		dev_pager_alloc_lock_want--;
1249507Sdg	}
1259507Sdg	dev_pager_alloc_lock = 1;
1269507Sdg
1279507Sdg	/*
1281541Srgrimes	 * Look up pager, creating as necessary.
1291541Srgrimes	 */
1309507Sdg	object = vm_pager_object_lookup(&dev_pager_object_list, handle);
1319507Sdg	if (object == NULL) {
1321541Srgrimes		/*
1331541Srgrimes		 * Allocate object and associate it with the pager.
1341541Srgrimes		 */
1359507Sdg		object = vm_object_allocate(OBJT_DEVICE, foff + size);
1369507Sdg		object->handle = handle;
1379507Sdg		TAILQ_INIT(&object->un_pager.devp.devp_pglist);
1389507Sdg		TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list);
1391541Srgrimes	} else {
1401541Srgrimes		/*
1416585Sdg		 * Gain a reference to the object.
1421541Srgrimes		 */
1439507Sdg		vm_object_reference(object);
1448585Sdg		if (foff + size > object->size)
1458585Sdg			object->size = foff + size;
1461541Srgrimes	}
1479507Sdg
1489507Sdg	dev_pager_alloc_lock = 0;
1499507Sdg	if (dev_pager_alloc_lock_want)
1509507Sdg		wakeup(&dev_pager_alloc_lock);
1519507Sdg
1529507Sdg	return (object);
1531541Srgrimes}
1541541Srgrimes
1559507Sdgvoid
1569507Sdgdev_pager_dealloc(object)
1579507Sdg	vm_object_t object;
1581541Srgrimes{
1591541Srgrimes	vm_page_t m;
1601541Srgrimes
1619507Sdg	TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list);
1621541Srgrimes	/*
1631541Srgrimes	 * Free up our fake pages.
1641541Srgrimes	 */
1659507Sdg	while ((m = object->un_pager.devp.devp_pglist.tqh_first) != 0) {
1669507Sdg		TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq);
1671541Srgrimes		dev_pager_putfake(m);
1681541Srgrimes	}
1691541Srgrimes}
1701541Srgrimes
1719507Sdgint
1729507Sdgdev_pager_getpages(object, m, count, reqpage)
1739507Sdg	vm_object_t object;
1749507Sdg	vm_page_t *m;
1759507Sdg	int count;
1769507Sdg	int reqpage;
1771541Srgrimes{
1781541Srgrimes	vm_offset_t offset, paddr;
1791541Srgrimes	vm_page_t page;
1801541Srgrimes	dev_t dev;
1819507Sdg	int i, s;
1825455Sdg	int (*mapfunc) (), prot;
1831541Srgrimes
1849507Sdg	dev = (dev_t) (u_long) object->handle;
1859507Sdg	offset = m[reqpage]->offset + object->paging_offset;
1861541Srgrimes	prot = PROT_READ;	/* XXX should pass in? */
1871541Srgrimes	mapfunc = cdevsw[major(dev)].d_mmap;
1881549Srgrimes
1891541Srgrimes	if (mapfunc == NULL || mapfunc == enodev || mapfunc == nullop)
1901541Srgrimes		panic("dev_pager_getpage: no map function");
1911549Srgrimes
1925455Sdg	paddr = pmap_phys_address((*mapfunc) ((dev_t) dev, (int) offset, prot));
1931541Srgrimes#ifdef DIAGNOSTIC
1941541Srgrimes	if (paddr == -1)
1951541Srgrimes		panic("dev_pager_getpage: map function returns error");
1961541Srgrimes#endif
1971541Srgrimes	/*
1989507Sdg	 * Replace the passed in reqpage page with our own fake page and free up the
1999507Sdg	 * all of the original pages.
2001541Srgrimes	 */
2011541Srgrimes	page = dev_pager_getfake(paddr);
2029507Sdg	TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq);
2039507Sdg	for (i = 0; i < count; i++) {
2049507Sdg		PAGE_WAKEUP(m[i]);
2059507Sdg		vm_page_free(m[i]);
2069507Sdg	}
2071549Srgrimes	s = splhigh();
2081541Srgrimes	vm_page_insert(page, object, offset);
2091549Srgrimes	splx(s);
2101541Srgrimes
2115455Sdg	return (VM_PAGER_OK);
2121541Srgrimes}
2131541Srgrimes
2149507Sdgint
2159507Sdgdev_pager_putpages(object, m, count, sync, rtvals)
2169507Sdg	vm_object_t object;
2179507Sdg	vm_page_t *m;
2189507Sdg	int count;
2191541Srgrimes	boolean_t sync;
2209507Sdg	int *rtvals;
2211541Srgrimes{
2221541Srgrimes	panic("dev_pager_putpage called");
2231541Srgrimes}
2241541Srgrimes
2259507Sdgboolean_t
2269507Sdgdev_pager_haspage(object, offset, before, after)
2279507Sdg	vm_object_t object;
2281541Srgrimes	vm_offset_t offset;
2299507Sdg	int *before;
2309507Sdg	int *after;
2311541Srgrimes{
2329507Sdg	if (before != NULL)
2339507Sdg		*before = 0;
2349507Sdg	if (after != NULL)
2359507Sdg		*after = 0;
2365455Sdg	return (TRUE);
2371541Srgrimes}
2381541Srgrimes
2391541Srgrimesstatic vm_page_t
2401541Srgrimesdev_pager_getfake(paddr)
2411541Srgrimes	vm_offset_t paddr;
2421541Srgrimes{
2431541Srgrimes	vm_page_t m;
2441541Srgrimes	int i;
2451541Srgrimes
2461541Srgrimes	if (dev_pager_fakelist.tqh_first == NULL) {
2475455Sdg		m = (vm_page_t) malloc(PAGE_SIZE * 2, M_VMPGDATA, M_WAITOK);
2485455Sdg		for (i = (PAGE_SIZE * 2) / sizeof(*m); i > 0; i--) {
2491541Srgrimes			TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq);
2501541Srgrimes			m++;
2511541Srgrimes		}
2521541Srgrimes	}
2531541Srgrimes	m = dev_pager_fakelist.tqh_first;
2541541Srgrimes	TAILQ_REMOVE(&dev_pager_fakelist, m, pageq);
2551549Srgrimes
2565455Sdg	m->flags = PG_BUSY | PG_FICTITIOUS;
2579507Sdg	m->valid = VM_PAGE_BITS_ALL;
2585455Sdg	m->dirty = 0;
2595455Sdg	m->busy = 0;
2605455Sdg	m->bmapped = 0;
2611549Srgrimes
2621549Srgrimes	m->wire_count = 1;
2631541Srgrimes	m->phys_addr = paddr;
2641549Srgrimes
2655455Sdg	return (m);
2661541Srgrimes}
2671541Srgrimes
2681541Srgrimesstatic void
2691541Srgrimesdev_pager_putfake(m)
2701541Srgrimes	vm_page_t m;
2711541Srgrimes{
2721541Srgrimes	if (!(m->flags & PG_FICTITIOUS))
2731541Srgrimes		panic("dev_pager_putfake: bad page");
2741541Srgrimes	TAILQ_INSERT_TAIL(&dev_pager_fakelist, m, pageq);
2751541Srgrimes}
276