version 1.308.2.2, 2007/12/08 15:52:45 |
version 1.308.2.3, 2007/12/10 19:31:48 |
Line 107 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 107 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/filedesc.h> |
#include <sys/filedesc.h> |
#include <sys/kauth.h> |
#include <sys/kauth.h> |
#include <sys/atomic.h> |
#include <sys/atomic.h> |
|
#include <sys/kthread.h> |
|
|
#include <miscfs/specfs/specdev.h> |
#include <miscfs/specfs/specdev.h> |
#include <miscfs/syncfs/syncfs.h> |
#include <miscfs/syncfs/syncfs.h> |
Line 122 extern int vfs_magiclinks; /* 1 => expan |
|
Line 123 extern int vfs_magiclinks; /* 1 => expan |
|
|
|
static vnodelst_t vnode_free_list = TAILQ_HEAD_INITIALIZER(vnode_free_list); |
static vnodelst_t vnode_free_list = TAILQ_HEAD_INITIALIZER(vnode_free_list); |
static vnodelst_t vnode_hold_list = TAILQ_HEAD_INITIALIZER(vnode_hold_list); |
static vnodelst_t vnode_hold_list = TAILQ_HEAD_INITIALIZER(vnode_hold_list); |
|
static vnodelst_t vrele_list = TAILQ_HEAD_INITIALIZER(vrele_list); |
|
|
|
static int vrele_pending; |
|
static kmutex_t vrele_lock; |
|
static kcondvar_t vrele_cv; |
|
|
pool_cache_t vnode_cache; |
pool_cache_t vnode_cache; |
|
|
Line 131 MALLOC_DEFINE(M_VNODE, "vnodes", "Dynami |
|
Line 137 MALLOC_DEFINE(M_VNODE, "vnodes", "Dynami |
|
* Local declarations. |
* Local declarations. |
*/ |
*/ |
|
|
|
static void vrele_thread(void *); |
static void insmntque(vnode_t *, struct mount *); |
static void insmntque(vnode_t *, struct mount *); |
static int getdevvp(dev_t, vnode_t **, enum vtype); |
static int getdevvp(dev_t, vnode_t **, enum vtype); |
static vnode_t *getcleanvnode(void);; |
static vnode_t *getcleanvnode(void);; |
void vpanic(vnode_t *, const char *); |
void vpanic(vnode_t *, const char *); |
|
static void vrelenow(vnode_t *); |
|
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
void |
void |
Line 148 vpanic(vnode_t *vp, const char *msg) |
|
Line 156 vpanic(vnode_t *vp, const char *msg) |
|
#define vpanic(vp, msg) /* nothing */ |
#define vpanic(vp, msg) /* nothing */ |
#endif |
#endif |
|
|
|
void |
|
vn_init1(void) |
|
{ |
|
|
|
/* Create deferred release thread. */ |
|
mutex_init(&vrele_lock, MUTEX_DEFAULT, IPL_NONE); |
|
cv_init(&vrele_cv, "vrele"); |
|
if (kthread_create(PRI_VM, KTHREAD_MPSAFE, NULL, vrele_thread, |
|
NULL, NULL, "vrele")) |
|
panic("fork vrele"); |
|
} |
|
|
int |
int |
vfs_drainvnodes(long target, struct lwp *l) |
vfs_drainvnodes(long target, struct lwp *l) |
{ |
{ |
Line 733 checkalias(vnode_t *nvp, dev_t nvp_rdev, |
|
Line 753 checkalias(vnode_t *nvp, dev_t nvp_rdev, |
|
loop: |
loop: |
mutex_enter(&spechash_lock); |
mutex_enter(&spechash_lock); |
for (vp = *vpp; vp; vp = vp->v_specnext) { |
for (vp = *vpp; vp; vp = vp->v_specnext) { |
|
if (vp->v_specinfo == NULL) { |
|
vpanic(vp, "checkalias: no specinfo"); |
|
} |
if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type) |
if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type) |
continue; |
continue; |
/* |
/* |
|
|
*/ |
*/ |
if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) |
if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) |
goto loop; |
goto loop; |
|
mutex_enter(&spechash_lock); |
if (vp->v_specinfo == NULL) { |
if (vp->v_specinfo == NULL) { |
|
mutex_exit(&spechash_lock); |
vput(vp); |
vput(vp); |
goto loop; |
goto loop; |
} |
} |
mutex_enter(&spechash_lock); |
|
break; |
break; |
} |
} |
if (vp == NULL || vp->v_tag != VT_NON || vp->v_type != VBLK) { |
if (vp == NULL || vp->v_tag != VT_NON || vp->v_type != VBLK) { |
|
|
if (nvp->v_specinfo == NULL) { |
if (nvp->v_specinfo == NULL) { |
mutex_exit(&spechash_lock); |
mutex_exit(&spechash_lock); |
uvm_wait("checkalias"); |
uvm_wait("checkalias"); |
|
if (vp != NULL) |
|
vput(vp); |
goto loop; |
goto loop; |
} |
} |
|
|
Line 892 vput(vnode_t *vp) |
|
Line 918 vput(vnode_t *vp) |
|
void |
void |
vrelel(vnode_t *vp, int doinactive, int onhead) |
vrelel(vnode_t *vp, int doinactive, int onhead) |
{ |
{ |
bool recycle; |
|
|
|
KASSERT(mutex_owned(&vp->v_interlock)); |
KASSERT(mutex_owned(&vp->v_interlock)); |
KASSERT((vp->v_iflag & VI_MARKER) == 0); |
KASSERT((vp->v_iflag & VI_MARKER) == 0); |
KASSERT(vp->v_freelisthd == NULL); |
|
|
|
if (vp->v_op == dead_vnodeop_p && (vp->v_iflag & VI_CLEAN) == 0) { |
if (vp->v_op == dead_vnodeop_p && (vp->v_iflag & VI_CLEAN) == 0) { |
vpanic(vp, "dead but not clean"); |
vpanic(vp, "dead but not clean"); |
Line 911 vrelel(vnode_t *vp, int doinactive, int |
|
Line 935 vrelel(vnode_t *vp, int doinactive, int |
|
mutex_exit(&vp->v_interlock); |
mutex_exit(&vp->v_interlock); |
return; |
return; |
} |
} |
|
|
|
/* |
|
* If the vnode has been cleaned out, release it now. |
|
*/ |
|
if ((vp->v_iflag & VI_CLEAN) != 0) { |
|
vrelenow(vp); |
|
return; |
|
} |
|
|
|
/* |
|
* Otherwise, defer reclaim to the kthread; we donate it our |
|
* last reference. |
|
*/ |
if (vp->v_usecount <= 0 || vp->v_writecount != 0) { |
if (vp->v_usecount <= 0 || vp->v_writecount != 0) { |
vpanic(vp, "vput: bad ref count"); |
vpanic(vp, "vput: bad ref count"); |
} |
} |
|
if ((vp->v_iflag & VI_INACTPEND) == 0) { |
|
vp->v_iflag |= VI_INACTPEND; |
|
mutex_enter(&vrele_lock); |
|
TAILQ_INSERT_TAIL(&vrele_list, vp, v_freelist); |
|
if (++vrele_pending > (desiredvnodes >> 8)) |
|
cv_signal(&vrele_cv); |
|
mutex_exit(&vrele_lock); |
|
} |
|
mutex_exit(&vp->v_interlock); |
|
} |
|
|
|
static void |
|
vrelenow(vnode_t *vp) |
|
{ |
|
bool recycle; |
|
int error; |
|
|
|
KASSERT(mutex_owned(&vp->v_interlock)); |
|
|
/* |
/* |
* If not clean, deactivate the vnode, but preserve our reference |
* If not clean, deactivate the vnode, but preserve |
* across the call to VOP_INACTIVE() to prevent another thread from |
* our reference across the call to VOP_INACTIVE(). |
* trying to do the same. |
|
*/ |
*/ |
recycle = false; |
recycle = false; |
if ((vp->v_iflag & VI_CLEAN) == 0) { |
if ((vp->v_iflag & VI_CLEAN) == 0) { |
if (vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK) == 0) { |
error = vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY); |
|
if (error == 0) { |
VOP_INACTIVE(vp, &recycle); |
VOP_INACTIVE(vp, &recycle); |
|
} else { |
|
/* XXX */ |
|
vprint("vrelenow: unable to lock %p", vp); |
} |
} |
|
/* |
|
* The vnode may have gained another reference |
|
* while being deactivated. |
|
*/ |
mutex_enter(&vp->v_interlock); |
mutex_enter(&vp->v_interlock); |
if (vp->v_usecount > 1) { |
if (vp->v_usecount > 1) { |
/* |
|
* Gained another reference while being |
|
* deactivated. |
|
*/ |
|
vp->v_usecount--; |
vp->v_usecount--; |
mutex_exit(&vp->v_interlock); |
mutex_exit(&vp->v_interlock); |
return; |
return; |
Line 938 vrelel(vnode_t *vp, int doinactive, int |
|
Line 996 vrelel(vnode_t *vp, int doinactive, int |
|
} |
} |
|
|
/* |
/* |
* Recycle the vnode if the file is now unused (unlinked), |
* Take care of space accounting. |
* otherwise just free it. |
|
* |
* |
* XXXAD may need to re-inactivate due to race w/another |
* XXXAD may need to re-inactivate due to race w/another |
* thread gaining and dropping a reference above. |
* thread gaining and dropping a reference above. |
Line 950 vrelel(vnode_t *vp, int doinactive, int |
|
Line 1007 vrelel(vnode_t *vp, int doinactive, int |
|
} |
} |
vp->v_iflag &= ~(VI_TEXT|VI_EXECMAP|VI_WRMAP|VI_MAPPED); |
vp->v_iflag &= ~(VI_TEXT|VI_EXECMAP|VI_WRMAP|VI_MAPPED); |
vp->v_vflag &= ~VV_MAPPED; |
vp->v_vflag &= ~VV_MAPPED; |
|
|
|
/* |
|
* Recycle the vnode if the file is now unused (unlinked), |
|
* otherwise just free it. |
|
*/ |
if (recycle) { |
if (recycle) { |
vclean(vp, DOCLOSE); |
vclean(vp, DOCLOSE); |
} |
} |
|
|
KASSERT(vp->v_usecount > 0); |
KASSERT(vp->v_usecount > 0); |
KASSERT(vp->v_freelisthd == NULL); |
|
|
|
if (vp->v_op == dead_vnodeop_p && (vp->v_iflag & VI_CLEAN) == 0) { |
if (vp->v_op == dead_vnodeop_p && |
|
(vp->v_iflag & VI_CLEAN) == 0) { |
vpanic(vp, "dead but not clean"); |
vpanic(vp, "dead but not clean"); |
} |
} |
|
|
Line 1005 vrele(vnode_t *vp) |
|
Line 1066 vrele(vnode_t *vp) |
|
vrelel(vp, 1, 0); |
vrelel(vp, 1, 0); |
} |
} |
|
|
|
static void |
|
vrele_thread(void *cookie) |
|
{ |
|
vnode_t *vp; |
|
|
|
for (;;) { |
|
mutex_enter(&vrele_lock); |
|
while (TAILQ_EMPTY(&vrele_list)) { |
|
cv_timedwait(&vrele_cv, &vrele_lock, hz); |
|
} |
|
vp = TAILQ_FIRST(&vrele_list); |
|
TAILQ_REMOVE(&vrele_list, vp, v_freelist); |
|
vrele_pending--; |
|
mutex_exit(&vrele_lock); |
|
|
|
/* |
|
* If not the last reference, then ignore the vnode |
|
* and look for more work. |
|
*/ |
|
mutex_enter(&vp->v_interlock); |
|
KASSERT((vp->v_iflag & VI_INACTPEND) != 0); |
|
vp->v_iflag &= ~VI_INACTPEND; |
|
if (vp->v_usecount > 1) { |
|
vp->v_usecount--; |
|
mutex_exit(&vp->v_interlock); |
|
continue; |
|
} |
|
|
|
/* Otherwise, release it. */ |
|
vrelenow(vp); |
|
} |
|
} |
|
|
/* |
/* |
* Page or buffer structure gets a reference. |
* Page or buffer structure gets a reference. |
* Called with v_interlock held. |
* Called with v_interlock held. |
Line 1070 vref(vnode_t *vp) |
|
Line 1164 vref(vnode_t *vp) |
|
vpanic(vp, "vref: usecount overflow"); |
vpanic(vp, "vref: usecount overflow"); |
} |
} |
mutex_exit(&vp->v_interlock); |
mutex_exit(&vp->v_interlock); |
|
|
KASSERT(vp->v_freelisthd == NULL); |
|
} |
} |
|
|
/* |
/* |
Line 1146 vflush(struct mount *mp, vnode_t *skipvp |
|
Line 1238 vflush(struct mount *mp, vnode_t *skipvp |
|
mutex_enter(&mntvnode_lock); |
mutex_enter(&mntvnode_lock); |
continue; |
continue; |
} |
} |
KASSERT(vp->v_freelisthd == NULL); |
|
/* |
/* |
* If FORCECLOSE is set, forcibly close the vnode. |
* If FORCECLOSE is set, forcibly close the vnode. |
* For block or character devices, revert to an |
* For block or character devices, revert to an |
Line 1190 vclean(vnode_t *vp, int flags) |
|
Line 1281 vclean(vnode_t *vp, int flags) |
|
{ |
{ |
lwp_t *l = curlwp; |
lwp_t *l = curlwp; |
bool recycle, active; |
bool recycle, active; |
|
struct specinfo *si; |
|
|
KASSERT(mutex_owned(&vp->v_interlock)); |
KASSERT(mutex_owned(&vp->v_interlock)); |
KASSERT((vp->v_iflag & VI_MARKER) == 0); |
KASSERT((vp->v_iflag & VI_MARKER) == 0); |
KASSERT(vp->v_usecount != 0); |
KASSERT(vp->v_usecount != 0); |
KASSERT(vp->v_freelisthd == NULL); |
|
|
|
/* If cleaning is already in progress wait until done and return. */ |
/* If cleaning is already in progress wait until done and return. */ |
if (vp->v_iflag & VI_XLOCK) { |
if (vp->v_iflag & VI_XLOCK) { |
Line 1211 vclean(vnode_t *vp, int flags) |
|
Line 1302 vclean(vnode_t *vp, int flags) |
|
* Prevent the vnode from being recycled or brought into use |
* Prevent the vnode from being recycled or brought into use |
* while we clean it out. |
* while we clean it out. |
*/ |
*/ |
if (vp->v_iflag & VI_XLOCK) { |
|
vpanic(vp, "vclean: deadlock"); |
|
} |
|
vp->v_iflag |= VI_XLOCK; |
vp->v_iflag |= VI_XLOCK; |
if (vp->v_iflag & VI_EXECMAP) { |
if (vp->v_iflag & VI_EXECMAP) { |
atomic_add_int(&uvmexp.execpages, -vp->v_uobj.uo_npages); |
atomic_add_int(&uvmexp.execpages, -vp->v_uobj.uo_npages); |
Line 1221 vclean(vnode_t *vp, int flags) |
|
Line 1309 vclean(vnode_t *vp, int flags) |
|
} |
} |
vp->v_iflag &= ~(VI_TEXT|VI_EXECMAP); |
vp->v_iflag &= ~(VI_TEXT|VI_EXECMAP); |
active = (vp->v_usecount > 1); |
active = (vp->v_usecount > 1); |
|
VOP_LOCK(vp, LK_EXCLUSIVE | LK_INTERLOCK); |
/* |
|
* Even if the count is zero, the VOP_INACTIVE routine may still |
|
* have the object locked while it cleans it out. For |
|
* active vnodes, it ensures that no other activity can |
|
* occur while the underlying object is being cleaned out. |
|
* |
|
* We drain the lock to make sure we are the last one trying to |
|
* get it and immediately resurrect the lock. Future accesses |
|
* for locking this _vnode_ will be protected by VXLOCK. However, |
|
* upper layers might be using the _lock_ in case the file system |
|
* exported it and might access it while the vnode lingers in |
|
* deadfs. |
|
* |
|
* XXXAD not true any more. |
|
*/ |
|
VOP_LOCK(vp, LK_DRAIN | LK_RESURRECT | LK_INTERLOCK); |
|
|
|
/* |
/* |
* Clean out any cached data associated with the vnode. |
* Clean out any cached data associated with the vnode. |
Line 1258 vclean(vnode_t *vp, int flags) |
|
Line 1330 vclean(vnode_t *vp, int flags) |
|
VOP_CLOSE(vp, FNONBLOCK, NOCRED); |
VOP_CLOSE(vp, FNONBLOCK, NOCRED); |
|
|
if ((vp->v_type == VBLK || vp->v_type == VCHR) && |
if ((vp->v_type == VBLK || vp->v_type == VCHR) && |
vp->v_specinfo != 0) { |
vp->v_specinfo != NULL) { |
mutex_enter(&spechash_lock); |
mutex_enter(&spechash_lock); |
if (vp->v_hashchain != NULL) { |
if (vp->v_hashchain != NULL) { |
if (*vp->v_hashchain == vp) { |
if (*vp->v_hashchain == vp) { |
Line 1292 vclean(vnode_t *vp, int flags) |
|
Line 1364 vclean(vnode_t *vp, int flags) |
|
vp->v_iflag &= ~VI_ALIASED; |
vp->v_iflag &= ~VI_ALIASED; |
} |
} |
} |
} |
mutex_exit(&spechash_lock); |
si = vp->v_specinfo; |
FREE(vp->v_specinfo, M_VNODE); |
|
vp->v_specinfo = NULL; |
vp->v_specinfo = NULL; |
|
mutex_exit(&spechash_lock); |
|
FREE(si, M_VNODE); |
} |
} |
} |
} |
|
|
Line 1425 vcount(vnode_t *vp) |
|
Line 1498 vcount(vnode_t *vp) |
|
|
|
loop: |
loop: |
mutex_enter(&spechash_lock); |
mutex_enter(&spechash_lock); |
|
mutex_enter(&vp->v_interlock); |
if ((vp->v_iflag & VI_ALIASED) == 0) { |
if ((vp->v_iflag & VI_ALIASED) == 0) { |
|
count = vp->v_usecount + ((vp->v_iflag & VI_INACTPEND) != 0); |
|
mutex_exit(&vp->v_interlock); |
mutex_exit(&spechash_lock); |
mutex_exit(&spechash_lock); |
return (vp->v_usecount); |
return (count); |
} |
} |
|
mutex_exit(&vp->v_interlock); |
for (count = 0, vq = *vp->v_hashchain; vq; vq = vnext) { |
for (count = 0, vq = *vp->v_hashchain; vq; vq = vnext) { |
vnext = vq->v_specnext; |
vnext = vq->v_specnext; |
if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) |
if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) |