Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_subr.c
Show First 20 Lines • Show All 5,624 Lines • ▼ Show 20 Lines | vfs_unixify_accmode(accmode_t *accmode) | ||||
* or VSYNCHRONIZE using file mode or POSIX.1e ACL. | * or VSYNCHRONIZE using file mode or POSIX.1e ACL. | ||||
*/ | */ | ||||
*accmode &= ~(VSTAT_PERMS | VSYNCHRONIZE); | *accmode &= ~(VSTAT_PERMS | VSYNCHRONIZE); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Clear out a doomed vnode (if any) and replace it with a new one as long | |||||
* as the fs is not being unmounted. Return the root vnode to the caller. | |||||
*/ | |||||
static int __noinline | |||||
vfs_cache_root_fallback(struct mount *mp, int flags, struct vnode **vpp) | |||||
{ | |||||
struct vnode *vp; | |||||
int error; | |||||
restart: | |||||
if (mp->mnt_rootvnode != NULL) { | |||||
MNT_ILOCK(mp); | |||||
vp = mp->mnt_rootvnode; | |||||
if (vp != NULL) { | |||||
if ((vp->v_iflag & VI_DOOMED) == 0) { | |||||
vrefact(vp); | |||||
MNT_IUNLOCK(mp); | |||||
error = vn_lock(vp, flags); | |||||
if (error == 0) { | |||||
*vpp = vp; | |||||
return (0); | |||||
} | |||||
vrele(vp); | |||||
goto restart; | |||||
} | |||||
/* | |||||
* Clear the old one. | |||||
*/ | |||||
mp->mnt_rootvnode = NULL; | |||||
} | |||||
MNT_IUNLOCK(mp); | |||||
if (vp != NULL) { | |||||
/* | |||||
* Paired with a fence in vfs_op_thread_exit(). | |||||
*/ | |||||
atomic_thread_fence_acq(); | |||||
vfs_op_barrier_wait(mp); | |||||
vrele(vp); | |||||
} | |||||
} | |||||
error = VFS_CACHEDROOT(mp, flags, vpp); | |||||
if (error != 0) | |||||
return (error); | |||||
if (mp->mnt_vfs_ops == 0) { | |||||
MNT_ILOCK(mp); | |||||
if (mp->mnt_rootvnode == NULL && mp->mnt_vfs_ops == 0) { | |||||
jeff: Do we want to assert that the same vnode is returned as is cached in the case of a collision? | |||||
mjgAuthorUnsubmitted Done Inline ActionsI can add the assert no problem. If that's a possibility it already exists in the current code (that is 2 roughly concurrent calls to the _root routine end up with different vnodes) and sounds like solid bug, but we should probably audit if this can happen in sufficiently wrong conditions. With a long enough delay the original thread may perhaps unlock the vnode and have it doomed, at which point the other thread might have found a different one. i.e. it indeed may be the newly cached vnode is already doomed. mjg: I can add the assert no problem.
If that's a possibility it already exists in the current code… | |||||
vrefact(*vpp); | |||||
mp->mnt_rootvnode = *vpp; | |||||
} | |||||
MNT_IUNLOCK(mp); | |||||
} | |||||
return (error); | |||||
} | |||||
Not Done Inline ActionsThis could be a single else if indented one level less. jeff: This could be a single else if indented one level less. | |||||
int | |||||
vfs_cache_root(struct mount *mp, int flags, struct vnode **vpp) | |||||
{ | |||||
struct vnode *vp; | |||||
int error; | |||||
if (!vfs_op_thread_enter(mp)) | |||||
return (vfs_cache_root_fallback(mp, flags, vpp)); | |||||
vp = (struct vnode *)atomic_load_ptr(&mp->mnt_rootvnode); | |||||
if (vp == NULL || (vp->v_iflag & VI_DOOMED)) { | |||||
vfs_op_thread_exit(mp); | |||||
return (vfs_cache_root_fallback(mp, flags, vpp)); | |||||
} | |||||
vrefact(vp); | |||||
vfs_op_thread_exit(mp); | |||||
error = vn_lock(vp, flags); | |||||
if (error != 0) { | |||||
vrele(vp); | |||||
return (vfs_cache_root_fallback(mp, flags, vpp)); | |||||
} | |||||
*vpp = vp; | |||||
return (0); | |||||
} | |||||
struct vnode * | |||||
vfs_cache_root_clear(struct mount *mp) | |||||
{ | |||||
struct vnode *vp; | |||||
/* | |||||
* ops > 0 guarantees there is nobody who can see this vnode | |||||
*/ | |||||
MPASS(mp->mnt_vfs_ops > 0); | |||||
vp = mp->mnt_rootvnode; | |||||
mp->mnt_rootvnode = NULL; | |||||
return (vp); | |||||
} | |||||
/* | |||||
* These are helper functions for filesystems to traverse all | * These are helper functions for filesystems to traverse all | ||||
* their vnodes. See MNT_VNODE_FOREACH_ALL() in sys/mount.h. | * their vnodes. See MNT_VNODE_FOREACH_ALL() in sys/mount.h. | ||||
* | * | ||||
* This interface replaces MNT_VNODE_FOREACH. | * This interface replaces MNT_VNODE_FOREACH. | ||||
*/ | */ | ||||
MALLOC_DEFINE(M_VNODE_MARKER, "vnodemarker", "vnode marker"); | MALLOC_DEFINE(M_VNODE_MARKER, "vnodemarker", "vnode marker"); | ||||
struct vnode * | struct vnode * | ||||
__mnt_vnode_next_all(struct vnode **mvp, struct mount *mp) | __mnt_vnode_next_all(struct vnode **mvp, struct mount *mp) | ||||
{ | { | ||||
struct vnode *vp; | struct vnode *vp; | ||||
if (should_yield()) | if (should_yield()) | ||||
kern_yield(PRI_USER); | kern_yield(PRI_USER); | ||||
MNT_ILOCK(mp); | MNT_ILOCK(mp); | ||||
KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); | KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); | ||||
for (vp = TAILQ_NEXT(*mvp, v_nmntvnodes); vp != NULL; | for (vp = TAILQ_NEXT(*mvp, v_nmntvnodes); vp != NULL; | ||||
Not Done Inline ActionsSo consumers of this function have usecount for the root vnode set to 2. kib: So consumers of this function have usecount for the root vnode set to 2. | |||||
Done Inline ActionsI don't think that's avoidable without playing with vflush. It's not a problem though. mjg: I don't think that's avoidable without playing with vflush. It's not a problem though. | |||||
vp = TAILQ_NEXT(vp, v_nmntvnodes)) { | vp = TAILQ_NEXT(vp, v_nmntvnodes)) { | ||||
/* Allow a racy peek at VI_DOOMED to save a lock acquisition. */ | /* Allow a racy peek at VI_DOOMED to save a lock acquisition. */ | ||||
if (vp->v_type == VMARKER || (vp->v_iflag & VI_DOOMED) != 0) | if (vp->v_type == VMARKER || (vp->v_iflag & VI_DOOMED) != 0) | ||||
continue; | continue; | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
if ((vp->v_iflag & VI_DOOMED) != 0) { | if ((vp->v_iflag & VI_DOOMED) != 0) { | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 254 Lines • Show Last 20 Lines |
Do we want to assert that the same vnode is returned as is cached in the case of a collision? Otherwise we may return with a vnode locked that is not the same as the cached root.