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