Index: sys/kern/vfs_cache.c =================================================================== --- sys/kern/vfs_cache.c +++ sys/kern/vfs_cache.c @@ -2924,7 +2924,8 @@ #define cache_fpl_handled(x) cache_fpl_handled_impl((x), __LINE__) #define CACHE_FPL_SUPPORTED_CN_FLAGS \ - (LOCKLEAF | FOLLOW | LOCKSHARED | SAVENAME | ISOPEN | AUDITVNODE1) + (LOCKLEAF | LOCKPARENT | WANTPARENT | FOLLOW | LOCKSHARED | SAVENAME | \ + ISOPEN | AUDITVNODE1) static bool cache_can_fplookup(struct cache_fpl *fpl) @@ -2997,10 +2998,45 @@ } static int -cache_fplookup_final(struct cache_fpl *fpl) +cache_fplookup_final_child(struct cache_fpl *fpl, enum vgetstate tvs) { struct componentname *cnp; - enum vgetstate tvs; + struct vnode *tvp; + seqc_t tvp_seqc; + int error; + + cnp = fpl->cnp; + tvp = fpl->next_vp; + tvp_seqc = fpl->next_vp_seqc; + + if ((cnp->cn_flags & LOCKLEAF) != 0) + error = vget_finish(tvp, cnp->cn_lkflags, tvs); + else + error = vget_finish_ref(tvp, tvs); + + if (error != 0) { + cache_fpl_unhandled(fpl); + return (CACHE_FPL_UNHANDLED); + } + + if (!seqc_consistent(&tvp->v_seqc, tvp_seqc)) { + cache_fpl_unhandled(fpl); + if ((cnp->cn_flags & LOCKLEAF) != 0) + vput(tvp); + else + vrele(tvp); + return (CACHE_FPL_UNHANDLED); + } + + cache_fpl_handled(fpl); + return (0); +} + +static int __noinline +cache_fplookup_final_withparent(struct cache_fpl *fpl) +{ + enum vgetstate dvs, tvs; + struct componentname *cnp; struct vnode *dvp, *tvp; seqc_t dvp_seqc, tvp_seqc; int error; @@ -3008,45 +3044,96 @@ cnp = fpl->cnp; dvp = fpl->vp; dvp_seqc = fpl->vp_seqc; + dvp_seqc = fpl->vp_seqc; tvp = fpl->next_vp; tvp_seqc = fpl->next_vp_seqc; - VNPASS(cache_fplookup_vnode_supported(dvp), dvp); + MPASS((cnp->cn_flags & (LOCKPARENT|WANTPARENT)) != 0); + /* + * This is less efficient than it can be for simplicity. + */ + dvs = vget_prep_smr(dvp); + if (dvs == VGET_NONE) { + cache_fpl_unhandled(fpl); + return (CACHE_FPL_UNHANDLED); + } tvs = vget_prep_smr(tvp); cache_fpl_smr_exit(fpl); if (tvs == VGET_NONE) { + vget_abort(dvp, dvs); cache_fpl_unhandled(fpl); return (CACHE_FPL_UNHANDLED); } - if (!seqc_consistent(&dvp->v_seqc, dvp_seqc)) { - cache_fpl_unhandled(fpl); + if ((cnp->cn_flags & LOCKPARENT) != 0) + error = vget_finish(dvp, LK_EXCLUSIVE, dvs); + else + error = vget_finish_ref(dvp, dvs); + + if (error != 0) { vget_abort(tvp, tvs); + cache_fpl_unhandled(fpl); return (CACHE_FPL_UNHANDLED); } - if ((cnp->cn_flags & LOCKLEAF) != 0) - error = vget_finish(tvp, cnp->cn_lkflags, tvs); - else - error = vget_finish_ref(tvp, tvs); + if (!seqc_consistent(&dvp->v_seqc, dvp_seqc)) { + vget_abort(tvp, tvs); + if ((cnp->cn_flags & LOCKPARENT) != 0) + vput(dvp); + else + vrele(dvp); + cache_fpl_unhandled(fpl); + return (error); + } + error = cache_fplookup_final_child(fpl, tvs); if (error != 0) { + if ((cnp->cn_flags & LOCKPARENT) != 0) + vput(dvp); + else + vrele(dvp); + cache_fpl_unhandled(fpl); + return (error); + } + + cache_fpl_handled(fpl); + return (0); +} + +static int +cache_fplookup_final(struct cache_fpl *fpl) +{ + struct componentname *cnp; + enum vgetstate tvs; + struct vnode *dvp, *tvp; + seqc_t dvp_seqc, tvp_seqc; + + cnp = fpl->cnp; + dvp = fpl->vp; + dvp_seqc = fpl->vp_seqc; + tvp = fpl->next_vp; + tvp_seqc = fpl->next_vp_seqc; + + VNPASS(cache_fplookup_vnode_supported(dvp), dvp); + + if ((cnp->cn_flags & (LOCKPARENT|WANTPARENT)) != 0) + return (cache_fplookup_final_withparent(fpl)); + + tvs = vget_prep_smr(tvp); + cache_fpl_smr_exit(fpl); + if (tvs == VGET_NONE) { cache_fpl_unhandled(fpl); return (CACHE_FPL_UNHANDLED); } - if (!seqc_consistent(&tvp->v_seqc, tvp_seqc)) { + if (!seqc_consistent(&dvp->v_seqc, dvp_seqc)) { cache_fpl_unhandled(fpl); - if ((cnp->cn_flags & LOCKLEAF) != 0) - vput(tvp); - else - vrele(tvp); + vget_abort(tvp, tvs); return (CACHE_FPL_UNHANDLED); } - cache_fpl_handled(fpl); - return (0); + return (cache_fplookup_final_child(fpl, tvs)); } static int Index: sys/sys/vnode.h =================================================================== --- sys/sys/vnode.h +++ sys/sys/vnode.h @@ -686,6 +686,7 @@ int vget(struct vnode *vp, int flags, struct thread *td); enum vgetstate vget_prep_smr(struct vnode *vp); enum vgetstate vget_prep(struct vnode *vp); +void vget_abort(struct vnode *vp, enum vgetstate vs); int vget_finish(struct vnode *vp, int flags, enum vgetstate vs); int vget_finish_ref(struct vnode *vp, enum vgetstate vs); void vget_abort(struct vnode *vp, enum vgetstate vs);