From owner-svn-src-projects@freebsd.org Mon Jun 10 22:23:38 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 55A9F15CAB8B for ; Mon, 10 Jun 2019 22:23:38 +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 C0EEB8FB03; Mon, 10 Jun 2019 22:23:37 +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 B042620973; Mon, 10 Jun 2019 22:23:37 +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 x5AMNbNR010417; Mon, 10 Jun 2019 22:23:37 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5AMNbmT010416; Mon, 10 Jun 2019 22:23:37 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906102223.x5AMNbmT010416@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Mon, 10 Jun 2019 22:23:37 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r348884 - projects/fuse2/tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: projects/fuse2/tests/sys/fs/fusefs X-SVN-Commit-Revision: 348884 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: C0EEB8FB03 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.96 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; NEURAL_HAM_SHORT(-0.97)[-0.965,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 10 Jun 2019 22:23:38 -0000 Author: asomers Date: Mon Jun 10 22:23:37 2019 New Revision: 348884 URL: https://svnweb.freebsd.org/changeset/base/348884 Log: fusefs: fix a comment. No functional change. Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/tests/sys/fs/fusefs/xattr.cc Modified: projects/fuse2/tests/sys/fs/fusefs/xattr.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/xattr.cc Mon Jun 10 22:06:40 2019 (r348883) +++ projects/fuse2/tests/sys/fs/fusefs/xattr.cc Mon Jun 10 22:23:37 2019 (r348884) @@ -28,6 +28,8 @@ * SUCH DAMAGE. */ +/* Tests for all things relating to extended attributes and FUSE */ + extern "C" { #include #include @@ -42,7 +44,6 @@ using namespace testing; const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; -/* For testing filesystems without posix locking support */ class Xattr: public FuseTest { public: void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r) From owner-svn-src-projects@freebsd.org Tue Jun 11 16:16:17 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id AE2EE15BFD71 for ; Tue, 11 Jun 2019 16:16:17 +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 4CF256C974; Tue, 11 Jun 2019 16:16:17 +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 25214421C; Tue, 11 Jun 2019 16:16:17 +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 x5BGGH3b074241; Tue, 11 Jun 2019 16:16:17 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5BGGHfZ074240; Tue, 11 Jun 2019 16:16:17 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906111616.x5BGGHfZ074240@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Tue, 11 Jun 2019 16:16:17 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r348930 - projects/fuse2/tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: projects/fuse2/tests/sys/fs/fusefs X-SVN-Commit-Revision: 348930 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 4CF256C974 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.96 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; NEURAL_HAM_SHORT(-0.96)[-0.963,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 11 Jun 2019 16:16:17 -0000 Author: asomers Date: Tue Jun 11 16:16:16 2019 New Revision: 348930 URL: https://svnweb.freebsd.org/changeset/base/348930 Log: fusefs: fix an infinite loop in the tests It was possible for the MockFS thread to infinitely loop if it got an error reading from /dev/fuse. Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mockfs.cc Tue Jun 11 15:52:41 2019 (r348929) +++ projects/fuse2/tests/sys/fs/fusefs/mockfs.cc Tue Jun 11 16:16:16 2019 (r348930) @@ -637,8 +637,10 @@ void MockFS::read_request(mockfs_buf_in &in) { } res = read(m_fuse_fd, &in, sizeof(in)); - if (res < 0 && !m_quit) - perror("read"); + if (res < 0 && !m_quit) { + FAIL() << "read: " << strerror(errno); + m_quit = true; + } ASSERT_TRUE(res >= static_cast(sizeof(in.header)) || m_quit); } From owner-svn-src-projects@freebsd.org Tue Jun 11 16:32:35 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 0A35A15C00C7 for ; Tue, 11 Jun 2019 16:32:35 +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 9B4DD6D0F9; Tue, 11 Jun 2019 16:32:34 +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 867CC458F; Tue, 11 Jun 2019 16:32:34 +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 x5BGWYJ8084191; Tue, 11 Jun 2019 16:32:34 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5BGWXus084187; Tue, 11 Jun 2019 16:32:33 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906111632.x5BGWXus084187@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Tue, 11 Jun 2019 16:32:33 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r348931 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Commit-Revision: 348931 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 9B4DD6D0F9 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.967,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 11 Jun 2019 16:32:35 -0000 Author: asomers Date: Tue Jun 11 16:32:33 2019 New Revision: 348931 URL: https://svnweb.freebsd.org/changeset/base/348931 Log: fusefs: WIP fixing writeback cacheing The current "writeback" cache mode, selected by the vfs.fusefs.data_cache_mode sysctl, doesn't do writeback cacheing at all. It merely goes through the motions of using buf(9), but then writes every buffer synchronously. This commit: * Enables delayed writes when the sysctl is set to writeback cacheing * Fixes a cache-coherency problem when extending a file whose last page has just been written. * Removes the "sync" mount option, which had been set unconditionally. * Adjusts some SDT probes * Adds several new tests that mimic what fsx does but with more control and without a real file system. As I discover failures with fsx, I add regression tests to this file. * Adds a test that ensures we can append to a file without reading any data from it. This change is still incomplete. Clustered writing is not yet supported, and there are frequent "panic: vm_fault_hold: fault on nofault entry" panics that I need to fix. Sponsored by: The FreeBSD Foundation Added: projects/fuse2/tests/sys/fs/fusefs/io.cc (contents, props changed) Modified: projects/fuse2/sys/fs/fuse/fuse_io.c projects/fuse2/sys/fs/fuse/fuse_vfsops.c projects/fuse2/tests/sys/fs/fusefs/Makefile 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 Tue Jun 11 16:16:16 2019 (r348930) +++ projects/fuse2/sys/fs/fuse/fuse_io.c Tue Jun 11 16:32:33 2019 (r348931) @@ -84,6 +84,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -265,9 +266,10 @@ out: return (err); } -SDT_PROBE_DEFINE3(fusefs, , io, read_bio_backend_start, "int", "int", "int"); +SDT_PROBE_DEFINE4(fusefs, , io, read_bio_backend_start, "int", "int", "int", "int"); SDT_PROBE_DEFINE2(fusefs, , io, read_bio_backend_feed, "int", "int"); -SDT_PROBE_DEFINE3(fusefs, , io, read_bio_backend_end, "int", "ssize_t", "int"); +SDT_PROBE_DEFINE4(fusefs, , io, read_bio_backend_end, "int", "ssize_t", "int", + "struct buf*"); static int fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred, struct fuse_filehandle *fufh, pid_t pid) @@ -297,9 +299,6 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio lbn = uio->uio_offset / biosize; on = uio->uio_offset & (biosize - 1); - SDT_PROBE3(fusefs, , io, read_bio_backend_start, - biosize, (int)lbn, on); - if ((off_t)lbn * biosize >= filesize) { bcount = 0; } else if ((off_t)(lbn + 1) * biosize > filesize) { @@ -308,6 +307,9 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio bcount = biosize; } + SDT_PROBE4(fusefs, , io, read_bio_backend_start, + biosize, (int)lbn, on, bcount); + /* TODO: readahead. See ext2_read for an example */ err = bread(vp, lbn, bcount, NOCRED, &bp); if (err) { @@ -333,8 +335,8 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio err = uiomove(bp->b_data + on, n, uio); } vfs_bio_brelse(bp, ioflag); - SDT_PROBE3(fusefs, , io, read_bio_backend_end, err, - uio->uio_resid, n); + SDT_PROBE4(fusefs, , io, read_bio_backend_end, err, + uio->uio_resid, n, bp); } return (err); @@ -564,6 +566,7 @@ fuse_write_biobackend(struct vnode *vp, struct uio *ui off_t filesize; int bcount; int n, on, err = 0; + bool last_page; const int biosize = fuse_iosize(vp); @@ -612,6 +615,11 @@ again: extending = true; bcount = on + n; } + if (howmany(((off_t)lbn * biosize + on + n - 1), PAGE_SIZE) >= + howmany(filesize, PAGE_SIZE)) + last_page = true; + else + last_page = false; if (direct_append) { /* * Take care to preserve the buffer's B_CACHE state so @@ -642,6 +650,8 @@ again: break; } } + if (biosize > bcount) + vfs_bio_clrbuf(bp); SDT_PROBE6(fusefs, , io, write_biobackend_start, lbn, on, n, uio, bcount, direct_append); @@ -689,7 +699,6 @@ again: * If the chopping creates a reverse-indexed or degenerate * situation with dirtyoff/end, we 0 both of them. */ - if (bp->b_dirtyend > bcount) { SDT_PROBE2(fusefs, , io, write_biobackend_append_race, (long)bp->b_blkno * biosize, @@ -738,6 +747,7 @@ again: bp->b_error = err; brelse(bp); break; + /* TODO: vfs_bio_clrbuf like ffs_write does? */ } /* * Only update dirtyoff/dirtyend if not a degenerate @@ -753,7 +763,42 @@ again: } vfs_bio_set_valid(bp, on, n); } - err = bwrite(bp); + + vfs_bio_set_flags(bp, ioflag); + + if (last_page) { + /* + * When writing the last page of a file we must write + * synchronously. If we didn't, then a subsequent + * operation could extend the file, making the last + * page of this buffer invalid because it would only be + * partially cached. + * + * As an optimization, it would be allowable to only + * write the last page synchronously. Or, it should be + * possible to synchronously flush the last + * already-written page whenever extending a file with + * ftruncate or another write. + */ + err = bwrite(bp); + } else if (ioflag & IO_SYNC) { + err = bwrite(bp); + } else if (vm_page_count_severe() || + buf_dirty_count_severe() || + (ioflag & IO_ASYNC)) { + /* TODO: enable write clustering later */ + bawrite(bp); + } else if (on == 0 && n == bcount) { + if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) + bdwrite(bp); + else + bawrite(bp); + } else if (ioflag & IO_DIRECT) { + bawrite(bp); + } else { + bp->b_flags &= ~B_CLUSTEROK; + bdwrite(bp); + } if (err) break; } while (uio->uio_resid > 0 && n > 0); @@ -819,7 +864,7 @@ fuse_io_strategy(struct vnode *vp, struct buf *bp) io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; - uiop->uio_offset = ((off_t)bp->b_blkno) * biosize; + uiop->uio_offset = ((off_t)bp->b_lblkno) * biosize; error = fuse_read_directbackend(vp, uiop, cred, fufh); if (!error && uiop->uio_resid) { @@ -854,14 +899,14 @@ fuse_io_strategy(struct vnode *vp, struct buf *bp) return (error); } - if ((off_t)bp->b_blkno * biosize + bp->b_dirtyend > filesize) + if ((off_t)bp->b_lblkno * biosize + bp->b_dirtyend > filesize) bp->b_dirtyend = filesize - - (off_t)bp->b_blkno * biosize; + (off_t)bp->b_lblkno * biosize; if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; - uiop->uio_offset = (off_t)bp->b_blkno * biosize + uiop->uio_offset = (off_t)bp->b_lblkno * biosize + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; Modified: projects/fuse2/sys/fs/fuse/fuse_vfsops.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_vfsops.c Tue Jun 11 16:16:16 2019 (r348930) +++ projects/fuse2/sys/fs/fuse/fuse_vfsops.c Tue Jun 11 16:32:33 2019 (r348931) @@ -314,9 +314,6 @@ fuse_vfsop_mount(struct mount *mp) __mntopts = 0; td = curthread; - MNT_ILOCK(mp); - mp->mnt_flag |= MNT_SYNCHRONOUS; - MNT_IUNLOCK(mp); /* Get the new options passed to mount */ opts = mp->mnt_optnew; Modified: projects/fuse2/tests/sys/fs/fusefs/Makefile ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/Makefile Tue Jun 11 16:16:16 2019 (r348930) +++ projects/fuse2/tests/sys/fs/fusefs/Makefile Tue Jun 11 16:32:33 2019 (r348931) @@ -21,6 +21,7 @@ GTESTS+= fsync GTESTS+= fsyncdir GTESTS+= getattr GTESTS+= interrupt +GTESTS+= io GTESTS+= link GTESTS+= locks GTESTS+= lookup Added: projects/fuse2/tests/sys/fs/fusefs/io.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/fuse2/tests/sys/fs/fusefs/io.cc Tue Jun 11 16:32:33 2019 (r348931) @@ -0,0 +1,276 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +/* + * For testing I/O like fsx does, but deterministically and without a real + * underlying file system + * + * TODO: after fusefs gains the options to select cache mode for each mount + * point, run each of these tests for all cache modes. + */ + +using namespace testing; + +const char FULLPATH[] = "mountpoint/some_file.txt"; +const char RELPATH[] = "some_file.txt"; +const uint64_t ino = 42; + +class Io: public FuseTest { +public: +int m_backing_fd, m_control_fd, m_test_fd; + +Io(): m_backing_fd(-1), m_control_fd(-1) {}; + +void SetUp() +{ + m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC); + if (m_backing_fd < 0) + FAIL() << strerror(errno); + m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC); + if (m_control_fd < 0) + FAIL() << strerror(errno); + srandom(22'9'1982); // Seed with my birthday + FuseTest::SetUp(); + if (IsSkipped()) + return; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_open(ino, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_WRITE && + in.header.nodeid == ino); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { + const char *buf = (const char*)in.body.bytes + + sizeof(struct fuse_write_in); + ssize_t isize = in.body.write.size; + off_t iofs = in.body.write.offset; + + ASSERT_EQ(isize, pwrite(m_backing_fd, buf, isize, iofs)) + << strerror(errno); + SET_OUT_HEADER_LEN(out, write); + out.body.write.size = isize; + }))); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_READ && + in.header.nodeid == ino); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { + ssize_t isize = in.body.write.size; + off_t iofs = in.body.write.offset; + void *buf = out.body.bytes; + + ASSERT_LE(0, pread(m_backing_fd, buf, isize, iofs)) + << strerror(errno); + out.header.len = sizeof(struct fuse_out_header) + isize; + }))); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + uint32_t valid = FATTR_SIZE | FATTR_FH; + return (in.header.opcode == FUSE_SETATTR && + in.header.nodeid == ino && + in.body.setattr.valid == valid); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { + ASSERT_EQ(0, ftruncate(m_backing_fd, in.body.setattr.size)) + << strerror(errno); + SET_OUT_HEADER_LEN(out, attr); + out.body.attr.attr.ino = ino; + out.body.attr.attr.mode = S_IFREG | 0755; + out.body.attr.attr.size = in.body.setattr.size; + out.body.attr.attr_valid = UINT64_MAX; + }))); + /* Any test that close()s will send FUSE_FLUSH and FUSE_RELEASE */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_FLUSH && + in.header.nodeid == ino); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnErrno(0))); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_RELEASE && + in.header.nodeid == ino); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnErrno(0))); + + m_test_fd = open(FULLPATH, O_RDWR ); + EXPECT_LE(0, m_test_fd) << strerror(errno); +} + +void TearDown() +{ + if (m_backing_fd >= 0) + close(m_backing_fd); + if (m_control_fd >= 0) + close(m_control_fd); + FuseTest::TearDown(); + /* Deliberately leak test_fd */ +} + +void do_ftruncate(off_t offs) +{ + ASSERT_EQ(0, ftruncate(m_test_fd, offs)) << strerror(errno); + ASSERT_EQ(0, ftruncate(m_control_fd, offs)) << strerror(errno); +} + +void do_read(ssize_t size, off_t offs) +{ + void *test_buf, *control_buf; + + test_buf = malloc(size); + ASSERT_NE(NULL, test_buf) << strerror(errno); + control_buf = malloc(size); + ASSERT_NE(NULL, control_buf) << strerror(errno); + + ASSERT_EQ(size, pread(m_test_fd, test_buf, size, offs)) + << strerror(errno); + ASSERT_EQ(size, pread(m_control_fd, control_buf, size, offs)) + << strerror(errno); + + ASSERT_EQ(0, memcmp(test_buf, control_buf, size)); + + free(control_buf); + free(test_buf); +} + +void do_write(ssize_t size, off_t offs) +{ + char *buf; + long i; + + buf = (char*)malloc(size); + ASSERT_NE(NULL, buf) << strerror(errno); + for (i=0; i < size; i++) + buf[i] = random(); + + ASSERT_EQ(size, pwrite(m_test_fd, buf, size, offs )) + << strerror(errno); + ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs)) + << strerror(errno); +} + +}; + +/* + * Extend a file with dirty data in the last page of the last block. + * + * fsx -WR -P /tmp -S8 -N3 fsx.bin + */ +TEST_F(Io, extend_from_dirty_page) +{ + off_t wofs = 0x21a0; + ssize_t wsize = 0xf0a8; + off_t rofs = 0xb284; + ssize_t rsize = 0x9b22; + off_t truncsize = 0x28702; + + do_write(wsize, wofs); + do_ftruncate(truncsize); + do_read(rsize, rofs); +} + +/* + * When writing the last page of a file, it must be written synchronously. + * Otherwise the cached page can become invalid by a subsequent extend + * operation. + * + * fsx -WR -P /tmp -S642 -N3 fsx.bin + */ +TEST_F(Io, last_page) +{ + off_t wofs0 = 0x1134f; + ssize_t wsize0 = 0xcc77; + off_t wofs1 = 0x2096a; + ssize_t wsize1 = 0xdfa7; + off_t rofs = 0x1a3aa; + ssize_t rsize = 0xb5b7; + + do_write(wsize0, wofs0); + do_write(wsize1, wofs1); + do_read(rsize, rofs); +} + +/* + * Read a hole from a block that contains some cached data. + * + * fsx -WR -P /tmp -S55 fsx.bin + */ +TEST_F(Io, read_hole_from_cached_block) +{ + off_t wofs = 0x160c5; + ssize_t wsize = 0xa996; + off_t rofs = 0x472e; + ssize_t rsize = 0xd8d5; + + do_write(wsize, wofs); + do_read(rsize, rofs); +} + +/* + * Reliable panic; I don't yet know why. + * Disabled because it panics. + * + * fsx -WR -P /tmp -S839 -d -N6 fsx.bin + */ +TEST_F(Io, DISABLED_fault_on_nofault_entry) +{ + off_t wofs0 = 0x3bad7; + ssize_t wsize0 = 0x4529; + off_t wofs1 = 0xc30d; + ssize_t wsize1 = 0x5f77; + off_t truncsize0 = 0x10916; + off_t rofs = 0xdf17; + ssize_t rsize = 0x29ff; + off_t truncsize1 = 0x152b4; + + do_write(wsize0, wofs0); + do_write(wsize1, wofs1); + do_ftruncate(truncsize0); + do_read(rsize, rofs); + do_ftruncate(truncsize1); + close(m_test_fd); +} Modified: projects/fuse2/tests/sys/fs/fusefs/write.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/write.cc Tue Jun 11 16:16:16 2019 (r348930) +++ projects/fuse2/tests/sys/fs/fusefs/write.cc Tue Jun 11 16:32:33 2019 (r348931) @@ -249,6 +249,44 @@ TEST_F(Write, append) /* Deliberately leak fd. close(2) will be tested in release.cc */ } +/* If a file is cached, then appending to the end should not cause a read */ +TEST_F(Write, append_to_cached) +{ + const ssize_t BUFSIZE = 9; + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + char *oldcontents, *oldbuf; + const char CONTENTS[BUFSIZE] = "abcdefgh"; + uint64_t ino = 42; + /* + * Set offset in between maxbcachebuf boundary to test buffer handling + */ + uint64_t oldsize = m_maxbcachebuf / 2; + int fd; + + oldcontents = (char*)calloc(1, oldsize); + ASSERT_NE(NULL, oldcontents) << strerror(errno); + oldbuf = (char*)malloc(oldsize); + ASSERT_NE(NULL, oldbuf) << strerror(errno); + + expect_lookup(RELPATH, ino, oldsize); + expect_open(ino, 0, 1); + expect_read(ino, 0, oldsize, oldsize, oldcontents); + expect_write(ino, oldsize, BUFSIZE, BUFSIZE, CONTENTS); + + /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ + fd = open(FULLPATH, O_RDWR | O_APPEND); + EXPECT_LE(0, fd) << strerror(errno); + + /* Read the old data into the cache */ + ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize)) + << strerror(errno); + + /* Write the new data. There should be no more read operations */ + ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + TEST_F(Write, append_direct_io) { const ssize_t BUFSIZE = 9; @@ -789,7 +827,7 @@ TEST_F(WriteThrough, DISABLED_writethrough) EXPECT_LE(0, fd) << strerror(errno); ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* + /* * A subsequent read should be serviced by cache, without querying the * filesystem daemon */ From owner-svn-src-projects@freebsd.org Tue Jun 11 23:46:33 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 368A215CA36F for ; Tue, 11 Jun 2019 23:46:33 +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 D112185D3C; Tue, 11 Jun 2019 23:46:32 +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 8BCC78E7A; Tue, 11 Jun 2019 23:46:32 +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 x5BNkWGA013386; Tue, 11 Jun 2019 23:46:32 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5BNkWVI013382; Tue, 11 Jun 2019 23:46:32 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906112346.x5BNkWVI013382@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Tue, 11 Jun 2019 23:46:32 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r348978 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Commit-Revision: 348978 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: D112185D3C 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_LONG(-1.00)[-1.000,0]; NEURAL_HAM_SHORT(-0.97)[-0.967,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 11 Jun 2019 23:46:33 -0000 Author: asomers Date: Tue Jun 11 23:46:31 2019 New Revision: 348978 URL: https://svnweb.freebsd.org/changeset/base/348978 Log: fusefs: fix a page fault with writeback cacheing When truncating a file downward through a dirty buffer, it's neccessary to update the buffer's b->dirtyend. Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sys/fs/fuse/fuse_node.c projects/fuse2/tests/sys/fs/fusefs/io.cc Modified: projects/fuse2/sys/fs/fuse/fuse_node.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_node.c Tue Jun 11 23:43:29 2019 (r348977) +++ projects/fuse2/sys/fs/fuse/fuse_node.c Tue Jun 11 23:46:31 2019 (r348978) @@ -415,6 +415,7 @@ fuse_vnode_setsize(struct vnode *vp, off_t newsize) goto out; /* Nothing to do */ MPASS(bp->b_flags & B_VMIO); vfs_bio_clrbuf(bp); + bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize); } out: if (bp) Modified: projects/fuse2/tests/sys/fs/fusefs/io.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/io.cc Tue Jun 11 23:43:29 2019 (r348977) +++ projects/fuse2/tests/sys/fs/fusefs/io.cc Tue Jun 11 23:46:31 2019 (r348978) @@ -59,10 +59,10 @@ Io(): m_backing_fd(-1), m_control_fd(-1) {}; void SetUp() { - m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC); + m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC, 0644); if (m_backing_fd < 0) FAIL() << strerror(errno); - m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC); + m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC, 0644); if (m_control_fd < 0) FAIL() << strerror(errno); srandom(22'9'1982); // Seed with my birthday @@ -251,12 +251,12 @@ TEST_F(Io, read_hole_from_cached_block) } /* - * Reliable panic; I don't yet know why. - * Disabled because it panics. + * Truncating a file into a dirty buffer should not causing anything untoward + * to happen when that buffer is eventually flushed. * * fsx -WR -P /tmp -S839 -d -N6 fsx.bin */ -TEST_F(Io, DISABLED_fault_on_nofault_entry) +TEST_F(Io, truncate_into_dirty_buffer) { off_t wofs0 = 0x3bad7; ssize_t wsize0 = 0x4529; @@ -272,5 +272,39 @@ TEST_F(Io, DISABLED_fault_on_nofault_entry) do_ftruncate(truncsize0); do_read(rsize, rofs); do_ftruncate(truncsize1); + close(m_test_fd); +} + +/* + * Truncating a file into a dirty buffer should not causing anything untoward + * to happen when that buffer is eventually flushed, even when the buffer's + * dirty_off is > 0. + * + * Based on this command with a few steps removed: + * fsx -WR -P /tmp -S677 -d -N8 fsx.bin + */ +TEST_F(Io, truncate_into_dirty_buffer2) +{ + off_t truncsize0 = 0x344f3; + off_t wofs = 0x2790c; + ssize_t wsize = 0xd86a; + off_t truncsize1 = 0x2de38; + off_t rofs2 = 0x1fd7a; + ssize_t rsize2 = 0xc594; + off_t truncsize2 = 0x31e71; + + /* Sets the file size to something larger than the next write */ + do_ftruncate(truncsize0); + /* + * Creates a dirty buffer. The part in lbn 2 doesn't flush + * synchronously. + */ + do_write(wsize, wofs); + /* Truncates part of the dirty buffer created in step 2 */ + do_ftruncate(truncsize1); + /* XXX ?I don't know why this is necessary? */ + do_read(rsize2, rofs2); + /* Truncates the dirty buffer */ + do_ftruncate(truncsize2); close(m_test_fd); } From owner-svn-src-projects@freebsd.org Thu Jun 13 19:07:05 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id E49FE15BA3CB for ; Thu, 13 Jun 2019 19:07:04 +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 5CAF56AD73; Thu, 13 Jun 2019 19:07:04 +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 371DE4B14; Thu, 13 Jun 2019 19:07:04 +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 x5DJ738i081773; Thu, 13 Jun 2019 19:07:03 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5DJ73WV081771; Thu, 13 Jun 2019 19:07:03 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906131907.x5DJ73WV081771@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Thu, 13 Jun 2019 19:07:03 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r349021 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Commit-Revision: 349021 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 5CAF56AD73 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_SHORT(-0.98)[-0.976,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; NEURAL_HAM_MEDIUM(-1.00)[-0.998,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 13 Jun 2019 19:07:05 -0000 Author: asomers Date: Thu Jun 13 19:07:03 2019 New Revision: 349021 URL: https://svnweb.freebsd.org/changeset/base/349021 Log: fusefs: fix a bug with WriteBack cacheing An errant vfs_bio_clrbuf snuck in in r348931. Surprisingly, it doesn't have any effect most of the time. But under some circumstances it cause the buffer to behave in a write-only fashion. Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sys/fs/fuse/fuse_io.c projects/fuse2/tests/sys/fs/fusefs/io.cc Modified: projects/fuse2/sys/fs/fuse/fuse_io.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_io.c Thu Jun 13 19:04:49 2019 (r349020) +++ projects/fuse2/sys/fs/fuse/fuse_io.c Thu Jun 13 19:07:03 2019 (r349021) @@ -267,7 +267,7 @@ out: } SDT_PROBE_DEFINE4(fusefs, , io, read_bio_backend_start, "int", "int", "int", "int"); -SDT_PROBE_DEFINE2(fusefs, , io, read_bio_backend_feed, "int", "int"); +SDT_PROBE_DEFINE2(fusefs, , io, read_bio_backend_feed, "int", "struct buf*"); SDT_PROBE_DEFINE4(fusefs, , io, read_bio_backend_end, "int", "ssize_t", "int", "struct buf*"); static int @@ -330,8 +330,7 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio if (on < bcount) n = MIN((unsigned)(bcount - on), uio->uio_resid); if (n > 0) { - SDT_PROBE2(fusefs, , io, read_bio_backend_feed, - n, n + (int)bp->b_resid); + SDT_PROBE2(fusefs, , io, read_bio_backend_feed, n, bp); err = uiomove(bp->b_data + on, n, uio); } vfs_bio_brelse(bp, ioflag); @@ -344,8 +343,8 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio SDT_PROBE_DEFINE1(fusefs, , io, read_directbackend_start, "struct fuse_read_in*"); -SDT_PROBE_DEFINE2(fusefs, , io, read_directbackend_complete, - "struct fuse_dispatcher*", "struct uio*"); +SDT_PROBE_DEFINE3(fusefs, , io, read_directbackend_complete, + "struct fuse_dispatcher*", "struct fuse_read_in*", "struct uio*"); static int fuse_read_directbackend(struct vnode *vp, struct uio *uio, @@ -390,8 +389,8 @@ fuse_read_directbackend(struct vnode *vp, struct uio * if ((err = fdisp_wait_answ(&fdi))) goto out; - SDT_PROBE2(fusefs, , io, read_directbackend_complete, - fdi.iosize, uio); + SDT_PROBE3(fusefs, , io, read_directbackend_complete, + &fdi, fri, uio); if ((err = uiomove(fdi.answ, MIN(fri->size, fdi.iosize), uio))) break; @@ -555,6 +554,7 @@ retry: SDT_PROBE_DEFINE6(fusefs, , io, write_biobackend_start, "int64_t", "int", "int", "struct uio*", "int", "bool"); SDT_PROBE_DEFINE2(fusefs, , io, write_biobackend_append_race, "long", "int"); +SDT_PROBE_DEFINE2(fusefs, , io, write_biobackend_issue, "int", "struct buf*"); static int fuse_write_biobackend(struct vnode *vp, struct uio *uio, @@ -602,14 +602,14 @@ fuse_write_biobackend(struct vnode *vp, struct uio *ui again: /* Get or create a buffer for the write */ direct_append = uio->uio_offset == filesize && n; - if ((off_t)lbn * biosize + on + n < filesize) { + if (uio->uio_offset + n < filesize) { extending = false; if ((off_t)(lbn + 1) * biosize < filesize) { /* Not the file's last block */ bcount = biosize; } else { /* The file's last block */ - bcount = filesize - (off_t)lbn *biosize; + bcount = filesize - (off_t)lbn * biosize; } } else { extending = true; @@ -650,8 +650,6 @@ again: break; } } - if (biosize > bcount) - vfs_bio_clrbuf(bp); SDT_PROBE6(fusefs, , io, write_biobackend_start, lbn, on, n, uio, bcount, direct_append); @@ -733,6 +731,7 @@ again: * reasons: the only way to know if a write is valid * if its actually written out.) */ + SDT_PROBE2(fusefs, , io, write_biobackend_issue, 0, bp); bwrite(bp); if (bp->b_error == EINTR) { err = EINTR; @@ -780,23 +779,33 @@ again: * already-written page whenever extending a file with * ftruncate or another write. */ + SDT_PROBE2(fusefs, , io, write_biobackend_issue, 1, bp); err = bwrite(bp); } else if (ioflag & IO_SYNC) { + SDT_PROBE2(fusefs, , io, write_biobackend_issue, 2, bp); err = bwrite(bp); } else if (vm_page_count_severe() || buf_dirty_count_severe() || (ioflag & IO_ASYNC)) { /* TODO: enable write clustering later */ + SDT_PROBE2(fusefs, , io, write_biobackend_issue, 3, bp); bawrite(bp); } else if (on == 0 && n == bcount) { - if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) + if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) { + SDT_PROBE2(fusefs, , io, write_biobackend_issue, + 4, bp); bdwrite(bp); - else + } else { + SDT_PROBE2(fusefs, , io, write_biobackend_issue, + 5, bp); bawrite(bp); + } } else if (ioflag & IO_DIRECT) { + SDT_PROBE2(fusefs, , io, write_biobackend_issue, 6, bp); bawrite(bp); } else { bp->b_flags &= ~B_CLUSTEROK; + SDT_PROBE2(fusefs, , io, write_biobackend_issue, 7, bp); bdwrite(bp); } if (err) Modified: projects/fuse2/tests/sys/fs/fusefs/io.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/io.cc Thu Jun 13 19:04:49 2019 (r349020) +++ projects/fuse2/tests/sys/fs/fusefs/io.cc Thu Jun 13 19:07:03 2019 (r349021) @@ -51,6 +51,27 @@ const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; const uint64_t ino = 42; +static void compare(const void *tbuf, const void *controlbuf, off_t baseofs, + ssize_t size) +{ + int i; + + for (i = 0; i < size; i++) { + if (((const char*)tbuf)[i] != ((const char*)controlbuf)[i]) { + off_t ofs = baseofs + i; + FAIL() << "miscompare at offset " + << std::hex + << std::showbase + << ofs + << ". expected = " + << std::setw(2) + << (unsigned)((const uint8_t*)controlbuf)[i] + << " got = " + << (unsigned)((const uint8_t*)tbuf)[i]; + } + } +} + class Io: public FuseTest { public: int m_backing_fd, m_control_fd, m_test_fd; @@ -171,7 +192,7 @@ void do_read(ssize_t size, off_t offs) ASSERT_EQ(size, pread(m_control_fd, control_buf, size, offs)) << strerror(errno); - ASSERT_EQ(0, memcmp(test_buf, control_buf, size)); + compare(test_buf, control_buf, offs, size); free(control_buf); free(test_buf); @@ -306,5 +327,37 @@ TEST_F(Io, truncate_into_dirty_buffer2) do_read(rsize2, rofs2); /* Truncates the dirty buffer */ do_ftruncate(truncsize2); + close(m_test_fd); +} + +/* + * Regression test for a bug introduced in r348931 + * + * Sequence of operations: + * 1) The first write reads lbn so it can modify it + * 2) The first write flushes lbn 3 immediately because it's the end of file + * 3) The first write then flushes lbn 4 because it's the end of the file + * 4) The second write modifies the cached versions of lbn 3 and 4 + * 5) The third write's getblkx invalidates lbn 4's B_CACHE because it's + * extending the buffer. Then it flushes lbn 4 because B_DELWRI was set but + * B_CACHE was clear. + * 6) fuse_write_biobackend erroneously called vfs_bio_clrbuf, putting the + * buffer into a weird write-only state. All read operations would return + * 0. Writes were apparently still processed, because the buffer's contents + * were correct when examined in a core dump. + * 7) The third write reads lbn 4 because cache is clear + * 9) uiomove dutifully copies new data into the buffer + * 10) The buffer's dirty is flushed to lbn 4 + * 11) The read returns all zeros because of step 6. + * + * Based on: + * fsx -WR -l 524388 -o 131072 -P /tmp -S6456 -q fsx.bin + */ +TEST_F(Io, resize_a_valid_buffer_while_extending) +{ + do_write(0x14530, 0x36ee6); /* [0x36ee6, 0x4b415] */ + do_write(0x1507c, 0x33256); /* [0x33256, 0x482d1] */ + do_write(0x175c, 0x4c03d); /* [0x4c03d, 0x4d798] */ + do_read(0xe277, 0x3599c); /* [0x3599c, 0x43c12] */ close(m_test_fd); } From owner-svn-src-projects@freebsd.org Fri Jun 14 18:14:54 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 66AC915AE548 for ; Fri, 14 Jun 2019 18:14:54 +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 08F6B74359; Fri, 14 Jun 2019 18:14:54 +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 D5C501B72E; Fri, 14 Jun 2019 18:14:53 +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 x5EIErVq014427; Fri, 14 Jun 2019 18:14:53 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5EIEplW014418; Fri, 14 Jun 2019 18:14:51 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906141814.x5EIEplW014418@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Fri, 14 Jun 2019 18:14:51 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r349036 - in projects/fuse2: sbin/mount_fusefs sys/fs/fuse tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: in projects/fuse2: sbin/mount_fusefs sys/fs/fuse tests/sys/fs/fusefs X-SVN-Commit-Revision: 349036 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 08F6B74359 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.975,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; NEURAL_HAM_LONG(-1.00)[-1.000,0] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 14 Jun 2019 18:14:54 -0000 Author: asomers Date: Fri Jun 14 18:14:51 2019 New Revision: 349036 URL: https://svnweb.freebsd.org/changeset/base/349036 Log: fusefs: enable write clustering Enable write clustering in fusefs whenever cache mode is set to writeback and the "async" mount option is used. With default values for MAXPHYS, DFLTPHYS, and the fuse max_write mount parameter, that means sequential writes will now be written 128KB at a time instead of 64KB. Also, add a regression test for PR 238565, a panic during unmount that probably affects UFS, ext2, and msdosfs as well as fusefs. PR: 238565 Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sbin/mount_fusefs/mount_fusefs.8 projects/fuse2/sbin/mount_fusefs/mount_fusefs.c projects/fuse2/sys/fs/fuse/fuse_io.c projects/fuse2/sys/fs/fuse/fuse_vfsops.c projects/fuse2/tests/sys/fs/fusefs/mockfs.cc projects/fuse2/tests/sys/fs/fusefs/mockfs.hh projects/fuse2/tests/sys/fs/fusefs/utils.cc projects/fuse2/tests/sys/fs/fusefs/utils.hh projects/fuse2/tests/sys/fs/fusefs/write.cc Modified: projects/fuse2/sbin/mount_fusefs/mount_fusefs.8 ============================================================================== --- projects/fuse2/sbin/mount_fusefs/mount_fusefs.8 Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/sbin/mount_fusefs/mount_fusefs.8 Fri Jun 14 18:14:51 2019 (r349036) @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 19, 2019 +.Dd June 14, 2019 .Dt MOUNT_FUSEFS 8 .Os .Sh NAME @@ -136,21 +136,24 @@ The following options are available (and also their ne by prefixing them with .Dq no ) : .Bl -tag -width indent -.It Cm default_permissions -Enable traditional (file mode based) permission checking in kernel .It Cm allow_other Do not apply .Sx STRICT ACCESS POLICY . Only root can use this option +.It Cm async +I/O to the file system may be done asynchronously. +Writes may delayed and/or reordered. +.It Cm default_permissions +Enable traditional (file mode based) permission checking in kernel .It Cm max_read Ns = Ns Ar n Limit size of read requests to .Ar n +.It Cm neglect_shares +Do not refuse unmounting if there are secondary mounts .It Cm private Refuse shared mounting of the daemon. This is the default behaviour, to allow sharing, expicitly use .Fl o Cm noprivate -.It Cm neglect_shares -Do not refuse unmounting if there are secondary mounts .It Cm push_symlinks_in Prefix absolute symlinks with the mountpoint .It Cm subtype Ns = Ns Ar fsname Modified: projects/fuse2/sbin/mount_fusefs/mount_fusefs.c ============================================================================== --- projects/fuse2/sbin/mount_fusefs/mount_fusefs.c Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/sbin/mount_fusefs/mount_fusefs.c Fri Jun 14 18:14:51 2019 (r349036) @@ -88,6 +88,8 @@ static struct mntopt mopts[] = { { "large_read", 0, 0x00, 1 }, /* "nonempty", just the first two chars are stripped off during parsing */ { "nempty", 0, 0x00, 1 }, + { "async", 0, MNT_ASYNC, 0}, + { "noasync", 1, MNT_ASYNC, 0}, MOPT_STDOPTS, MOPT_END }; Modified: projects/fuse2/sys/fs/fuse/fuse_io.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_io.c Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/sys/fs/fuse/fuse_io.c Fri Jun 14 18:14:51 2019 (r349036) @@ -565,11 +565,13 @@ fuse_write_biobackend(struct vnode *vp, struct uio *ui daddr_t lbn; off_t filesize; int bcount; - int n, on, err = 0; + int n, on, seqcount, err = 0; bool last_page; const int biosize = fuse_iosize(vp); + seqcount = ioflag >> IO_SEQSHIFT; + KASSERT(uio->uio_rw == UIO_WRITE, ("fuse_write_biobackend mode")); if (vp->v_type != VREG) return (EIO); @@ -644,6 +646,7 @@ again: * with other readers */ err = fuse_vnode_setsize(vp, uio->uio_offset + n); + filesize = uio->uio_offset + n; fvdat->flag |= FN_SIZECHANGE; if (err) { brelse(bp); @@ -787,20 +790,22 @@ again: } else if (vm_page_count_severe() || buf_dirty_count_severe() || (ioflag & IO_ASYNC)) { - /* TODO: enable write clustering later */ + bp->b_flags |= B_CLUSTEROK; SDT_PROBE2(fusefs, , io, write_biobackend_issue, 3, bp); bawrite(bp); } else if (on == 0 && n == bcount) { if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) { + bp->b_flags |= B_CLUSTEROK; SDT_PROBE2(fusefs, , io, write_biobackend_issue, 4, bp); - bdwrite(bp); + cluster_write(vp, bp, filesize, seqcount, 0); } else { SDT_PROBE2(fusefs, , io, write_biobackend_issue, 5, bp); bawrite(bp); } } else if (ioflag & IO_DIRECT) { + bp->b_flags |= B_CLUSTEROK; SDT_PROBE2(fusefs, , io, write_biobackend_issue, 6, bp); bawrite(bp); } else { Modified: projects/fuse2/sys/fs/fuse/fuse_vfsops.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_vfsops.c Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/sys/fs/fuse/fuse_vfsops.c Fri Jun 14 18:14:51 2019 (r349036) @@ -433,6 +433,7 @@ fuse_vfsop_mount(struct mount *mp) } copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &len); bzero(mp->mnt_stat.f_mntfromname + len, MNAMELEN - len); + mp->mnt_iosize_max = MAXPHYS; /* Now handshaking with daemon */ fuse_internal_send_init(data, td); Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mockfs.cc Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/tests/sys/fs/fusefs/mockfs.cc Fri Jun 14 18:14:51 2019 (r349036) @@ -336,7 +336,7 @@ void MockFS::debug_response(const mockfs_buf_out &out) MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, - uint32_t kernel_minor_version) + uint32_t kernel_minor_version, uint32_t max_write, bool async) { struct sigaction sa; struct iovec *iov = NULL; @@ -347,6 +347,7 @@ MockFS::MockFS(int max_readahead, bool allow_other, bo m_daemon_id = NULL; m_kernel_minor_version = kernel_minor_version; m_maxreadahead = max_readahead; + m_maxwrite = max_write; m_nready = -1; m_pm = pm; m_quit = false; @@ -404,6 +405,10 @@ MockFS::MockFS(int max_readahead, bool allow_other, bo build_iovec(&iov, &iovlen, "ro", __DECONST(void*, &trueval), sizeof(bool)); } + if (async) { + build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), + sizeof(bool)); + } if (nmount(iov, iovlen, 0)) throw(std::system_error(errno, std::system_category(), "Couldn't mount filesystem")); @@ -449,15 +454,7 @@ void MockFS::init(uint32_t flags) { out->body.init.minor = m_kernel_minor_version;; out->body.init.flags = in->body.init.flags & flags; - /* - * The default max_write is set to this formula in libfuse, though - * individual filesystems can lower it. The "- 4096" was added in - * commit 154ffe2, with the commit message "fix". - */ - uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096; - /* For testing purposes, it should be distinct from MAXPHYS */ - m_max_write = MIN(default_max_write, MAXPHYS / 2); - out->body.init.max_write = m_max_write; + out->body.init.max_write = m_maxwrite; out->body.init.max_readahead = m_maxreadahead; SET_OUT_HEADER_LEN(*out, init); Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.hh ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mockfs.hh Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/tests/sys/fs/fusefs/mockfs.hh Fri Jun 14 18:14:51 2019 (r349036) @@ -252,7 +252,7 @@ class MockFS { int m_kq; - /* The max_readahead filesystem option */ + /* The max_readahead file system option */ uint32_t m_maxreadahead; /* pid of the test process */ @@ -288,7 +288,7 @@ class MockFS { pid_t m_child_pid; /* Maximum size of a FUSE_WRITE write */ - uint32_t m_max_write; + uint32_t m_maxwrite; /* * Number of events that were available from /dev/fuse after the last @@ -303,7 +303,7 @@ class MockFS { MockFS(int max_readahead, bool allow_other, bool default_permissions, bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, - uint32_t kernel_minor_version); + uint32_t kernel_minor_version, uint32_t max_write, bool async); virtual ~MockFS(); Modified: projects/fuse2/tests/sys/fs/fusefs/utils.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.cc Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/tests/sys/fs/fusefs/utils.cc Fri Jun 14 18:14:51 2019 (r349036) @@ -50,6 +50,20 @@ extern "C" { using namespace testing; +/* + * The default max_write is set to this formula in libfuse, though + * individual filesystems can lower it. The "- 4096" was added in + * commit 154ffe2, with the commit message "fix". + */ +const uint32_t libfuse_max_write = 32 * getpagesize() + 0x1000 - 4096; + +/* + * Set the default max_write to a distinct value from MAXPHYS to catch bugs + * that confuse the two. + */ +const uint32_t default_max_write = MIN(libfuse_max_write, MAXPHYS / 2); + + /* Check that fusefs(4) is accessible and the current user can mount(2) */ void check_environment() { @@ -98,7 +112,8 @@ void FuseTest::SetUp() { try { m_mock = new MockFS(m_maxreadahead, m_allow_other, m_default_permissions, m_push_symlinks_in, m_ro, - m_pm, m_init_flags, m_kernel_minor_version); + m_pm, m_init_flags, m_kernel_minor_version, + m_maxwrite, m_async); /* * FUSE_ACCESS is called almost universally. Expecting it in * each test case would be super-annoying. Instead, set a Modified: projects/fuse2/tests/sys/fs/fusefs/utils.hh ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.hh Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/tests/sys/fs/fusefs/utils.hh Fri Jun 14 18:14:51 2019 (r349036) @@ -40,9 +40,12 @@ inline void nap() usleep(NAP_NS / 1000); } +extern const uint32_t libfuse_max_write; +extern const uint32_t default_max_write; class FuseTest : public ::testing::Test { protected: uint32_t m_maxreadahead; + uint32_t m_maxwrite; uint32_t m_init_flags; bool m_allow_other; bool m_default_permissions; @@ -50,6 +53,7 @@ class FuseTest : public ::testing::Test { enum poll_method m_pm; bool m_push_symlinks_in; bool m_ro; + bool m_async; MockFS *m_mock = NULL; const static uint64_t FH = 0xdeadbeef1a7ebabe; @@ -62,13 +66,15 @@ class FuseTest : public ::testing::Test { * be lowered */ m_maxreadahead(UINT_MAX), + m_maxwrite(default_max_write), m_init_flags(0), m_allow_other(false), m_default_permissions(false), m_kernel_minor_version(FUSE_KERNEL_MINOR_VERSION), m_pm(BLOCKING), m_push_symlinks_in(false), - m_ro(false) + m_ro(false), + m_async(false) {} virtual void SetUp(); Modified: projects/fuse2/tests/sys/fs/fusefs/write.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/write.cc Fri Jun 14 17:09:39 2019 (r349035) +++ projects/fuse2/tests/sys/fs/fusefs/write.cc Fri Jun 14 18:14:51 2019 (r349036) @@ -29,7 +29,7 @@ */ extern "C" { -#include +#include #include #include #include @@ -179,6 +179,19 @@ void expect_write(uint64_t ino, uint64_t offset, uint6 } }; +/* Tests for clustered writes with WriteBack cacheing */ +class WriteCluster: public WriteBack { +public: +virtual void SetUp() { + if (MAXPHYS < 2 * DFLTPHYS) + GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS" + << "for this test"; + m_async = true; + m_maxwrite = MAXPHYS; + WriteBack::SetUp(); +} +}; + void sigxfsz_handler(int __unused sig) { Write::s_sigxfsz = 1; } @@ -617,7 +630,7 @@ TEST_F(Write, write_large) int fd; ssize_t halfbufsize, bufsize; - halfbufsize = m_mock->m_max_write; + halfbufsize = m_mock->m_maxwrite; bufsize = halfbufsize * 2; contents = (int*)malloc(bufsize); ASSERT_NE(NULL, contents); @@ -708,6 +721,89 @@ TEST_F(WriteBack, close) ASSERT_LE(0, fd) << strerror(errno); ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + close(fd); +} + +/* In writeback mode, adjacent writes will be clustered together */ +TEST_F(WriteCluster, clustering) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int i, fd; + void *wbuf, *wbuf2x; + ssize_t bufsize = 65536; + off_t filesize = 327680; + + wbuf = malloc(bufsize); + ASSERT_NE(NULL, wbuf) << strerror(errno); + memset(wbuf, 'X', bufsize); + wbuf2x = malloc(2 * bufsize); + ASSERT_NE(NULL, wbuf2x) << strerror(errno); + memset(wbuf2x, 'X', 2 * bufsize); + + expect_lookup(RELPATH, ino, filesize); + expect_open(ino, 0, 1); + /* + * Writes of bufsize-bytes each should be clustered into greater sizes. + * The amount of clustering is adaptive, so the first write actually + * issued will be 2x bufsize and subsequent writes may be larger + */ + expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x); + expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x); + expect_flush(ino, 1, ReturnErrno(0)); + expect_release(ino, ReturnErrno(0)); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + + for (i = 0; i < 4; i++) { + ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) + << strerror(errno); + } + close(fd); +} + +/* + * When clustering writes, an I/O error to any of the cluster's children should + * not panic the system on unmount + */ +/* + * Disabled because it panics. + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565 + */ +TEST_F(WriteCluster, DISABLED_cluster_write_err) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int i, fd; + void *wbuf; + ssize_t bufsize = 65536; + off_t filesize = 262144; + + wbuf = malloc(bufsize); + ASSERT_NE(NULL, wbuf) << strerror(errno); + memset(wbuf, 'X', bufsize); + + expect_lookup(RELPATH, ino, filesize); + expect_open(ino, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_WRITE); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnErrno(EIO))); + expect_flush(ino, 1, ReturnErrno(0)); + expect_release(ino, ReturnErrno(0)); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + + for (i = 0; i < 3; i++) { + ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) + << strerror(errno); + } close(fd); } From owner-svn-src-projects@freebsd.org Fri Jun 14 19:47:50 2019 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 15AFF15AFD35 for ; Fri, 14 Jun 2019 19:47:50 +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 AF14D76777; Fri, 14 Jun 2019 19:47:49 +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 7B15C1C648; Fri, 14 Jun 2019 19:47:49 +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 x5EJlnAW060275; Fri, 14 Jun 2019 19:47:49 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x5EJlnqu060274; Fri, 14 Jun 2019 19:47:49 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201906141947.x5EJlnqu060274@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Fri, 14 Jun 2019 19:47:49 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r349038 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs X-SVN-Commit-Revision: 349038 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: AF14D76777 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.96 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; NEURAL_HAM_SHORT(-0.96)[-0.960,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 14 Jun 2019 19:47:50 -0000 Author: asomers Date: Fri Jun 14 19:47:48 2019 New Revision: 349038 URL: https://svnweb.freebsd.org/changeset/base/349038 Log: fusefs: fix the "write-through" of write-through cacheing Our fusefs(5) module supports three cache modes: uncached, write-through, and write-back. However, the write-through mode (which is the default) has never actually worked as its name suggests. Rather, it's always been more like "write-around". It wrote directly, bypassing the cache. The cache would only be populated by a subsequent read of the same data. This commit fixes that problem. Now the write-through mode works as one would expect: write(2) immediately adds data to the cache and then blocks while the daemon processes the write operation. A side effect of this change is that non-cache-block-aligned writes will now incur a read-modify-write cycle of the cache block. The old behavior (bypassing write cache entirely) can still be achieved by opening a file with O_DIRECT. PR: 237588 Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sys/fs/fuse/fuse_io.c 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 Jun 14 18:33:41 2019 (r349037) +++ projects/fuse2/sys/fs/fuse/fuse_io.c Fri Jun 14 19:47:48 2019 (r349038) @@ -219,13 +219,7 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, in } break; case UIO_WRITE: - /* - * Kludge: simulate write-through caching via write-around - * caching. Same effect, as far as never caching dirty data, - * but slightly pessimal in that newly written data is not - * cached. - */ - if (directio || fuse_data_cache_mode == FUSE_CACHE_WT) { + if (directio) { const int iosize = fuse_iosize(vp); off_t start, end, filesize; @@ -250,6 +244,8 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, in } else { SDT_PROBE2(fusefs, , io, trace, 1, "buffered write of vnode"); + if (fuse_data_cache_mode == FUSE_CACHE_WT) + ioflag |= IO_SYNC; err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag, pid); } Modified: projects/fuse2/tests/sys/fs/fusefs/write.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/write.cc Fri Jun 14 18:33:41 2019 (r349037) +++ projects/fuse2/tests/sys/fs/fusefs/write.cc Fri Jun 14 19:47:48 2019 (r349038) @@ -531,60 +531,13 @@ TEST_F(Write, mmap) 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"; - ssize_t bufsize = 65536; - /* End the write in the middle of a page */ - ssize_t wrsize = bufsize - 1000; - char *contents0, *contents1, *readbuf, *expected; - uint64_t ino = 42; - int fd; - - contents0 = (char*)malloc(bufsize); - memset(contents0, 'X', bufsize); - contents0[bufsize - 1] = '\0'; // Null-terminate - contents1 = (char*)malloc(wrsize); - memset(contents1, 'Y', wrsize); - readbuf = (char*)calloc(bufsize, 1); - expected = (char*)malloc(bufsize); - memset(expected, 'Y', wrsize); - memset(expected + wrsize, 'X', bufsize - wrsize); - expected[bufsize - 1] = '\0'; // Null-terminate - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_read(ino, 0, bufsize, bufsize, contents0); - expect_write(ino, 0, wrsize, wrsize, 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(wrsize, write(fd, contents1, wrsize)) << strerror(errno); - - // Read again. Cache should be bypassed - expect_read(ino, 0, bufsize, bufsize, expected); - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - ASSERT_STREQ(readbuf, expected); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - TEST_F(WriteThrough, pwrite) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; const char *CONTENTS = "abcdefgh"; uint64_t ino = 42; - uint64_t offset = 4096; + uint64_t offset = 65536; int fd; ssize_t bufsize = strlen(CONTENTS); @@ -901,11 +854,7 @@ TEST_F(WriteBack, o_direct) /* * Without direct_io, writes should be committed to cache */ -/* - * Disabled because we don't yet implement write-through caching. No bugzilla - * entry, because that's a feature request, not a bug. - */ -TEST_F(WriteThrough, DISABLED_writethrough) +TEST_F(WriteThrough, writethrough) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; @@ -927,6 +876,7 @@ TEST_F(WriteThrough, DISABLED_writethrough) * A subsequent read should be serviced by cache, without querying the * filesystem daemon */ + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); /* Deliberately leak fd. close(2) will be tested in release.cc */ }