Index: head/sys/kern/vfs_cache.c =================================================================== --- head/sys/kern/vfs_cache.c +++ head/sys/kern/vfs_cache.c @@ -3011,8 +3011,8 @@ #define cache_fpl_handled(x, e) cache_fpl_handled_impl((x), (e), __LINE__) #define CACHE_FPL_SUPPORTED_CN_FLAGS \ - (LOCKLEAF | FOLLOW | LOCKSHARED | SAVENAME | ISOPEN | NOMACCHECK | \ - AUDITVNODE1 | AUDITVNODE2) + (LOCKLEAF | LOCKPARENT | WANTPARENT | FOLLOW | LOCKSHARED | SAVENAME | \ + ISOPEN | NOMACCHECK | AUDITVNODE1 | AUDITVNODE2) static bool cache_can_fplookup(struct cache_fpl *fpl) @@ -3208,10 +3208,42 @@ } 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); + if (error != 0) { + return (cache_fpl_aborted(fpl)); + } + } else { + vget_finish_ref(tvp, tvs); + } + + if (!vn_seqc_consistent(tvp, tvp_seqc)) { + if ((cnp->cn_flags & LOCKLEAF) != 0) + vput(tvp); + else + vrele(tvp); + return (cache_fpl_aborted(fpl)); + } + + return (cache_fpl_handled(fpl, 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; @@ -3222,39 +3254,90 @@ 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_aborted(fpl)); + } tvs = vget_prep_smr(tvp); if (tvs == VGET_NONE) { - return (cache_fpl_partial(fpl)); - } - - if (!vn_seqc_consistent(dvp, dvp_seqc)) { cache_fpl_smr_exit(fpl); - vget_abort(tvp, tvs); + vget_abort(dvp, dvs); return (cache_fpl_aborted(fpl)); } cache_fpl_smr_exit(fpl); - if ((cnp->cn_flags & LOCKLEAF) != 0) { - error = vget_finish(tvp, cnp->cn_lkflags, tvs); + if ((cnp->cn_flags & LOCKPARENT) != 0) { + error = vget_finish(dvp, LK_EXCLUSIVE, dvs); if (error != 0) { + vget_abort(tvp, tvs); return (cache_fpl_aborted(fpl)); } } else { - vget_finish_ref(tvp, tvs); + vget_finish_ref(dvp, dvs); } - if (!vn_seqc_consistent(tvp, tvp_seqc)) { - if ((cnp->cn_flags & LOCKLEAF) != 0) - vput(tvp); + if (!vn_seqc_consistent(dvp, dvp_seqc)) { + vget_abort(tvp, tvs); + if ((cnp->cn_flags & LOCKPARENT) != 0) + vput(dvp); else - vrele(tvp); + vrele(dvp); + cache_fpl_aborted(fpl); + return (error); + } + + error = cache_fplookup_final_child(fpl, tvs); + if (error != 0) { + MPASS(fpl->status == CACHE_FPL_STATUS_ABORTED); + if ((cnp->cn_flags & LOCKPARENT) != 0) + vput(dvp); + else + vrele(dvp); + return (error); + } + + MPASS(fpl->status == CACHE_FPL_STATUS_HANDLED); + 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); + if (tvs == VGET_NONE) { + return (cache_fpl_partial(fpl)); + } + + if (!vn_seqc_consistent(dvp, dvp_seqc)) { + cache_fpl_smr_exit(fpl); + vget_abort(tvp, tvs); return (cache_fpl_aborted(fpl)); } - return (cache_fpl_handled(fpl, 0)); + cache_fpl_smr_exit(fpl); + return (cache_fplookup_final_child(fpl, tvs)); } static int