xref: /csrg-svn/sys/vm/vnode_pager.c (revision 65231)
1 /*
2  * Copyright (c) 1990 University of Utah.
3  * Copyright (c) 1991, 1993
4  *	The Regents of the University of California.  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	8.4 (Berkeley) 12/30/93
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 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/proc.h>
26 #include <sys/malloc.h>
27 #include <sys/vnode.h>
28 #include <sys/uio.h>
29 #include <sys/mount.h>
30 
31 #include <vm/vm.h>
32 #include <vm/vm_page.h>
33 #include <vm/vnode_pager.h>
34 
35 struct pagerlst	vnode_pager_list;	/* list of managed vnodes */
36 
37 #ifdef DEBUG
38 int	vpagerdebug = 0x00;
39 #define	VDB_FOLLOW	0x01
40 #define VDB_INIT	0x02
41 #define VDB_IO		0x04
42 #define VDB_FAIL	0x08
43 #define VDB_ALLOC	0x10
44 #define VDB_SIZE	0x20
45 #endif
46 
47 static vm_pager_t	 vnode_pager_alloc
48 			    __P((caddr_t, vm_size_t, vm_prot_t, vm_offset_t));
49 static void		 vnode_pager_dealloc __P((vm_pager_t));
50 static int		 vnode_pager_getpage
51 			    __P((vm_pager_t, vm_page_t, boolean_t));
52 static boolean_t	 vnode_pager_haspage __P((vm_pager_t, vm_offset_t));
53 static void		 vnode_pager_init __P((void));
54 static int		 vnode_pager_io
55 			    __P((vn_pager_t, vm_page_t, enum uio_rw));
56 static boolean_t	 vnode_pager_putpage
57 			    __P((vm_pager_t, vm_page_t, boolean_t));
58 
59 struct pagerops vnodepagerops = {
60 	vnode_pager_init,
61 	vnode_pager_alloc,
62 	vnode_pager_dealloc,
63 	vnode_pager_getpage,
64 	vnode_pager_putpage,
65 	vnode_pager_haspage
66 };
67 
68 static void
69 vnode_pager_init()
70 {
71 #ifdef DEBUG
72 	if (vpagerdebug & VDB_FOLLOW)
73 		printf("vnode_pager_init()\n");
74 #endif
75 	TAILQ_INIT(&vnode_pager_list);
76 }
77 
78 /*
79  * Allocate (or lookup) pager for a vnode.
80  * Handle is a vnode pointer.
81  */
82 static vm_pager_t
83 vnode_pager_alloc(handle, size, prot, foff)
84 	caddr_t handle;
85 	vm_size_t size;
86 	vm_prot_t prot;
87 	vm_offset_t foff;
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 		TAILQ_INSERT_TAIL(&vnode_pager_list, pager, pg_list);
144 		pager->pg_handle = handle;
145 		pager->pg_type = PG_VNODE;
146 		pager->pg_ops = &vnodepagerops;
147 		pager->pg_data = 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 	TAILQ_REMOVE(&vnode_pager_list, pager, 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->flags |= PG_CLEAN;			/* 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, NULL);
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 	for (pager = vnode_pager_list.tqh_first; pager != NULL; pager = npager){
346 		/*
347 		 * Save the next pointer now since uncaching may
348 		 * terminate the object and render pager invalid
349 		 */
350 		npager = pager->pg_list.tqe_next;
351 		vp = ((vn_pager_t)pager->pg_data)->vnp_vp;
352 		if (mp == (struct mount *)0 || vp->v_mount == mp)
353 			(void) vnode_pager_uncache(vp);
354 	}
355 }
356 
357 /*
358  * Remove vnode associated object from the object cache.
359  *
360  * Note: this routine may be invoked as a result of a pager put
361  * operation (possibly at object termination time), so we must be careful.
362  */
363 boolean_t
364 vnode_pager_uncache(vp)
365 	register struct vnode *vp;
366 {
367 	register vm_object_t object;
368 	boolean_t uncached, locked;
369 	vm_pager_t pager;
370 
371 	/*
372 	 * Not a mapped vnode
373 	 */
374 	pager = (vm_pager_t)vp->v_vmdata;
375 	if (pager == NULL)
376 		return (TRUE);
377 	/*
378 	 * Unlock the vnode if it is currently locked.
379 	 * We do this since uncaching the object may result
380 	 * in its destruction which may initiate paging
381 	 * activity which may necessitate locking the vnode.
382 	 */
383 	locked = VOP_ISLOCKED(vp);
384 	if (locked)
385 		VOP_UNLOCK(vp);
386 	/*
387 	 * Must use vm_object_lookup() as it actually removes
388 	 * the object from the cache list.
389 	 */
390 	object = vm_object_lookup(pager);
391 	if (object) {
392 		uncached = (object->ref_count <= 1);
393 		pager_cache(object, FALSE);
394 	} else
395 		uncached = TRUE;
396 	if (locked)
397 		VOP_LOCK(vp);
398 	return(uncached);
399 }
400 
401 static int
402 vnode_pager_io(vnp, m, rw)
403 	register vn_pager_t vnp;
404 	vm_page_t m;
405 	enum uio_rw rw;
406 {
407 	struct uio auio;
408 	struct iovec aiov;
409 	vm_offset_t kva, foff;
410 	int error, size;
411 	struct proc *p = curproc;		/* XXX */
412 
413 #ifdef DEBUG
414 	if (vpagerdebug & VDB_FOLLOW)
415 		printf("vnode_pager_io(%x, %x, %c): vnode %x\n",
416 		       vnp, m, rw == UIO_READ ? 'R' : 'W', vnp->vnp_vp);
417 #endif
418 	foff = m->offset + m->object->paging_offset;
419 	/*
420 	 * Return failure if beyond current EOF
421 	 */
422 	if (foff >= vnp->vnp_size) {
423 #ifdef DEBUG
424 		if (vpagerdebug & VDB_SIZE)
425 			printf("vnode_pager_io: vp %x, off %d size %d\n",
426 			       vnp->vnp_vp, foff, vnp->vnp_size);
427 #endif
428 		return(VM_PAGER_BAD);
429 	}
430 	if (foff + PAGE_SIZE > vnp->vnp_size)
431 		size = vnp->vnp_size - foff;
432 	else
433 		size = PAGE_SIZE;
434 	/*
435 	 * Allocate a kernel virtual address and initialize so that
436 	 * we can use VOP_READ/WRITE routines.
437 	 */
438 	kva = vm_pager_map_page(m);
439 	aiov.iov_base = (caddr_t)kva;
440 	aiov.iov_len = size;
441 	auio.uio_iov = &aiov;
442 	auio.uio_iovcnt = 1;
443 	auio.uio_offset = foff;
444 	auio.uio_segflg = UIO_SYSSPACE;
445 	auio.uio_rw = rw;
446 	auio.uio_resid = size;
447 	auio.uio_procp = (struct proc *)0;
448 #ifdef DEBUG
449 	if (vpagerdebug & VDB_IO)
450 		printf("vnode_pager_io: vp %x kva %x foff %x size %x",
451 		       vnp->vnp_vp, kva, foff, size);
452 #endif
453 	if (rw == UIO_READ)
454 		error = VOP_READ(vnp->vnp_vp, &auio, 0, p->p_ucred);
455 	else
456 		error = VOP_WRITE(vnp->vnp_vp, &auio, 0, p->p_ucred);
457 #ifdef DEBUG
458 	if (vpagerdebug & VDB_IO) {
459 		if (error || auio.uio_resid)
460 			printf(" returns error %x, resid %x",
461 			       error, auio.uio_resid);
462 		printf("\n");
463 	}
464 #endif
465 	if (!error) {
466 		register int count = size - auio.uio_resid;
467 
468 		if (count == 0)
469 			error = EINVAL;
470 		else if (count != PAGE_SIZE && rw == UIO_READ)
471 			bzero((void *)(kva + count), PAGE_SIZE - count);
472 	}
473 	vm_pager_unmap_page(kva);
474 	return (error ? VM_PAGER_ERROR : VM_PAGER_OK);
475 }
476