From owner-svn-src-projects@freebsd.org Tue Sep 3 14:06:47 2019 Return-Path: Delivered-To: svn-src-projects@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 4D5A7DCF3C for ; Tue, 3 Sep 2019 14:06:47 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2610:1c1:1:6074::16:84]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46N7zq0bd7z4PvC; Tue, 3 Sep 2019 14:06:47 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 1452) id 41A9D1AB84; Tue, 3 Sep 2019 14:06:17 +0000 (UTC) X-Original-To: yuripv@localmail.freebsd.org Delivered-To: yuripv@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id F03AA18E3E; Fri, 12 Apr 2019 19:05:11 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [96.47.72.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 38FBA884AC; Fri, 12 Apr 2019 19:05:11 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 538) id 0E9C118DD6; Fri, 12 Apr 2019 19:05:11 +0000 (UTC) Delivered-To: src-committers@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [96.47.72.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id 70C6018DD1 for ; Fri, 12 Apr 2019 19:05:08 +0000 (UTC) (envelope-from asomers@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) server-signature RSA-PSS (4096 bits) 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 455BE884A5; Fri, 12 Apr 2019 19:05:08 +0000 (UTC) (envelope-from asomers@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 1F99625996; Fri, 12 Apr 2019 19:05:08 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x3CJ58kp033448; Fri, 12 Apr 2019 19:05:08 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x3CJ57Mk033444; Fri, 12 Apr 2019 19:05:07 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201904121905.x3CJ57Mk033444@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r346162 - in projects/fuse2: sys/fs/fuse sys/kern sys/sys tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: in projects/fuse2: sys/fs/fuse sys/kern sys/sys tests/sys/fs/fusefs X-SVN-Commit-Revision: 346162 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk X-Loop: FreeBSD.org Sender: owner-src-committers@freebsd.org X-Rspamd-Queue-Id: 38FBA884AC X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[freebsd.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_SHORT(-0.97)[-0.972,0]; ASN(0.00)[asn:11403, ipnet:96.47.64.0/20, country:US]; NEURAL_HAM_LONG(-1.00)[-1.000,0] Status: O X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Date: Tue, 03 Sep 2019 14:06:47 -0000 X-Original-Date: Fri, 12 Apr 2019 19:05:07 +0000 (UTC) X-List-Received-Date: Tue, 03 Sep 2019 14:06:47 -0000 Author: asomers Date: Fri Apr 12 19:05:06 2019 New Revision: 346162 URL: https://svnweb.freebsd.org/changeset/base/346162 Log: fusefs: evict invalidated cache contents during write-through fusefs's default cache mode is "writethrough", although it currently works more like "write-around"; writes bypass the cache completely. Since writes bypass the cache, they were leaving stale previously-read data in the cache. This commit invalidates that stale data. It also adds a new global v_inval_buf_range method, like vtruncbuf but for a range of a file. PR: 235774 Reported by: cem Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sys/fs/fuse/fuse_io.c projects/fuse2/sys/kern/vfs_subr.c projects/fuse2/sys/sys/vnode.h projects/fuse2/tests/sys/fs/fusefs/write.cc Modified: projects/fuse2/sys/fs/fuse/fuse_io.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_io.c Fri Apr 12 18:54:09 2019 (r346161) +++ projects/fuse2/sys/fs/fuse/fuse_io.c Fri Apr 12 19:05:06 2019 (r346162) @@ -171,8 +171,13 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, in * cached. */ if (directio || fuse_data_cache_mode == FUSE_CACHE_WT) { + off_t start, end; + SDT_PROBE2(fuse, , io, trace, 1, "direct write of vnode"); + start = uio->uio_offset; + end = start + uio->uio_resid; + v_inval_buf_range(vp, start, end, fuse_iosize(vp)); err = fuse_write_directbackend(vp, uio, cred, fufh, ioflag); } else { Modified: projects/fuse2/sys/kern/vfs_subr.c ============================================================================== --- projects/fuse2/sys/kern/vfs_subr.c Fri Apr 12 18:54:09 2019 (r346161) +++ projects/fuse2/sys/kern/vfs_subr.c Fri Apr 12 19:05:06 2019 (r346162) @@ -116,6 +116,8 @@ static void vfs_knl_assert_locked(void *arg); static void vfs_knl_assert_unlocked(void *arg); static void vnlru_return_batches(struct vfsops *mnt_op); static void destroy_vpollinfo(struct vpollinfo *vi); +static int v_inval_buf_range1(struct vnode *vp, struct bufobj *bo, + daddr_t startlbn, daddr_t endlbn); /* * These fences are intended for cases where some synchronization is @@ -1865,9 +1867,8 @@ int vtruncbuf(struct vnode *vp, struct ucred *cred, off_t length, int blksize) { struct buf *bp, *nbp; - int anyfreed; - daddr_t trunclbn; struct bufobj *bo; + daddr_t startlbn; CTR5(KTR_VFS, "%s: vp %p with cred %p and block %d:%ju", __func__, vp, cred, blksize, (uintmax_t)length); @@ -1875,22 +1876,111 @@ vtruncbuf(struct vnode *vp, struct ucred *cred, off_t /* * Round up to the *next* lbn. */ - trunclbn = howmany(length, blksize); + startlbn = howmany(length, blksize); ASSERT_VOP_LOCKED(vp, "vtruncbuf"); + restart: bo = &vp->v_bufobj; BO_LOCK(bo); + if (v_inval_buf_range1(vp, bo, length, INT64_MAX) == EAGAIN) + goto restart; + + if (length > 0) { +restartsync: + TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { + if (bp->b_lblkno > 0) + continue; + /* + * Since we hold the vnode lock this should only + * fail if we're racing with the buf daemon. + */ + if (BUF_LOCK(bp, + LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, + BO_LOCKPTR(bo)) == ENOLCK) { + goto restart; + } + VNASSERT((bp->b_flags & B_DELWRI), vp, + ("buf(%p) on dirty queue without DELWRI", bp)); + + bremfree(bp); + bawrite(bp); + BO_LOCK(bo); + goto restartsync; + } + } + + bufobj_wwait(bo, 0, 0); + BO_UNLOCK(bo); + vnode_pager_setsize(vp, length); + + return (0); +} + +/* + * Invalidate the cached pages of a file's buffer within the range of block + * numbers [startlbn, endlbn). Every buffer that overlaps that range will be + * invalidated. This must not result in any dirty data being lost. + */ +void +v_inval_buf_range(struct vnode *vp, off_t start, off_t end, int blksize) +{ + struct bufobj *bo; + daddr_t startlbn, endlbn; + + /* Round "outwards" */ + startlbn = start / blksize; + endlbn = howmany(end, blksize); + + ASSERT_VOP_LOCKED(vp, "v_inval_buf_range"); + +restart: + bo = &vp->v_bufobj; + BO_LOCK(bo); + +#ifdef INVARIANTS + struct buf *bp, *nbp; + + TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { + /* + * Disallow invalidating dirty data outside of the requested + * offsets. Assume that data within the requested offsets is + * being invalidated for a good reason. + */ + off_t blkstart, blkend; + + blkstart = bp->b_offset; + blkend = bp->b_offset + bp->b_bcount; + KASSERT(blkstart >= start && blkend <= end, + ("Invalidating extra dirty data!")); + } +#endif + + if (v_inval_buf_range1(vp, bo, startlbn, endlbn) == EAGAIN) + goto restart; + + BO_UNLOCK(bo); + vn_pages_remove(vp, OFF_TO_IDX(start), OFF_TO_IDX(end)); +} + +/* Like v_inval_buf_range, but operates on whole buffers instead of offsets */ +static int +v_inval_buf_range1(struct vnode *vp, struct bufobj *bo, + daddr_t startlbn, daddr_t endlbn) +{ + struct buf *bp, *nbp; + int anyfreed; + anyfreed = 1; for (;anyfreed;) { anyfreed = 0; TAILQ_FOREACH_SAFE(bp, &bo->bo_clean.bv_hd, b_bobufs, nbp) { - if (bp->b_lblkno < trunclbn) + if (bp->b_lblkno < startlbn || bp->b_lblkno >= endlbn) continue; if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, BO_LOCKPTR(bo)) == ENOLCK) - goto restart; + return EAGAIN; bremfree(bp); bp->b_flags |= (B_INVAL | B_RELBUF); @@ -1904,17 +1994,17 @@ restart: (nbp->b_vp != vp) || (nbp->b_flags & B_DELWRI))) { BO_UNLOCK(bo); - goto restart; + return EAGAIN; } } TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { - if (bp->b_lblkno < trunclbn) + if (bp->b_lblkno < startlbn || bp->b_lblkno >= endlbn) continue; if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, BO_LOCKPTR(bo)) == ENOLCK) - goto restart; + return EAGAIN; bremfree(bp); bp->b_flags |= (B_INVAL | B_RELBUF); bp->b_flags &= ~B_ASYNC; @@ -1927,40 +2017,11 @@ restart: (nbp->b_vp != vp) || (nbp->b_flags & B_DELWRI) == 0)) { BO_UNLOCK(bo); - goto restart; + return EAGAIN; } } } - - if (length > 0) { -restartsync: - TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { - if (bp->b_lblkno > 0) - continue; - /* - * Since we hold the vnode lock this should only - * fail if we're racing with the buf daemon. - */ - if (BUF_LOCK(bp, - LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, - BO_LOCKPTR(bo)) == ENOLCK) { - goto restart; - } - VNASSERT((bp->b_flags & B_DELWRI), vp, - ("buf(%p) on dirty queue without DELWRI", bp)); - - bremfree(bp); - bawrite(bp); - BO_LOCK(bo); - goto restartsync; - } - } - - bufobj_wwait(bo, 0, 0); - BO_UNLOCK(bo); - vnode_pager_setsize(vp, length); - - return (0); + return 0; } static void Modified: projects/fuse2/sys/sys/vnode.h ============================================================================== --- projects/fuse2/sys/sys/vnode.h Fri Apr 12 18:54:09 2019 (r346161) +++ projects/fuse2/sys/sys/vnode.h Fri Apr 12 19:05:06 2019 (r346162) @@ -660,6 +660,8 @@ void vinactive(struct vnode *, struct thread *); int vinvalbuf(struct vnode *vp, int save, int slpflag, int slptimeo); int vtruncbuf(struct vnode *vp, struct ucred *cred, off_t length, int blksize); +void v_inval_buf_range(struct vnode *vp, off_t start, off_t end, + int blksize); void vunref(struct vnode *); void vn_printf(struct vnode *vp, const char *fmt, ...) __printflike(2,3); int vrecycle(struct vnode *vp); Modified: projects/fuse2/tests/sys/fs/fusefs/write.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/write.cc Fri Apr 12 18:54:09 2019 (r346161) +++ projects/fuse2/tests/sys/fs/fusefs/write.cc Fri Apr 12 19:05:06 2019 (r346162) @@ -228,8 +228,7 @@ TEST_F(Write, append_direct_io) } /* A direct write should evict any overlapping cached data */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235774 */ -TEST_F(Write, DISABLED_direct_io_evicts_cache) +TEST_F(Write, direct_io_evicts_cache) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; @@ -407,6 +406,42 @@ TEST_F(Write, DISABLED_mmap) free(expected); free(zeros); +} + +/* In WriteThrough mode, a write should evict overlapping cached data */ +TEST_F(WriteThrough, evicts_read_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char CONTENTS0[] = "abcdefgh"; + const char CONTENTS1[] = "ijklmnop"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS0) + 1; + char readbuf[bufsize]; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_read(ino, 0, bufsize, bufsize, CONTENTS0); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1); + + fd = open(FULLPATH, O_RDWR); + EXPECT_LE(0, fd) << strerror(errno); + + // Prime cache + ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); + + // Write directly, evicting cache + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno); + + // Read again. Cache should be bypassed + expect_read(ino, 0, bufsize, bufsize, CONTENTS1); + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); + ASSERT_STREQ(readbuf, CONTENTS1); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ } TEST_F(WriteThrough, pwrite)