Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_cache.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
#include <sys/fnv_hash.h> | #include <sys/fnv_hash.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/fcntl.h> | #include <sys/fcntl.h> | ||||
#include <sys/mount.h> | #include <sys/mount.h> | ||||
#include <sys/namei.h> | #include <sys/namei.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/rwlock.h> | #include <sys/rmlock.h> | ||||
#include <sys/sdt.h> | #include <sys/sdt.h> | ||||
#include <sys/syscallsubr.h> | #include <sys/syscallsubr.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/sysproto.h> | #include <sys/sysproto.h> | ||||
#include <sys/vnode.h> | #include <sys/vnode.h> | ||||
#ifdef KTRACE | #ifdef KTRACE | ||||
#include <sys/ktrace.h> | #include <sys/ktrace.h> | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | |||||
SYSCTL_ULONG(_debug, OID_AUTO, numcachehv, CTLFLAG_RD, &numcachehv, 0, | SYSCTL_ULONG(_debug, OID_AUTO, numcachehv, CTLFLAG_RD, &numcachehv, 0, | ||||
"Number of namecache entries with vnodes held"); | "Number of namecache entries with vnodes held"); | ||||
static u_int ncsizefactor = 2; | static u_int ncsizefactor = 2; | ||||
SYSCTL_UINT(_vfs, OID_AUTO, ncsizefactor, CTLFLAG_RW, &ncsizefactor, 0, | SYSCTL_UINT(_vfs, OID_AUTO, ncsizefactor, CTLFLAG_RW, &ncsizefactor, 0, | ||||
"Size factor for namecache"); | "Size factor for namecache"); | ||||
struct nchstats nchstats; /* cache effectiveness statistics */ | struct nchstats nchstats; /* cache effectiveness statistics */ | ||||
static struct rwlock cache_lock; | static struct rmlock cache_lock; | ||||
RW_SYSINIT(vfscache, &cache_lock, "Name Cache"); | RM_SYSINIT(vfscache, &cache_lock, "Name Cache"); | ||||
#define CACHE_UPGRADE_LOCK() rw_try_upgrade(&cache_lock) | /* | ||||
#define CACHE_RLOCK() rw_rlock(&cache_lock) | * The abstract way to assert that the write lock is held by the caller in routines that require it to be held. | ||||
#define CACHE_RUNLOCK() rw_runlock(&cache_lock) | */ | ||||
#define CACHE_WLOCK() rw_wlock(&cache_lock) | #define CACHE_WASSERT(remark_string) KASSERT(rm_wowned(&cache_lock), (remark_string)); | ||||
#define CACHE_WUNLOCK() rw_wunlock(&cache_lock) | /* | ||||
* Using rm_lock (read mostly) so that priority elevation will be possible. | |||||
* An rmlock cannot be upgraded at this rev of the OS. | |||||
* Report the inability to upgrade so that the existing legacy code in this file will do it the slow way. | |||||
*/ | |||||
#define CACHE_UPGRADE_LOCK() (0) | |||||
#define CACHE_RLOCK(tracker) rm_rlock(&cache_lock, (tracker)) | |||||
#define CACHE_RUNLOCK(tracker) rm_runlock(&cache_lock, (tracker)) | |||||
#define CACHE_WLOCK() rm_wlock(&cache_lock) | |||||
#define CACHE_WUNLOCK() rm_wunlock(&cache_lock) | |||||
/* | /* | ||||
* UMA zones for the VFS cache. | * UMA zones for the VFS cache. | ||||
* | * | ||||
* The small cache is used for entries with short names, which are the | * The small cache is used for entries with short names, which are the | ||||
* most common. The large cache is used for entries which are too big to | * most common. The large cache is used for entries which are too big to | ||||
* fit in the small cache. | * fit in the small cache. | ||||
*/ | */ | ||||
static uma_zone_t cache_zone_small; | static uma_zone_t cache_zone_small; | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | |||||
SYSCTL_OPAQUE(_vfs_cache, OID_AUTO, nchstats, CTLFLAG_RD | CTLFLAG_MPSAFE, | SYSCTL_OPAQUE(_vfs_cache, OID_AUTO, nchstats, CTLFLAG_RD | CTLFLAG_MPSAFE, | ||||
&nchstats, sizeof(nchstats), "LU", | &nchstats, sizeof(nchstats), "LU", | ||||
"VFS cache effectiveness statistics"); | "VFS cache effectiveness statistics"); | ||||
static void cache_zap(struct namecache *ncp); | static void cache_zap(struct namecache *ncp); | ||||
/* | |||||
* If vn_vptocnp_locked returns 0, the lock is still held. If it returns an error code, | |||||
* then it has released the lock. | |||||
* It might release and re-acquire the lock. | |||||
*/ | |||||
static int vn_vptocnp_locked(struct vnode **vp, struct ucred *cred, char *buf, | static int vn_vptocnp_locked(struct vnode **vp, struct ucred *cred, char *buf, | ||||
u_int *buflen); | u_int *buflen, struct rm_priotracker *parent_rm_tracker); | ||||
static int vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir, | static int vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir, | ||||
char *buf, char **retbuf, u_int buflen); | char *buf, char **retbuf, u_int buflen); | ||||
static MALLOC_DEFINE(M_VFSCACHE, "vfscache", "VFS name cache entries"); | static MALLOC_DEFINE(M_VFSCACHE, "vfscache", "VFS name cache entries"); | ||||
#ifdef DIAGNOSTIC | #ifdef DIAGNOSTIC | ||||
/* | /* | ||||
* Grab an atomic snapshot of the name cache hash chain lengths | * Grab an atomic snapshot of the name cache hash chain lengths | ||||
*/ | */ | ||||
static SYSCTL_NODE(_debug, OID_AUTO, hashstat, CTLFLAG_RW, NULL, | static SYSCTL_NODE(_debug, OID_AUTO, hashstat, CTLFLAG_RW, NULL, | ||||
"hash table stats"); | "hash table stats"); | ||||
static int | static int | ||||
sysctl_debug_hashstat_rawnchash(SYSCTL_HANDLER_ARGS) | sysctl_debug_hashstat_rawnchash(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct rm_priotracker rm_tracker; | |||||
int error; | int error; | ||||
struct nchashhead *ncpp; | struct nchashhead *ncpp; | ||||
struct namecache *ncp; | struct namecache *ncp; | ||||
int n_nchash; | int n_nchash; | ||||
int count; | int count; | ||||
n_nchash = nchash + 1; /* nchash is max index, not count */ | n_nchash = nchash + 1; /* nchash is max index, not count */ | ||||
if (!req->oldptr) | if (!req->oldptr) | ||||
return SYSCTL_OUT(req, 0, n_nchash * sizeof(int)); | return SYSCTL_OUT(req, 0, n_nchash * sizeof(int)); | ||||
/* Scan hash tables for applicable entries */ | /* Scan hash tables for applicable entries */ | ||||
for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { | for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
count = 0; | count = 0; | ||||
LIST_FOREACH(ncp, ncpp, nc_hash) { | LIST_FOREACH(ncp, ncpp, nc_hash) { | ||||
count++; | count++; | ||||
} | } | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
error = SYSCTL_OUT(req, &count, sizeof(count)); | error = SYSCTL_OUT(req, &count, sizeof(count)); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
SYSCTL_PROC(_debug_hashstat, OID_AUTO, rawnchash, CTLTYPE_INT|CTLFLAG_RD| | SYSCTL_PROC(_debug_hashstat, OID_AUTO, rawnchash, CTLTYPE_INT|CTLFLAG_RD| | ||||
CTLFLAG_MPSAFE, 0, 0, sysctl_debug_hashstat_rawnchash, "S,int", | CTLFLAG_MPSAFE, 0, 0, sysctl_debug_hashstat_rawnchash, "S,int", | ||||
"nchash chain lengths"); | "nchash chain lengths"); | ||||
static int | static int | ||||
sysctl_debug_hashstat_nchash(SYSCTL_HANDLER_ARGS) | sysctl_debug_hashstat_nchash(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct rm_priotracker rm_tracker; | |||||
int error; | int error; | ||||
struct nchashhead *ncpp; | struct nchashhead *ncpp; | ||||
struct namecache *ncp; | struct namecache *ncp; | ||||
int n_nchash; | int n_nchash; | ||||
int count, maxlength, used, pct; | int count, maxlength, used, pct; | ||||
if (!req->oldptr) | if (!req->oldptr) | ||||
return SYSCTL_OUT(req, 0, 4 * sizeof(int)); | return SYSCTL_OUT(req, 0, 4 * sizeof(int)); | ||||
n_nchash = nchash + 1; /* nchash is max index, not count */ | n_nchash = nchash + 1; /* nchash is max index, not count */ | ||||
used = 0; | used = 0; | ||||
maxlength = 0; | maxlength = 0; | ||||
/* Scan hash tables for applicable entries */ | /* Scan hash tables for applicable entries */ | ||||
for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { | for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { | ||||
count = 0; | count = 0; | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
LIST_FOREACH(ncp, ncpp, nc_hash) { | LIST_FOREACH(ncp, ncpp, nc_hash) { | ||||
count++; | count++; | ||||
} | } | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
if (count) | if (count) | ||||
used++; | used++; | ||||
if (maxlength < count) | if (maxlength < count) | ||||
maxlength = count; | maxlength = count; | ||||
} | } | ||||
n_nchash = nchash + 1; | n_nchash = nchash + 1; | ||||
pct = (used * 100) / (n_nchash / 100); | pct = (used * 100) / (n_nchash / 100); | ||||
error = SYSCTL_OUT(req, &n_nchash, sizeof(n_nchash)); | error = SYSCTL_OUT(req, &n_nchash, sizeof(n_nchash)); | ||||
Show All 22 Lines | |||||
* pointer to a vnode or if it is just a negative cache entry. | * pointer to a vnode or if it is just a negative cache entry. | ||||
*/ | */ | ||||
static void | static void | ||||
cache_zap(ncp) | cache_zap(ncp) | ||||
struct namecache *ncp; | struct namecache *ncp; | ||||
{ | { | ||||
struct vnode *vp; | struct vnode *vp; | ||||
rw_assert(&cache_lock, RA_WLOCKED); | CACHE_WASSERT("Attempt to zap vfs cache without wlock"); | ||||
CTR2(KTR_VFS, "cache_zap(%p) vp %p", ncp, ncp->nc_vp); | CTR2(KTR_VFS, "cache_zap(%p) vp %p", ncp, ncp->nc_vp); | ||||
#ifdef KDTRACE_HOOKS | #ifdef KDTRACE_HOOKS | ||||
if (ncp->nc_vp != NULL) { | if (ncp->nc_vp != NULL) { | ||||
SDT_PROBE(vfs, namecache, zap, done, ncp->nc_dvp, | SDT_PROBE(vfs, namecache, zap, done, ncp->nc_dvp, | ||||
nc_get_name(ncp), ncp->nc_vp, 0, 0); | nc_get_name(ncp), ncp->nc_vp, 0, 0); | ||||
} else { | } else { | ||||
SDT_PROBE(vfs, namecache, zap_negative, done, ncp->nc_dvp, | SDT_PROBE(vfs, namecache, zap_negative, done, ncp->nc_dvp, | ||||
nc_get_name(ncp), 0, 0, 0); | nc_get_name(ncp), 0, 0, 0); | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
int | int | ||||
cache_lookup(dvp, vpp, cnp, tsp, ticksp) | cache_lookup(dvp, vpp, cnp, tsp, ticksp) | ||||
struct vnode *dvp; | struct vnode *dvp; | ||||
struct vnode **vpp; | struct vnode **vpp; | ||||
struct componentname *cnp; | struct componentname *cnp; | ||||
struct timespec *tsp; | struct timespec *tsp; | ||||
int *ticksp; | int *ticksp; | ||||
{ | { | ||||
struct rm_priotracker rm_tracker; | |||||
struct namecache *ncp; | struct namecache *ncp; | ||||
uint32_t hash; | uint32_t hash; | ||||
int error, ltype, wlocked; | int error, ltype, wlocked; | ||||
if (!doingcache) { | if (!doingcache) { | ||||
cnp->cn_flags &= ~MAKEENTRY; | cnp->cn_flags &= ~MAKEENTRY; | ||||
return (0); | return (0); | ||||
} | } | ||||
retry: | retry: | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
wlocked = 0; | wlocked = 0; | ||||
numcalls++; | numcalls++; | ||||
error = 0; | error = 0; | ||||
retry_wlocked: | retry_wlocked: | ||||
if (cnp->cn_nameptr[0] == '.') { | if (cnp->cn_nameptr[0] == '.') { | ||||
if (cnp->cn_namelen == 1) { | if (cnp->cn_namelen == 1) { | ||||
*vpp = dvp; | *vpp = dvp; | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | negative_success: | ||||
CACHE_WUNLOCK(); | CACHE_WUNLOCK(); | ||||
return (ENOENT); | return (ENOENT); | ||||
wlock: | wlock: | ||||
/* | /* | ||||
* We need to update the cache after our lookup, so upgrade to | * We need to update the cache after our lookup, so upgrade to | ||||
* a write lock and retry the operation. | * a write lock and retry the operation. | ||||
*/ | */ | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
CACHE_WLOCK(); | CACHE_WLOCK(); | ||||
numupgrades++; | numupgrades++; | ||||
wlocked = 1; | wlocked = 1; | ||||
goto retry_wlocked; | goto retry_wlocked; | ||||
success: | success: | ||||
/* | /* | ||||
* On success we return a locked and ref'd vnode as per the lookup | * On success we return a locked and ref'd vnode as per the lookup | ||||
* protocol. | * protocol. | ||||
*/ | */ | ||||
if (dvp == *vpp) { /* lookup on "." */ | if (dvp == *vpp) { /* lookup on "." */ | ||||
VREF(*vpp); | VREF(*vpp); | ||||
if (wlocked) | if (wlocked) | ||||
CACHE_WUNLOCK(); | CACHE_WUNLOCK(); | ||||
else | else | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
/* | /* | ||||
* When we lookup "." we still can be asked to lock it | * When we lookup "." we still can be asked to lock it | ||||
* differently... | * differently... | ||||
*/ | */ | ||||
ltype = cnp->cn_lkflags & LK_TYPE_MASK; | ltype = cnp->cn_lkflags & LK_TYPE_MASK; | ||||
if (ltype != VOP_ISLOCKED(*vpp)) { | if (ltype != VOP_ISLOCKED(*vpp)) { | ||||
if (ltype == LK_EXCLUSIVE) { | if (ltype == LK_EXCLUSIVE) { | ||||
vn_lock(*vpp, LK_UPGRADE | LK_RETRY); | vn_lock(*vpp, LK_UPGRADE | LK_RETRY); | ||||
Show All 12 Lines | success: | ||||
if (cnp->cn_flags & ISDOTDOT) { | if (cnp->cn_flags & ISDOTDOT) { | ||||
ltype = VOP_ISLOCKED(dvp); | ltype = VOP_ISLOCKED(dvp); | ||||
VOP_UNLOCK(dvp, 0); | VOP_UNLOCK(dvp, 0); | ||||
} | } | ||||
VI_LOCK(*vpp); | VI_LOCK(*vpp); | ||||
if (wlocked) | if (wlocked) | ||||
CACHE_WUNLOCK(); | CACHE_WUNLOCK(); | ||||
else | else | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
error = vget(*vpp, cnp->cn_lkflags | LK_INTERLOCK, cnp->cn_thread); | error = vget(*vpp, cnp->cn_lkflags | LK_INTERLOCK, cnp->cn_thread); | ||||
if (cnp->cn_flags & ISDOTDOT) { | if (cnp->cn_flags & ISDOTDOT) { | ||||
vn_lock(dvp, ltype | LK_RETRY); | vn_lock(dvp, ltype | LK_RETRY); | ||||
if (dvp->v_iflag & VI_DOOMED) { | if (dvp->v_iflag & VI_DOOMED) { | ||||
if (error == 0) | if (error == 0) | ||||
vput(*vpp); | vput(*vpp); | ||||
*vpp = NULL; | *vpp = NULL; | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
} | } | ||||
if (error) { | if (error) { | ||||
*vpp = NULL; | *vpp = NULL; | ||||
goto retry; | goto retry; | ||||
} | } | ||||
if ((cnp->cn_flags & ISLASTCN) && | if ((cnp->cn_flags & ISLASTCN) && | ||||
(cnp->cn_lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE) { | (cnp->cn_lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE) { | ||||
ASSERT_VOP_ELOCKED(*vpp, "cache_lookup"); | ASSERT_VOP_ELOCKED(*vpp, "cache_lookup"); | ||||
} | } | ||||
return (-1); | return (-1); | ||||
unlock: | unlock: | ||||
if (wlocked) | if (wlocked) | ||||
CACHE_WUNLOCK(); | CACHE_WUNLOCK(); | ||||
else | else | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Add an entry to the cache. | * Add an entry to the cache. | ||||
*/ | */ | ||||
void | void | ||||
cache_enter_time(dvp, vp, cnp, tsp, dtsp) | cache_enter_time(dvp, vp, cnp, tsp, dtsp) | ||||
▲ Show 20 Lines • Show All 473 Lines • ▼ Show 20 Lines | vn_fullpath_global(struct thread *td, struct vnode *vn, | ||||
else | else | ||||
free(buf, M_TEMP); | free(buf, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
vn_vptocnp(struct vnode **vp, struct ucred *cred, char *buf, u_int *buflen) | vn_vptocnp(struct vnode **vp, struct ucred *cred, char *buf, u_int *buflen) | ||||
{ | { | ||||
struct rm_priotracker rm_tracker; | |||||
int error; | int error; | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
error = vn_vptocnp_locked(vp, cred, buf, buflen); | error = vn_vptocnp_locked(vp, cred, buf, buflen, &rm_tracker); | ||||
if (error == 0) | if (error == 0) | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
vn_vptocnp_locked(struct vnode **vp, struct ucred *cred, char *buf, | vn_vptocnp_locked(struct vnode **vp, struct ucred *cred, char *buf, | ||||
u_int *buflen) | u_int *buflen, struct rm_priotracker *rm_tracker) | ||||
{ | { | ||||
struct vnode *dvp; | struct vnode *dvp; | ||||
struct namecache *ncp; | struct namecache *ncp; | ||||
int error; | int error; | ||||
TAILQ_FOREACH(ncp, &((*vp)->v_cache_dst), nc_dst) { | TAILQ_FOREACH(ncp, &((*vp)->v_cache_dst), nc_dst) { | ||||
if ((ncp->nc_flag & NCF_ISDOTDOT) == 0) | if ((ncp->nc_flag & NCF_ISDOTDOT) == 0) | ||||
break; | break; | ||||
} | } | ||||
if (ncp != NULL) { | if (ncp != NULL) { | ||||
if (*buflen < ncp->nc_nlen) { | if (*buflen < ncp->nc_nlen) { | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(rm_tracker); | ||||
vrele(*vp); | vrele(*vp); | ||||
numfullpathfail4++; | numfullpathfail4++; | ||||
error = ENOMEM; | error = ENOMEM; | ||||
SDT_PROBE(vfs, namecache, fullpath, return, error, | SDT_PROBE(vfs, namecache, fullpath, return, error, | ||||
vp, NULL, 0, 0); | vp, NULL, 0, 0); | ||||
return (error); | return (error); | ||||
} | } | ||||
*buflen -= ncp->nc_nlen; | *buflen -= ncp->nc_nlen; | ||||
memcpy(buf + *buflen, nc_get_name(ncp), ncp->nc_nlen); | memcpy(buf + *buflen, nc_get_name(ncp), ncp->nc_nlen); | ||||
SDT_PROBE(vfs, namecache, fullpath, hit, ncp->nc_dvp, | SDT_PROBE(vfs, namecache, fullpath, hit, ncp->nc_dvp, | ||||
nc_get_name(ncp), vp, 0, 0); | nc_get_name(ncp), vp, 0, 0); | ||||
dvp = *vp; | dvp = *vp; | ||||
*vp = ncp->nc_dvp; | *vp = ncp->nc_dvp; | ||||
vref(*vp); | vref(*vp); | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(rm_tracker); | ||||
vrele(dvp); | vrele(dvp); | ||||
CACHE_RLOCK(); | CACHE_RLOCK(rm_tracker); | ||||
return (0); | return (0); | ||||
} | } | ||||
SDT_PROBE(vfs, namecache, fullpath, miss, vp, 0, 0, 0, 0); | SDT_PROBE(vfs, namecache, fullpath, miss, vp, 0, 0, 0, 0); | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(rm_tracker); | ||||
vn_lock(*vp, LK_SHARED | LK_RETRY); | vn_lock(*vp, LK_SHARED | LK_RETRY); | ||||
error = VOP_VPTOCNP(*vp, &dvp, cred, buf, buflen); | error = VOP_VPTOCNP(*vp, &dvp, cred, buf, buflen); | ||||
vput(*vp); | vput(*vp); | ||||
if (error) { | if (error) { | ||||
numfullpathfail2++; | numfullpathfail2++; | ||||
SDT_PROBE(vfs, namecache, fullpath, return, error, vp, | SDT_PROBE(vfs, namecache, fullpath, return, error, vp, | ||||
NULL, 0, 0); | NULL, 0, 0); | ||||
return (error); | return (error); | ||||
} | } | ||||
*vp = dvp; | *vp = dvp; | ||||
CACHE_RLOCK(); | CACHE_RLOCK(rm_tracker); | ||||
if (dvp->v_iflag & VI_DOOMED) { | if (dvp->v_iflag & VI_DOOMED) { | ||||
/* forced unmount */ | /* forced unmount */ | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(rm_tracker); | ||||
vrele(dvp); | vrele(dvp); | ||||
error = ENOENT; | error = ENOENT; | ||||
SDT_PROBE(vfs, namecache, fullpath, return, error, vp, | SDT_PROBE(vfs, namecache, fullpath, return, error, vp, | ||||
NULL, 0, 0); | NULL, 0, 0); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* *vp has its use count incremented still. | * *vp has its use count incremented still. | ||||
*/ | */ | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* The magic behind kern___getcwd() and vn_fullpath(). | * The magic behind kern___getcwd() and vn_fullpath(). | ||||
*/ | */ | ||||
static int | static int | ||||
vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir, | vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir, | ||||
char *buf, char **retbuf, u_int buflen) | char *buf, char **retbuf, u_int buflen) | ||||
{ | { | ||||
struct rm_priotracker rm_tracker; | |||||
int error, slash_prefixed; | int error, slash_prefixed; | ||||
#ifdef KDTRACE_HOOKS | #ifdef KDTRACE_HOOKS | ||||
struct vnode *startvp = vp; | struct vnode *startvp = vp; | ||||
#endif | #endif | ||||
struct vnode *vp1; | struct vnode *vp1; | ||||
buflen--; | buflen--; | ||||
buf[buflen] = '\0'; | buf[buflen] = '\0'; | ||||
error = 0; | error = 0; | ||||
slash_prefixed = 0; | slash_prefixed = 0; | ||||
SDT_PROBE(vfs, namecache, fullpath, entry, vp, 0, 0, 0, 0); | SDT_PROBE(vfs, namecache, fullpath, entry, vp, 0, 0, 0, 0); | ||||
numfullpathcalls++; | numfullpathcalls++; | ||||
vref(vp); | vref(vp); | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
if (vp->v_type != VDIR) { | if (vp->v_type != VDIR) { | ||||
error = vn_vptocnp_locked(&vp, td->td_ucred, buf, &buflen); | error = vn_vptocnp_locked(&vp, td->td_ucred, buf, &buflen, | ||||
&rm_tracker); | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
if (buflen == 0) { | if (buflen == 0) { | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
vrele(vp); | vrele(vp); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
buf[--buflen] = '/'; | buf[--buflen] = '/'; | ||||
slash_prefixed = 1; | slash_prefixed = 1; | ||||
} | } | ||||
while (vp != rdir && vp != rootvnode) { | while (vp != rdir && vp != rootvnode) { | ||||
if (vp->v_vflag & VV_ROOT) { | if (vp->v_vflag & VV_ROOT) { | ||||
if (vp->v_iflag & VI_DOOMED) { /* forced unmount */ | if (vp->v_iflag & VI_DOOMED) { /* forced unmount */ | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
vrele(vp); | vrele(vp); | ||||
error = ENOENT; | error = ENOENT; | ||||
SDT_PROBE(vfs, namecache, fullpath, return, | SDT_PROBE(vfs, namecache, fullpath, return, | ||||
error, vp, NULL, 0, 0); | error, vp, NULL, 0, 0); | ||||
break; | break; | ||||
} | } | ||||
vp1 = vp->v_mount->mnt_vnodecovered; | vp1 = vp->v_mount->mnt_vnodecovered; | ||||
vref(vp1); | vref(vp1); | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
vrele(vp); | vrele(vp); | ||||
vp = vp1; | vp = vp1; | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
continue; | continue; | ||||
} | } | ||||
if (vp->v_type != VDIR) { | if (vp->v_type != VDIR) { | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
vrele(vp); | vrele(vp); | ||||
numfullpathfail1++; | numfullpathfail1++; | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
SDT_PROBE(vfs, namecache, fullpath, return, | SDT_PROBE(vfs, namecache, fullpath, return, | ||||
error, vp, NULL, 0, 0); | error, vp, NULL, 0, 0); | ||||
break; | break; | ||||
} | } | ||||
error = vn_vptocnp_locked(&vp, td->td_ucred, buf, &buflen); | error = vn_vptocnp_locked(&vp, td->td_ucred, buf, &buflen, | ||||
&rm_tracker); | |||||
if (error) | if (error) | ||||
break; | break; | ||||
if (buflen == 0) { | if (buflen == 0) { | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
vrele(vp); | vrele(vp); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
SDT_PROBE(vfs, namecache, fullpath, return, error, | SDT_PROBE(vfs, namecache, fullpath, return, error, | ||||
startvp, NULL, 0, 0); | startvp, NULL, 0, 0); | ||||
break; | break; | ||||
} | } | ||||
buf[--buflen] = '/'; | buf[--buflen] = '/'; | ||||
slash_prefixed = 1; | slash_prefixed = 1; | ||||
} | } | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
if (!slash_prefixed) { | if (!slash_prefixed) { | ||||
if (buflen == 0) { | if (buflen == 0) { | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
vrele(vp); | vrele(vp); | ||||
numfullpathfail4++; | numfullpathfail4++; | ||||
SDT_PROBE(vfs, namecache, fullpath, return, ENOMEM, | SDT_PROBE(vfs, namecache, fullpath, return, ENOMEM, | ||||
startvp, NULL, 0, 0); | startvp, NULL, 0, 0); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
buf[--buflen] = '/'; | buf[--buflen] = '/'; | ||||
} | } | ||||
numfullpathfound++; | numfullpathfound++; | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
vrele(vp); | vrele(vp); | ||||
SDT_PROBE(vfs, namecache, fullpath, return, 0, startvp, buf + buflen, | SDT_PROBE(vfs, namecache, fullpath, return, 0, startvp, buf + buflen, | ||||
0, 0); | 0, 0); | ||||
*retbuf = buf + buflen; | *retbuf = buf + buflen; | ||||
return (0); | return (0); | ||||
} | } | ||||
struct vnode * | struct vnode * | ||||
vn_dir_dd_ino(struct vnode *vp) | vn_dir_dd_ino(struct vnode *vp) | ||||
{ | { | ||||
struct rm_priotracker rm_tracker; | |||||
struct namecache *ncp; | struct namecache *ncp; | ||||
struct vnode *ddvp; | struct vnode *ddvp; | ||||
ASSERT_VOP_LOCKED(vp, "vn_dir_dd_ino"); | ASSERT_VOP_LOCKED(vp, "vn_dir_dd_ino"); | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
TAILQ_FOREACH(ncp, &(vp->v_cache_dst), nc_dst) { | TAILQ_FOREACH(ncp, &(vp->v_cache_dst), nc_dst) { | ||||
if ((ncp->nc_flag & NCF_ISDOTDOT) != 0) | if ((ncp->nc_flag & NCF_ISDOTDOT) != 0) | ||||
continue; | continue; | ||||
ddvp = ncp->nc_dvp; | ddvp = ncp->nc_dvp; | ||||
VI_LOCK(ddvp); | VI_LOCK(ddvp); | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
if (vget(ddvp, LK_INTERLOCK | LK_SHARED | LK_NOWAIT, curthread)) | if (vget(ddvp, LK_INTERLOCK | LK_SHARED | LK_NOWAIT, curthread)) | ||||
return (NULL); | return (NULL); | ||||
return (ddvp); | return (ddvp); | ||||
} | } | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
int | int | ||||
vn_commname(struct vnode *vp, char *buf, u_int buflen) | vn_commname(struct vnode *vp, char *buf, u_int buflen) | ||||
{ | { | ||||
struct rm_priotracker rm_tracker; | |||||
struct namecache *ncp; | struct namecache *ncp; | ||||
int l; | int l; | ||||
CACHE_RLOCK(); | CACHE_RLOCK(&rm_tracker); | ||||
TAILQ_FOREACH(ncp, &vp->v_cache_dst, nc_dst) | TAILQ_FOREACH(ncp, &vp->v_cache_dst, nc_dst) | ||||
if ((ncp->nc_flag & NCF_ISDOTDOT) == 0) | if ((ncp->nc_flag & NCF_ISDOTDOT) == 0) | ||||
break; | break; | ||||
if (ncp == NULL) { | if (ncp == NULL) { | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
l = min(ncp->nc_nlen, buflen - 1); | l = min(ncp->nc_nlen, buflen - 1); | ||||
memcpy(buf, nc_get_name(ncp), l); | memcpy(buf, nc_get_name(ncp), l); | ||||
CACHE_RUNLOCK(); | CACHE_RUNLOCK(&rm_tracker); | ||||
buf[l] = '\0'; | buf[l] = '\0'; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* ABI compat shims for old kernel modules. */ | /* ABI compat shims for old kernel modules. */ | ||||
#undef cache_enter | #undef cache_enter | ||||
void cache_enter(struct vnode *dvp, struct vnode *vp, | void cache_enter(struct vnode *dvp, struct vnode *vp, | ||||
▲ Show 20 Lines • Show All 78 Lines • Show Last 20 Lines |