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