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