Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1990 University of Utah. | |
3 | * Copyright (c) 1991 The Regents of the University of California. | |
4 | * 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 | * | |
1284e777 | 38 | * from: @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91 |
fd76afd7 | 39 | * $Id: vnode_pager.c,v 1.6 1993/12/19 23:24:18 wollman Exp $ |
15637ed4 RG |
40 | */ |
41 | ||
42 | /* | |
43 | * Page to/from files (vnodes). | |
44 | * | |
45 | * TODO: | |
46 | * pageouts | |
47 | * fix credential use (uses current process credentials now) | |
48 | */ | |
15637ed4 RG |
49 | |
50 | #include "param.h" | |
fde1aeb2 | 51 | #include "systm.h" |
15637ed4 RG |
52 | #include "proc.h" |
53 | #include "malloc.h" | |
54 | #include "vnode.h" | |
55 | #include "uio.h" | |
56 | #include "mount.h" | |
57 | ||
58 | #include "vm_param.h" | |
59 | #include "lock.h" | |
60 | #include "queue.h" | |
61 | #include "vm_prot.h" | |
62 | #include "vm_object.h" | |
63 | #include "vm_page.h" | |
64 | #include "vnode_pager.h" | |
65 | ||
bbc3f849 GW |
66 | struct pagerops vnodepagerops = { |
67 | vnode_pager_init, | |
68 | vnode_pager_alloc, | |
69 | vnode_pager_dealloc, | |
70 | vnode_pager_getpage, | |
71 | vnode_pager_putpage, | |
72 | vnode_pager_haspage | |
73 | }; | |
74 | ||
fde1aeb2 GW |
75 | static int vnode_pager_io(vn_pager_t, vm_page_t, enum uio_rw); |
76 | ||
15637ed4 RG |
77 | queue_head_t vnode_pager_list; /* list of managed vnodes */ |
78 | ||
79 | #ifdef DEBUG | |
80 | int vpagerdebug = 0x00; | |
81 | #define VDB_FOLLOW 0x01 | |
82 | #define VDB_INIT 0x02 | |
83 | #define VDB_IO 0x04 | |
84 | #define VDB_FAIL 0x08 | |
85 | #define VDB_ALLOC 0x10 | |
86 | #define VDB_SIZE 0x20 | |
87 | #endif | |
88 | ||
89 | void | |
90 | vnode_pager_init() | |
91 | { | |
92 | #ifdef DEBUG | |
93 | if (vpagerdebug & VDB_FOLLOW) | |
94 | printf("vnode_pager_init()\n"); | |
95 | #endif | |
96 | queue_init(&vnode_pager_list); | |
97 | } | |
98 | ||
99 | /* | |
100 | * Allocate (or lookup) pager for a vnode. | |
101 | * Handle is a vnode pointer. | |
102 | */ | |
103 | vm_pager_t | |
104 | vnode_pager_alloc(handle, size, prot) | |
105 | caddr_t handle; | |
106 | vm_size_t size; | |
107 | vm_prot_t prot; | |
108 | { | |
109 | register vm_pager_t pager; | |
110 | register vn_pager_t vnp; | |
111 | vm_object_t object; | |
112 | struct vattr vattr; | |
113 | struct vnode *vp; | |
114 | struct proc *p = curproc; /* XXX */ | |
115 | ||
116 | #ifdef DEBUG | |
117 | if (vpagerdebug & (VDB_FOLLOW|VDB_ALLOC)) | |
118 | printf("vnode_pager_alloc(%x, %x, %x)\n", handle, size, prot); | |
119 | #endif | |
120 | /* | |
121 | * Pageout to vnode, no can do yet. | |
122 | */ | |
123 | if (handle == NULL) | |
124 | return(NULL); | |
125 | ||
126 | /* | |
127 | * Vnodes keep a pointer to any associated pager so no need to | |
128 | * lookup with vm_pager_lookup. | |
129 | */ | |
130 | vp = (struct vnode *)handle; | |
131 | pager = (vm_pager_t)vp->v_vmdata; | |
132 | if (pager == NULL) { | |
133 | /* | |
134 | * Allocate pager structures | |
135 | */ | |
136 | pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); | |
137 | if (pager == NULL) | |
138 | return(NULL); | |
139 | vnp = (vn_pager_t)malloc(sizeof *vnp, M_VMPGDATA, M_WAITOK); | |
140 | if (vnp == NULL) { | |
141 | free((caddr_t)pager, M_VMPAGER); | |
142 | return(NULL); | |
143 | } | |
144 | /* | |
145 | * And an object of the appropriate size | |
146 | */ | |
147 | if (VOP_GETATTR(vp, &vattr, p->p_ucred, p) == 0) { | |
148 | object = vm_object_allocate(round_page(vattr.va_size)); | |
149 | vm_object_enter(object, pager); | |
150 | vm_object_setpager(object, pager, 0, TRUE); | |
151 | } else { | |
152 | free((caddr_t)vnp, M_VMPGDATA); | |
153 | free((caddr_t)pager, M_VMPAGER); | |
154 | return(NULL); | |
155 | } | |
156 | /* | |
157 | * Hold a reference to the vnode and initialize pager data. | |
158 | */ | |
159 | VREF(vp); | |
160 | vnp->vnp_flags = 0; | |
161 | vnp->vnp_vp = vp; | |
162 | vnp->vnp_size = vattr.va_size; | |
163 | queue_enter(&vnode_pager_list, pager, vm_pager_t, pg_list); | |
164 | pager->pg_handle = handle; | |
165 | pager->pg_type = PG_VNODE; | |
166 | pager->pg_ops = &vnodepagerops; | |
167 | pager->pg_data = (caddr_t)vnp; | |
168 | vp->v_vmdata = (caddr_t)pager; | |
169 | } else { | |
170 | /* | |
171 | * vm_object_lookup() will remove the object from the | |
172 | * cache if found and also gain a reference to the object. | |
173 | */ | |
174 | object = vm_object_lookup(pager); | |
175 | #ifdef DEBUG | |
176 | vnp = (vn_pager_t)pager->pg_data; | |
177 | #endif | |
178 | } | |
179 | #ifdef DEBUG | |
180 | if (vpagerdebug & VDB_ALLOC) | |
181 | printf("vnode_pager_setup: vp %x sz %x pager %x object %x\n", | |
182 | vp, vnp->vnp_size, pager, object); | |
183 | #endif | |
184 | return(pager); | |
185 | } | |
186 | ||
187 | void | |
188 | vnode_pager_dealloc(pager) | |
189 | vm_pager_t pager; | |
190 | { | |
191 | register vn_pager_t vnp = (vn_pager_t)pager->pg_data; | |
192 | register struct vnode *vp; | |
193 | struct proc *p = curproc; /* XXX */ | |
194 | ||
195 | #ifdef DEBUG | |
196 | if (vpagerdebug & VDB_FOLLOW) | |
197 | printf("vnode_pager_dealloc(%x)\n", pager); | |
198 | #endif | |
199 | if (vp = vnp->vnp_vp) { | |
200 | vp->v_vmdata = NULL; | |
201 | vp->v_flag &= ~VTEXT; | |
202 | #if 0 | |
203 | /* can hang if done at reboot on NFS FS */ | |
204 | (void) VOP_FSYNC(vp, p->p_ucred, p); | |
205 | #endif | |
206 | vrele(vp); | |
207 | } | |
208 | queue_remove(&vnode_pager_list, pager, vm_pager_t, pg_list); | |
209 | free((caddr_t)vnp, M_VMPGDATA); | |
210 | free((caddr_t)pager, M_VMPAGER); | |
211 | } | |
212 | ||
4c45483e | 213 | int |
15637ed4 RG |
214 | vnode_pager_getpage(pager, m, sync) |
215 | vm_pager_t pager; | |
216 | vm_page_t m; | |
217 | boolean_t sync; | |
218 | { | |
219 | ||
220 | #ifdef DEBUG | |
221 | if (vpagerdebug & VDB_FOLLOW) | |
222 | printf("vnode_pager_getpage(%x, %x)\n", pager, m); | |
223 | #endif | |
224 | return(vnode_pager_io((vn_pager_t)pager->pg_data, m, UIO_READ)); | |
225 | } | |
226 | ||
227 | boolean_t | |
228 | vnode_pager_putpage(pager, m, sync) | |
229 | vm_pager_t pager; | |
230 | vm_page_t m; | |
231 | boolean_t sync; | |
232 | { | |
233 | int err; | |
234 | ||
235 | #ifdef DEBUG | |
236 | if (vpagerdebug & VDB_FOLLOW) | |
237 | printf("vnode_pager_putpage(%x, %x)\n", pager, m); | |
238 | #endif | |
239 | if (pager == NULL) | |
4c45483e | 240 | return 0; |
15637ed4 RG |
241 | err = vnode_pager_io((vn_pager_t)pager->pg_data, m, UIO_WRITE); |
242 | if (err == VM_PAGER_OK) { | |
fd76afd7 | 243 | m->flags |= PG_CLEAN; /* XXX - wrong place */ |
15637ed4 RG |
244 | pmap_clear_modify(VM_PAGE_TO_PHYS(m)); /* XXX - wrong place */ |
245 | } | |
246 | return(err); | |
247 | } | |
248 | ||
249 | boolean_t | |
250 | vnode_pager_haspage(pager, offset) | |
251 | vm_pager_t pager; | |
252 | vm_offset_t offset; | |
253 | { | |
254 | register vn_pager_t vnp = (vn_pager_t)pager->pg_data; | |
255 | daddr_t bn; | |
256 | int err; | |
257 | ||
258 | #ifdef DEBUG | |
259 | if (vpagerdebug & VDB_FOLLOW) | |
260 | printf("vnode_pager_haspage(%x, %x)\n", pager, offset); | |
261 | #endif | |
262 | ||
263 | /* | |
264 | * Offset beyond end of file, do not have the page | |
265 | */ | |
266 | if (offset >= vnp->vnp_size) { | |
267 | #ifdef DEBUG | |
268 | if (vpagerdebug & (VDB_FAIL|VDB_SIZE)) | |
269 | printf("vnode_pager_haspage: pg %x, off %x, size %x\n", | |
270 | pager, offset, vnp->vnp_size); | |
271 | #endif | |
272 | return(FALSE); | |
273 | } | |
274 | ||
275 | /* | |
276 | * Read the index to find the disk block to read | |
277 | * from. If there is no block, report that we don't | |
278 | * have this data. | |
279 | * | |
280 | * Assumes that the vnode has whole page or nothing. | |
281 | */ | |
282 | err = VOP_BMAP(vnp->vnp_vp, | |
283 | offset / vnp->vnp_vp->v_mount->mnt_stat.f_bsize, | |
284 | (struct vnode **)0, &bn); | |
285 | if (err) { | |
286 | #ifdef DEBUG | |
287 | if (vpagerdebug & VDB_FAIL) | |
288 | printf("vnode_pager_haspage: BMAP err %d, pg %x, off %x\n", | |
289 | err, pager, offset); | |
290 | #endif | |
291 | return(TRUE); | |
292 | } | |
293 | return((long)bn < 0 ? FALSE : TRUE); | |
294 | } | |
295 | ||
296 | /* | |
297 | * (XXX) | |
298 | * Lets the VM system know about a change in size for a file. | |
299 | * If this vnode is mapped into some address space (i.e. we have a pager | |
300 | * for it) we adjust our own internal size and flush any cached pages in | |
301 | * the associated object that are affected by the size change. | |
302 | * | |
303 | * Note: this routine may be invoked as a result of a pager put | |
304 | * operation (possibly at object termination time), so we must be careful. | |
305 | */ | |
4c45483e | 306 | void |
15637ed4 RG |
307 | vnode_pager_setsize(vp, nsize) |
308 | struct vnode *vp; | |
309 | u_long nsize; | |
310 | { | |
311 | register vn_pager_t vnp; | |
312 | register vm_object_t object; | |
313 | vm_pager_t pager; | |
314 | ||
315 | /* | |
316 | * Not a mapped vnode | |
317 | */ | |
318 | if (vp == NULL || vp->v_type != VREG || vp->v_vmdata == NULL) | |
319 | return; | |
320 | /* | |
321 | * Hasn't changed size | |
322 | */ | |
323 | pager = (vm_pager_t)vp->v_vmdata; | |
324 | vnp = (vn_pager_t)pager->pg_data; | |
325 | if (nsize == vnp->vnp_size) | |
326 | return; | |
327 | /* | |
328 | * No object. | |
329 | * This can happen during object termination since | |
330 | * vm_object_page_clean is called after the object | |
331 | * has been removed from the hash table, and clean | |
332 | * may cause vnode write operations which can wind | |
333 | * up back here. | |
334 | */ | |
335 | object = vm_object_lookup(pager); | |
336 | if (object == NULL) | |
337 | return; | |
338 | ||
339 | #ifdef DEBUG | |
340 | if (vpagerdebug & (VDB_FOLLOW|VDB_SIZE)) | |
341 | printf("vnode_pager_setsize: vp %x obj %x osz %d nsz %d\n", | |
342 | vp, object, vnp->vnp_size, nsize); | |
343 | #endif | |
344 | /* | |
345 | * File has shrunk. | |
346 | * Toss any cached pages beyond the new EOF. | |
347 | */ | |
348 | if (nsize < vnp->vnp_size) { | |
349 | vm_object_lock(object); | |
350 | vm_object_page_remove(object, | |
351 | (vm_offset_t)nsize, vnp->vnp_size); | |
352 | vm_object_unlock(object); | |
353 | } | |
354 | vnp->vnp_size = (vm_offset_t)nsize; | |
355 | vm_object_deallocate(object); | |
356 | } | |
357 | ||
4c45483e | 358 | void |
15637ed4 RG |
359 | vnode_pager_umount(mp) |
360 | register struct mount *mp; | |
361 | { | |
362 | register vm_pager_t pager, npager; | |
363 | struct vnode *vp; | |
364 | ||
365 | pager = (vm_pager_t) queue_first(&vnode_pager_list); | |
366 | while (!queue_end(&vnode_pager_list, (queue_entry_t)pager)) { | |
367 | /* | |
368 | * Save the next pointer now since uncaching may | |
369 | * terminate the object and render pager invalid | |
370 | */ | |
371 | vp = ((vn_pager_t)pager->pg_data)->vnp_vp; | |
372 | npager = (vm_pager_t) queue_next(&pager->pg_list); | |
373 | if (mp == (struct mount *)0 || vp->v_mount == mp) | |
374 | (void) vnode_pager_uncache(vp); | |
375 | pager = npager; | |
376 | } | |
377 | } | |
378 | ||
379 | /* | |
380 | * Remove vnode associated object from the object cache. | |
381 | * | |
382 | * Note: this routine may be invoked as a result of a pager put | |
383 | * operation (possibly at object termination time), so we must be careful. | |
384 | */ | |
385 | boolean_t | |
386 | vnode_pager_uncache(vp) | |
387 | register struct vnode *vp; | |
388 | { | |
389 | register vm_object_t object; | |
390 | boolean_t uncached, locked; | |
391 | vm_pager_t pager; | |
392 | ||
393 | /* | |
394 | * Not a mapped vnode | |
395 | */ | |
396 | pager = (vm_pager_t)vp->v_vmdata; | |
397 | if (pager == NULL) | |
398 | return (TRUE); | |
399 | /* | |
400 | * Unlock the vnode if it is currently locked. | |
401 | * We do this since uncaching the object may result | |
402 | * in its destruction which may initiate paging | |
403 | * activity which may necessitate locking the vnode. | |
404 | */ | |
405 | locked = VOP_ISLOCKED(vp); | |
406 | if (locked) | |
407 | VOP_UNLOCK(vp); | |
408 | /* | |
409 | * Must use vm_object_lookup() as it actually removes | |
410 | * the object from the cache list. | |
411 | */ | |
412 | object = vm_object_lookup(pager); | |
413 | if (object) { | |
414 | uncached = (object->ref_count <= 1); | |
415 | pager_cache(object, FALSE); | |
416 | } else | |
417 | uncached = TRUE; | |
418 | if (locked) | |
419 | VOP_LOCK(vp); | |
420 | return(uncached); | |
421 | } | |
422 | ||
fde1aeb2 | 423 | static int |
15637ed4 RG |
424 | vnode_pager_io(vnp, m, rw) |
425 | register vn_pager_t vnp; | |
426 | vm_page_t m; | |
427 | enum uio_rw rw; | |
428 | { | |
429 | struct uio auio; | |
430 | struct iovec aiov; | |
431 | vm_offset_t kva, foff; | |
432 | int error, size; | |
433 | struct proc *p = curproc; /* XXX */ | |
434 | ||
435 | #ifdef DEBUG | |
436 | if (vpagerdebug & VDB_FOLLOW) | |
437 | printf("vnode_pager_io(%x, %x, %c): vnode %x\n", | |
438 | vnp, m, rw == UIO_READ ? 'R' : 'W', vnp->vnp_vp); | |
439 | #endif | |
440 | foff = m->offset + m->object->paging_offset; | |
441 | /* | |
442 | * Return failure if beyond current EOF | |
443 | */ | |
444 | if (foff >= vnp->vnp_size) { | |
445 | #ifdef DEBUG | |
446 | if (vpagerdebug & VDB_SIZE) | |
447 | printf("vnode_pager_io: vp %x, off %d size %d\n", | |
448 | vnp->vnp_vp, foff, vnp->vnp_size); | |
449 | #endif | |
450 | return(VM_PAGER_BAD); | |
451 | } | |
452 | if (foff + PAGE_SIZE > vnp->vnp_size) | |
453 | size = vnp->vnp_size - foff; | |
454 | else | |
455 | size = PAGE_SIZE; | |
456 | /* | |
457 | * Allocate a kernel virtual address and initialize so that | |
458 | * we can use VOP_READ/WRITE routines. | |
459 | */ | |
460 | kva = vm_pager_map_page(m); | |
461 | aiov.iov_base = (caddr_t)kva; | |
462 | aiov.iov_len = size; | |
463 | auio.uio_iov = &aiov; | |
464 | auio.uio_iovcnt = 1; | |
465 | auio.uio_offset = foff; | |
466 | auio.uio_segflg = UIO_SYSSPACE; | |
467 | auio.uio_rw = rw; | |
468 | auio.uio_resid = size; | |
469 | auio.uio_procp = (struct proc *)0; | |
470 | #ifdef DEBUG | |
471 | if (vpagerdebug & VDB_IO) | |
472 | printf("vnode_pager_io: vp %x kva %x foff %x size %x", | |
473 | vnp->vnp_vp, kva, foff, size); | |
474 | #endif | |
475 | if (rw == UIO_READ) | |
476 | error = VOP_READ(vnp->vnp_vp, &auio, 0, p->p_ucred); | |
477 | else | |
478 | error = VOP_WRITE(vnp->vnp_vp, &auio, 0, p->p_ucred); | |
479 | #ifdef DEBUG | |
480 | if (vpagerdebug & VDB_IO) { | |
481 | if (error || auio.uio_resid) | |
482 | printf(" returns error %x, resid %x", | |
483 | error, auio.uio_resid); | |
484 | printf("\n"); | |
485 | } | |
486 | #endif | |
487 | if (!error) { | |
488 | register int count = size - auio.uio_resid; | |
489 | ||
490 | if (count == 0) | |
491 | error = EINVAL; | |
492 | else if (count != PAGE_SIZE && rw == UIO_READ) | |
fde1aeb2 | 493 | bzero((caddr_t)(kva + count), PAGE_SIZE - count); |
15637ed4 RG |
494 | } |
495 | vm_pager_unmap_page(kva); | |
496 | return (error ? VM_PAGER_FAIL : VM_PAGER_OK); | |
497 | } |