device_pager.c revision 93818
150397Sobrien/*
250397Sobrien * Copyright (c) 1990 University of Utah.
3117395Skan * Copyright (c) 1991, 1993
450397Sobrien *	The Regents of the University of California.  All rights reserved.
518334Speter *
618334Speter * This code is derived from software contributed to Berkeley by
718334Speter * the Systems Programming Group of the University of Utah Computer
818334Speter * Science Department.
918334Speter *
1018334Speter * Redistribution and use in source and binary forms, with or without
1118334Speter * modification, are permitted provided that the following conditions
1218334Speter * are met:
1318334Speter * 1. Redistributions of source code must retain the above copyright
1418334Speter *    notice, this list of conditions and the following disclaimer.
1518334Speter * 2. Redistributions in binary form must reproduce the above copyright
1618334Speter *    notice, this list of conditions and the following disclaimer in the
1718334Speter *    documentation and/or other materials provided with the distribution.
1818334Speter * 3. All advertising materials mentioning features or use of this software
1918334Speter *    must display the following acknowledgement:
2018334Speter *	This product includes software developed by the University of
2118334Speter *	California, Berkeley and its contributors.
2218334Speter * 4. Neither the name of the University nor the names of its contributors
2318334Speter *    may be used to endorse or promote products derived from this software
2418334Speter *    without specific prior written permission.
2518334Speter *
2618334Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2718334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2818334Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2918334Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3018334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3118334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3218334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3318334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3418334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3518334Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3618334Speter * SUCH DAMAGE.
3718334Speter *
3818334Speter *	@(#)device_pager.c	8.1 (Berkeley) 6/11/93
3918334Speter * $FreeBSD: head/sys/vm/device_pager.c 93818 2002-04-04 21:03:38Z jhb $
4018334Speter */
4118334Speter
4218334Speter#include <sys/param.h>
4318334Speter#include <sys/systm.h>
4418334Speter#include <sys/conf.h>
4518334Speter#include <sys/lock.h>
4618334Speter#include <sys/proc.h>
4718334Speter#include <sys/mutex.h>
4818334Speter#include <sys/mman.h>
4918334Speter#include <sys/sx.h>
5018334Speter
5118334Speter#include <vm/vm.h>
5218334Speter#include <vm/vm_object.h>
5318334Speter#include <vm/vm_page.h>
5418334Speter#include <vm/vm_pager.h>
5518334Speter#include <vm/uma.h>
5618334Speter
5718334Speterstatic void dev_pager_init(void);
5818334Speterstatic vm_object_t dev_pager_alloc(void *, vm_ooffset_t, vm_prot_t,
5918334Speter		vm_ooffset_t);
6018334Speterstatic void dev_pager_dealloc(vm_object_t);
6118334Speterstatic int dev_pager_getpages(vm_object_t, vm_page_t *, int, int);
6218334Speterstatic void dev_pager_putpages(vm_object_t, vm_page_t *, int,
6318334Speter		boolean_t, int *);
6490075Sobrienstatic boolean_t dev_pager_haspage(vm_object_t, vm_pindex_t, int *,
6518334Speter		int *);
6618334Speter
6718334Speter/* list of device pager objects */
6818334Speterstatic struct pagerlst dev_pager_object_list;
6918334Speter/* protect against object creation */
7018334Speterstatic struct sx dev_pager_sx;
7118334Speter/* protect list manipulation */
7218334Speterstatic struct mtx dev_pager_mtx;
7318334Speter
7418334Speter
7518334Speterstatic uma_zone_t fakepg_zone;
7618334Speter
7718334Speterstatic vm_page_t dev_pager_getfake(vm_offset_t);
7818334Speterstatic void dev_pager_putfake(vm_page_t);
7918334Speter
8018334Speterstruct pagerops devicepagerops = {
8118334Speter	dev_pager_init,
8218334Speter	dev_pager_alloc,
8318334Speter	dev_pager_dealloc,
8418334Speter	dev_pager_getpages,
8518334Speter	dev_pager_putpages,
8618334Speter	dev_pager_haspage,
8718334Speter	NULL
8818334Speter};
8918334Speter
9018334Speterstatic void
9118334Speterdev_pager_init()
9218334Speter{
9318334Speter	TAILQ_INIT(&dev_pager_object_list);
9418334Speter	sx_init(&dev_pager_sx, "dev_pager create");
9518334Speter	mtx_init(&dev_pager_mtx, "dev_pager list", NULL, MTX_DEF);
9618334Speter	fakepg_zone = uma_zcreate("DP fakepg", sizeof(struct vm_page),
9718334Speter	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
9818334Speter}
9918334Speter
10018334Speterstatic vm_object_t
10118334Speterdev_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, vm_ooffset_t foff)
10218334Speter{
10318334Speter	dev_t dev;
10418334Speter	d_mmap_t *mapfunc;
10550397Sobrien	vm_object_t object;
10650397Sobrien	unsigned int npages;
10750397Sobrien	vm_offset_t off;
10818334Speter
10950397Sobrien	mtx_assert(&Giant, MA_OWNED);
11018334Speter	/*
11118334Speter	 * Make sure this device can be mapped.
11218334Speter	 */
11318334Speter	dev = handle;
11418334Speter	mapfunc = devsw(dev)->d_mmap;
11518334Speter	if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) {
11618334Speter		printf("obsolete map function %p\n", (void *)mapfunc);
11718334Speter		return (NULL);
11890075Sobrien	}
11918334Speter
12018334Speter	/*
12118334Speter	 * Offset should be page aligned.
12218334Speter	 */
12318334Speter	if (foff & PAGE_MASK)
12418334Speter		return (NULL);
12518334Speter
12618334Speter	size = round_page(size);
12718334Speter
12818334Speter	/*
12918334Speter	 * Check that the specified range of the device allows the desired
13018334Speter	 * protection.
13118334Speter	 *
132117395Skan	 * XXX assumes VM_PROT_* == PROT_*
13318334Speter	 */
13418334Speter	npages = OFF_TO_IDX(size);
13518334Speter	for (off = foff; npages--; off += PAGE_SIZE)
13618334Speter		if ((*mapfunc) (dev, off, (int) prot) == -1)
13718334Speter			return (NULL);
13818334Speter
13918334Speter	/*
14018334Speter	 * Lock to prevent object creation race condition.
14118334Speter	 */
14218334Speter	sx_xlock(&dev_pager_sx);
14318334Speter
14418334Speter	/*
14518334Speter	 * Look up pager, creating as necessary.
14618334Speter	 */
14718334Speter	object = vm_pager_object_lookup(&dev_pager_object_list, handle);
14818334Speter	if (object == NULL) {
14918334Speter		/*
15018334Speter		 * Allocate object and associate it with the pager.
15118334Speter		 */
15218334Speter		object = vm_object_allocate(OBJT_DEVICE,
15350397Sobrien			OFF_TO_IDX(foff + size));
15418334Speter		object->handle = handle;
15518334Speter		TAILQ_INIT(&object->un_pager.devp.devp_pglist);
15618334Speter		mtx_lock(&dev_pager_mtx);
15718334Speter		TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list);
15850397Sobrien		mtx_unlock(&dev_pager_mtx);
15950397Sobrien	} else {
16018334Speter		/*
16118334Speter		 * Gain a reference to the object.
16218334Speter		 */
16318334Speter		vm_object_reference(object);
16418334Speter		if (OFF_TO_IDX(foff + size) > object->size)
16518334Speter			object->size = OFF_TO_IDX(foff + size);
16618334Speter	}
16718334Speter
16818334Speter	sx_xunlock(&dev_pager_sx);
16918334Speter
17018334Speter	return (object);
17118334Speter}
17218334Speter
17318334Speterstatic void
17418334Speterdev_pager_dealloc(object)
17518334Speter	vm_object_t object;
17618334Speter{
17718334Speter	vm_page_t m;
17818334Speter
17918334Speter	mtx_lock(&dev_pager_mtx);
18018334Speter	TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list);
18190075Sobrien	mtx_unlock(&dev_pager_mtx);
18218334Speter	/*
18318334Speter	 * Free up our fake pages.
18418334Speter	 */
18518334Speter	while ((m = TAILQ_FIRST(&object->un_pager.devp.devp_pglist)) != 0) {
18618334Speter		TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq);
187117395Skan		dev_pager_putfake(m);
18818334Speter	}
18918334Speter}
19018334Speter
19118334Speterstatic int
19218334Speterdev_pager_getpages(object, m, count, reqpage)
19318334Speter	vm_object_t object;
19418334Speter	vm_page_t *m;
19518334Speter	int count;
19690075Sobrien	int reqpage;
19718334Speter{
19890075Sobrien	vm_offset_t offset;
19990075Sobrien	vm_offset_t paddr;
20018334Speter	vm_page_t page;
20118334Speter	dev_t dev;
20218334Speter	int i;
20318334Speter	d_mmap_t *mapfunc;
20418334Speter	int prot;
20518334Speter
20618334Speter	mtx_assert(&Giant, MA_OWNED);
20718334Speter	dev = object->handle;
20818334Speter	offset = m[reqpage]->pindex;
20918334Speter	prot = PROT_READ;	/* XXX should pass in? */
21018334Speter	mapfunc = devsw(dev)->d_mmap;
21118334Speter
21218334Speter	if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop)
21318334Speter		panic("dev_pager_getpage: no map function");
21418334Speter
21518334Speter	paddr = pmap_phys_address((*mapfunc) (dev, (vm_offset_t) offset << PAGE_SHIFT, prot));
21618334Speter	KASSERT(paddr != -1,("dev_pager_getpage: map function returns error"));
21718334Speter	/*
21818334Speter	 * Replace the passed in reqpage page with our own fake page and free up the
21918334Speter	 * all of the original pages.
22018334Speter	 */
22118334Speter	page = dev_pager_getfake(paddr);
22218334Speter	TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq);
22318334Speter	for (i = 0; i < count; i++) {
22418334Speter		vm_page_free(m[i]);
22518334Speter	}
22618334Speter	vm_page_insert(page, object, offset);
22718334Speter
22818334Speter	return (VM_PAGER_OK);
22918334Speter}
23018334Speter
231117395Skanstatic void
23218334Speterdev_pager_putpages(object, m, count, sync, rtvals)
23318334Speter	vm_object_t object;
23418334Speter	vm_page_t *m;
23518334Speter	int count;
23618334Speter	boolean_t sync;
23718334Speter	int *rtvals;
23818334Speter{
23918334Speter	panic("dev_pager_putpage called");
24018334Speter}
24118334Speter
24218334Speterstatic boolean_t
24318334Speterdev_pager_haspage(object, pindex, before, after)
24490075Sobrien	vm_object_t object;
24518334Speter	vm_pindex_t pindex;
24618334Speter	int *before;
24718334Speter	int *after;
24818334Speter{
24918334Speter	if (before != NULL)
25018334Speter		*before = 0;
25118334Speter	if (after != NULL)
25218334Speter		*after = 0;
25318334Speter	return (TRUE);
25418334Speter}
25590075Sobrien
25618334Speterstatic vm_page_t
25718334Speterdev_pager_getfake(paddr)
25818334Speter	vm_offset_t paddr;
259{
260	vm_page_t m;
261
262	m = uma_zalloc(fakepg_zone, M_WAITOK);
263
264	m->flags = PG_BUSY | PG_FICTITIOUS;
265	m->valid = VM_PAGE_BITS_ALL;
266	m->dirty = 0;
267	m->busy = 0;
268	m->queue = PQ_NONE;
269	m->object = NULL;
270
271	m->wire_count = 1;
272	m->hold_count = 0;
273	m->phys_addr = paddr;
274
275	return (m);
276}
277
278static void
279dev_pager_putfake(m)
280	vm_page_t m;
281{
282	if (!(m->flags & PG_FICTITIOUS))
283		panic("dev_pager_putfake: bad page");
284	uma_zfree(fakepg_zone, m);
285}
286