* Copyright (c) 1990 University of Utah.
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)vnode_pager.c 8.8 (Berkeley) 2/13/94
* Page to/from files (vnodes).
* fix credential use (uses current process credentials now)
#include <vm/vnode_pager.h>
struct pagerlst vnode_pager_list
; /* list of managed vnodes */
static vm_pager_t vnode_pager_alloc
__P((caddr_t
, vm_size_t
, vm_prot_t
, vm_offset_t
));
static void vnode_pager_cluster
__P((vm_pager_t
, vm_offset_t
,
vm_offset_t
*, vm_offset_t
*));
static void vnode_pager_dealloc
__P((vm_pager_t
));
static int vnode_pager_getpage
__P((vm_pager_t
, vm_page_t
*, int, boolean_t
));
static boolean_t vnode_pager_haspage
__P((vm_pager_t
, vm_offset_t
));
static void vnode_pager_init
__P((void));
static int vnode_pager_io
__P((vn_pager_t
, vm_page_t
*, int,
boolean_t
, enum uio_rw
));
static boolean_t vnode_pager_putpage
__P((vm_pager_t
, vm_page_t
*, int, boolean_t
));
struct pagerops vnodepagerops
= {
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_init()\n");
TAILQ_INIT(&vnode_pager_list
);
* Allocate (or lookup) pager for a vnode.
* Handle is a vnode pointer.
vnode_pager_alloc(handle
, size
, prot
, foff
)
register vm_pager_t pager
;
struct proc
*p
= curproc
; /* XXX */
if (vpagerdebug
& (VDB_FOLLOW
|VDB_ALLOC
))
printf("vnode_pager_alloc(%x, %x, %x)\n", handle
, size
, prot
);
* Pageout to vnode, no can do yet.
* Vnodes keep a pointer to any associated pager so no need to
* lookup with vm_pager_lookup.
vp
= (struct vnode
*)handle
;
pager
= (vm_pager_t
)vp
->v_vmdata
;
* Allocate pager structures
pager
= (vm_pager_t
)malloc(sizeof *pager
, M_VMPAGER
, M_WAITOK
);
vnp
= (vn_pager_t
)malloc(sizeof *vnp
, M_VMPGDATA
, M_WAITOK
);
free((caddr_t
)pager
, M_VMPAGER
);
* And an object of the appropriate size
if (VOP_GETATTR(vp
, &vattr
, p
->p_ucred
, p
) == 0) {
object
= vm_object_allocate(round_page(vattr
.va_size
));
vm_object_enter(object
, pager
);
vm_object_setpager(object
, pager
, 0, TRUE
);
free((caddr_t
)vnp
, M_VMPGDATA
);
free((caddr_t
)pager
, M_VMPAGER
);
* Hold a reference to the vnode and initialize pager data.
vnp
->vnp_size
= vattr
.va_size
;
TAILQ_INSERT_TAIL(&vnode_pager_list
, pager
, pg_list
);
pager
->pg_handle
= handle
;
pager
->pg_type
= PG_VNODE
;
pager
->pg_ops
= &vnodepagerops
;
vp
->v_vmdata
= (caddr_t
)pager
;
* vm_object_lookup() will remove the object from the
* cache if found and also gain a reference to the object.
object
= vm_object_lookup(pager
);
vnp
= (vn_pager_t
)pager
->pg_data
;
if (vpagerdebug
& VDB_ALLOC
)
printf("vnode_pager_setup: vp %x sz %x pager %x object %x\n",
vp
, vnp
->vnp_size
, pager
, object
);
vnode_pager_dealloc(pager
)
register vn_pager_t vnp
= (vn_pager_t
)pager
->pg_data
;
register struct vnode
*vp
;
struct proc
*p
= curproc
; /* XXX */
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_dealloc(%x)\n", pager
);
/* can hang if done at reboot on NFS FS */
(void) VOP_FSYNC(vp
, p
->p_ucred
, p
);
TAILQ_REMOVE(&vnode_pager_list
, pager
, pg_list
);
free((caddr_t
)vnp
, M_VMPGDATA
);
free((caddr_t
)pager
, M_VMPAGER
);
vnode_pager_getpage(pager
, mlist
, npages
, sync
)
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_getpage(%x, %x, %x, %x)\n",
pager
, mlist
, npages
, sync
);
return(vnode_pager_io((vn_pager_t
)pager
->pg_data
,
mlist
, npages
, sync
, UIO_READ
));
vnode_pager_putpage(pager
, mlist
, npages
, sync
)
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_putpage(%x, %x, %x, %x)\n",
pager
, mlist
, npages
, sync
);
return (FALSE
); /* ??? */
err
= vnode_pager_io((vn_pager_t
)pager
->pg_data
,
mlist
, npages
, sync
, UIO_WRITE
);
* If the operation was successful, mark the pages clean.
if (err
== VM_PAGER_OK
) {
(*mlist
)->flags
|= PG_CLEAN
;
pmap_clear_modify(VM_PAGE_TO_PHYS(*mlist
));
vnode_pager_haspage(pager
, offset
)
register vn_pager_t vnp
= (vn_pager_t
)pager
->pg_data
;
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_haspage(%x, %x)\n", pager
, offset
);
* Offset beyond end of file, do not have the page
* Lock the vnode first to make sure we have the most recent
if (offset
>= vnp
->vnp_size
) {
if (vpagerdebug
& (VDB_FAIL
|VDB_SIZE
))
printf("vnode_pager_haspage: pg %x, off %x, size %x\n",
pager
, offset
, vnp
->vnp_size
);
* Read the index to find the disk block to read
* from. If there is no block, report that we don't
* Assumes that the vnode has whole page or nothing.
err
= VOP_BMAP(vnp
->vnp_vp
,
offset
/ vnp
->vnp_vp
->v_mount
->mnt_stat
.f_iosize
,
(struct vnode
**)0, &bn
, NULL
);
if (vpagerdebug
& VDB_FAIL
)
printf("vnode_pager_haspage: BMAP err %d, pg %x, off %x\n",
return((long)bn
< 0 ? FALSE
: TRUE
);
vnode_pager_cluster(pager
, offset
, loffset
, hoffset
)
vn_pager_t vnp
= (vn_pager_t
)pager
->pg_data
;
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_cluster(%x, %x) ", pager
, offset
);
if (loff
>= vnp
->vnp_size
)
panic("vnode_pager_cluster: bad offset");
* XXX could use VOP_BMAP to get maxcontig value
if (hoff
> round_page(vnp
->vnp_size
))
hoff
= round_page(vnp
->vnp_size
);
if (vpagerdebug
& VDB_FOLLOW
)
printf("returns [%x-%x]\n", loff
, hoff
);
* Lets the VM system know about a change in size for a file.
* If this vnode is mapped into some address space (i.e. we have a pager
* for it) we adjust our own internal size and flush any cached pages in
* the associated object that are affected by the size change.
* Note: this routine may be invoked as a result of a pager put
* operation (possibly at object termination time), so we must be careful.
vnode_pager_setsize(vp
, nsize
)
register vm_object_t object
;
if (vp
== NULL
|| vp
->v_type
!= VREG
|| vp
->v_vmdata
== NULL
)
pager
= (vm_pager_t
)vp
->v_vmdata
;
vnp
= (vn_pager_t
)pager
->pg_data
;
if (nsize
== vnp
->vnp_size
)
* This can happen during object termination since
* vm_object_page_clean is called after the object
* has been removed from the hash table, and clean
* may cause vnode write operations which can wind
object
= vm_object_lookup(pager
);
if (vpagerdebug
& (VDB_FOLLOW
|VDB_SIZE
))
printf("vnode_pager_setsize: vp %x obj %x osz %d nsz %d\n",
vp
, object
, vnp
->vnp_size
, nsize
);
* Toss any cached pages beyond the new EOF.
if (nsize
< vnp
->vnp_size
) {
vm_object_page_remove(object
,
(vm_offset_t
)nsize
, vnp
->vnp_size
);
vm_object_unlock(object
);
vnp
->vnp_size
= (vm_offset_t
)nsize
;
vm_object_deallocate(object
);
register struct mount
*mp
;
register vm_pager_t pager
, npager
;
for (pager
= vnode_pager_list
.tqh_first
; pager
!= NULL
; pager
= npager
){
* Save the next pointer now since uncaching may
* terminate the object and render pager invalid
npager
= pager
->pg_list
.tqe_next
;
vp
= ((vn_pager_t
)pager
->pg_data
)->vnp_vp
;
if (mp
== (struct mount
*)0 || vp
->v_mount
== mp
) {
(void) vnode_pager_uncache(vp
);
* Remove vnode associated object from the object cache.
* XXX unlock the vnode if it is currently locked.
* We must do this since uncaching the object may result in its
* destruction which may initiate paging activity which may necessitate
register struct vnode
*vp
;
register vm_object_t object
;
pager
= (vm_pager_t
)vp
->v_vmdata
;
extern int (**nfsv2_vnodeop_p
)();
if (vp
->v_op
!= nfsv2_vnodeop_p
)
panic("vnode_pager_uncache: vnode not locked!");
* Must use vm_object_lookup() as it actually removes
* the object from the cache list.
object
= vm_object_lookup(pager
);
uncached
= (object
->ref_count
<= 1);
pager_cache(object
, FALSE
);
vnode_pager_io(vnp
, mlist
, npages
, sync
, rw
)
struct proc
*p
= curproc
; /* XXX */
panic("vnode_pager_io: cannot handle multiple pages");
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_io(%x, %x, %c): vnode %x\n",
vnp
, m
, rw
== UIO_READ
? 'R' : 'W', vnp
->vnp_vp
);
foff
= m
->offset
+ m
->object
->paging_offset
;
* Allocate a kernel virtual address and initialize so that
* we can use VOP_READ/WRITE routines.
kva
= vm_pager_map_pages(mlist
, npages
, sync
);
* After all of the potentially blocking operations have been
* performed, we can do the size checks:
* read beyond EOF (returns error)
if (foff
>= vnp
->vnp_size
) {
vm_pager_unmap_pages(kva
, npages
);
if (vpagerdebug
& VDB_SIZE
)
printf("vnode_pager_io: vp %x, off %d size %d\n",
vnp
->vnp_vp
, foff
, vnp
->vnp_size
);
if (foff
+ PAGE_SIZE
> vnp
->vnp_size
)
size
= vnp
->vnp_size
- foff
;
aiov
.iov_base
= (caddr_t
)kva
;
auio
.uio_segflg
= UIO_SYSSPACE
;
auio
.uio_procp
= (struct proc
*)0;
if (vpagerdebug
& VDB_IO
)
printf("vnode_pager_io: vp %x kva %x foff %x size %x",
vnp
->vnp_vp
, kva
, foff
, size
);
error
= VOP_READ(vnp
->vnp_vp
, &auio
, 0, p
->p_ucred
);
error
= VOP_WRITE(vnp
->vnp_vp
, &auio
, 0, p
->p_ucred
);
if (vpagerdebug
& VDB_IO
) {
if (error
|| auio
.uio_resid
)
printf(" returns error %x, resid %x",
register int count
= size
- auio
.uio_resid
;
else if (count
!= PAGE_SIZE
&& rw
== UIO_READ
)
bzero((void *)(kva
+ count
), PAGE_SIZE
- count
);
vm_pager_unmap_pages(kva
, npages
);
return (error
? VM_PAGER_ERROR
: VM_PAGER_OK
);