version 1.17, 1998/11/04 06:21:40 |
version 1.17.2.6, 1999/04/30 04:29:15 |
|
|
*/ |
*/ |
|
|
#include "fs_nfs.h" |
#include "fs_nfs.h" |
|
#include "opt_uvm.h" |
#include "opt_uvmhist.h" |
#include "opt_uvmhist.h" |
|
|
/* |
/* |
|
|
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
|
#include <sys/kernel.h> |
#include <sys/proc.h> |
#include <sys/proc.h> |
#include <sys/malloc.h> |
#include <sys/malloc.h> |
#include <sys/vnode.h> |
#include <sys/vnode.h> |
Line 94 lock_data_t uvn_sync_lock; /* locks sy |
|
Line 96 lock_data_t uvn_sync_lock; /* locks sy |
|
* functions |
* functions |
*/ |
*/ |
|
|
static int uvn_asyncget __P((struct uvm_object *, vaddr_t, |
static int uvn_asyncget __P((struct uvm_object *, vaddr_t, |
int)); |
int)); |
struct uvm_object *uvn_attach __P((void *, vm_prot_t)); |
struct uvm_object * uvn_attach __P((void *, vm_prot_t)); |
static void uvn_cluster __P((struct uvm_object *, vaddr_t, |
static void uvn_cluster __P((struct uvm_object *, vaddr_t, |
vaddr_t *, vaddr_t *)); |
vaddr_t *, vaddr_t *)); |
static void uvn_detach __P((struct uvm_object *)); |
static void uvn_detach __P((struct uvm_object *)); |
static boolean_t uvn_flush __P((struct uvm_object *, vaddr_t, |
static int uvn_findpage __P((struct uvm_object *, vaddr_t, |
vaddr_t, int)); |
struct vm_page **, int)); |
static int uvn_get __P((struct uvm_object *, vaddr_t, |
static boolean_t uvn_flush __P((struct uvm_object *, vaddr_t, |
vm_page_t *, int *, int, |
vaddr_t, int)); |
vm_prot_t, int, int)); |
static int uvn_get __P((struct uvm_object *, vaddr_t, |
static void uvn_init __P((void)); |
vm_page_t *, int *, int, |
static int uvn_io __P((struct uvm_vnode *, vm_page_t *, |
vm_prot_t, int, int)); |
int, int, int)); |
static void uvn_init __P((void)); |
static int uvn_put __P((struct uvm_object *, vm_page_t *, |
static int uvn_put __P((struct uvm_object *, vm_page_t *, |
int, boolean_t)); |
int, boolean_t)); |
static void uvn_reference __P((struct uvm_object *)); |
static void uvn_reference __P((struct uvm_object *)); |
static boolean_t uvn_releasepg __P((struct vm_page *, |
static boolean_t uvn_releasepg __P((struct vm_page *, |
struct vm_page **)); |
struct vm_page **)); |
|
|
/* |
/* |
* master pager structure |
* master pager structure |
Line 180 uvn_attach(arg, accessprot) |
|
Line 182 uvn_attach(arg, accessprot) |
|
struct vattr vattr; |
struct vattr vattr; |
int oldflags, result; |
int oldflags, result; |
struct partinfo pi; |
struct partinfo pi; |
u_quad_t used_vnode_size; |
off_t used_vnode_size; |
UVMHIST_FUNC("uvn_attach"); UVMHIST_CALLED(maphist); |
UVMHIST_FUNC("uvn_attach"); UVMHIST_CALLED(maphist); |
|
|
UVMHIST_LOG(maphist, "(vn=0x%x)", arg,0,0,0); |
UVMHIST_LOG(maphist, "(vn=0x%x)", arg,0,0,0); |
Line 201 uvn_attach(arg, accessprot) |
|
Line 203 uvn_attach(arg, accessprot) |
|
} |
} |
|
|
/* |
/* |
* if we're maping a BLK device, make sure it is a disk. |
* if we're mapping a BLK device, make sure it is a disk. |
*/ |
*/ |
if (vp->v_type == VBLK && bdevsw[major(vp->v_rdev)].d_type != D_DISK) { |
if (vp->v_type == VBLK && bdevsw[major(vp->v_rdev)].d_type != D_DISK) { |
simple_unlock(&uvn->u_obj.vmobjlock); /* drop lock */ |
simple_unlock(&uvn->u_obj.vmobjlock); |
UVMHIST_LOG(maphist,"<- done (VBLK not D_DISK!)", 0,0,0,0); |
UVMHIST_LOG(maphist,"<- done (VBLK not D_DISK!)", 0,0,0,0); |
return(NULL); |
return(NULL); |
} |
} |
|
|
/* |
oldflags = 0; |
* now we have lock and uvn must not be in a blocked state. |
|
* first check to see if it is already active, in which case |
|
* we can bump the reference count, check to see if we need to |
|
* add it to the writeable list, and then return. |
|
*/ |
|
if (uvn->u_flags & UVM_VNODE_VALID) { /* already active? */ |
|
|
|
/* regain VREF if we were persisting */ |
|
if (uvn->u_obj.uo_refs == 0) { |
|
VREF(vp); |
|
UVMHIST_LOG(maphist," VREF (reclaim persisting vnode)", |
|
0,0,0,0); |
|
} |
|
uvn->u_obj.uo_refs++; /* bump uvn ref! */ |
|
|
|
/* check for new writeable uvn */ |
#ifdef DIAGNOSTIC |
if ((accessprot & VM_PROT_WRITE) != 0 && |
if (vp->v_type != VREG) { |
(uvn->u_flags & UVM_VNODE_WRITEABLE) == 0) { |
panic("uvn_attach: vp %p not VREG", vp); |
simple_lock(&uvn_wl_lock); |
} |
LIST_INSERT_HEAD(&uvn_wlist, uvn, u_wlist); |
#endif |
simple_unlock(&uvn_wl_lock); |
|
/* we are now on wlist! */ |
|
uvn->u_flags |= UVM_VNODE_WRITEABLE; |
|
} |
|
|
|
/* unlock and return */ |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
UVMHIST_LOG(maphist,"<- done, refcnt=%d", uvn->u_obj.uo_refs, |
|
0, 0, 0); |
|
return (&uvn->u_obj); |
|
} |
|
|
|
/* |
/* |
* need to call VOP_GETATTR() to get the attributes, but that could |
* set up our idea of the size |
* block (due to I/O), so we want to unlock the object before calling. |
* if this hasn't been done already. |
* however, we want to keep anyone else from playing with the object |
|
* while it is unlocked. to do this we set UVM_VNODE_ALOCK which |
|
* prevents anyone from attaching to the vnode until we are done with |
|
* it. |
|
*/ |
*/ |
|
if (uvn->u_size == VSIZENOTSET) { |
|
|
uvn->u_flags = UVM_VNODE_ALOCK; |
uvn->u_flags = UVM_VNODE_ALOCK; |
simple_unlock(&uvn->u_obj.vmobjlock); /* drop lock in case we sleep */ |
simple_unlock(&uvn->u_obj.vmobjlock); /* drop lock in case we sleep */ |
/* XXX: curproc? */ |
/* XXX: curproc? */ |
|
|
if (vp->v_type == VBLK) { |
if (vp->v_type == VBLK) { |
/* |
/* |
* We could implement this as a specfs getattr call, but: |
* We could implement this as a specfs getattr call, but: |
Line 276 uvn_attach(arg, accessprot) |
|
Line 250 uvn_attach(arg, accessprot) |
|
used_vnode_size = vattr.va_size; |
used_vnode_size = vattr.va_size; |
} |
} |
|
|
/* relock object */ |
|
simple_lock(&uvn->u_obj.vmobjlock); |
|
|
|
if (result != 0) { |
|
if (uvn->u_flags & UVM_VNODE_WANTED) |
|
wakeup(uvn); |
|
uvn->u_flags = 0; |
|
simple_unlock(&uvn->u_obj.vmobjlock); /* drop lock */ |
|
UVMHIST_LOG(maphist,"<- done (VOP_GETATTR FAILED!)", 0,0,0,0); |
|
return(NULL); |
|
} |
|
|
|
/* |
/* |
* make sure that the newsize fits within a vaddr_t |
* make sure that the newsize fits within a vaddr_t |
* XXX: need to revise addressing data types |
* XXX: need to revise addressing data types |
*/ |
*/ |
if (vp->v_type == VBLK) printf("used_vnode_size = %qu\n", used_vnode_size); |
|
if (used_vnode_size > (vaddr_t) -PAGE_SIZE) { |
if (used_vnode_size > (vaddr_t) -PAGE_SIZE) { |
#ifdef DEBUG |
#ifdef DEBUG |
printf("uvn_attach: vn %p size truncated %qx->%x\n", vp, |
printf("uvn_attach: vn %p size truncated %qx->%x\n", vp, |
Line 301 if (vp->v_type == VBLK) printf("used_vno |
|
Line 263 if (vp->v_type == VBLK) printf("used_vno |
|
used_vnode_size = (vaddr_t) -PAGE_SIZE; |
used_vnode_size = (vaddr_t) -PAGE_SIZE; |
} |
} |
|
|
/* |
/* relock object */ |
* now set up the uvn. |
simple_lock(&uvn->u_obj.vmobjlock); |
*/ |
|
uvn->u_obj.pgops = &uvm_vnodeops; |
if (uvn->u_flags & UVM_VNODE_WANTED) |
TAILQ_INIT(&uvn->u_obj.memq); |
wakeup(uvn); |
uvn->u_obj.uo_npages = 0; |
uvn->u_flags = 0; |
uvn->u_obj.uo_refs = 1; /* just us... */ |
|
oldflags = uvn->u_flags; |
if (result != 0) { |
uvn->u_flags = UVM_VNODE_VALID|UVM_VNODE_CANPERSIST; |
simple_unlock(&uvn->u_obj.vmobjlock); /* drop lock */ |
uvn->u_nio = 0; |
UVMHIST_LOG(maphist,"<- done (VOP_GETATTR FAILED!)", 0,0,0,0); |
|
return(NULL); |
|
} |
uvn->u_size = used_vnode_size; |
uvn->u_size = used_vnode_size; |
|
|
/* if write access, we need to add it to the wlist */ |
} |
if (accessprot & VM_PROT_WRITE) { |
|
|
/* check for new writeable uvn */ |
|
if ((accessprot & VM_PROT_WRITE) != 0 && |
|
(uvn->u_flags & UVM_VNODE_WRITEABLE) == 0) { |
simple_lock(&uvn_wl_lock); |
simple_lock(&uvn_wl_lock); |
LIST_INSERT_HEAD(&uvn_wlist, uvn, u_wlist); |
LIST_INSERT_HEAD(&uvn_wlist, uvn, u_wlist); |
|
uvn->u_flags |= UVM_VNODE_WRITEABLE; |
simple_unlock(&uvn_wl_lock); |
simple_unlock(&uvn_wl_lock); |
uvn->u_flags |= UVM_VNODE_WRITEABLE; /* we are on wlist! */ |
/* we are now on wlist! */ |
} |
} |
|
|
/* |
/* unlock and return */ |
* add a reference to the vnode. this reference will stay as long |
|
* as there is a valid mapping of the vnode. dropped when the |
|
* reference count goes to zero [and we either free or persist]. |
|
*/ |
|
VREF(vp); |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
simple_unlock(&uvn->u_obj.vmobjlock); |
if (oldflags & UVM_VNODE_WANTED) |
UVMHIST_LOG(maphist,"<- done, refcnt=%d", uvn->u_obj.uo_refs, |
wakeup(uvn); |
0, 0, 0); |
|
return (&uvn->u_obj); |
UVMHIST_LOG(maphist,"<- done/VREF, ret 0x%x", &uvn->u_obj,0,0,0); |
|
return(&uvn->u_obj); |
|
} |
} |
|
|
|
|
|
|
uvn_reference(uobj) |
uvn_reference(uobj) |
struct uvm_object *uobj; |
struct uvm_object *uobj; |
{ |
{ |
#ifdef DIAGNOSTIC |
|
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj; |
|
#endif |
|
UVMHIST_FUNC("uvn_reference"); UVMHIST_CALLED(maphist); |
UVMHIST_FUNC("uvn_reference"); UVMHIST_CALLED(maphist); |
|
|
simple_lock(&uobj->vmobjlock); |
VREF((struct vnode *)uobj); |
#ifdef DIAGNOSTIC |
|
if ((uvn->u_flags & UVM_VNODE_VALID) == 0) { |
|
printf("uvn_reference: ref=%d, flags=0x%x\n", uvn->u_flags, |
|
uobj->uo_refs); |
|
panic("uvn_reference: invalid state"); |
|
} |
|
#endif |
|
uobj->uo_refs++; |
|
UVMHIST_LOG(maphist, "<- done (uobj=0x%x, ref = %d)", |
|
uobj, uobj->uo_refs,0,0); |
|
simple_unlock(&uobj->vmobjlock); |
|
} |
} |
|
|
/* |
/* |
|
|
uvn_detach(uobj) |
uvn_detach(uobj) |
struct uvm_object *uobj; |
struct uvm_object *uobj; |
{ |
{ |
struct uvm_vnode *uvn; |
|
struct vnode *vp; |
|
int oldflags; |
|
UVMHIST_FUNC("uvn_detach"); UVMHIST_CALLED(maphist); |
UVMHIST_FUNC("uvn_detach"); UVMHIST_CALLED(maphist); |
|
|
simple_lock(&uobj->vmobjlock); |
vrele((struct vnode *)uobj); |
|
|
UVMHIST_LOG(maphist," (uobj=0x%x) ref=%d", uobj,uobj->uo_refs,0,0); |
|
uobj->uo_refs--; /* drop ref! */ |
|
if (uobj->uo_refs) { /* still more refs */ |
|
simple_unlock(&uobj->vmobjlock); |
|
UVMHIST_LOG(maphist, "<- done (rc>0)", 0,0,0,0); |
|
return; |
|
} |
|
|
|
/* |
|
* get other pointers ... |
|
*/ |
|
|
|
uvn = (struct uvm_vnode *) uobj; |
|
vp = (struct vnode *) uobj; |
|
|
|
/* |
|
* clear VTEXT flag now that there are no mappings left (VTEXT is used |
|
* to keep an active text file from being overwritten). |
|
*/ |
|
vp->v_flag &= ~VTEXT; |
|
|
|
/* |
|
* we just dropped the last reference to the uvn. see if we can |
|
* let it "stick around". |
|
*/ |
|
|
|
if (uvn->u_flags & UVM_VNODE_CANPERSIST) { |
|
/* won't block */ |
|
uvn_flush(uobj, 0, 0, PGO_DEACTIVATE|PGO_ALLPAGES); |
|
simple_unlock(&uobj->vmobjlock); |
|
vrele(vp); /* drop vnode reference */ |
|
UVMHIST_LOG(maphist,"<- done/vrele! (persist)", 0,0,0,0); |
|
return; |
|
} |
|
|
|
/* |
|
* its a goner! |
|
*/ |
|
|
|
UVMHIST_LOG(maphist," its a goner (flushing)!", 0,0,0,0); |
|
|
|
uvn->u_flags |= UVM_VNODE_DYING; |
|
|
|
/* |
|
* even though we may unlock in flush, no one can gain a reference |
|
* to us until we clear the "dying" flag [because it blocks |
|
* attaches]. we will not do that until after we've disposed of all |
|
* the pages with uvn_flush(). note that before the flush the only |
|
* pages that could be marked PG_BUSY are ones that are in async |
|
* pageout by the daemon. (there can't be any pending "get"'s |
|
* because there are no references to the object). |
|
*/ |
|
|
|
(void) uvn_flush(uobj, 0, 0, PGO_CLEANIT|PGO_FREE|PGO_ALLPAGES); |
|
|
|
UVMHIST_LOG(maphist," its a goner (done flush)!", 0,0,0,0); |
|
|
|
/* |
|
* given the structure of this pager, the above flush request will |
|
* create the following state: all the pages that were in the object |
|
* have either been free'd or they are marked PG_BUSY|PG_RELEASED. |
|
* the PG_BUSY bit was set either by us or the daemon for async I/O. |
|
* in either case, if we have pages left we can't kill the object |
|
* yet because i/o is pending. in this case we set the "relkill" |
|
* flag which will cause pgo_releasepg to kill the object once all |
|
* the I/O's are done [pgo_releasepg will be called from the aiodone |
|
* routine or from the page daemon]. |
|
*/ |
|
|
|
if (uobj->uo_npages) { /* I/O pending. iodone will free */ |
|
#ifdef DIAGNOSTIC |
|
/* |
|
* XXXCDC: very unlikely to happen until we have async i/o |
|
* so print a little info message in case it does. |
|
*/ |
|
printf("uvn_detach: vn %p has pages left after flush - " |
|
"relkill mode\n", uobj); |
|
#endif |
|
uvn->u_flags |= UVM_VNODE_RELKILL; |
|
simple_unlock(&uobj->vmobjlock); |
|
UVMHIST_LOG(maphist,"<- done! (releasepg will kill obj)", 0, 0, |
|
0, 0); |
|
return; |
|
} |
|
|
|
/* |
|
* kill object now. note that we can't be on the sync q because |
|
* all references are gone. |
|
*/ |
|
if (uvn->u_flags & UVM_VNODE_WRITEABLE) { |
|
simple_lock(&uvn_wl_lock); /* protect uvn_wlist */ |
|
LIST_REMOVE(uvn, u_wlist); |
|
simple_unlock(&uvn_wl_lock); |
|
} |
|
#ifdef DIAGNOSTIC |
|
if (uobj->memq.tqh_first != NULL) |
|
panic("uvn_deref: vnode VM object still has pages afer " |
|
"syncio/free flush"); |
|
#endif |
|
oldflags = uvn->u_flags; |
|
uvn->u_flags = 0; |
|
simple_unlock(&uobj->vmobjlock); |
|
|
|
/* wake up any sleepers */ |
|
if (oldflags & UVM_VNODE_WANTED) |
|
wakeup(uvn); |
|
|
|
/* |
|
* drop our reference to the vnode. |
|
*/ |
|
vrele(vp); |
|
UVMHIST_LOG(maphist,"<- done (vrele) final", 0,0,0,0); |
|
|
|
return; |
|
} |
} |
|
|
/* |
/* |
Line 540 uvm_vnp_terminate(vp) |
|
Line 369 uvm_vnp_terminate(vp) |
|
struct vnode *vp; |
struct vnode *vp; |
{ |
{ |
struct uvm_vnode *uvn = &vp->v_uvm; |
struct uvm_vnode *uvn = &vp->v_uvm; |
int oldflags; |
|
UVMHIST_FUNC("uvm_vnp_terminate"); UVMHIST_CALLED(maphist); |
|
|
|
/* |
if (uvn->u_flags & UVM_VNODE_WRITEABLE) { |
* lock object and check if it is valid |
simple_lock(&uvn_wl_lock); |
*/ |
LIST_REMOVE(uvn, u_wlist); |
simple_lock(&uvn->u_obj.vmobjlock); |
uvn->u_flags &= ~(UVM_VNODE_WRITEABLE); |
UVMHIST_LOG(maphist, " vp=0x%x, ref=%d, flag=0x%x", vp, |
simple_unlock(&uvn_wl_lock); |
uvn->u_obj.uo_refs, uvn->u_flags, 0); |
|
if ((uvn->u_flags & UVM_VNODE_VALID) == 0) { |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
UVMHIST_LOG(maphist, "<- done (not active)", 0, 0, 0, 0); |
|
return; |
|
} |
|
|
|
/* |
|
* must be a valid uvn that is not already dying (because XLOCK |
|
* protects us from that). the uvn can't in the the ALOCK state |
|
* because it is valid, and uvn's that are in the ALOCK state haven't |
|
* been marked valid yet. |
|
*/ |
|
|
|
#ifdef DEBUG |
|
/* |
|
* debug check: are we yanking the vnode out from under our uvn? |
|
*/ |
|
if (uvn->u_obj.uo_refs) { |
|
printf("uvm_vnp_terminate(%p): terminating active vnode " |
|
"(refs=%d)\n", uvn, uvn->u_obj.uo_refs); |
|
} |
|
#endif |
|
|
|
/* |
|
* it is possible that the uvn was detached and is in the relkill |
|
* state [i.e. waiting for async i/o to finish so that releasepg can |
|
* kill object]. we take over the vnode now and cancel the relkill. |
|
* we want to know when the i/o is done so we can recycle right |
|
* away. note that a uvn can only be in the RELKILL state if it |
|
* has a zero reference count. |
|
*/ |
|
|
|
if (uvn->u_flags & UVM_VNODE_RELKILL) |
|
uvn->u_flags &= ~UVM_VNODE_RELKILL; /* cancel RELKILL */ |
|
|
|
/* |
|
* block the uvn by setting the dying flag, and then flush the |
|
* pages. (note that flush may unlock object while doing I/O, but |
|
* it will re-lock it before it returns control here). |
|
* |
|
* also, note that we tell I/O that we are already VOP_LOCK'd so |
|
* that uvn_io doesn't attempt to VOP_LOCK again. |
|
* |
|
* XXXCDC: setting VNISLOCKED on an active uvn which is being terminated |
|
* due to a forceful unmount might not be a good idea. maybe we |
|
* need a way to pass in this info to uvn_flush through a |
|
* pager-defined PGO_ constant [currently there are none]. |
|
*/ |
|
uvn->u_flags |= UVM_VNODE_DYING|UVM_VNODE_VNISLOCKED; |
|
|
|
(void) uvn_flush(&uvn->u_obj, 0, 0, PGO_CLEANIT|PGO_FREE|PGO_ALLPAGES); |
|
|
|
/* |
|
* as we just did a flush we expect all the pages to be gone or in |
|
* the process of going. sleep to wait for the rest to go [via iosync]. |
|
*/ |
|
|
|
while (uvn->u_obj.uo_npages) { |
|
#ifdef DIAGNOSTIC |
|
struct vm_page *pp; |
|
for (pp = uvn->u_obj.memq.tqh_first ; pp != NULL ; |
|
pp = pp->listq.tqe_next) { |
|
if ((pp->flags & PG_BUSY) == 0) |
|
panic("uvm_vnp_terminate: detected unbusy pg"); |
|
} |
|
if (uvn->u_nio == 0) |
|
panic("uvm_vnp_terminate: no I/O to wait for?"); |
|
printf("uvm_vnp_terminate: waiting for I/O to fin.\n"); |
|
/* |
|
* XXXCDC: this is unlikely to happen without async i/o so we |
|
* put a printf in just to keep an eye on it. |
|
*/ |
|
#endif |
|
uvn->u_flags |= UVM_VNODE_IOSYNC; |
|
UVM_UNLOCK_AND_WAIT(&uvn->u_nio, &uvn->u_obj.vmobjlock, FALSE, |
|
"uvn_term",0); |
|
simple_lock(&uvn->u_obj.vmobjlock); |
|
} |
|
|
|
/* |
|
* done. now we free the uvn if its reference count is zero |
|
* (true if we are zapping a persisting uvn). however, if we are |
|
* terminating a uvn with active mappings we let it live ... future |
|
* calls down to the vnode layer will fail. |
|
*/ |
|
|
|
oldflags = uvn->u_flags; |
|
if (uvn->u_obj.uo_refs) { |
|
|
|
/* |
|
* uvn must live on it is dead-vnode state until all references |
|
* are gone. restore flags. clear CANPERSIST state. |
|
*/ |
|
|
|
uvn->u_flags &= ~(UVM_VNODE_DYING|UVM_VNODE_VNISLOCKED| |
|
UVM_VNODE_WANTED|UVM_VNODE_CANPERSIST); |
|
|
|
} else { |
|
|
|
/* |
|
* free the uvn now. note that the VREF reference is already |
|
* gone [it is dropped when we enter the persist state]. |
|
*/ |
|
if (uvn->u_flags & UVM_VNODE_IOSYNCWANTED) |
|
panic("uvm_vnp_terminate: io sync wanted bit set"); |
|
|
|
if (uvn->u_flags & UVM_VNODE_WRITEABLE) { |
|
simple_lock(&uvn_wl_lock); |
|
LIST_REMOVE(uvn, u_wlist); |
|
simple_unlock(&uvn_wl_lock); |
|
} |
|
uvn->u_flags = 0; /* uvn is history, clear all bits */ |
|
} |
} |
|
|
if (oldflags & UVM_VNODE_WANTED) |
|
wakeup(uvn); /* object lock still held */ |
|
|
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
UVMHIST_LOG(maphist, "<- done", 0, 0, 0, 0); |
|
|
|
} |
} |
|
|
/* |
/* |
Line 710 uvn_releasepg(pg, nextpgp) |
|
Line 417 uvn_releasepg(pg, nextpgp) |
|
if (!nextpgp) |
if (!nextpgp) |
uvm_unlock_pageq(); |
uvm_unlock_pageq(); |
|
|
|
#ifdef UBC |
|
/* XXX I'm sure we need to do something here. */ |
|
uvn = uvn; |
|
#else |
/* |
/* |
* now see if we need to kill the object |
* now see if we need to kill the object |
*/ |
*/ |
Line 736 uvn_releasepg(pg, nextpgp) |
|
Line 447 uvn_releasepg(pg, nextpgp) |
|
return (FALSE); |
return (FALSE); |
} |
} |
} |
} |
|
#endif |
return (TRUE); |
return (TRUE); |
} |
} |
|
|
Line 840 uvn_flush(uobj, start, stop, flags) |
|
Line 552 uvn_flush(uobj, start, stop, flags) |
|
int flags; |
int flags; |
{ |
{ |
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj; |
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj; |
|
struct vnode *vp = (struct vnode *)uobj; |
struct vm_page *pp, *ppnext, *ptmp; |
struct vm_page *pp, *ppnext, *ptmp; |
struct vm_page *pps[MAXBSIZE >> PAGE_SHIFT], **ppsp; |
struct vm_page *pps[MAXBSIZE >> PAGE_SHIFT], **ppsp; |
int npages, result, lcv; |
int npages, result, lcv; |
Line 848 uvn_flush(uobj, start, stop, flags) |
|
Line 561 uvn_flush(uobj, start, stop, flags) |
|
u_short pp_version; |
u_short pp_version; |
UVMHIST_FUNC("uvn_flush"); UVMHIST_CALLED(maphist); |
UVMHIST_FUNC("uvn_flush"); UVMHIST_CALLED(maphist); |
|
|
|
#ifdef UBC |
|
if (uvn->u_size == VSIZENOTSET) { |
|
void vp_name(void *); |
|
|
|
#ifdef DEBUG |
|
printf("uvn_flush: size not set vp %p\n", uvn); |
|
if ((flags & PGO_ALLPAGES) == 0) |
|
printf("... and PGO_ALLPAGES not set: " |
|
"start 0x%lx end 0x%lx flags 0x%x\n", |
|
start, stop, flags); |
|
vp_name(uvn); |
|
#endif |
|
flags |= PGO_ALLPAGES; |
|
} |
|
#if 0 |
|
/* XXX unfortunately this is legitimate */ |
|
if ((flags & PGO_FREE) && uobj->uo_refs) { |
|
printf("uvn_flush: PGO_FREE on ref'd vp %p\n", uobj); |
|
Debugger(); |
|
} |
|
#endif |
|
#endif |
|
|
curoff = 0; /* XXX: shut up gcc */ |
curoff = 0; /* XXX: shut up gcc */ |
/* |
/* |
* get init vals and determine how we are going to traverse object |
* get init vals and determine how we are going to traverse object |
Line 857 uvn_flush(uobj, start, stop, flags) |
|
Line 593 uvn_flush(uobj, start, stop, flags) |
|
retval = TRUE; /* return value */ |
retval = TRUE; /* return value */ |
if (flags & PGO_ALLPAGES) { |
if (flags & PGO_ALLPAGES) { |
start = 0; |
start = 0; |
|
#ifdef UBC |
|
stop = -1; |
|
#else |
stop = round_page(uvn->u_size); |
stop = round_page(uvn->u_size); |
|
#endif |
by_list = TRUE; /* always go by the list */ |
by_list = TRUE; /* always go by the list */ |
} else { |
} else { |
start = trunc_page(start); |
start = trunc_page(start); |
stop = round_page(stop); |
stop = round_page(stop); |
if (stop > round_page(uvn->u_size)) |
if (stop > round_page(uvn->u_size)) { |
printf("uvn_flush: strange, got an out of range " |
printf("uvn_flush: oor vp %p start 0x%x stop 0x%x size 0x%x\n", uvn, (int)start, (int)stop, (int)round_page(uvn->u_size)); |
"flush (fixed)\n"); |
} |
|
|
by_list = (uobj->uo_npages <= |
by_list = (uobj->uo_npages <= |
((stop - start) >> PAGE_SHIFT) * UVN_HASH_PENALTY); |
((stop - start) >> PAGE_SHIFT) * UVN_HASH_PENALTY); |
Line 888 uvn_flush(uobj, start, stop, flags) |
|
Line 628 uvn_flush(uobj, start, stop, flags) |
|
if ((flags & PGO_CLEANIT) != 0 && |
if ((flags & PGO_CLEANIT) != 0 && |
uobj->pgops->pgo_mk_pcluster != NULL) { |
uobj->pgops->pgo_mk_pcluster != NULL) { |
if (by_list) { |
if (by_list) { |
for (pp = uobj->memq.tqh_first ; pp != NULL ; |
for (pp = TAILQ_FIRST(&uobj->memq); |
pp = pp->listq.tqe_next) { |
pp != NULL ; |
if (pp->offset < start || pp->offset >= stop) |
pp = TAILQ_NEXT(pp, listq)) { |
|
if (pp->offset < start || |
|
(pp->offset >= stop && stop != -1)) |
continue; |
continue; |
pp->flags &= ~PG_CLEANCHK; |
pp->flags &= ~PG_CLEANCHK; |
} |
} |
Line 912 uvn_flush(uobj, start, stop, flags) |
|
Line 654 uvn_flush(uobj, start, stop, flags) |
|
*/ |
*/ |
|
|
if (by_list) { |
if (by_list) { |
pp = uobj->memq.tqh_first; |
pp = TAILQ_FIRST(&uobj->memq); |
} else { |
} else { |
curoff = start; |
curoff = start; |
pp = uvm_pagelookup(uobj, curoff); |
pp = uvm_pagelookup(uobj, curoff); |
Line 933 uvn_flush(uobj, start, stop, flags) |
|
Line 675 uvn_flush(uobj, start, stop, flags) |
|
*/ |
*/ |
|
|
if (pp->offset < start || pp->offset >= stop) { |
if (pp->offset < start || pp->offset >= stop) { |
ppnext = pp->listq.tqe_next; |
ppnext = TAILQ_NEXT(pp, listq); |
continue; |
continue; |
} |
} |
|
|
|
|
|
|
/* locked: page queues, uobj */ |
/* locked: page queues, uobj */ |
result = uvm_pager_put(uobj, pp, &ppsp, &npages, |
result = uvm_pager_put(uobj, pp, &ppsp, &npages, |
flags | PGO_DOACTCLUST, start, stop); |
flags | PGO_DOACTCLUST, start, stop); |
/* unlocked: page queues, uobj */ |
/* unlocked: page queues, uobj */ |
|
|
/* |
/* |
|
|
if (result != VM_PAGER_PEND) { |
if (result != VM_PAGER_PEND) { |
if (ptmp->flags & PG_WANTED) |
if (ptmp->flags & PG_WANTED) |
/* still holding object lock */ |
/* still holding object lock */ |
thread_wakeup(ptmp); |
wakeup(ptmp); |
|
|
ptmp->flags &= ~(PG_WANTED|PG_BUSY); |
ptmp->flags &= ~(PG_WANTED|PG_BUSY); |
UVM_PAGE_OWN(ptmp, NULL); |
UVM_PAGE_OWN(ptmp, NULL); |
|
|
} else { |
} else { |
if (result != VM_PAGER_OK) { |
if (result != VM_PAGER_OK) { |
printf("uvn_flush: obj=%p, " |
printf("uvn_flush: obj=%p, " |
"offset=0x%lx. error " |
"offset=0x%lx. error %d\n", |
"during pageout.\n", |
pp->uobject, pp->offset, |
pp->uobject, pp->offset); |
result); |
printf("uvn_flush: WARNING: " |
printf("uvn_flush: WARNING: " |
"changes to page may be " |
"changes to page may be " |
"lost!\n"); |
"lost!\n"); |
|
|
if (need_iosync) { |
if (need_iosync) { |
|
|
UVMHIST_LOG(maphist," <<DOING IOSYNC>>",0,0,0,0); |
UVMHIST_LOG(maphist," <<DOING IOSYNC>>",0,0,0,0); |
|
#ifdef UBC |
|
/* |
|
* XXX this doesn't use the new two-flag scheme, |
|
* but to use that, all i/o initiators will have to change. |
|
*/ |
|
|
|
while (vp->v_numoutput != 0) { |
|
vp->v_flag |= VBWAIT; |
|
UVM_UNLOCK_AND_WAIT(&vp->v_numoutput, |
|
&uvn->u_obj.vmobjlock, |
|
FALSE, "uvn_flush",0); |
|
simple_lock(&uvn->u_obj.vmobjlock); |
|
} |
|
#else |
while (uvn->u_nio != 0) { |
while (uvn->u_nio != 0) { |
uvn->u_flags |= UVM_VNODE_IOSYNC; |
uvn->u_flags |= UVM_VNODE_IOSYNC; |
UVM_UNLOCK_AND_WAIT(&uvn->u_nio, &uvn->u_obj.vmobjlock, |
UVM_UNLOCK_AND_WAIT(&uvn->u_nio, &uvn->u_obj.vmobjlock, |
|
|
if (uvn->u_flags & UVM_VNODE_IOSYNCWANTED) |
if (uvn->u_flags & UVM_VNODE_IOSYNCWANTED) |
wakeup(&uvn->u_flags); |
wakeup(&uvn->u_flags); |
uvn->u_flags &= ~(UVM_VNODE_IOSYNC|UVM_VNODE_IOSYNCWANTED); |
uvn->u_flags &= ~(UVM_VNODE_IOSYNC|UVM_VNODE_IOSYNCWANTED); |
|
#endif |
} |
} |
|
|
/* return, with object locked! */ |
/* return, with object locked! */ |
Line 1269 uvn_cluster(uobj, offset, loffset, hoffs |
|
Line 1026 uvn_cluster(uobj, offset, loffset, hoffs |
|
vaddr_t *loffset, *hoffset; /* OUT */ |
vaddr_t *loffset, *hoffset; /* OUT */ |
{ |
{ |
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj; |
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj; |
|
UVMHIST_FUNC("uvn_cluster"); UVMHIST_CALLED(ubchist); |
|
|
*loffset = offset; |
*loffset = offset; |
|
|
if (*loffset >= uvn->u_size) |
if (*loffset >= uvn->u_size) |
panic("uvn_cluster: offset out of range"); |
#ifdef UBC |
|
{ |
|
/* XXX nfs writes cause trouble with this */ |
|
*loffset = *hoffset = offset; |
|
UVMHIST_LOG(ubchist, "uvn_cluster: offset out of range: vp %p loffset 0x%x", |
|
uobj, (int)*loffset, 0,0); |
|
Debugger(); |
|
return; |
|
} |
|
#else |
|
panic("uvn_cluster: offset out of range: vp %p loffset 0x%x", |
|
uobj, (int) *loffset); |
|
#endif |
|
|
/* |
/* |
* XXX: old pager claims we could use VOP_BMAP to get maxcontig value. |
* XXX: old pager claims we could use VOP_BMAP to get maxcontig value. |
Line 1301 uvn_put(uobj, pps, npages, flags) |
|
Line 1072 uvn_put(uobj, pps, npages, flags) |
|
struct vm_page **pps; |
struct vm_page **pps; |
int npages, flags; |
int npages, flags; |
{ |
{ |
int retval; |
int retval, sync; |
|
|
|
sync = (flags & PGO_SYNCIO) ? 1 : 0; |
|
|
/* note: object locked */ |
/* note: object locked */ |
retval = uvn_io((struct uvm_vnode*)uobj, pps, npages, flags, UIO_WRITE); |
simple_lock_assert(&uobj->vmobjlock, SLOCK_LOCKED); |
|
|
|
/* XXX why would the VOP need it locked? */ |
|
/* currently, just to increment vp->v_numoutput (aka uvn->u_nio) */ |
|
simple_unlock(&uobj->vmobjlock); |
|
retval = VOP_PUTPAGES((struct vnode *)uobj, pps, npages, sync, &retval); |
/* note: object unlocked */ |
/* note: object unlocked */ |
|
simple_lock_assert(&uobj->vmobjlock, SLOCK_UNLOCKED); |
|
|
return(retval); |
return(retval); |
} |
} |
Line 1331 uvn_get(uobj, offset, pps, npagesp, cent |
|
Line 1110 uvn_get(uobj, offset, pps, npagesp, cent |
|
int centeridx, advice, flags; |
int centeridx, advice, flags; |
vm_prot_t access_type; |
vm_prot_t access_type; |
{ |
{ |
vaddr_t current_offset; |
struct vnode *vp = (struct vnode *)uobj; |
struct vm_page *ptmp; |
int error; |
int lcv, result, gotpages; |
|
boolean_t done; |
|
UVMHIST_FUNC("uvn_get"); UVMHIST_CALLED(maphist); |
|
UVMHIST_LOG(maphist, "flags=%d", flags,0,0,0); |
|
|
|
/* |
|
* step 1: handled the case where fault data structures are locked. |
|
*/ |
|
|
|
if (flags & PGO_LOCKED) { |
|
|
|
/* |
|
* gotpages is the current number of pages we've gotten (which |
|
* we pass back up to caller via *npagesp. |
|
*/ |
|
|
|
gotpages = 0; |
|
|
|
/* |
|
* step 1a: get pages that are already resident. only do this |
|
* if the data structures are locked (i.e. the first time |
|
* through). |
|
*/ |
|
|
|
done = TRUE; /* be optimistic */ |
|
|
|
for (lcv = 0, current_offset = offset ; lcv < *npagesp ; |
|
lcv++, current_offset += PAGE_SIZE) { |
|
|
|
/* do we care about this page? if not, skip it */ |
simple_lock_assert(&uobj->vmobjlock, SLOCK_LOCKED); |
if (pps[lcv] == PGO_DONTCARE) |
error = VOP_GETPAGES(vp, offset, pps, npagesp, centeridx, |
continue; |
access_type, advice, flags); |
|
simple_lock_assert(&uobj->vmobjlock, flags & PGO_LOCKED ? |
/* lookup page */ |
SLOCK_LOCKED : SLOCK_UNLOCKED); |
ptmp = uvm_pagelookup(uobj, current_offset); |
|
|
|
/* to be useful must get a non-busy, non-released pg */ |
|
if (ptmp == NULL || |
|
(ptmp->flags & (PG_BUSY|PG_RELEASED)) != 0) { |
|
if (lcv == centeridx || (flags & PGO_ALLPAGES) |
|
!= 0) |
|
done = FALSE; /* need to do a wait or I/O! */ |
|
continue; |
|
} |
|
|
|
/* |
|
* useful page: busy/lock it and plug it in our |
|
* result array |
|
*/ |
|
ptmp->flags |= PG_BUSY; /* loan up to caller */ |
|
UVM_PAGE_OWN(ptmp, "uvn_get1"); |
|
pps[lcv] = ptmp; |
|
gotpages++; |
|
|
|
} /* "for" lcv loop */ |
return error ? VM_PAGER_ERROR : VM_PAGER_OK; |
|
} |
|
|
/* |
/* |
* XXX: given the "advice", should we consider async read-ahead? |
* uvn_findpages: |
* XXX: fault current does deactive of pages behind us. is |
* return the page for the uobj and offset requested, allocating if needed. |
* this good (other callers might now). |
* => uobj must be locked. |
*/ |
* => returned page will be BUSY. |
/* |
*/ |
* XXX: read-ahead currently handled by buffer cache (bread) |
|
* level. |
|
* XXX: no async i/o available. |
|
* XXX: so we don't do anything now. |
|
*/ |
|
|
|
/* |
void |
* step 1c: now we've either done everything needed or we to |
uvn_findpages(uobj, offset, npagesp, pps, flags) |
* unlock and do some waiting or I/O. |
struct uvm_object *uobj; |
*/ |
vaddr_t offset; |
|
int *npagesp; |
|
struct vm_page **pps; |
|
int flags; |
|
{ |
|
int i, rv, npages; |
|
|
*npagesp = gotpages; /* let caller know */ |
rv = 0; |
if (done) |
npages = *npagesp; |
return(VM_PAGER_OK); /* bingo! */ |
for (i = 0; i < npages; i++, offset += PAGE_SIZE) { |
else |
rv += uvn_findpage(uobj, offset, &pps[i], flags); |
/* EEK! Need to unlock and I/O */ |
|
return(VM_PAGER_UNLOCK); |
|
} |
} |
|
*npagesp = rv; |
|
} |
|
|
/* |
|
* step 2: get non-resident or busy pages. |
|
* object is locked. data structures are unlocked. |
|
* |
|
* XXX: because we can't do async I/O at this level we get things |
|
* page at a time (otherwise we'd chunk). the VOP_READ() will do |
|
* async-read-ahead for us at a lower level. |
|
*/ |
|
|
|
for (lcv = 0, current_offset = offset ; |
static int |
lcv < *npagesp ; lcv++, current_offset += PAGE_SIZE) { |
uvn_findpage(uobj, offset, pps, flags) |
|
struct uvm_object *uobj; |
/* skip over pages we've already gotten or don't want */ |
vaddr_t offset; |
/* skip over pages we don't _have_ to get */ |
struct vm_page **pps; |
if (pps[lcv] != NULL || (lcv != centeridx && |
int flags; |
(flags & PGO_ALLPAGES) == 0)) |
{ |
continue; |
struct vm_page *ptmp; |
|
UVMHIST_FUNC("uvn_findpage"); UVMHIST_CALLED(ubchist); |
|
UVMHIST_LOG(ubchist, "vp %p off 0x%lx", uobj, offset,0,0); |
|
|
/* |
simple_lock_assert(&uobj->vmobjlock, SLOCK_LOCKED); |
* we have yet to locate the current page (pps[lcv]). we first |
|
* look for a page that is already at the current offset. if |
|
* we fine a page, we check to see if it is busy or released. |
|
* if that is the case, then we sleep on the page until it is |
|
* no longer busy or released and repeat the lookup. if the |
|
* page we found is neither busy nor released, then we busy it |
|
* (so we own it) and plug it into pps[lcv]. this breaks the |
|
* following while loop and indicates we are ready to move on |
|
* to the next page in the "lcv" loop above. |
|
* |
|
* if we exit the while loop with pps[lcv] still set to NULL, |
|
* then it means that we allocated a new busy/fake/clean page |
|
* ptmp in the object and we need to do I/O to fill in the data. |
|
*/ |
|
|
|
while (pps[lcv] == NULL) { /* top of "pps" while loop */ |
if (*pps == PGO_DONTCARE) { |
|
UVMHIST_LOG(ubchist, "dontcare", 0,0,0,0); |
/* look for a current page */ |
return 0; |
ptmp = uvm_pagelookup(uobj, current_offset); |
} |
|
#ifdef DIAGNOTISTIC |
|
if (*pps != NULL) { |
|
panic("uvn_findpage: *pps not NULL"); |
|
} |
|
#endif |
|
|
/* nope? allocate one now (if we can) */ |
for (;;) { |
|
/* look for an existing page */ |
|
ptmp = uvm_pagelookup(uobj, offset); |
|
|
|
/* nope? allocate one now */ |
|
if (ptmp == NULL) { |
|
if (flags & UFP_NOALLOC) { |
|
UVMHIST_LOG(ubchist, "noalloc", 0,0,0,0); |
|
return 0; |
|
} |
|
ptmp = uvm_pagealloc(uobj, offset, NULL); |
if (ptmp == NULL) { |
if (ptmp == NULL) { |
|
if (flags & UFP_NOWAIT) { |
ptmp = uvm_pagealloc(uobj, current_offset, |
UVMHIST_LOG(ubchist, "nowait",0,0,0,0); |
NULL); /* alloc */ |
return 0; |
|
|
/* out of RAM? */ |
|
if (ptmp == NULL) { |
|
simple_unlock(&uobj->vmobjlock); |
|
uvm_wait("uvn_getpage"); |
|
simple_lock(&uobj->vmobjlock); |
|
|
|
/* goto top of pps while loop */ |
|
continue; |
|
} |
} |
|
simple_unlock(&uobj->vmobjlock); |
/* |
uvm_wait("uvn_fp1"); |
* got new page ready for I/O. break pps |
|
* while loop. pps[lcv] is still NULL. |
|
*/ |
|
break; |
|
} |
|
|
|
/* page is there, see if we need to wait on it */ |
|
if ((ptmp->flags & (PG_BUSY|PG_RELEASED)) != 0) { |
|
ptmp->flags |= PG_WANTED; |
|
UVM_UNLOCK_AND_WAIT(ptmp, |
|
&uobj->vmobjlock, 0, "uvn_get",0); |
|
simple_lock(&uobj->vmobjlock); |
simple_lock(&uobj->vmobjlock); |
continue; /* goto top of pps while loop */ |
continue; |
} |
} |
|
UVMHIST_LOG(ubchist, "alloced",0,0,0,0); |
/* |
break; |
* if we get here then the page has become resident |
} else if (flags & UFP_NOCACHE) { |
* and unbusy between steps 1 and 2. we busy it |
UVMHIST_LOG(ubchist, "nocache",0,0,0,0); |
* now (so we own it) and set pps[lcv] (so that we |
return 0; |
* exit the while loop). |
|
*/ |
|
ptmp->flags |= PG_BUSY; |
|
UVM_PAGE_OWN(ptmp, "uvn_get2"); |
|
pps[lcv] = ptmp; |
|
} |
} |
|
|
/* |
/* page is there, see if we need to wait on it */ |
* if we own the a valid page at the correct offset, pps[lcv] |
if ((ptmp->flags & (PG_BUSY|PG_RELEASED)) != 0) { |
* will point to it. nothing more to do except go to the |
if (flags & UFP_NOWAIT) { |
* next page. |
UVMHIST_LOG(ubchist, "nowait",0,0,0,0); |
*/ |
return 0; |
|
} |
if (pps[lcv]) |
ptmp->flags |= PG_WANTED; |
continue; /* next lcv */ |
UVM_UNLOCK_AND_WAIT(ptmp, &uobj->vmobjlock, 0, |
|
"uvn_fp2",0); |
/* |
simple_lock(&uobj->vmobjlock); |
* we have a "fake/busy/clean" page that we just allocated. do |
continue; |
* I/O to fill it with valid data. note that object must be |
|
* locked going into uvn_io, but will be unlocked afterwards. |
|
*/ |
|
|
|
result = uvn_io((struct uvm_vnode *) uobj, &ptmp, 1, |
|
PGO_SYNCIO, UIO_READ); |
|
|
|
/* |
|
* I/O done. object is unlocked (by uvn_io). because we used |
|
* syncio the result can not be PEND or AGAIN. we must relock |
|
* and check for errors. |
|
*/ |
|
|
|
/* lock object. check for errors. */ |
|
simple_lock(&uobj->vmobjlock); |
|
if (result != VM_PAGER_OK) { |
|
if (ptmp->flags & PG_WANTED) |
|
/* object lock still held */ |
|
thread_wakeup(ptmp); |
|
|
|
ptmp->flags &= ~(PG_WANTED|PG_BUSY); |
|
UVM_PAGE_OWN(ptmp, NULL); |
|
uvm_lock_pageq(); |
|
uvm_pagefree(ptmp); |
|
uvm_unlock_pageq(); |
|
simple_unlock(&uobj->vmobjlock); |
|
return(result); |
|
} |
} |
|
|
/* |
/* BUSY the page and we're done. */ |
* we got the page! clear the fake flag (indicates valid |
ptmp->flags |= PG_BUSY; |
* data now in page) and plug into our result array. note |
UVM_PAGE_OWN(ptmp, "uvn_findpage"); |
* that page is still busy. |
UVMHIST_LOG(ubchist, "found",0,0,0,0); |
* |
break; |
* it is the callers job to: |
} |
* => check if the page is released |
*pps = ptmp; |
* => unbusy the page |
return 1; |
* => activate the page |
|
*/ |
|
|
|
ptmp->flags &= ~PG_FAKE; /* data is valid ... */ |
|
pmap_clear_modify(PMAP_PGARG(ptmp)); /* ... and clean */ |
|
pps[lcv] = ptmp; |
|
|
|
} /* lcv loop */ |
|
|
|
/* |
|
* finally, unlock object and return. |
|
*/ |
|
|
|
simple_unlock(&uobj->vmobjlock); |
|
return (VM_PAGER_OK); |
|
} |
} |
|
|
/* |
/* |
Line 1584 uvn_asyncget(uobj, offset, npages) |
|
Line 1245 uvn_asyncget(uobj, offset, npages) |
|
} |
} |
|
|
/* |
/* |
* uvn_io: do I/O to a vnode |
|
* |
|
* => prefer map unlocked (not required) |
|
* => object must be locked! we will _unlock_ it before starting I/O. |
|
* => flags: PGO_SYNCIO -- use sync. I/O |
|
* => XXX: currently we use VOP_READ/VOP_WRITE which are only sync. |
|
* [thus we never do async i/o! see iodone comment] |
|
*/ |
|
|
|
static int |
|
uvn_io(uvn, pps, npages, flags, rw) |
|
struct uvm_vnode *uvn; |
|
vm_page_t *pps; |
|
int npages, flags, rw; |
|
{ |
|
struct vnode *vn; |
|
struct uio uio; |
|
struct iovec iov; |
|
vaddr_t kva, file_offset; |
|
int waitf, result, got, wanted; |
|
UVMHIST_FUNC("uvn_io"); UVMHIST_CALLED(maphist); |
|
|
|
UVMHIST_LOG(maphist, "rw=%d", rw,0,0,0); |
|
|
|
/* |
|
* init values |
|
*/ |
|
|
|
waitf = (flags & PGO_SYNCIO) ? M_WAITOK : M_NOWAIT; |
|
vn = (struct vnode *) uvn; |
|
file_offset = pps[0]->offset; |
|
|
|
/* |
|
* check for sync'ing I/O. |
|
*/ |
|
|
|
while (uvn->u_flags & UVM_VNODE_IOSYNC) { |
|
if (waitf == M_NOWAIT) { |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
UVMHIST_LOG(maphist,"<- try again (iosync)",0,0,0,0); |
|
return(VM_PAGER_AGAIN); |
|
} |
|
uvn->u_flags |= UVM_VNODE_IOSYNCWANTED; |
|
UVM_UNLOCK_AND_WAIT(&uvn->u_flags, &uvn->u_obj.vmobjlock, |
|
FALSE, "uvn_iosync",0); |
|
simple_lock(&uvn->u_obj.vmobjlock); |
|
} |
|
|
|
/* |
|
* check size |
|
*/ |
|
|
|
if (file_offset >= uvn->u_size) { |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
UVMHIST_LOG(maphist,"<- BAD (size check)",0,0,0,0); |
|
#ifdef DIAGNOSTIC |
|
printf("uvn_io: note: size check fired\n"); |
|
#endif |
|
return(VM_PAGER_BAD); |
|
} |
|
|
|
/* |
|
* first try and map the pages in (without waiting) |
|
*/ |
|
|
|
kva = uvm_pagermapin(pps, npages, NULL, M_NOWAIT); |
|
if (kva == NULL && waitf == M_NOWAIT) { |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
UVMHIST_LOG(maphist,"<- mapin failed (try again)",0,0,0,0); |
|
return(VM_PAGER_AGAIN); |
|
} |
|
|
|
/* |
|
* ok, now bump u_nio up. at this point we are done with uvn |
|
* and can unlock it. if we still don't have a kva, try again |
|
* (this time with sleep ok). |
|
*/ |
|
|
|
uvn->u_nio++; /* we have an I/O in progress! */ |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
/* NOTE: object now unlocked */ |
|
if (kva == NULL) { |
|
kva = uvm_pagermapin(pps, npages, NULL, M_WAITOK); |
|
} |
|
|
|
/* |
|
* ok, mapped in. our pages are PG_BUSY so they are not going to |
|
* get touched (so we can look at "offset" without having to lock |
|
* the object). set up for I/O. |
|
*/ |
|
|
|
/* |
|
* fill out uio/iov |
|
*/ |
|
|
|
iov.iov_base = (caddr_t) kva; |
|
wanted = npages << PAGE_SHIFT; |
|
if (file_offset + wanted > uvn->u_size) |
|
wanted = uvn->u_size - file_offset; /* XXX: needed? */ |
|
iov.iov_len = wanted; |
|
uio.uio_iov = &iov; |
|
uio.uio_iovcnt = 1; |
|
uio.uio_offset = file_offset; |
|
uio.uio_segflg = UIO_SYSSPACE; |
|
uio.uio_rw = rw; |
|
uio.uio_resid = wanted; |
|
uio.uio_procp = NULL; |
|
|
|
/* |
|
* do the I/O! (XXX: curproc?) |
|
*/ |
|
|
|
UVMHIST_LOG(maphist, "calling VOP",0,0,0,0); |
|
|
|
if ((uvn->u_flags & UVM_VNODE_VNISLOCKED) == 0) |
|
vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); |
|
/* NOTE: vnode now locked! */ |
|
|
|
if (rw == UIO_READ) |
|
result = VOP_READ(vn, &uio, 0, curproc->p_ucred); |
|
else |
|
result = VOP_WRITE(vn, &uio, 0, curproc->p_ucred); |
|
|
|
if ((uvn->u_flags & UVM_VNODE_VNISLOCKED) == 0) |
|
VOP_UNLOCK(vn, 0); |
|
/* NOTE: vnode now unlocked (unless vnislocked) */ |
|
|
|
UVMHIST_LOG(maphist, "done calling VOP",0,0,0,0); |
|
|
|
/* |
|
* result == unix style errno (0 == OK!) |
|
* |
|
* zero out rest of buffer (if needed) |
|
*/ |
|
|
|
if (result == 0) { |
|
got = wanted - uio.uio_resid; |
|
|
|
if (wanted && got == 0) { |
|
result = EIO; /* XXX: error? */ |
|
} else if (got < PAGE_SIZE * npages && rw == UIO_READ) { |
|
memset((void *) (kva + got), 0, |
|
(npages << PAGE_SHIFT) - got); |
|
} |
|
} |
|
|
|
/* |
|
* now remove pager mapping |
|
*/ |
|
uvm_pagermapout(kva, npages); |
|
|
|
/* |
|
* now clean up the object (i.e. drop I/O count) |
|
*/ |
|
|
|
simple_lock(&uvn->u_obj.vmobjlock); |
|
/* NOTE: object now locked! */ |
|
|
|
uvn->u_nio--; /* I/O DONE! */ |
|
if ((uvn->u_flags & UVM_VNODE_IOSYNC) != 0 && uvn->u_nio == 0) { |
|
wakeup(&uvn->u_nio); |
|
} |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
/* NOTE: object now unlocked! */ |
|
|
|
/* |
|
* done! |
|
*/ |
|
|
|
UVMHIST_LOG(maphist, "<- done (result %d)", result,0,0,0); |
|
if (result == 0) |
|
return(VM_PAGER_OK); |
|
else |
|
return(VM_PAGER_ERROR); |
|
} |
|
|
|
/* |
|
* uvm_vnp_uncache: disable "persisting" in a vnode... when last reference |
* uvm_vnp_uncache: disable "persisting" in a vnode... when last reference |
* is gone we will kill the object (flushing dirty pages back to the vnode |
* is gone we will kill the object (flushing dirty pages back to the vnode |
* if needed). |
* if needed). |
|
|
uvm_vnp_uncache(vp) |
uvm_vnp_uncache(vp) |
struct vnode *vp; |
struct vnode *vp; |
{ |
{ |
struct uvm_vnode *uvn = &vp->v_uvm; |
|
|
|
/* |
|
* lock uvn part of the vnode and check to see if we need to do anything |
|
*/ |
|
|
|
simple_lock(&uvn->u_obj.vmobjlock); |
|
if ((uvn->u_flags & UVM_VNODE_VALID) == 0 || |
|
(uvn->u_flags & UVM_VNODE_BLOCKED) != 0) { |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
return(TRUE); |
|
} |
|
|
|
/* |
|
* we have a valid, non-blocked uvn. clear persist flag. |
|
* if uvn is currently active we can return now. |
|
*/ |
|
|
|
uvn->u_flags &= ~UVM_VNODE_CANPERSIST; |
|
if (uvn->u_obj.uo_refs) { |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
return(FALSE); |
|
} |
|
|
|
/* |
|
* uvn is currently persisting! we have to gain a reference to |
|
* it so that we can call uvn_detach to kill the uvn. |
|
*/ |
|
|
|
VREF(vp); /* seems ok, even with VOP_LOCK */ |
|
uvn->u_obj.uo_refs++; /* value is now 1 */ |
|
simple_unlock(&uvn->u_obj.vmobjlock); |
|
|
|
|
|
#ifdef DEBUG |
|
/* |
|
* carry over sanity check from old vnode pager: the vnode should |
|
* be VOP_LOCK'd, and we confirm it here. |
|
*/ |
|
if (!VOP_ISLOCKED(vp)) { |
|
boolean_t is_ok_anyway = FALSE; |
|
#ifdef NFS |
|
extern int (**nfsv2_vnodeop_p) __P((void *)); |
|
extern int (**spec_nfsv2nodeop_p) __P((void *)); |
|
extern int (**fifo_nfsv2nodeop_p) __P((void *)); |
|
|
|
/* vnode is NOT VOP_LOCKed: some vnode types _never_ lock */ |
|
if (vp->v_op == nfsv2_vnodeop_p || |
|
vp->v_op == spec_nfsv2nodeop_p) { |
|
is_ok_anyway = TRUE; |
|
} |
|
if (vp->v_op == fifo_nfsv2nodeop_p) { |
|
is_ok_anyway = TRUE; |
|
} |
|
#endif /* NFS */ |
|
if (!is_ok_anyway) |
|
panic("uvm_vnp_uncache: vnode not locked!"); |
|
} |
|
#endif /* DEBUG */ |
|
|
|
/* |
|
* now drop our reference to the vnode. if we have the sole |
|
* reference to the vnode then this will cause it to die [as we |
|
* just cleared the persist flag]. we have to unlock the vnode |
|
* while we are doing this as it may trigger I/O. |
|
* |
|
* XXX: it might be possible for uvn to get reclaimed while we are |
|
* unlocked causing us to return TRUE when we should not. we ignore |
|
* this as a false-positive return value doesn't hurt us. |
|
*/ |
|
VOP_UNLOCK(vp, 0); |
|
uvn_detach(&uvn->u_obj); |
|
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
|
|
|
/* |
|
* and return... |
|
*/ |
|
|
|
return(TRUE); |
return(TRUE); |
} |
} |
|
|
Line 1910 uvm_vnp_setsize(vp, newsize) |
|
Line 1316 uvm_vnp_setsize(vp, newsize) |
|
* lock uvn and check for valid object, and if valid: do it! |
* lock uvn and check for valid object, and if valid: do it! |
*/ |
*/ |
simple_lock(&uvn->u_obj.vmobjlock); |
simple_lock(&uvn->u_obj.vmobjlock); |
|
#ifdef UBC |
|
#else |
if (uvn->u_flags & UVM_VNODE_VALID) { |
if (uvn->u_flags & UVM_VNODE_VALID) { |
|
#endif |
/* |
/* |
* make sure that the newsize fits within a vaddr_t |
* make sure that the newsize fits within a vaddr_t |
* XXX: need to revise addressing data types |
* XXX: need to revise addressing data types |
Line 1930 uvm_vnp_setsize(vp, newsize) |
|
Line 1338 uvm_vnp_setsize(vp, newsize) |
|
* toss some pages... |
* toss some pages... |
*/ |
*/ |
|
|
|
#ifdef UBC |
|
if (uvn->u_size > newsize && uvn->u_size != VSIZENOTSET) { |
|
#else |
|
/* |
if (uvn->u_size > newsize) { |
if (uvn->u_size > newsize) { |
(void)uvn_flush(&uvn->u_obj, (vaddr_t) newsize, |
*/ |
uvn->u_size, PGO_FREE); |
#endif |
|
(void)uvn_flush(&uvn->u_obj, (vaddr_t)newsize, |
|
uvn->u_size, PGO_FREE); |
} |
} |
|
#ifdef DEBUGxx |
|
printf("uvm_vnp_setsize: vp %p newsize 0x%x\n", vp, (int)newsize); |
|
#endif |
uvn->u_size = (vaddr_t)newsize; |
uvn->u_size = (vaddr_t)newsize; |
|
#ifdef UBC |
|
#else |
} |
} |
|
#endif |
simple_unlock(&uvn->u_obj.vmobjlock); |
simple_unlock(&uvn->u_obj.vmobjlock); |
|
|
/* |
|
* done |
|
*/ |
|
return; |
|
} |
} |
|
|
/* |
/* |
Line 1977 uvm_vnp_sync(mp) |
|
Line 1392 uvm_vnp_sync(mp) |
|
*/ |
*/ |
SIMPLEQ_INIT(&uvn_sync_q); |
SIMPLEQ_INIT(&uvn_sync_q); |
simple_lock(&uvn_wl_lock); |
simple_lock(&uvn_wl_lock); |
for (uvn = uvn_wlist.lh_first ; uvn != NULL ; |
for (uvn = LIST_FIRST(&uvn_wlist); uvn != NULL; |
uvn = uvn->u_wlist.le_next) { |
uvn = LIST_NEXT(uvn, u_wlist)) { |
|
|
vp = (struct vnode *) uvn; |
vp = (struct vnode *) uvn; |
if (mp && vp->v_mount != mp) |
if (mp && vp->v_mount != mp) |
Line 1987 uvm_vnp_sync(mp) |
|
Line 1402 uvm_vnp_sync(mp) |
|
/* attempt to gain reference */ |
/* attempt to gain reference */ |
while ((got_lock = simple_lock_try(&uvn->u_obj.vmobjlock)) == |
while ((got_lock = simple_lock_try(&uvn->u_obj.vmobjlock)) == |
FALSE && |
FALSE && |
(uvn->u_flags & UVM_VNODE_BLOCKED) == 0) |
(uvn->u_flags & UVM_VNODE_BLOCKED) == 0) |
/* spin */ ; |
/* spin */ ; |
|
|
/* |
/* |
Line 2014 uvm_vnp_sync(mp) |
|
Line 1429 uvm_vnp_sync(mp) |
|
* gain reference. watch out for persisting uvns (need to |
* gain reference. watch out for persisting uvns (need to |
* regain vnode REF). |
* regain vnode REF). |
*/ |
*/ |
|
#ifdef UBC |
|
vget(vp, LK_INTERLOCK); |
|
#else |
if (uvn->u_obj.uo_refs == 0) |
if (uvn->u_obj.uo_refs == 0) |
VREF(vp); |
VREF(vp); |
uvn->u_obj.uo_refs++; |
uvn->u_obj.uo_refs++; |
simple_unlock(&uvn->u_obj.vmobjlock); |
simple_unlock(&uvn->u_obj.vmobjlock); |
|
#endif |
|
|
/* |
/* |
* got it! |
* got it! |
Line 2034 uvm_vnp_sync(mp) |
|
Line 1453 uvm_vnp_sync(mp) |
|
|
|
for (uvn = uvn_sync_q.sqh_first ; uvn ; uvn = uvn->u_syncq.sqe_next) { |
for (uvn = uvn_sync_q.sqh_first ; uvn ; uvn = uvn->u_syncq.sqe_next) { |
simple_lock(&uvn->u_obj.vmobjlock); |
simple_lock(&uvn->u_obj.vmobjlock); |
|
#ifdef UBC |
|
#else |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (uvn->u_flags & UVM_VNODE_DYING) { |
if (uvn->u_flags & UVM_VNODE_DYING) { |
printf("uvm_vnp_sync: dying vnode on sync list\n"); |
printf("uvm_vnp_sync: dying vnode on sync list\n"); |
} |
} |
#endif |
#endif |
|
#endif |
|
/* |
|
* XXX use PGO_SYNCIO for now to avoid problems with |
|
* uvmexp.paging. |
|
*/ |
|
|
uvn_flush(&uvn->u_obj, 0, 0, |
uvn_flush(&uvn->u_obj, 0, 0, |
PGO_CLEANIT|PGO_ALLPAGES|PGO_DOACTCLUST); |
PGO_CLEANIT|PGO_ALLPAGES|PGO_DOACTCLUST|PGO_SYNCIO); |
|
|
/* |
/* |
* if we have the only reference and we just cleaned the uvn, |
* if we have the only reference and we just cleaned the uvn, |
Line 2050 uvm_vnp_sync(mp) |
|
Line 1477 uvm_vnp_sync(mp) |
|
*/ |
*/ |
if (uvn->u_obj.uo_refs == 1 && |
if (uvn->u_obj.uo_refs == 1 && |
(uvn->u_flags & UVM_VNODE_WRITEABLE)) { |
(uvn->u_flags & UVM_VNODE_WRITEABLE)) { |
|
simple_lock(&uvn_wl_lock); |
LIST_REMOVE(uvn, u_wlist); |
LIST_REMOVE(uvn, u_wlist); |
uvn->u_flags &= ~UVM_VNODE_WRITEABLE; |
uvn->u_flags &= ~UVM_VNODE_WRITEABLE; |
|
simple_unlock(&uvn_wl_lock); |
} |
} |
|
|
simple_unlock(&uvn->u_obj.vmobjlock); |
simple_unlock(&uvn->u_obj.vmobjlock); |
Line 2065 uvm_vnp_sync(mp) |
|
Line 1494 uvm_vnp_sync(mp) |
|
*/ |
*/ |
lockmgr(&uvn_sync_lock, LK_RELEASE, (void *)0); |
lockmgr(&uvn_sync_lock, LK_RELEASE, (void *)0); |
} |
} |
|
|
|
|
|
/* |
|
* uvm_vnp_setpageblknos: find pages and set their blknos. |
|
* this is used for two purposes: updating blknos in existing pages |
|
* when the data is relocated on disk, and preallocating pages when |
|
* those pages are about to be completely overwritten. |
|
* |
|
* => vp's uobj should not be locked, and is returned not locked. |
|
*/ |
|
|
|
void |
|
uvm_vnp_setpageblknos(vp, off, len, blkno, ufp_flags, zero) |
|
struct vnode *vp; |
|
off_t off, len; |
|
daddr_t blkno; |
|
int ufp_flags; |
|
boolean_t zero; |
|
{ |
|
int i; |
|
int npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
|
struct vm_page *pgs[16]; |
|
struct uvm_object *uobj = &vp->v_uvm.u_obj; |
|
|
|
simple_lock(&uobj->vmobjlock); |
|
while (npages > 0) { |
|
int pages = min(npages, 16); |
|
|
|
memset(pgs, 0, pages); |
|
uvn_findpages(uobj, trunc_page(off), &pages, pgs, ufp_flags); |
|
for (i = 0; i < pages; i++) { |
|
if (pgs[i] == NULL) { |
|
continue; |
|
} |
|
pgs[i]->blkno = blkno; |
|
blkno += PAGE_SIZE >> DEV_BSHIFT; |
|
if (zero) { |
|
uvm_pagezero(pgs[i]); |
|
} |
|
} |
|
uvm_pager_dropcluster(uobj, NULL, pgs, &pages, PGO_PDFREECLUST, |
|
0); |
|
|
|
off += pages << PAGE_SHIFT; |
|
npages -= pages; |
|
} |
|
simple_unlock(&uobj->vmobjlock); |
|
} |
|
|
|
|
|
/* |
|
* uvm_vnp_zerorange: set a range of bytes in a file to zero. |
|
* this is called from fs-specific code when truncating a file |
|
* to zero the part of last block that is past the new end-of-file. |
|
*/ |
|
void |
|
uvm_vnp_zerorange(vp, off, len) |
|
struct vnode *vp; |
|
off_t off; |
|
size_t len; |
|
{ |
|
void *win; |
|
|
|
/* |
|
* XXX invent kzero() and use it |
|
*/ |
|
|
|
while (len) { |
|
vsize_t bytelen = len; |
|
|
|
win = ubc_alloc(&vp->v_uvm.u_obj, off, &bytelen, UBC_WRITE); |
|
memset(win, 0, bytelen); |
|
ubc_release(win, 0); |
|
len -= bytelen; |
|
} |
|
} |