UNMATCHED SPL() CALL IN VM SYSTEM
[unix-history] / usr / src / sys.386bsd / vm / vnode_pager.c
CommitLineData
b688fc87
WJ
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 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91
39 */
40
41/*
42 * Page to/from files (vnodes).
43 *
44 * TODO:
45 * pageouts
46 * fix credential use (uses current process credentials now)
47 */
48#include "vnodepager.h"
49#if NVNODEPAGER > 0
50
51#include "param.h"
52#include "proc.h"
53#include "malloc.h"
54#include "vnode.h"
55#include "uio.h"
56#include "mount.h"
57
58#include "vm_param.h"
59#include "lock.h"
60#include "queue.h"
61#include "vm_prot.h"
62#include "vm_object.h"
63#include "vm_page.h"
64#include "vnode_pager.h"
65
66queue_head_t vnode_pager_list; /* list of managed vnodes */
67
68#ifdef DEBUG
69int vpagerdebug = 0x00;
70#define VDB_FOLLOW 0x01
71#define VDB_INIT 0x02
72#define VDB_IO 0x04
73#define VDB_FAIL 0x08
74#define VDB_ALLOC 0x10
75#define VDB_SIZE 0x20
76#endif
77
78void
79vnode_pager_init()
80{
81#ifdef DEBUG
82 if (vpagerdebug & VDB_FOLLOW)
83 printf("vnode_pager_init()\n");
84#endif
85 queue_init(&vnode_pager_list);
86}
87
88/*
89 * Allocate (or lookup) pager for a vnode.
90 * Handle is a vnode pointer.
91 */
92vm_pager_t
93vnode_pager_alloc(handle, size, prot)
94 caddr_t handle;
95 vm_size_t size;
96 vm_prot_t prot;
97{
98 register vm_pager_t pager;
99 register vn_pager_t vnp;
100 vm_object_t object;
101 struct vattr vattr;
102 struct vnode *vp;
103 struct proc *p = curproc; /* XXX */
104
105#ifdef DEBUG
106 if (vpagerdebug & (VDB_FOLLOW|VDB_ALLOC))
107 printf("vnode_pager_alloc(%x, %x, %x)\n", handle, size, prot);
108#endif
109 /*
110 * Pageout to vnode, no can do yet.
111 */
112 if (handle == NULL)
113 return(NULL);
114
115 /*
116 * Vnodes keep a pointer to any associated pager so no need to
117 * lookup with vm_pager_lookup.
118 */
119 vp = (struct vnode *)handle;
120 pager = (vm_pager_t)vp->v_vmdata;
121 if (pager == NULL) {
122 /*
123 * Allocate pager structures
124 */
125 pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK);
126 if (pager == NULL)
127 return(NULL);
128 vnp = (vn_pager_t)malloc(sizeof *vnp, M_VMPGDATA, M_WAITOK);
129 if (vnp == NULL) {
130 free((caddr_t)pager, M_VMPAGER);
131 return(NULL);
132 }
133 /*
134 * And an object of the appropriate size
135 */
136 if (VOP_GETATTR(vp, &vattr, p->p_ucred, p) == 0) {
137 object = vm_object_allocate(round_page(vattr.va_size));
138 vm_object_enter(object, pager);
139 vm_object_setpager(object, pager, 0, TRUE);
140 } else {
141 free((caddr_t)vnp, M_VMPGDATA);
142 free((caddr_t)pager, M_VMPAGER);
143 return(NULL);
144 }
145 /*
146 * Hold a reference to the vnode and initialize pager data.
147 */
148 VREF(vp);
149 vnp->vnp_flags = 0;
150 vnp->vnp_vp = vp;
151 vnp->vnp_size = vattr.va_size;
152 queue_enter(&vnode_pager_list, pager, vm_pager_t, pg_list);
153 pager->pg_handle = handle;
154 pager->pg_type = PG_VNODE;
155 pager->pg_ops = &vnodepagerops;
156 pager->pg_data = (caddr_t)vnp;
157 vp->v_vmdata = (caddr_t)pager;
158 } else {
159 /*
160 * vm_object_lookup() will remove the object from the
161 * cache if found and also gain a reference to the object.
162 */
163 object = vm_object_lookup(pager);
164#ifdef DEBUG
165 vnp = (vn_pager_t)pager->pg_data;
166#endif
167 }
168#ifdef DEBUG
169 if (vpagerdebug & VDB_ALLOC)
170 printf("vnode_pager_setup: vp %x sz %x pager %x object %x\n",
171 vp, vnp->vnp_size, pager, object);
172#endif
173 return(pager);
174}
175
176void
177vnode_pager_dealloc(pager)
178 vm_pager_t pager;
179{
180 register vn_pager_t vnp = (vn_pager_t)pager->pg_data;
181 register struct vnode *vp;
182 struct proc *p = curproc; /* XXX */
183
184#ifdef DEBUG
185 if (vpagerdebug & VDB_FOLLOW)
186 printf("vnode_pager_dealloc(%x)\n", pager);
187#endif
188 if (vp = vnp->vnp_vp) {
189 vp->v_vmdata = NULL;
190 vp->v_flag &= ~VTEXT;
191#if 0
192 /* can hang if done at reboot on NFS FS */
193 (void) VOP_FSYNC(vp, p->p_ucred, p);
194#endif
195 vrele(vp);
196 }
197 queue_remove(&vnode_pager_list, pager, vm_pager_t, pg_list);
198 free((caddr_t)vnp, M_VMPGDATA);
199 free((caddr_t)pager, M_VMPAGER);
200}
201
202vnode_pager_getpage(pager, m, sync)
203 vm_pager_t pager;
204 vm_page_t m;
205 boolean_t sync;
206{
207
208#ifdef DEBUG
209 if (vpagerdebug & VDB_FOLLOW)
210 printf("vnode_pager_getpage(%x, %x)\n", pager, m);
211#endif
212 return(vnode_pager_io((vn_pager_t)pager->pg_data, m, UIO_READ));
213}
214
215boolean_t
216vnode_pager_putpage(pager, m, sync)
217 vm_pager_t pager;
218 vm_page_t m;
219 boolean_t sync;
220{
221 int err;
222
223#ifdef DEBUG
224 if (vpagerdebug & VDB_FOLLOW)
225 printf("vnode_pager_putpage(%x, %x)\n", pager, m);
226#endif
227 if (pager == NULL)
228 return;
229 err = vnode_pager_io((vn_pager_t)pager->pg_data, m, UIO_WRITE);
230 if (err == VM_PAGER_OK) {
231 m->clean = TRUE; /* XXX - wrong place */
232 pmap_clear_modify(VM_PAGE_TO_PHYS(m)); /* XXX - wrong place */
233 }
234 return(err);
235}
236
237boolean_t
238vnode_pager_haspage(pager, offset)
239 vm_pager_t pager;
240 vm_offset_t offset;
241{
242 register vn_pager_t vnp = (vn_pager_t)pager->pg_data;
243 daddr_t bn;
244 int err;
245
246#ifdef DEBUG
247 if (vpagerdebug & VDB_FOLLOW)
248 printf("vnode_pager_haspage(%x, %x)\n", pager, offset);
249#endif
250
251 /*
252 * Offset beyond end of file, do not have the page
253 */
254 if (offset >= vnp->vnp_size) {
255#ifdef DEBUG
256 if (vpagerdebug & (VDB_FAIL|VDB_SIZE))
257 printf("vnode_pager_haspage: pg %x, off %x, size %x\n",
258 pager, offset, vnp->vnp_size);
259#endif
260 return(FALSE);
261 }
262
263 /*
264 * Read the index to find the disk block to read
265 * from. If there is no block, report that we don't
266 * have this data.
267 *
268 * Assumes that the vnode has whole page or nothing.
269 */
270 err = VOP_BMAP(vnp->vnp_vp,
271 offset / vnp->vnp_vp->v_mount->mnt_stat.f_bsize,
272 (struct vnode **)0, &bn);
273 if (err) {
274#ifdef DEBUG
275 if (vpagerdebug & VDB_FAIL)
276 printf("vnode_pager_haspage: BMAP err %d, pg %x, off %x\n",
277 err, pager, offset);
278#endif
279 return(TRUE);
280 }
281 return((long)bn < 0 ? FALSE : TRUE);
282}
283
284/*
285 * (XXX)
286 * Lets the VM system know about a change in size for a file.
287 * If this vnode is mapped into some address space (i.e. we have a pager
288 * for it) we adjust our own internal size and flush any cached pages in
289 * the associated object that are affected by the size change.
290 *
291 * Note: this routine may be invoked as a result of a pager put
292 * operation (possibly at object termination time), so we must be careful.
293 */
294vnode_pager_setsize(vp, nsize)
295 struct vnode *vp;
296 u_long nsize;
297{
298 register vn_pager_t vnp;
299 register vm_object_t object;
300 vm_pager_t pager;
301
302 /*
303 * Not a mapped vnode
304 */
305 if (vp == NULL || vp->v_type != VREG || vp->v_vmdata == NULL)
306 return;
307 /*
308 * Hasn't changed size
309 */
310 pager = (vm_pager_t)vp->v_vmdata;
311 vnp = (vn_pager_t)pager->pg_data;
312 if (nsize == vnp->vnp_size)
313 return;
314 /*
315 * No object.
316 * This can happen during object termination since
317 * vm_object_page_clean is called after the object
318 * has been removed from the hash table, and clean
319 * may cause vnode write operations which can wind
320 * up back here.
321 */
322 object = vm_object_lookup(pager);
323 if (object == NULL)
324 return;
325
326#ifdef DEBUG
327 if (vpagerdebug & (VDB_FOLLOW|VDB_SIZE))
328 printf("vnode_pager_setsize: vp %x obj %x osz %d nsz %d\n",
329 vp, object, vnp->vnp_size, nsize);
330#endif
331 /*
332 * File has shrunk.
333 * Toss any cached pages beyond the new EOF.
334 */
335 if (nsize < vnp->vnp_size) {
336 vm_object_lock(object);
337 vm_object_page_remove(object,
338 (vm_offset_t)nsize, vnp->vnp_size);
339 vm_object_unlock(object);
340 }
341 vnp->vnp_size = (vm_offset_t)nsize;
342 vm_object_deallocate(object);
343}
344
345vnode_pager_umount(mp)
346 register struct mount *mp;
347{
348 register vm_pager_t pager, npager;
349 struct vnode *vp;
350
351 pager = (vm_pager_t) queue_first(&vnode_pager_list);
352 while (!queue_end(&vnode_pager_list, (queue_entry_t)pager)) {
353 /*
354 * Save the next pointer now since uncaching may
355 * terminate the object and render pager invalid
356 */
357 vp = ((vn_pager_t)pager->pg_data)->vnp_vp;
358 npager = (vm_pager_t) queue_next(&pager->pg_list);
359 if (mp == (struct mount *)0 || vp->v_mount == mp)
360 (void) vnode_pager_uncache(vp);
361 pager = npager;
362 }
363}
364
365/*
366 * Remove vnode associated object from the object cache.
367 *
368 * Note: this routine may be invoked as a result of a pager put
369 * operation (possibly at object termination time), so we must be careful.
370 */
371boolean_t
372vnode_pager_uncache(vp)
373 register struct vnode *vp;
374{
375 register vm_object_t object;
376 boolean_t uncached, locked;
377 vm_pager_t pager;
378
379 /*
380 * Not a mapped vnode
381 */
382 pager = (vm_pager_t)vp->v_vmdata;
383 if (pager == NULL)
384 return (TRUE);
385 /*
386 * Unlock the vnode if it is currently locked.
387 * We do this since uncaching the object may result
388 * in its destruction which may initiate paging
389 * activity which may necessitate locking the vnode.
390 */
391 locked = VOP_ISLOCKED(vp);
392 if (locked)
393 VOP_UNLOCK(vp);
394 /*
395 * Must use vm_object_lookup() as it actually removes
396 * the object from the cache list.
397 */
398 object = vm_object_lookup(pager);
399 if (object) {
400 uncached = (object->ref_count <= 1);
401 pager_cache(object, FALSE);
402 } else
403 uncached = TRUE;
404 if (locked)
405 VOP_LOCK(vp);
406 return(uncached);
407}
408
409vnode_pager_io(vnp, m, rw)
410 register vn_pager_t vnp;
411 vm_page_t m;
412 enum uio_rw rw;
413{
414 struct uio auio;
415 struct iovec aiov;
416 vm_offset_t kva, foff;
417 int error, size;
418 struct proc *p = curproc; /* XXX */
419
420#ifdef DEBUG
421 if (vpagerdebug & VDB_FOLLOW)
422 printf("vnode_pager_io(%x, %x, %c): vnode %x\n",
423 vnp, m, rw == UIO_READ ? 'R' : 'W', vnp->vnp_vp);
424#endif
425 foff = m->offset + m->object->paging_offset;
426 /*
427 * Return failure if beyond current EOF
428 */
429 if (foff >= vnp->vnp_size) {
430#ifdef DEBUG
431 if (vpagerdebug & VDB_SIZE)
432 printf("vnode_pager_io: vp %x, off %d size %d\n",
433 vnp->vnp_vp, foff, vnp->vnp_size);
434#endif
435 return(VM_PAGER_BAD);
436 }
437 if (foff + PAGE_SIZE > vnp->vnp_size)
438 size = vnp->vnp_size - foff;
439 else
440 size = PAGE_SIZE;
441 /*
442 * Allocate a kernel virtual address and initialize so that
443 * we can use VOP_READ/WRITE routines.
444 */
445 kva = vm_pager_map_page(m);
446 aiov.iov_base = (caddr_t)kva;
447 aiov.iov_len = size;
448 auio.uio_iov = &aiov;
449 auio.uio_iovcnt = 1;
450 auio.uio_offset = foff;
451 auio.uio_segflg = UIO_SYSSPACE;
452 auio.uio_rw = rw;
453 auio.uio_resid = size;
454 auio.uio_procp = (struct proc *)0;
455#ifdef DEBUG
456 if (vpagerdebug & VDB_IO)
457 printf("vnode_pager_io: vp %x kva %x foff %x size %x",
458 vnp->vnp_vp, kva, foff, size);
459#endif
460 if (rw == UIO_READ)
461 error = VOP_READ(vnp->vnp_vp, &auio, 0, p->p_ucred);
462 else
463 error = VOP_WRITE(vnp->vnp_vp, &auio, 0, p->p_ucred);
464#ifdef DEBUG
465 if (vpagerdebug & VDB_IO) {
466 if (error || auio.uio_resid)
467 printf(" returns error %x, resid %x",
468 error, auio.uio_resid);
469 printf("\n");
470 }
471#endif
472 if (!error) {
473 register int count = size - auio.uio_resid;
474
475 if (count == 0)
476 error = EINVAL;
477 else if (count != PAGE_SIZE && rw == UIO_READ)
478 bzero(kva + count, PAGE_SIZE - count);
479 }
480 vm_pager_unmap_page(kva);
481 return (error ? VM_PAGER_FAIL : VM_PAGER_OK);
482}
483#endif