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