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