From owner-svn-src-all@freebsd.org Tue Aug 4 23:04:30 2020 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id C90C73A2FAD; Tue, 4 Aug 2020 23:04:30 +0000 (UTC) (envelope-from mjg@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4BLr1B4xhvz41D0; Tue, 4 Aug 2020 23:04:30 +0000 (UTC) (envelope-from mjg@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 8DE9DCB50; Tue, 4 Aug 2020 23:04:30 +0000 (UTC) (envelope-from mjg@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 074N4Upm026726; Tue, 4 Aug 2020 23:04:30 GMT (envelope-from mjg@FreeBSD.org) Received: (from mjg@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 074N4UN0026724; Tue, 4 Aug 2020 23:04:30 GMT (envelope-from mjg@FreeBSD.org) Message-Id: <202008042304.074N4UN0026724@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mjg set sender to mjg@FreeBSD.org using -f From: Mateusz Guzik Date: Tue, 4 Aug 2020 23:04:30 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r363872 - in head/sys: kern sys X-SVN-Group: head X-SVN-Commit-Author: mjg X-SVN-Commit-Paths: in head/sys: kern sys X-SVN-Commit-Revision: 363872 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.33 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 04 Aug 2020 23:04:30 -0000 Author: mjg Date: Tue Aug 4 23:04:29 2020 New Revision: 363872 URL: https://svnweb.freebsd.org/changeset/base/363872 Log: cache: add cache_purge_vgone cache_purge locklessly checks whether the vnode at hand has any namecache entries. This can race with a concurrent purge which managed to remove the last entry, but may not be done touching the vnode. Make sure we observe the relevant vnode lock as not taken before proceeding with vgone. Paired with the fact that doomed vnodes cannnot receive entries this restores the invariant that there are no namecache-related writing users past cache_purge in vgone. Reported by: pho Modified: head/sys/kern/vfs_cache.c head/sys/kern/vfs_subr.c head/sys/sys/vnode.h Modified: head/sys/kern/vfs_cache.c ============================================================================== --- head/sys/kern/vfs_cache.c Tue Aug 4 23:00:00 2020 (r363871) +++ head/sys/kern/vfs_cache.c Tue Aug 4 23:04:29 2020 (r363872) @@ -2138,22 +2138,17 @@ cache_changesize(u_long newmaxvnodes) /* * Invalidate all entries from and to a particular vnode. */ -void -cache_purge(struct vnode *vp) +static void +cache_purge_impl(struct vnode *vp) { TAILQ_HEAD(, namecache) ncps; struct namecache *ncp, *nnp; struct mtx *vlp, *vlp2; - CTR1(KTR_VFS, "cache_purge(%p)", vp); - SDT_PROBE1(vfs, namecache, purge, done, vp); - if (LIST_EMPTY(&vp->v_cache_src) && TAILQ_EMPTY(&vp->v_cache_dst) && - vp->v_cache_dd == NULL) - return; TAILQ_INIT(&ncps); vlp = VP2VNODELOCK(vp); vlp2 = NULL; - mtx_lock(vlp); + mtx_assert(vlp, MA_OWNED); retry: while (!LIST_EMPTY(&vp->v_cache_src)) { ncp = LIST_FIRST(&vp->v_cache_src); @@ -2182,6 +2177,53 @@ retry: TAILQ_FOREACH_SAFE(ncp, &ncps, nc_dst, nnp) { cache_free(ncp); } +} + +void +cache_purge(struct vnode *vp) +{ + struct mtx *vlp; + + SDT_PROBE1(vfs, namecache, purge, done, vp); + if (LIST_EMPTY(&vp->v_cache_src) && TAILQ_EMPTY(&vp->v_cache_dst) && + vp->v_cache_dd == NULL) + return; + vlp = VP2VNODELOCK(vp); + mtx_lock(vlp); + cache_purge_impl(vp); +} + +/* + * Only to be used by vgone. + */ +void +cache_purge_vgone(struct vnode *vp) +{ + struct mtx *vlp; + + VNPASS(VN_IS_DOOMED(vp), vp); + vlp = VP2VNODELOCK(vp); + if (!(LIST_EMPTY(&vp->v_cache_src) && TAILQ_EMPTY(&vp->v_cache_dst) && + vp->v_cache_dd == NULL)) { + mtx_lock(vlp); + cache_purge_impl(vp); + mtx_assert(vlp, MA_NOTOWNED); + return; + } + + /* + * All the NULL pointer state we found above may be transient. + * Serialize against a possible thread doing cache_purge. + */ + mtx_wait_unlocked(vlp); + if (!(LIST_EMPTY(&vp->v_cache_src) && TAILQ_EMPTY(&vp->v_cache_dst) && + vp->v_cache_dd == NULL)) { + mtx_lock(vlp); + cache_purge_impl(vp); + mtx_assert(vlp, MA_NOTOWNED); + return; + } + return; } /* Modified: head/sys/kern/vfs_subr.c ============================================================================== --- head/sys/kern/vfs_subr.c Tue Aug 4 23:00:00 2020 (r363871) +++ head/sys/kern/vfs_subr.c Tue Aug 4 23:04:29 2020 (r363872) @@ -4146,7 +4146,7 @@ vgonel(struct vnode *vp) * Delete from old mount point vnode list. */ delmntque(vp); - cache_purge(vp); + cache_purge_vgone(vp); /* * Done with purge, reset to the standard lock and invalidate * the vnode. Modified: head/sys/sys/vnode.h ============================================================================== --- head/sys/sys/vnode.h Tue Aug 4 23:00:00 2020 (r363871) +++ head/sys/sys/vnode.h Tue Aug 4 23:04:29 2020 (r363872) @@ -638,6 +638,7 @@ int cache_lookup(struct vnode *dvp, struct vnode **vpp struct componentname *cnp, struct timespec *tsp, int *ticksp); void cache_vnode_init(struct vnode *vp); void cache_purge(struct vnode *vp); +void cache_purge_vgone(struct vnode *vp); void cache_purge_negative(struct vnode *vp); void cache_purgevfs(struct mount *mp, bool force); int change_dir(struct vnode *vp, struct thread *td);