diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 3c32808..15c70eb 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -292,7 +292,10 @@ cache_zap(ncp) } if (ncp->nc_vp) { TAILQ_REMOVE(&ncp->nc_vp->v_cache_dst, ncp, nc_dst); - ncp->nc_vp->v_dd = NULL; + if (ncp->nc_dvp == ncp->nc_vp->v_dd) { + LIST_REMOVE(ncp->nc_vp, v_cache_ptr); + ncp->nc_vp->v_dd = NULL; + } } else { TAILQ_REMOVE(&ncneg, ncp, nc_dst); numneg--; @@ -536,17 +539,12 @@ cache_enter(dvp, vp, cnp) if (cnp->cn_namelen == 1) { return; } - /* - * For dotdot lookups only cache the v_dd pointer if the - * directory has a link back to its parent via v_cache_dst. - * Without this an unlinked directory would keep a soft - * reference to its parent which could not be NULLd at - * cache_purge() time. - */ if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { CACHE_WLOCK(); - if (!TAILQ_EMPTY(&dvp->v_cache_dst)) - dvp->v_dd = vp; + if (dvp->v_dd != NULL) + LIST_REMOVE(dvp, v_cache_ptr); + LIST_INSERT_HEAD(&vp->v_cache_rev, dvp, v_cache_ptr); + dvp->v_dd = vp; CACHE_WUNLOCK(); return; } @@ -589,6 +587,9 @@ cache_enter(dvp, vp, cnp) numneg++; ncp->nc_flag = cnp->cn_flags & ISWHITEOUT ? NCF_WHITE : 0; } else if (vp->v_type == VDIR) { + if (vp->v_dd != NULL) + LIST_REMOVE(vp, v_cache_ptr); + LIST_INSERT_HEAD(&dvp->v_cache_rev, vp, v_cache_ptr); vp->v_dd = dvp; } else { vp->v_dd = NULL; @@ -651,6 +652,7 @@ void cache_purge(vp) struct vnode *vp; { + struct vnode *cvp; CTR1(KTR_VFS, "cache_purge(%p)", vp); CACHE_WLOCK(); @@ -658,7 +660,17 @@ cache_purge(vp) cache_zap(LIST_FIRST(&vp->v_cache_src)); while (!TAILQ_EMPTY(&vp->v_cache_dst)) cache_zap(TAILQ_FIRST(&vp->v_cache_dst)); - vp->v_dd = NULL; + /* Clean v_dd pointer in orphaned children vnodes. */ + while ((cvp = LIST_FIRST(&vp->v_cache_rev)) != NULL) { + KASSERT(cvp->v_dd == vp, ("v_dd pointer mismatch")); + LIST_REMOVE(cvp, v_cache_ptr); + cvp->v_dd = NULL; + } + /* Remove this vnode from parent rev list. */ + if (vp->v_dd != NULL) { + LIST_REMOVE(vp, v_cache_ptr); + vp->v_dd = NULL; + } CACHE_WUNLOCK(); } diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 2d6c0e4..afeb4c5 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -136,6 +136,9 @@ struct vnode { LIST_HEAD(, namecache) v_cache_src; /* c Cache entries from us */ TAILQ_HEAD(, namecache) v_cache_dst; /* c Cache entries to us */ struct vnode *v_dd; /* c .. vnode */ + LIST_HEAD(, vnode) v_cache_rev; /* c vnodes with v_dd pointing + to us. */ + LIST_ENTRY(vnode) v_cache_ptr; /* c linkage field for above */ /* * clustering stuff diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 9411e81..635b0e8 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -860,6 +860,7 @@ vdestroy(struct vnode *vp) VNASSERT(bo->bo_dirty.bv_root == NULL, vp, ("dirtyblkroot not NULL")); VNASSERT(TAILQ_EMPTY(&vp->v_cache_dst), vp, ("vp has namecache dst")); VNASSERT(LIST_EMPTY(&vp->v_cache_src), vp, ("vp has namecache src")); + VNASSERT(LIST_EMPTY(&vp->v_cache_rev), vp, ("vp has rev namecache")); VI_UNLOCK(vp); #ifdef MAC mac_vnode_destroy(vp); @@ -1004,6 +1005,7 @@ alloc: */ LIST_INIT(&vp->v_cache_src); TAILQ_INIT(&vp->v_cache_dst); + LIST_INIT(&vp->v_cache_rev); /* * Finalize various vnode identity bits. */