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 */
40
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD: releng/5.2/sys/vm/device_pager.c 120824 2003-10-05 22:23:44Z alc $");
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/conf.h>
47 #include <sys/lock.h>
48 #include <sys/proc.h>
49 #include <sys/mutex.h>
50 #include <sys/mman.h>
51 #include <sys/sx.h>
52
53 #include <vm/vm.h>
54 #include <vm/vm_object.h>
55 #include <vm/vm_page.h>
56 #include <vm/vm_pager.h>
57 #include <vm/uma.h>
58
59 static void dev_pager_init(void);
60 static vm_object_t dev_pager_alloc(void *, vm_ooffset_t, vm_prot_t,
61 vm_ooffset_t);
62 static void dev_pager_dealloc(vm_object_t);
63 static int dev_pager_getpages(vm_object_t, vm_page_t *, int, int);
64 static void dev_pager_putpages(vm_object_t, vm_page_t *, int,
65 boolean_t, int *);
66 static boolean_t dev_pager_haspage(vm_object_t, vm_pindex_t, int *,
67 int *);
68
69 /* list of device pager objects */
70 static struct pagerlst dev_pager_object_list;
71 /* protect against object creation */
72 static struct sx dev_pager_sx;
73 /* protect list manipulation */
74 static struct mtx dev_pager_mtx;
75
76
77 static uma_zone_t fakepg_zone;
78
79 static vm_page_t dev_pager_getfake(vm_paddr_t);
80 static void dev_pager_putfake(vm_page_t);
81
82 struct pagerops devicepagerops = {
83 .pgo_init = dev_pager_init,
84 .pgo_alloc = dev_pager_alloc,
85 .pgo_dealloc = dev_pager_dealloc,
86 .pgo_getpages = dev_pager_getpages,
87 .pgo_putpages = dev_pager_putpages,
88 .pgo_haspage = dev_pager_haspage,
89 };
90
91 static void
92 dev_pager_init()
93 {
94 TAILQ_INIT(&dev_pager_object_list);
95 sx_init(&dev_pager_sx, "dev_pager create");
96 mtx_init(&dev_pager_mtx, "dev_pager list", NULL, MTX_DEF);
97 fakepg_zone = uma_zcreate("DP fakepg", sizeof(struct vm_page),
98 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR,
99 UMA_ZONE_NOFREE|UMA_ZONE_VM);
100 }
101
102 /*
103 * MPSAFE
104 */
105 static vm_object_t
106 dev_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, vm_ooffset_t foff)
107 {
108 dev_t dev;
109 d_mmap_t *mapfunc;
110 vm_object_t object;
111 unsigned int npages;
112 vm_paddr_t paddr;
113 vm_offset_t off;
114
115 /*
116 * Offset should be page aligned.
117 */
118 if (foff & PAGE_MASK)
119 return (NULL);
120
121 size = round_page(size);
122
123 /*
124 * Make sure this device can be mapped.
125 */
126 dev = handle;
127 mtx_lock(&Giant);
128 mapfunc = devsw(dev)->d_mmap;
129 if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop) {
130 printf("obsolete map function %p\n", (void *)mapfunc);
131 mtx_unlock(&Giant);
132 return (NULL);
133 }
134
135 /*
136 * Check that the specified range of the device allows the desired
137 * protection.
138 *
139 * XXX assumes VM_PROT_* == PROT_*
140 */
141 npages = OFF_TO_IDX(size);
142 for (off = foff; npages--; off += PAGE_SIZE)
143 if ((*mapfunc)(dev, off, &paddr, (int)prot) != 0) {
144 mtx_unlock(&Giant);
145 return (NULL);
146 }
147
148 /*
149 * Lock to prevent object creation race condition.
150 */
151 sx_xlock(&dev_pager_sx);
152
153 /*
154 * Look up pager, creating as necessary.
155 */
156 object = vm_pager_object_lookup(&dev_pager_object_list, handle);
157 if (object == NULL) {
158 /*
159 * Allocate object and associate it with the pager.
160 */
161 object = vm_object_allocate(OBJT_DEVICE,
162 OFF_TO_IDX(foff + size));
163 object->handle = handle;
164 TAILQ_INIT(&object->un_pager.devp.devp_pglist);
165 mtx_lock(&dev_pager_mtx);
166 TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list);
167 mtx_unlock(&dev_pager_mtx);
168 } else {
169 /*
170 * Gain a reference to the object.
171 */
172 vm_object_reference(object);
173 if (OFF_TO_IDX(foff + size) > object->size)
174 object->size = OFF_TO_IDX(foff + size);
175 }
176
177 sx_xunlock(&dev_pager_sx);
178 mtx_unlock(&Giant);
179 return (object);
180 }
181
182 static void
183 dev_pager_dealloc(object)
184 vm_object_t object;
185 {
186 vm_page_t m;
187
188 mtx_lock(&dev_pager_mtx);
189 TAILQ_REMOVE(&dev_pager_object_list, object, pager_object_list);
190 mtx_unlock(&dev_pager_mtx);
191 /*
192 * Free up our fake pages.
193 */
194 while ((m = TAILQ_FIRST(&object->un_pager.devp.devp_pglist)) != 0) {
195 TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq);
196 dev_pager_putfake(m);
197 }
198 }
199
200 static int
201 dev_pager_getpages(object, m, count, reqpage)
202 vm_object_t object;
203 vm_page_t *m;
204 int count;
205 int reqpage;
206 {
207 vm_pindex_t offset;
208 vm_paddr_t paddr;
209 vm_page_t page;
210 dev_t dev;
211 int i, ret;
212 d_mmap_t *mapfunc;
213 int prot;
214
215 VM_OBJECT_LOCK_ASSERT(object, MA_OWNED);
216 dev = object->handle;
217 offset = m[reqpage]->pindex;
218 VM_OBJECT_UNLOCK(object);
219 prot = PROT_READ; /* XXX should pass in? */
220 mapfunc = devsw(dev)->d_mmap;
221
222 if (mapfunc == NULL || mapfunc == (d_mmap_t *)nullop)
223 panic("dev_pager_getpage: no map function");
224
225 ret = (*mapfunc)(dev, (vm_offset_t)offset << PAGE_SHIFT, &paddr, prot);
226 KASSERT(ret == 0, ("dev_pager_getpage: map function returns error"));
227 /*
228 * Replace the passed in reqpage page with our own fake page and
229 * free up the all of the original pages.
230 */
231 page = dev_pager_getfake(paddr);
232 VM_OBJECT_LOCK(object);
233 TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, page, pageq);
234 vm_page_lock_queues();
235 for (i = 0; i < count; i++)
236 vm_page_free(m[i]);
237 vm_page_unlock_queues();
238 vm_page_insert(page, object, offset);
239 m[reqpage] = page;
240
241 return (VM_PAGER_OK);
242 }
243
244 static void
245 dev_pager_putpages(object, m, count, sync, rtvals)
246 vm_object_t object;
247 vm_page_t *m;
248 int count;
249 boolean_t sync;
250 int *rtvals;
251 {
252 panic("dev_pager_putpage called");
253 }
254
255 static boolean_t
256 dev_pager_haspage(object, pindex, before, after)
257 vm_object_t object;
258 vm_pindex_t pindex;
259 int *before;
260 int *after;
261 {
262 if (before != NULL)
263 *before = 0;
264 if (after != NULL)
265 *after = 0;
266 return (TRUE);
267 }
268
269 static vm_page_t
270 dev_pager_getfake(paddr)
271 vm_paddr_t paddr;
272 {
273 vm_page_t m;
274
275 m = uma_zalloc(fakepg_zone, M_WAITOK);
276
277 m->flags = PG_BUSY | PG_FICTITIOUS;
278 m->valid = VM_PAGE_BITS_ALL;
279 m->dirty = 0;
280 m->busy = 0;
281 m->queue = PQ_NONE;
282 m->object = NULL;
283
284 m->wire_count = 1;
285 m->hold_count = 0;
286 m->phys_addr = paddr;
287
288 return (m);
289 }
290
291 static void
292 dev_pager_putfake(m)
293 vm_page_t m;
294 {
295 if (!(m->flags & PG_FICTITIOUS))
296 panic("dev_pager_putfake: bad page");
297 uma_zfree(fakepg_zone, m);
298 }
Cache object: a0afb99be9a226c5af95e4a5047cde9a
|