device_pager.c revision 132884
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 * 4. Neither the name of the University nor the names of its contributors
191541Srgrimes *    may be used to endorse or promote products derived from this software
201541Srgrimes *    without specific prior written permission.
211541Srgrimes *
221541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321541Srgrimes * SUCH DAMAGE.
331541Srgrimes *
341549Srgrimes *	@(#)device_pager.c	8.1 (Berkeley) 6/11/93
351541Srgrimes */
361541Srgrimes
37116226Sobrien#include <sys/cdefs.h>
38116226Sobrien__FBSDID("$FreeBSD: head/sys/vm/device_pager.c 132884 2004-07-30 11:09:18Z dfr $");
39116226Sobrien
401541Srgrimes#include <sys/param.h>
411541Srgrimes#include <sys/systm.h>
421541Srgrimes#include <sys/conf.h>
4376166Smarkm#include <sys/lock.h>
4479224Sdillon#include <sys/proc.h>
4576166Smarkm#include <sys/mutex.h>
461541Srgrimes#include <sys/mman.h>
4775675Salfred#include <sys/sx.h>
481541Srgrimes
491541Srgrimes#include <vm/vm.h>
5012662Sdg#include <vm/vm_object.h>
511541Srgrimes#include <vm/vm_page.h>
529507Sdg#include <vm/vm_pager.h>
5392748Sjeff#include <vm/uma.h>
541541Srgrimes
5592727Salfredstatic void dev_pager_init(void);
5692727Salfredstatic vm_object_t dev_pager_alloc(void *, vm_ooffset_t, vm_prot_t,
5792727Salfred		vm_ooffset_t);
5892727Salfredstatic void dev_pager_dealloc(vm_object_t);
5992727Salfredstatic int dev_pager_getpages(vm_object_t, vm_page_t *, int, int);
6092727Salfredstatic void dev_pager_putpages(vm_object_t, vm_page_t *, int,
6192727Salfred		boolean_t, int *);
6292727Salfredstatic boolean_t dev_pager_haspage(vm_object_t, vm_pindex_t, int *,
6392727Salfred		int *);
641541Srgrimes
6512820Sphk/* list of device pager objects */
6612820Sphkstatic struct pagerlst dev_pager_object_list;
6775675Salfred/* protect against object creation */
6875675Salfredstatic struct sx dev_pager_sx;
6975675Salfred/* protect list manipulation */
7075675Salfredstatic struct mtx dev_pager_mtx;
7112820Sphk
7275675Salfred
7392748Sjeffstatic uma_zone_t fakepg_zone;
7412820Sphk
75112569Sjakestatic vm_page_t dev_pager_getfake(vm_paddr_t);
7692727Salfredstatic void dev_pager_putfake(vm_page_t);
77132884Sdfrstatic void dev_pager_updatefake(vm_page_t, vm_paddr_t);
781541Srgrimes
791541Srgrimesstruct pagerops devicepagerops = {
80118466Sphk	.pgo_init =	dev_pager_init,
81118466Sphk	.pgo_alloc =	dev_pager_alloc,
82118466Sphk	.pgo_dealloc =	dev_pager_dealloc,
83118466Sphk	.pgo_getpages =	dev_pager_getpages,
84118466Sphk	.pgo_putpages =	dev_pager_putpages,
85118466Sphk	.pgo_haspage =	dev_pager_haspage,
861541Srgrimes};
871541Srgrimes
8812820Sphkstatic void
891541Srgrimesdev_pager_init()
901541Srgrimes{
919507Sdg	TAILQ_INIT(&dev_pager_object_list);
9275675Salfred	sx_init(&dev_pager_sx, "dev_pager create");
9393818Sjhb	mtx_init(&dev_pager_mtx, "dev_pager list", NULL, MTX_DEF);
9492748Sjeff	fakepg_zone = uma_zcreate("DP fakepg", sizeof(struct vm_page),
95120739Sjeff	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR,
96120739Sjeff	    UMA_ZONE_NOFREE|UMA_ZONE_VM);
971541Srgrimes}
981541Srgrimes
9998630Salc/*
10098630Salc * MPSAFE
10198630Salc */
10212820Sphkstatic vm_object_t
10340286Sdgdev_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, vm_ooffset_t foff)
1041541Srgrimes{
105130585Sphk	struct cdev *dev;
10612591Sbde	d_mmap_t *mapfunc;
1071541Srgrimes	vm_object_t object;
108124133Salc	vm_pindex_t pindex;
10941004Sdfr	unsigned int npages;
110112569Sjake	vm_paddr_t paddr;
111112569Sjake	vm_offset_t off;
1121541Srgrimes
1131541Srgrimes	/*
11498630Salc	 * Offset should be page aligned.
11598630Salc	 */
11698630Salc	if (foff & PAGE_MASK)
11798630Salc		return (NULL);
11898630Salc
11998630Salc	size = round_page(size);
120124133Salc	pindex = OFF_TO_IDX(foff + size);
12198630Salc
12298630Salc	/*
1231541Srgrimes	 * Make sure this device can be mapped.
1241541Srgrimes	 */
12547111Sbde	dev = handle;
12698630Salc	mtx_lock(&Giant);
12746676Sphk	mapfunc = devsw(dev)->d_mmap;
12812610Sbde	if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) {
12912610Sbde		printf("obsolete map function %p\n", (void *)mapfunc);
13098630Salc		mtx_unlock(&Giant);
1315455Sdg		return (NULL);
13212610Sbde	}
1331541Srgrimes
1341541Srgrimes	/*
1355455Sdg	 * Check that the specified range of the device allows the desired
1365455Sdg	 * protection.
1378876Srgrimes	 *
1381541Srgrimes	 * XXX assumes VM_PROT_* == PROT_*
1391541Srgrimes	 */
14040286Sdg	npages = OFF_TO_IDX(size);
1411541Srgrimes	for (off = foff; npages--; off += PAGE_SIZE)
142111462Smux		if ((*mapfunc)(dev, off, &paddr, (int)prot) != 0) {
14398630Salc			mtx_unlock(&Giant);
1445455Sdg			return (NULL);
14598630Salc		}
1461541Srgrimes
1471541Srgrimes	/*
14858634Scharnier	 * Lock to prevent object creation race condition.
1499507Sdg	 */
15075675Salfred	sx_xlock(&dev_pager_sx);
1519507Sdg
1529507Sdg	/*
1531541Srgrimes	 * Look up pager, creating as necessary.
1541541Srgrimes	 */
1559507Sdg	object = vm_pager_object_lookup(&dev_pager_object_list, handle);
1569507Sdg	if (object == NULL) {
1571541Srgrimes		/*
1581541Srgrimes		 * Allocate object and associate it with the pager.
1591541Srgrimes		 */
160124133Salc		object = vm_object_allocate(OBJT_DEVICE, pindex);
1619507Sdg		object->handle = handle;
1629507Sdg		TAILQ_INIT(&object->un_pager.devp.devp_pglist);
16375675Salfred		mtx_lock(&dev_pager_mtx);
1649507Sdg		TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list);
16575675Salfred		mtx_unlock(&dev_pager_mtx);
1661541Srgrimes	} else {
1671541Srgrimes		/*
1686585Sdg		 * Gain a reference to the object.
1691541Srgrimes		 */
1709507Sdg		vm_object_reference(object);
171124133Salc		if (pindex > object->size)
172124133Salc			object->size = pindex;
1731541Srgrimes	}
1749507Sdg
17575675Salfred	sx_xunlock(&dev_pager_sx);
17698630Salc	mtx_unlock(&Giant);
1779507Sdg	return (object);
1781541Srgrimes}
1791541Srgrimes
18012820Sphkstatic void
1819507Sdgdev_pager_dealloc(object)
1829507Sdg	vm_object_t object;
1831541Srgrimes{
1841541Srgrimes	vm_page_t m;
1851541Srgrimes
18675675Salfred	mtx_lock(&dev_pager_mtx);
1879507Sdg	TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list);
18875675Salfred	mtx_unlock(&dev_pager_mtx);
1891541Srgrimes	/*
1901541Srgrimes	 * Free up our fake pages.
1911541Srgrimes	 */
19215809Sdyson	while ((m = TAILQ_FIRST(&object->un_pager.devp.devp_pglist)) != 0) {
1939507Sdg		TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq);
1941541Srgrimes		dev_pager_putfake(m);
1951541Srgrimes	}
1961541Srgrimes}
1971541Srgrimes
19812820Sphkstatic int
1999507Sdgdev_pager_getpages(object, m, count, reqpage)
2009507Sdg	vm_object_t object;
2019507Sdg	vm_page_t *m;
2029507Sdg	int count;
2039507Sdg	int reqpage;
2041541Srgrimes{
20598824Siedowse	vm_pindex_t offset;
206112569Sjake	vm_paddr_t paddr;
2071541Srgrimes	vm_page_t page;
208130585Sphk	struct cdev *dev;
209111462Smux	int i, ret;
21012591Sbde	d_mmap_t *mapfunc;
21112591Sbde	int prot;
2121541Srgrimes
213116793Salc	VM_OBJECT_LOCK_ASSERT(object, MA_OWNED);
21447111Sbde	dev = object->handle;
21542957Sdillon	offset = m[reqpage]->pindex;
216116279Salc	VM_OBJECT_UNLOCK(object);
217128570Salc	mtx_lock(&Giant);
2181541Srgrimes	prot = PROT_READ;	/* XXX should pass in? */
21946676Sphk	mapfunc = devsw(dev)->d_mmap;
2201549Srgrimes
22112610Sbde	if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop)
2221541Srgrimes		panic("dev_pager_getpage: no map function");
2231549Srgrimes
224111462Smux	ret = (*mapfunc)(dev, (vm_offset_t)offset << PAGE_SHIFT, &paddr, prot);
225111462Smux	KASSERT(ret == 0, ("dev_pager_getpage: map function returns error"));
226128570Salc	mtx_unlock(&Giant);
227128570Salc
228132884Sdfr	if ((m[reqpage]->flags & PG_FICTITIOUS) != 0) {
229132884Sdfr		/*
230132884Sdfr		 * If the passed in reqpage page is a fake page, update it with
231132884Sdfr		 * the new physical address.
232132884Sdfr		 */
233132884Sdfr		dev_pager_updatefake(m[reqpage], paddr);
234132884Sdfr		VM_OBJECT_LOCK(object);
235132884Sdfr		if (count > 1) {
236132884Sdfr			vm_page_lock_queues();
237132884Sdfr			for (i = 0; i < count; i++) {
238132884Sdfr				if (i != reqpage)
239132884Sdfr					vm_page_free(m[i]);
240132884Sdfr			}
241132884Sdfr			vm_page_unlock_queues();
242132884Sdfr		}
243132884Sdfr	} else {
244132884Sdfr		/*
245132884Sdfr		 * Replace the passed in reqpage page with our own fake page and
246132884Sdfr		 * free up the all of the original pages.
247132884Sdfr		 */
248132884Sdfr		page = dev_pager_getfake(paddr);
249132884Sdfr		VM_OBJECT_LOCK(object);
250132884Sdfr		TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq);
251132884Sdfr		vm_page_lock_queues();
252132884Sdfr		for (i = 0; i < count; i++)
253132884Sdfr			vm_page_free(m[i]);
254132884Sdfr		vm_page_unlock_queues();
255132884Sdfr		vm_page_insert(page, object, offset);
256132884Sdfr		m[reqpage] = page;
257132884Sdfr	}
2581541Srgrimes
2595455Sdg	return (VM_PAGER_OK);
2601541Srgrimes}
2611541Srgrimes
26243129Sdillonstatic void
2639507Sdgdev_pager_putpages(object, m, count, sync, rtvals)
2649507Sdg	vm_object_t object;
2659507Sdg	vm_page_t *m;
2669507Sdg	int count;
2671541Srgrimes	boolean_t sync;
2689507Sdg	int *rtvals;
2691541Srgrimes{
2701541Srgrimes	panic("dev_pager_putpage called");
2711541Srgrimes}
2721541Srgrimes
27312820Sphkstatic boolean_t
27412767Sdysondev_pager_haspage(object, pindex, before, after)
2759507Sdg	vm_object_t object;
27612767Sdyson	vm_pindex_t pindex;
2779507Sdg	int *before;
2789507Sdg	int *after;
2791541Srgrimes{
2809507Sdg	if (before != NULL)
2819507Sdg		*before = 0;
2829507Sdg	if (after != NULL)
2839507Sdg		*after = 0;
2845455Sdg	return (TRUE);
2851541Srgrimes}
2861541Srgrimes
2871541Srgrimesstatic vm_page_t
2881541Srgrimesdev_pager_getfake(paddr)
289112569Sjake	vm_paddr_t paddr;
2901541Srgrimes{
2911541Srgrimes	vm_page_t m;
2921541Srgrimes
293111119Simp	m = uma_zalloc(fakepg_zone, M_WAITOK);
2941549Srgrimes
2955455Sdg	m->flags = PG_BUSY | PG_FICTITIOUS;
2969507Sdg	m->valid = VM_PAGE_BITS_ALL;
2975455Sdg	m->dirty = 0;
2985455Sdg	m->busy = 0;
29913490Sdyson	m->queue = PQ_NONE;
30040557Sdg	m->object = NULL;
3011549Srgrimes
3021549Srgrimes	m->wire_count = 1;
30314430Sdyson	m->hold_count = 0;
3041541Srgrimes	m->phys_addr = paddr;
3051549Srgrimes
3065455Sdg	return (m);
3071541Srgrimes}
3081541Srgrimes
3091541Srgrimesstatic void
3101541Srgrimesdev_pager_putfake(m)
3111541Srgrimes	vm_page_t m;
3121541Srgrimes{
3131541Srgrimes	if (!(m->flags & PG_FICTITIOUS))
3141541Srgrimes		panic("dev_pager_putfake: bad page");
31592748Sjeff	uma_zfree(fakepg_zone, m);
3161541Srgrimes}
317132884Sdfr
318132884Sdfrstatic void
319132884Sdfrdev_pager_updatefake(m, paddr)
320132884Sdfr	vm_page_t m;
321132884Sdfr	vm_paddr_t paddr;
322132884Sdfr{
323132884Sdfr	if (!(m->flags & PG_FICTITIOUS))
324132884Sdfr		panic("dev_pager_updatefake: bad page");
325132884Sdfr	m->phys_addr = paddr;
326132884Sdfr}
327