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