Converted vm_page bit fields to flags to allow for some optimizations
[unix-history] / sys / vm / vnode_pager.c
CommitLineData
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
66struct 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
75static int vnode_pager_io(vn_pager_t, vm_page_t, enum uio_rw);
76
15637ed4
RG
77queue_head_t vnode_pager_list; /* list of managed vnodes */
78
79#ifdef DEBUG
80int 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
89void
90vnode_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 */
103vm_pager_t
104vnode_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
187void
188vnode_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 213int
15637ed4
RG
214vnode_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
227boolean_t
228vnode_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
249boolean_t
250vnode_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 306void
15637ed4
RG
307vnode_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 358void
15637ed4
RG
359vnode_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 */
385boolean_t
386vnode_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 423static int
15637ed4
RG
424vnode_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}