FreeBSD/Linux Kernel Cross Reference
sys/vm/device_pager.c
1 /*
2 * Copyright (c) 1990 University of Utah.
3 * Copyright (c) 1991, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * @(#)device_pager.c 8.1 (Berkeley) 6/11/93
39 * $FreeBSD: releng/5.1/sys/vm/device_pager.c 112569 2003-03-25 00:07:06Z jake $
40 */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/conf.h>
45 #include <sys/lock.h>
46 #include <sys/proc.h>
47 #include <sys/mutex.h>
48 #include <sys/mman.h>
49 #include <sys/sx.h>
50
51 #include <vm/vm.h>
52 #include <vm/vm_object.h>
53 #include <vm/vm_page.h>
54 #include <vm/vm_pager.h>
55 #include <vm/uma.h>
56
57 static void dev_pager_init(void);
58 static vm_object_t dev_pager_alloc(void *, vm_ooffset_t, vm_prot_t,
59 vm_ooffset_t);
60 static void dev_pager_dealloc(vm_object_t);
61 static int dev_pager_getpages(vm_object_t, vm_page_t *, int, int);
62 static void dev_pager_putpages(vm_object_t, vm_page_t *, int,
63 boolean_t, int *);
64 static boolean_t dev_pager_haspage(vm_object_t, vm_pindex_t, int *,
65 int *);
66
67 /* list of device pager objects */
68 static struct pagerlst dev_pager_object_list;
69 /* protect against object creation */
70 static struct sx dev_pager_sx;
71 /* protect list manipulation */
72 static struct mtx dev_pager_mtx;
73
74
75 static uma_zone_t fakepg_zone;
76
77 static vm_page_t dev_pager_getfake(vm_paddr_t);
78 static void dev_pager_putfake(vm_page_t);
79
80 struct pagerops devicepagerops = {
81 dev_pager_init,
82 dev_pager_alloc,
83 dev_pager_dealloc,
84 dev_pager_getpages,
85 dev_pager_putpages,
86 dev_pager_haspage,
87 NULL
88 };
89
90 static void
91 dev_pager_init()
92 {
93 TAILQ_INIT(&dev_pager_object_list);
94 sx_init(&dev_pager_sx, "dev_pager create");
95 mtx_init(&dev_pager_mtx, "dev_pager list", NULL, MTX_DEF);
96 fakepg_zone = uma_zcreate("DP fakepg", sizeof(struct vm_page),
97 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
98 }
99
100 /*
101 * MPSAFE
102 */
103 static vm_object_t
104 dev_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, vm_ooffset_t foff)
105 {
106 dev_t dev;
107 d_mmap_t *mapfunc;
108 vm_object_t object;
109 unsigned int npages;
110 vm_paddr_t paddr;
111 vm_offset_t off;
112
113 /*
114 * Offset should be page aligned.
115 */
116 if (foff & PAGE_MASK)
117 return (NULL);
118
119 size = round_page(size);
120
121 /*
122 * Make sure this device can be mapped.
123 */
124 dev = handle;
125 mtx_lock(&Giant);
126 mapfunc = devsw(dev)->d_mmap;
127 if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) {
128 printf("obsolete map function %p\n", (void *)mapfunc);
129 mtx_unlock(&Giant);
130 return (NULL);
131 }
132
133 /*
134 * Check that the specified range of the device allows the desired
135 * protection.
136 *
137 * XXX assumes VM_PROT_* == PROT_*
138 */
139 npages = OFF_TO_IDX(size);
140 for (off = foff; npages--; off += PAGE_SIZE)
141 if ((*mapfunc)(dev, off, &paddr, (int)prot) != 0) {
142 mtx_unlock(&Giant);
143 return (NULL);
144 }
145
146 /*
147 * Lock to prevent object creation race condition.
148 */
149 sx_xlock(&dev_pager_sx);
150
151 /*
152 * Look up pager, creating as necessary.
153 */
154 object = vm_pager_object_lookup(&dev_pager_object_list, handle);
155 if (object == NULL) {
156 /*
157 * Allocate object and associate it with the pager.
158 */
159 object = vm_object_allocate(OBJT_DEVICE,
160 OFF_TO_IDX(foff + size));
161 object->handle = handle;
162 TAILQ_INIT(&object->un_pager.devp.devp_pglist);
163 mtx_lock(&dev_pager_mtx);
164 TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list);
165 mtx_unlock(&dev_pager_mtx);
166 } else {
167 /*
168 * Gain a reference to the object.
169 */
170 vm_object_reference(object);
171 if (OFF_TO_IDX(foff + size) > object->size)
172 object->size = OFF_TO_IDX(foff + size);
173 }
174
175 sx_xunlock(&dev_pager_sx);
176 mtx_unlock(&Giant);
177 return (object);
178 }
179
180 static void
181 dev_pager_dealloc(object)
182 vm_object_t object;
183 {
184 vm_page_t m;
185
186 mtx_lock(&dev_pager_mtx);
187 TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list);
188 mtx_unlock(&dev_pager_mtx);
189 /*
190 * Free up our fake pages.
191 */
192 while ((m = TAILQ_FIRST(&object->un_pager.devp.devp_pglist)) != 0) {
193 TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq);
194 dev_pager_putfake(m);
195 }
196 }
197
198 static int
199 dev_pager_getpages(object, m, count, reqpage)
200 vm_object_t object;
201 vm_page_t *m;
202 int count;
203 int reqpage;
204 {
205 vm_pindex_t offset;
206 vm_paddr_t paddr;
207 vm_page_t page;
208 dev_t dev;
209 int i, ret;
210 d_mmap_t *mapfunc;
211 int prot;
212
213 mtx_assert(&Giant, MA_OWNED);
214 dev = object->handle;
215 offset = m[reqpage]->pindex;
216 prot = PROT_READ; /* XXX should pass in? */
217 mapfunc = devsw(dev)->d_mmap;
218
219 if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop)
220 panic("dev_pager_getpage: no map function");
221
222 ret = (*mapfunc)(dev, (vm_offset_t)offset << PAGE_SHIFT, &paddr, prot);
223 KASSERT(ret == 0, ("dev_pager_getpage: map function returns error"));
224 /*
225 * Replace the passed in reqpage page with our own fake page and
226 * free up the all of the original pages.
227 */
228 page = dev_pager_getfake(paddr);
229 TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq);
230 vm_page_lock_queues();
231 for (i = 0; i < count; i++)
232 vm_page_free(m[i]);
233 vm_page_unlock_queues();
234 vm_page_insert(page, object, offset);
235
236 return (VM_PAGER_OK);
237 }
238
239 static void
240 dev_pager_putpages(object, m, count, sync, rtvals)
241 vm_object_t object;
242 vm_page_t *m;
243 int count;
244 boolean_t sync;
245 int *rtvals;
246 {
247 panic("dev_pager_putpage called");
248 }
249
250 static boolean_t
251 dev_pager_haspage(object, pindex, before, after)
252 vm_object_t object;
253 vm_pindex_t pindex;
254 int *before;
255 int *after;
256 {
257 if (before != NULL)
258 *before = 0;
259 if (after != NULL)
260 *after = 0;
261 return (TRUE);
262 }
263
264 static vm_page_t
265 dev_pager_getfake(paddr)
266 vm_paddr_t paddr;
267 {
268 vm_page_t m;
269
270 m = uma_zalloc(fakepg_zone, M_WAITOK);
271
272 m->flags = PG_BUSY | PG_FICTITIOUS;
273 m->valid = VM_PAGE_BITS_ALL;
274 m->dirty = 0;
275 m->busy = 0;
276 m->queue = PQ_NONE;
277 m->object = NULL;
278
279 m->wire_count = 1;
280 m->hold_count = 0;
281 m->phys_addr = paddr;
282
283 return (m);
284 }
285
286 static void
287 dev_pager_putfake(m)
288 vm_page_t m;
289 {
290 if (!(m->flags & PG_FICTITIOUS))
291 panic("dev_pager_putfake: bad page");
292 uma_zfree(fakepg_zone, m);
293 }
Cache object: e65b651a5ae47f8e6cb3273d1caf445b
|