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