Bug #129 » ncc3.diff
| sys/kern/vfs_cache.c Sun Mar 26 07:56:54 2006 +0000 → sys/kern/vfs_cache.c Wed Mar 29 10:23:26 2006 +0200 | ||
|---|---|---|
|
#define NCHHASH(hash) (&nchashtbl[(hash) & nchash])
|
||
|
#define MINNEG 1024
|
||
|
/* Modes for shadow group traversal */
|
||
|
#define SG_ALL 0 /* traverse whole group */
|
||
|
#define SG_SUBTREE 1 /* traverse only subtree */
|
||
|
MALLOC_DEFINE(M_VFSCACHE, "vfscache", "VFS name cache entries");
|
||
|
static LIST_HEAD(nchashhead, namecache) *nchashtbl; /* Hash Table */
|
||
| ... | ... | |
|
static u_long numneghits; STATNODE(CTLFLAG_RD, numneghits, &numneghits);
|
||
|
struct nchstats nchstats[SMP_MAXCPU];
|
||
|
static STAILQ_HEAD(, shadowinfo) shadowinfo_freeq;
|
||
|
static u_long numshadowinfo = 0;
|
||
|
STATNODE(CTLFLAG_RD, numshadowinfo, &numshadowinfo);
|
||
|
static long maxnumshadowinfo = -1;
|
||
|
SYSCTL_LONG(_vfs_cache, OID_AUTO, maxnumshadowinfo, CTLFLAG_RW,
|
||
|
&maxnumshadowinfo, 0, "");
|
||
|
MALLOC_DEFINE(M_SHADOWINFO, "shadowinfo", "VFS name cache shadowinfo");
|
||
|
/*
|
||
|
* Export VFS cache effectiveness statistics to user-land.
|
||
|
*
|
||
| ... | ... | |
|
SYSCTL_PROC(_vfs_cache, OID_AUTO, nchstats, CTLTYPE_OPAQUE|CTLFLAG_RD,
|
||
|
0, 0, sysctl_nchstats, "S,nchstats", "VFS cache effectiveness statistics");
|
||
|
/* XXX stubs for later MPSAFE work */
|
||
|
#define shadowinfo_freeq_lock()
|
||
|
#define shadowinfo_freeq_unlock()
|
||
|
static struct shadowinfo *
|
||
|
shadowinfo_fetch(void)
|
||
|
{
|
||
|
struct shadowinfo *shinf = STAILQ_FIRST(&shadowinfo_freeq);
|
||
|
if (! shinf)
|
||
|
goto alloc;
|
||
|
shadowinfo_freeq_lock();
|
||
|
if ((shinf = STAILQ_FIRST(&shadowinfo_freeq)))
|
||
|
STAILQ_REMOVE_HEAD(&shadowinfo_freeq, sh_entry);
|
||
|
shadowinfo_freeq_unlock();
|
||
|
if (shinf)
|
||
|
return (shinf);
|
||
|
alloc:
|
||
|
shinf = malloc(sizeof(*shinf), M_SHADOWINFO, M_WAITOK|M_ZERO);
|
||
|
numshadowinfo++;
|
||
|
return (shinf);
|
||
|
}
|
||
|
static __inline
|
||
|
struct shadowinfo*
|
||
|
shadowinfo_ref(struct shadowinfo *shinf)
|
||
|
{
|
||
|
shinf->sh_refs++;
|
||
|
return (shinf);
|
||
|
}
|
||
|
static void
|
||
|
shadowinfo_put(struct shadowinfo *shinf)
|
||
|
{
|
||
|
if (--shinf->sh_refs > 0)
|
||
|
return;
|
||
|
if (maxnumshadowinfo >= 0 && numshadowinfo > maxnumshadowinfo) {
|
||
|
free(shinf, M_SHADOWINFO);
|
||
|
numshadowinfo--;
|
||
|
return;
|
||
|
}
|
||
|
shinf->sh_exlocks = 0;
|
||
|
shinf->sh_locktd = NULL;
|
||
|
shadowinfo_freeq_lock();
|
||
|
STAILQ_INSERT_TAIL(&shadowinfo_freeq, shinf, sh_entry);
|
||
|
shadowinfo_freeq_unlock();
|
||
|
}
|
||
|
static void cache_zap(struct namecache *ncp);
|
||
|
/*
|
||
| ... | ... | |
|
(ncp->nc_flag & NCF_UNRESOLVED) &&
|
||
|
TAILQ_EMPTY(&ncp->nc_list)
|
||
|
) {
|
||
|
KKASSERT(ncp->nc_exlocks == 0);
|
||
|
KKASSERT(ncp->nc_shadowinfo->sh_exlocks == 0);
|
||
|
cache_lock(ncp);
|
||
|
cache_zap(ncp);
|
||
|
} else {
|
||
| ... | ... | |
|
ncp->nc_error = ENOTCONN; /* needs to be resolved */
|
||
|
ncp->nc_refs = 1;
|
||
|
ncp->nc_fsmid = 1;
|
||
|
ncp->nc_shadowinfo = &ncp->nc_shadowinfo_internal;
|
||
|
ncp->nc_shadowinfo_internal.sh_refs = 2;
|
||
|
ncp->nc_shadow_prev = NULL;
|
||
|
ncp->nc_shadow_next = NULL;
|
||
|
TAILQ_INIT(&ncp->nc_list);
|
||
|
cache_lock(ncp);
|
||
|
return(ncp);
|
||
| ... | ... | |
|
static void
|
||
|
cache_free(struct namecache *ncp)
|
||
|
{
|
||
|
KKASSERT(ncp->nc_refs == 1 && ncp->nc_exlocks == 1);
|
||
|
KKASSERT(ncp->nc_refs == 1 && ncp->nc_shadowinfo->sh_exlocks == 1);
|
||
|
if (ncp->nc_name)
|
||
|
free(ncp->nc_name, M_VFSCACHE);
|
||
|
free(ncp, M_VFSCACHE);
|
||
| ... | ... | |
|
cache_drop(struct namecache *ncp)
|
||
|
{
|
||
|
_cache_drop(ncp);
|
||
|
}
|
||
|
/*
|
||
|
* Iterate an "updater" function over a shadow group.
|
||
|
* All-group and subtree-only traversals are supported.
|
||
|
*/
|
||
|
static struct namecache *
|
||
|
cache_group_walk(struct namecache *ncp,
|
||
|
int (*updater)(struct namecache *xncp, void *param),
|
||
|
int flags, void *param)
|
||
|
{
|
||
|
struct namecache *xncp = ncp, *yncp;
|
||
|
for (;;) {
|
||
|
yncp = xncp->nc_shadow_next;
|
||
|
if (updater(xncp, param))
|
||
|
break;
|
||
|
if (! yncp || yncp == ncp ||
|
||
|
(flags & SG_SUBTREE &&
|
||
|
yncp->nc_shadowheight <= ncp->nc_shadowheight))
|
||
|
break;
|
||
|
xncp = yncp;
|
||
|
}
|
||
|
return(xncp);
|
||
|
}
|
||
|
struct migrate_param {
|
||
|
int heightdelta;
|
||
|
int exlocks;
|
||
|
struct shadowinfo *shadowinfo;
|
||
|
};
|
||
|
static int
|
||
|
migrate_updater(struct namecache *ncp, void *param)
|
||
|
{
|
||
|
struct migrate_param *mpm = param;
|
||
|
struct shadowinfo *shinf = mpm->shadowinfo;
|
||
|
struct shadowinfo *oldshinf = ncp->nc_shadowinfo;
|
||
|
if (! shinf)
|
||
|
shinf = &ncp->nc_shadowinfo_internal;
|
||
|
if (shinf == oldshinf)
|
||
|
goto out;
|
||
|
shinf->sh_locktd = oldshinf->sh_locktd;
|
||
|
ncp->nc_shadowinfo = shadowinfo_ref(shinf);
|
||
|
shadowinfo_put(oldshinf);
|
||
|
out:
|
||
|
ncp->nc_shadowheight += mpm->heightdelta;
|
||
|
if (mpm->exlocks >= 0)
|
||
|
shinf->sh_exlocks = mpm->exlocks;
|
||
|
return (0);
|
||
|
}
|
||
|
static __inline
|
||
|
void
|
||
|
cache_shadow_link(struct namecache *sncp, struct namecache *ncp)
|
||
|
{
|
||
|
struct namecache *pncp;
|
||
|
struct namecache *nsncp;
|
||
|
pncp = ncp->nc_shadow_prev ?: ncp;
|
||
|
nsncp = sncp->nc_shadow_next ?: sncp;
|
||
|
pncp->nc_shadow_next = nsncp;
|
||
|
nsncp->nc_shadow_prev = pncp;
|
||
|
sncp->nc_shadow_next = ncp;
|
||
|
ncp->nc_shadow_prev = sncp;
|
||
|
}
|
||
|
static __inline
|
||
|
void
|
||
|
cache_shadow_unlink(struct namecache *ncp)
|
||
|
{
|
||
|
if (! ncp->nc_shadow_next)
|
||
|
return;
|
||
|
KKASSERT(ncp->nc_shadow_prev);
|
||
|
if (ncp->nc_shadow_prev == ncp->nc_shadow_next) {
|
||
|
ncp->nc_shadow_prev->nc_shadow_next = NULL;
|
||
|
ncp->nc_shadow_next->nc_shadow_prev = NULL;
|
||
|
} else {
|
||
|
ncp->nc_shadow_prev->nc_shadow_next = ncp->nc_shadow_next;
|
||
|
ncp->nc_shadow_next->nc_shadow_prev = ncp->nc_shadow_prev;
|
||
|
}
|
||
|
ncp->nc_shadow_prev = ncp->nc_shadow_next = NULL;
|
||
|
}
|
||
|
/*
|
||
|
* Join ncp into the shadow group of sncp.
|
||
|
*
|
||
|
* ncp must be unlocked on entry, while sncp must be locked on entry.
|
||
|
*
|
||
|
* The routine will fail and return ELOOP if the intended shadowing association
|
||
|
* doesnt' make sense (currently this boils down to ncp being the same as
|
||
|
* sncp).
|
||
|
* It will fail with EEXIST if ncp gets resolved or acquires a shadow
|
||
|
* association from elsewhere during the attach attempt (it is possbile due to
|
||
|
* the fact that ncp is unlocked).
|
||
|
*
|
||
|
* - On success ncp will be a representative of the joint shadow group, which
|
||
|
* then will be locked.
|
||
|
* - On failure the namecache entries will exist separately just as they did
|
||
|
* before; both entries will be locked.
|
||
|
*/
|
||
|
int
|
||
|
cache_shadow_attach(struct namecache *ncp, struct namecache *sncp)
|
||
|
{
|
||
|
struct migrate_param mpm;
|
||
|
if (ncp == sncp)
|
||
|
return(ELOOP);
|
||
|
KKASSERT(ncp->nc_shadowinfo->sh_locktd != curthread);
|
||
|
KKASSERT(sncp->nc_shadowinfo->sh_locktd == curthread);
|
||
|
cache_lock_two(ncp, sncp);
|
||
|
if ((ncp->nc_flag & NCF_UNRESOLVED) == 0 || ncp->nc_shadowheight != 0)
|
||
|
return(EEXIST);
|
||
|
if (sncp->nc_shadowinfo == &sncp->nc_shadowinfo_internal) {
|
||
|
mpm.heightdelta = 0;
|
||
|
mpm.shadowinfo = shadowinfo_fetch();
|
||
|
mpm.exlocks = sncp->nc_shadowinfo->sh_exlocks;
|
||
|
migrate_updater(sncp, &mpm);
|
||
|
}
|
||
|
mpm.heightdelta = sncp->nc_shadowheight + 1;
|
||
|
mpm.shadowinfo = sncp->nc_shadowinfo;
|
||
|
mpm.exlocks = -1;
|
||
|
cache_group_walk(ncp, &migrate_updater, SG_ALL, &mpm);
|
||
|
cache_shadow_link(sncp, ncp);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
/*
|
||
|
* Take out namecache entry from its shadow group.
|
||
|
*
|
||
|
* The shadow group must be locked upon entry.
|
||
|
*
|
||
|
* On return both the entry and its former group remain locked.
|
||
|
*/
|
||
|
void
|
||
|
cache_shadow_detach(struct namecache *ncp)
|
||
|
{
|
||
|
struct namecache *pncp, *nncp;
|
||
|
struct migrate_param mpm;
|
||
|
mpm.shadowinfo = NULL;
|
||
|
again:
|
||
|
mpm.heightdelta = -ncp->nc_shadowheight;
|
||
|
mpm.exlocks = ncp->nc_shadowinfo->sh_exlocks;
|
||
|
pncp = ncp->nc_shadow_prev;
|
||
|
nncp = ncp->nc_shadow_next;
|
||
|
migrate_updater(ncp, &mpm);
|
||
|
cache_shadow_unlink(ncp);
|
||
|
if (nncp && nncp == pncp) {
|
||
|
ncp = nncp;
|
||
|
goto again;
|
||
|
}
|
||
|
}
|
||
|
static int
|
||
|
vref_updater(struct namecache *ncp, void *param)
|
||
|
{
|
||
|
if (ncp->nc_vp)
|
||
|
*(int *)param > 0 ? vhold(ncp->nc_vp) : vdrop(ncp->nc_vp);
|
||
|
return(0);
|
||
|
}
|
||
|
/*
|
||
| ... | ... | |
|
{
|
||
|
thread_t td;
|
||
|
int didwarn;
|
||
|
struct shadowinfo *shinf;
|
||
|
KKASSERT(ncp->nc_refs != 0);
|
||
|
didwarn = 0;
|
||
|
td = curthread;
|
||
|
for (;;) {
|
||
|
if (ncp->nc_exlocks == 0) {
|
||
|
ncp->nc_exlocks = 1;
|
||
|
ncp->nc_locktd = td;
|
||
|
shinf = ncp->nc_shadowinfo;
|
||
|
KKASSERT(shinf);
|
||
|
KKASSERT(shinf->sh_refs != 0);
|
||
|
if (shinf->sh_exlocks == 0) {
|
||
|
int ref = 1;
|
||
|
shinf->sh_exlocks = 1;
|
||
|
shinf->sh_locktd = td;
|
||
|
/*
|
||
|
* The vp associated with a locked ncp must be held
|
||
|
* to prevent it from being recycled (which would
|
||
| ... | ... | |
|
*
|
||
|
* XXX loop on race for later MPSAFE work.
|
||
|
*/
|
||
|
if (ncp->nc_vp)
|
||
|
vhold(ncp->nc_vp);
|
||
|
cache_group_walk(ncp, &vref_updater, SG_ALL, &ref);
|
||
|
break;
|
||
|
}
|
||
|
if (ncp->nc_locktd == td) {
|
||
|
++ncp->nc_exlocks;
|
||
|
if (shinf->sh_locktd == td) {
|
||
|
++shinf->sh_exlocks;
|
||
|
break;
|
||
|
}
|
||
|
ncp->nc_flag |= NCF_LOCKREQ;
|
||
|
if (tsleep(ncp, 0, "clock", nclockwarn) == EWOULDBLOCK) {
|
||
|
shinf->sh_lockreq = 1;
|
||
|
if (tsleep(shinf, 0, "clock", nclockwarn) == EWOULDBLOCK) {
|
||
|
if (didwarn)
|
||
|
continue;
|
||
|
didwarn = 1;
|
||
| ... | ... | |
|
cache_lock_nonblock(struct namecache *ncp)
|
||
|
{
|
||
|
thread_t td;
|
||
|
struct shadowinfo *shinf = ncp->nc_shadowinfo;
|
||
|
KKASSERT(ncp->nc_refs != 0);
|
||
|
KKASSERT(shinf);
|
||
|
KKASSERT(shinf->sh_refs != 0);
|
||
|
td = curthread;
|
||
|
if (ncp->nc_exlocks == 0) {
|
||
|
ncp->nc_exlocks = 1;
|
||
|
ncp->nc_locktd = td;
|
||
|
if (shinf->sh_exlocks == 0) {
|
||
|
int ref = 1;
|
||
|
shinf->sh_exlocks = 1;
|
||
|
shinf->sh_locktd = td;
|
||
|
/*
|
||
|
* The vp associated with a locked ncp must be held
|
||
|
* to prevent it from being recycled (which would
|
||
| ... | ... | |
|
*
|
||
|
* XXX loop on race for later MPSAFE work.
|
||
|
*/
|
||
|
if (ncp->nc_vp)
|
||
|
vhold(ncp->nc_vp);
|
||
|
cache_group_walk(ncp, &vref_updater, SG_ALL, &ref);
|
||
|
return(0);
|
||
|
} else {
|
||
|
return(EWOULDBLOCK);
|
||
| ... | ... | |
|
cache_unlock(struct namecache *ncp)
|
||
|
{
|
||
|
thread_t td = curthread;
|
||
|
struct shadowinfo *shinf = ncp->nc_shadowinfo;
|
||
|
KKASSERT(ncp->nc_refs > 0);
|
||
|
KKASSERT(ncp->nc_exlocks > 0);
|
||
|
KKASSERT(ncp->nc_locktd == td);
|
||
|
if (--ncp->nc_exlocks == 0) {
|
||
|
if (ncp->nc_vp)
|
||
|
vdrop(ncp->nc_vp);
|
||
|
ncp->nc_locktd = NULL;
|
||
|
if (ncp->nc_flag & NCF_LOCKREQ) {
|
||
|
ncp->nc_flag &= ~NCF_LOCKREQ;
|
||
|
wakeup(ncp);
|
||
|
KKASSERT(shinf);
|
||
|
KKASSERT(shinf->sh_refs > 0);
|
||
|
KKASSERT(shinf->sh_exlocks > 0);
|
||
|
KKASSERT(shinf->sh_locktd == td);
|
||
|
if (shinf->sh_exlocks == 1) {
|
||
|
int ref = -1;
|
||
|
cache_group_walk(ncp, &vref_updater, SG_ALL, &ref);
|
||
|
}
|
||
|
if (--shinf->sh_exlocks == 0) {
|
||
|
shinf->sh_locktd = NULL;
|
||
|
if (shinf->sh_lockreq) {
|
||
|
shinf->sh_lockreq = 0;
|
||
|
wakeup(shinf);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* Obtain lock on both of uncp and lncp.
|
||
|
*
|
||
|
* On entry, uncp is assumed to be unlocked, and lncp is assumed to be
|
||
|
* locked.
|
||
|
*
|
||
|
* After this function returns, caller is responsible for checking
|
||
|
* the state of lncp which might have got unlocked temporarily.
|
||
|
*/
|
||
|
void
|
||
|
cache_lock_two(struct namecache *uncp, struct namecache *lncp)
|
||
|
{
|
||
|
if (cache_lock_nonblock(uncp) != 0) {
|
||
|
if (uncp > lncp)
|
||
|
cache_lock(uncp);
|
||
|
else {
|
||
|
cache_unlock(lncp);
|
||
|
cache_lock(uncp);
|
||
|
cache_lock(lncp);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
cache_get_nonblock(struct namecache *ncp)
|
||
|
{
|
||
|
/* XXX MP */
|
||
|
if (ncp->nc_exlocks == 0 || ncp->nc_locktd == curthread) {
|
||
|
if (ncp->nc_shadowinfo->sh_exlocks == 0 ||
|
||
|
ncp->nc_shadowinfo->sh_locktd == curthread) {
|
||
|
_cache_hold(ncp);
|
||
|
cache_lock(ncp);
|
||
|
return(0);
|
||
| ... | ... | |
|
if (!TAILQ_EMPTY(&ncp->nc_list))
|
||
|
vhold(vp);
|
||
|
TAILQ_INSERT_HEAD(&vp->v_namecache, ncp, nc_vnode);
|
||
|
if (ncp->nc_exlocks)
|
||
|
if (ncp->nc_shadowinfo->sh_exlocks)
|
||
|
vhold(vp);
|
||
|
/*
|
||
| ... | ... | |
|
ncp->nc_timeout = 1;
|
||
|
}
|
||
|
static int unresolver_updater(struct namecache *ncp, void *param);
|
||
|
/*
|
||
|
* Disassociate the vnode or negative-cache association and mark a
|
||
|
* namecache entry as unresolved again. Note that the ncp is still
|
||
| ... | ... | |
|
void
|
||
|
cache_setunresolved(struct namecache *ncp)
|
||
|
{
|
||
|
struct namecache *nncp;
|
||
|
cache_group_walk(ncp, &unresolver_updater, SG_SUBTREE, ncp);
|
||
|
nncp = ncp->nc_shadow_next;
|
||
|
if (nncp)
|
||
|
cache_hold(nncp);
|
||
|
unresolver_updater(ncp, NULL);
|
||
|
if (nncp)
|
||
|
cache_put(nncp);
|
||
|
}
|
||
|
static int
|
||
|
unresolver_updater(struct namecache *ncp, void *param)
|
||
|
{
|
||
|
struct vnode *vp;
|
||
|
if (ncp == param)
|
||
|
return(0);
|
||
|
if ((ncp->nc_flag & NCF_UNRESOLVED) == 0) {
|
||
|
ncp->nc_flag |= NCF_UNRESOLVED;
|
||
| ... | ... | |
|
*/
|
||
|
if (!TAILQ_EMPTY(&ncp->nc_list))
|
||
|
vdrop(vp);
|
||
|
if (ncp->nc_exlocks)
|
||
|
if (ncp->nc_shadowinfo->sh_exlocks)
|
||
|
vdrop(vp);
|
||
|
} else {
|
||
|
TAILQ_REMOVE(&ncneglist, ncp, nc_vnode);
|
||
|
--numneg;
|
||
|
}
|
||
|
}
|
||
|
cache_shadow_detach(ncp);
|
||
|
}
|
||
|
if (ncp->nc_refs == 0) {
|
||
|
cache_hold(ncp);
|
||
|
cache_put(ncp);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
/*
|
||
| ... | ... | |
|
struct namecache *nextkid;
|
||
|
int rcnt = 0;
|
||
|
KKASSERT(ncp->nc_exlocks);
|
||
|
KKASSERT(ncp->nc_shadowinfo->sh_exlocks);
|
||
|
cache_setunresolved(ncp);
|
||
|
if (flags & CINV_DESTROY)
|
||
| ... | ... | |
|
* XXX the disconnection could pose a problem, check code paths to make
|
||
|
* sure any code that blocks can handle the parent being changed out from
|
||
|
* under it. Maybe we should lock the children (watch out for deadlocks) ?
|
||
|
* [UPDATE: attempt made to lock children, see in situ explanation]
|
||
|
*
|
||
|
* After we return the caller has the option of calling cache_setvp() if
|
||
|
* the vnode of the new target ncp is known.
|
||
| ... | ... | |
|
cache_rename(struct namecache *fncp, struct namecache *tncp)
|
||
|
{
|
||
|
struct namecache *scan;
|
||
|
int didwarn = 0;
|
||
|
int didwarn[] = { 0, 0 };
|
||
|
/* XXX should we rather make here a non-equality assertion? */
|
||
|
if (fncp == tncp)
|
||
|
return;
|
||
|
again:
|
||
|
cache_setunresolved(fncp);
|
||
|
cache_setunresolved(tncp);
|
||
|
/*
|
||
|
* It seems we need to unlock fncp before calling cache_inval():
|
||
|
* cache_inval() does a lot of lock/unlock/relock-ing (with tncp
|
||
|
* and its children), therefore keeping fncp locked might be
|
||
|
* deadlocky...
|
||
|
*/
|
||
|
cache_unlock(fncp);
|
||
|
|
||
|
while (cache_inval(tncp, CINV_CHILDREN) != 0) {
|
||
|
if (didwarn++ % 10 == 0) {
|
||
|
printf("Warning: cache_rename: race during "
|
||
|
if (didwarn[0]++ % 10 == 0) {
|
||
|
printf("Warning: cache_rename: race #1 during "
|
||
|
"rename %s->%s\n",
|
||
|
fncp->nc_name, tncp->nc_name);
|
||
|
}
|
||
|
tsleep(tncp, 0, "mvrace", hz / 10);
|
||
|
cache_setunresolved(tncp);
|
||
|
}
|
||
|
cache_unlock(tncp);
|
||
|
cache_lock(fncp);
|
||
|
while ((scan = TAILQ_FIRST(&fncp->nc_list)) != NULL) {
|
||
|
cache_hold(scan);
|
||
|
cache_unlock(fncp);
|
||
|
/*
|
||
|
* We have to lock fncp's kids in order to unresolve
|
||
|
* their shadow kids...
|
||
|
*/
|
||
|
cache_get(scan);
|
||
|
cache_unlink_parent(scan);
|
||
|
cache_group_walk(scan, &unresolver_updater, SG_SUBTREE, scan);
|
||
|
cache_link_parent(scan, tncp);
|
||
|
if (scan->nc_flag & NCF_HASHED)
|
||
|
cache_rehash(scan);
|
||
|
cache_drop(scan);
|
||
|
cache_put(scan);
|
||
|
cache_lock(fncp);
|
||
|
}
|
||
|
cache_lock_two(tncp, fncp);
|
||
|
if ((fncp->nc_flag & tncp->nc_flag & NCF_UNRESOLVED) == 0) {
|
||
|
if (didwarn[1]++ % 10 == 0) {
|
||
|
printf("Warning: cache_rename: race #2 during "
|
||
|
"rename %s->%s\n",
|
||
|
fncp->nc_name, tncp->nc_name);
|
||
|
}
|
||
|
goto again;
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
cache_drop(ncp);
|
||
|
return;
|
||
|
}
|
||
|
KKASSERT(par->nc_exlocks == 0);
|
||
|
KKASSERT(par->nc_shadowinfo->sh_exlocks == 0);
|
||
|
cache_lock(ncp);
|
||
|
}
|
||
|
done:
|
||
| ... | ... | |
|
if (ncp->nc_timeout &&
|
||
|
(int)(ncp->nc_timeout - ticks) < 0 &&
|
||
|
(ncp->nc_flag & NCF_UNRESOLVED) == 0 &&
|
||
|
ncp->nc_exlocks == 0
|
||
|
ncp->nc_shadowinfo->sh_exlocks == 0
|
||
|
) {
|
||
|
cache_zap(cache_get(ncp));
|
||
|
goto restart;
|
||
| ... | ... | |
|
gd->gd_nchstats = &nchstats[i];
|
||
|
}
|
||
|
TAILQ_INIT(&ncneglist);
|
||
|
STAILQ_INIT(&shadowinfo_freeq);
|
||
|
nchashtbl = hashinit(desiredvnodes*2, M_VFSCACHE, &nchash);
|
||
|
nclockwarn = 1 * hz;
|
||
|
}
|
||
| sys/sys/namecache.h Sun Mar 26 07:56:54 2006 +0000 → sys/sys/namecache.h Wed Mar 29 10:23:26 2006 +0200 | ||
|---|---|---|
|
struct vnode;
|
||
|
/*
|
||
|
* Auxiliary structure for locking namecache entries,
|
||
|
* either on their own or grouped into "shadow groups".
|
||
|
*/
|
||
|
struct shadowinfo {
|
||
|
STAILQ_ENTRY(shadowinfo) sh_entry; /* entry for free list */
|
||
|
int sh_exlocks; /* namespace locking */
|
||
|
struct thread *sh_locktd; /* namespace locking */
|
||
|
int sh_refs; /* reference count */
|
||
|
uint8_t sh_lockreq :1; /* lock intent sign */
|
||
|
};
|
||
|
TAILQ_HEAD(namecache_list, namecache);
|
||
|
LIST_HEAD(namecache_shadow_list, namecache);
|
||
|
/*
|
||
|
* The namecache structure is used to manage the filesystem namespace. Most
|
||
| ... | ... | |
|
char *nc_name; /* Separately allocated seg name */
|
||
|
int nc_error;
|
||
|
int nc_timeout; /* compared against ticks, or 0 */
|
||
|
int nc_exlocks; /* namespace locking */
|
||
|
struct thread *nc_locktd; /* namespace locking */
|
||
|
struct shadowinfo *nc_shadowinfo; /* namespace locking */
|
||
|
struct shadowinfo nc_shadowinfo_internal; /* private locking information */
|
||
|
struct namecache *nc_shadow_prev; /* previous entry in shadow group */
|
||
|
struct namecache *nc_shadow_next; /* next entry in shadow group */
|
||
|
int nc_shadowheight; /* measure within shadow group */
|
||
|
struct namecache *nc_shadowed; /* lower layer entry in layered fs */
|
||
|
struct mount *nc_mount; /* associated mount for vopops */
|
||
|
int64_t nc_fsmid; /* filesystem modified id */
|
||
|
};
|
||
| ... | ... | |
|
#define NCF_MOUNTPT 0x0008 /* mount point */
|
||
|
#define NCF_ROOT 0x0010 /* namecache root (static) */
|
||
|
#define NCF_HASHED 0x0020 /* namecache entry in hash table */
|
||
|
#define NCF_LOCKREQ 0x0040
|
||
|
#define NCF_UNUSED040 0x0040
|
||
|
#define NCF_UNUSED080 0x0080
|
||
|
#define NCF_ISSYMLINK 0x0100 /* represents a symlink */
|
||
|
#define NCF_ISDIR 0x0200 /* represents a directory */
|
||
| ... | ... | |
|
void cache_lock(struct namecache *ncp);
|
||
|
int cache_lock_nonblock(struct namecache *ncp);
|
||
|
void cache_unlock(struct namecache *ncp);
|
||
|
void cache_lock_two(struct namecache *uncp, struct namecache *lncp);
|
||
|
int cache_shadow_attach(struct namecache *ncp, struct namecache *sncp);
|
||
|
void cache_shadow_detach(struct namecache *ncp);
|
||
|
void cache_setvp(struct namecache *ncp, struct vnode *vp);
|
||
|
void cache_settimeout(struct namecache *ncp, int nticks);
|
||
|
void cache_setunresolved(struct namecache *ncp);
|
||