* Copyright (c) 1990 University of Utah.
* Copyright (c) 1991 The Regents of the University of California.
* 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
* from: @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91
* $Id: vnode_pager.c,v 1.2 1993/10/16 16:21:02 rgrimes Exp $
* Page to/from files (vnodes).
* fix credential use (uses current process credentials now)
struct pagerops vnodepagerops
= {
queue_head_t vnode_pager_list
; /* list of managed vnodes */
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_init()\n");
queue_init(&vnode_pager_list
);
* Allocate (or lookup) pager for a vnode.
* Handle is a vnode pointer.
vnode_pager_alloc(handle
, size
, prot
)
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
;
queue_enter(&vnode_pager_list
, pager
, vm_pager_t
, pg_list
);
pager
->pg_handle
= handle
;
pager
->pg_type
= PG_VNODE
;
pager
->pg_ops
= &vnodepagerops
;
pager
->pg_data
= (caddr_t
)vnp
;
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
);
queue_remove(&vnode_pager_list
, pager
, vm_pager_t
, pg_list
);
free((caddr_t
)vnp
, M_VMPGDATA
);
free((caddr_t
)pager
, M_VMPAGER
);
vnode_pager_getpage(pager
, m
, sync
)
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_getpage(%x, %x)\n", pager
, m
);
return(vnode_pager_io((vn_pager_t
)pager
->pg_data
, m
, UIO_READ
));
vnode_pager_putpage(pager
, m
, sync
)
if (vpagerdebug
& VDB_FOLLOW
)
printf("vnode_pager_putpage(%x, %x)\n", pager
, m
);
err
= vnode_pager_io((vn_pager_t
)pager
->pg_data
, m
, UIO_WRITE
);
if (err
== VM_PAGER_OK
) {
m
->clean
= TRUE
; /* XXX - wrong place */
pmap_clear_modify(VM_PAGE_TO_PHYS(m
)); /* XXX - wrong place */
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
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_bsize
,
(struct vnode
**)0, &bn
);
if (vpagerdebug
& VDB_FAIL
)
printf("vnode_pager_haspage: BMAP err %d, pg %x, off %x\n",
return((long)bn
< 0 ? FALSE
: TRUE
);
* 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
;
pager
= (vm_pager_t
) queue_first(&vnode_pager_list
);
while (!queue_end(&vnode_pager_list
, (queue_entry_t
)pager
)) {
* Save the next pointer now since uncaching may
* terminate the object and render pager invalid
vp
= ((vn_pager_t
)pager
->pg_data
)->vnp_vp
;
npager
= (vm_pager_t
) queue_next(&pager
->pg_list
);
if (mp
== (struct mount
*)0 || vp
->v_mount
== mp
)
(void) vnode_pager_uncache(vp
);
* Remove vnode associated object from the object cache.
* Note: this routine may be invoked as a result of a pager put
* operation (possibly at object termination time), so we must be careful.
register struct vnode
*vp
;
register vm_object_t object
;
boolean_t uncached
, locked
;
pager
= (vm_pager_t
)vp
->v_vmdata
;
* Unlock the vnode if it is currently locked.
* We do this since uncaching the object may result
* in its destruction which may initiate paging
* activity which may necessitate locking the vnode.
locked
= VOP_ISLOCKED(vp
);
* 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
, m
, rw
)
struct proc
*p
= curproc
; /* XXX */
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
;
* Return failure if beyond current EOF
if (foff
>= vnp
->vnp_size
) {
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
;
* Allocate a kernel virtual address and initialize so that
* we can use VOP_READ/WRITE routines.
kva
= vm_pager_map_page(m
);
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(kva
+ count
, PAGE_SIZE
- count
);
vm_pager_unmap_page(kva
);
return (error
? VM_PAGER_FAIL
: VM_PAGER_OK
);