Index: sys/kern/vfs_cache.c =================================================================== --- sys/kern/vfs_cache.c +++ sys/kern/vfs_cache.c @@ -2925,7 +2925,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,43 @@ } 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->tvp; + tvp_seqc = fpl->tvp_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) { + return (cache_fpl_unhandled(fpl)); + } + + if (!seqc_consistent(&tvp->v_seqc, tvp_seqc)) { + if ((cnp->cn_flags & LOCKLEAF) != 0) + vput(tvp); + else + vrele(tvp); + return (cache_fpl_unhandled(fpl)); + } + + 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,41 +3042,91 @@ cnp = fpl->cnp; dvp = fpl->dvp; dvp_seqc = fpl->dvp_seqc; + dvp_seqc = fpl->dvp_seqc; tvp = fpl->tvp; tvp_seqc = fpl->tvp_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) { + return (cache_fpl_unhandled(fpl)); + } tvs = vget_prep_smr(tvp); cache_fpl_smr_exit(fpl); if (tvs == VGET_NONE) { + vget_abort(dvp, dvs); return (cache_fpl_unhandled(fpl)); } - if (!seqc_consistent(&dvp->v_seqc, dvp_seqc)) { + 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); return (cache_fpl_unhandled(fpl)); } - 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->dvp; + dvp_seqc = fpl->dvp_seqc; + tvp = fpl->tvp; + tvp_seqc = fpl->tvp_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) { return (cache_fpl_unhandled(fpl)); } - if (!seqc_consistent(&tvp->v_seqc, tvp_seqc)) { - if ((cnp->cn_flags & LOCKLEAF) != 0) - vput(tvp); - else - vrele(tvp); + if (!seqc_consistent(&dvp->v_seqc, dvp_seqc)) { + vget_abort(tvp, tvs); return (cache_fpl_unhandled(fpl)); } - cache_fpl_handled(fpl); - return (0); + return (cache_fplookup_final_child(fpl, tvs)); } static int