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