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