Index: sys/fs/fuse/fuse_device.c =================================================================== --- sys/fs/fuse/fuse_device.c +++ sys/fs/fuse/fuse_device.c @@ -276,6 +276,7 @@ return (revents); } +SDT_PROBE_DEFINE1(fusefs, , device, read_tick, "struct fuse_ticket*"); /* * fuse_device_read hangs on the queue of VFS messages. * When it's notified that there is a new one, it picks that and @@ -287,9 +288,8 @@ int err; struct fuse_data *data; struct fuse_ticket *tick; - void *buf[] = {NULL, NULL, NULL}; - int buflen[3]; - int i; + void *buf; + int buflen; SDT_PROBE2(fusefs, , device, trace, 1, "fuse device read"); @@ -332,6 +332,7 @@ goto again; } fuse_lck_mtx_unlock(data->ms_mtx); + SDT_PROBE1(fusefs, , device, read_tick, tick); if (fdata_get_dead(data)) { /* @@ -352,46 +353,27 @@ SDT_PROBE2(fusefs, , device, trace, 1, "fuse device read message successfully"); - KASSERT(tick->tk_ms_bufdata || tick->tk_ms_bufsize == 0, - ("non-null buf pointer with positive size")); - - switch (tick->tk_ms_type) { - case FT_M_FIOV: - buf[0] = tick->tk_ms_fiov.base; - buflen[0] = tick->tk_ms_fiov.len; - break; - case FT_M_BUF: - buf[0] = tick->tk_ms_fiov.base; - buflen[0] = tick->tk_ms_fiov.len; - buf[1] = tick->tk_ms_bufdata; - buflen[1] = tick->tk_ms_bufsize; - break; - default: - panic("unknown message type for fuse_ticket %p", tick); - } + buf = tick->tk_ms_fiov.base; + buflen = tick->tk_ms_fiov.len; - for (i = 0; buf[i]; i++) { - /* - * Why not ban mercilessly stupid daemons who can't keep up - * with us? (There is no much use of a partial read here...) - */ - /* - * XXX note that in such cases Linux FUSE throws EIO at the - * syscall invoker and stands back to the message queue. The - * rationale should be made clear (and possibly adopt that - * behaviour). Keeping the current scheme at least makes - * fallacy as loud as possible... - */ - if (uio->uio_resid < buflen[i]) { - fdata_set_dead(data); - SDT_PROBE2(fusefs, , device, trace, 2, - "daemon is stupid, kick it off..."); - err = ENODEV; - break; - } - err = uiomove(buf[i], buflen[i], uio); - if (err) - break; + /* + * Why not ban mercilessly stupid daemons who can't keep up + * with us? (There is no much use of a partial read here...) + */ + /* + * XXX note that in such cases Linux FUSE throws EIO at the + * syscall invoker and stands back to the message queue. The + * rationale should be made clear (and possibly adopt that + * behaviour). Keeping the current scheme at least makes + * fallacy as loud as possible... + */ + if (uio->uio_resid < buflen) { + fdata_set_dead(data); + SDT_PROBE2(fusefs, , device, trace, 2, + "daemon is stupid, kick it off..."); + err = ENODEV; + } else { + err = uiomove(buf, buflen, uio); } FUSE_ASSERT_MS_DONE(tick); Index: sys/fs/fuse/fuse_ipc.h =================================================================== --- sys/fs/fuse/fuse_ipc.h +++ sys/fs/fuse/fuse_ipc.h @@ -123,17 +123,10 @@ /* fields for initiating an upgoing message */ struct fuse_iov tk_ms_fiov; - void *tk_ms_bufdata; - size_t tk_ms_bufsize; - enum { FT_M_FIOV, FT_M_BUF } tk_ms_type; STAILQ_ENTRY(fuse_ticket) tk_ms_link; /* fields for handling answers coming from userspace */ struct fuse_iov tk_aw_fiov; - void *tk_aw_bufdata; - size_t tk_aw_bufsize; - enum { FT_A_FIOV, FT_A_BUF } tk_aw_type; - struct fuse_out_header tk_aw_ohead; int tk_aw_errno; struct mtx tk_aw_mtx; Index: sys/fs/fuse/fuse_ipc.c =================================================================== --- sys/fs/fuse/fuse_ipc.c +++ sys/fs/fuse/fuse_ipc.c @@ -356,11 +356,9 @@ bzero(ftick, sizeof(struct fuse_ticket)); fiov_init(&ftick->tk_ms_fiov, sizeof(struct fuse_in_header)); - ftick->tk_ms_type = FT_M_FIOV; mtx_init(&ftick->tk_aw_mtx, "fuse answer delivery mutex", NULL, MTX_DEF); fiov_init(&ftick->tk_aw_fiov, 0); - ftick->tk_aw_type = FT_A_FIOV; return 0; } @@ -395,18 +393,11 @@ FUSE_ASSERT_AW_DONE(ftick); fiov_refresh(&ftick->tk_ms_fiov); - ftick->tk_ms_bufdata = NULL; - ftick->tk_ms_bufsize = 0; - ftick->tk_ms_type = FT_M_FIOV; bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header)); fiov_refresh(&ftick->tk_aw_fiov); ftick->tk_aw_errno = 0; - ftick->tk_aw_bufdata = NULL; - ftick->tk_aw_bufsize = 0; - ftick->tk_aw_type = FT_A_FIOV; - ftick->tk_flag = 0; } @@ -417,17 +408,9 @@ FUSE_ASSERT_MS_DONE(ftick); FUSE_ASSERT_AW_DONE(ftick); - ftick->tk_ms_bufdata = NULL; - ftick->tk_ms_bufsize = 0; - ftick->tk_ms_type = FT_M_FIOV; - bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header)); ftick->tk_aw_errno = 0; - ftick->tk_aw_bufdata = NULL; - ftick->tk_aw_bufsize = 0; - ftick->tk_aw_type = FT_A_FIOV; - ftick->tk_flag = 0; } @@ -546,20 +529,8 @@ size_t len = uio_resid(uio); if (len) { - switch (ftick->tk_aw_type) { - case FT_A_FIOV: - fiov_adjust(fticket_resp(ftick), len); - err = uiomove(fticket_resp(ftick)->base, len, uio); - break; - - case FT_A_BUF: - ftick->tk_aw_bufsize = len; - err = uiomove(ftick->tk_aw_bufdata, len, uio); - break; - - default: - panic("FUSE: unknown answer type for ticket %p", ftick); - } + fiov_adjust(fticket_resp(ftick), len); + err = uiomove(fticket_resp(ftick)->base, len, uio); } return err; } Index: tests/sys/fs/fusefs/bmap.cc =================================================================== --- tests/sys/fs/fusefs/bmap.cc +++ tests/sys/fs/fusefs/bmap.cc @@ -83,9 +83,9 @@ TEST_F(Bmap, bmap) { struct fiobmap2_arg arg; - const off_t filesize = 1 << 20; + const off_t filesize = 1 << 30; const ino_t ino = 42; - int64_t lbn = 10; + int64_t lbn = 100; int64_t pbn = 12345; int fd; @@ -112,7 +112,7 @@ TEST_F(Bmap, default_) { struct fiobmap2_arg arg; - const off_t filesize = 1 << 20; + const off_t filesize = 1 << 30; const ino_t ino = 42; int64_t lbn; int fd; Index: tests/sys/fs/fusefs/mockfs.hh =================================================================== --- tests/sys/fs/fusefs/mockfs.hh +++ tests/sys/fs/fusefs/mockfs.hh @@ -72,6 +72,14 @@ extern int verbosity; +/* + * The maximum that a test case can set max_write, limited by the buffer + * supplied when reading from /dev/fuse. This limitation is imposed by + * fusefs-libs, but not by the FUSE protocol. + */ +const uint32_t max_max_write = 0x20000; + + /* This struct isn't defined by fuse_kernel.h or libfuse, but it should be */ struct fuse_create_out { struct fuse_entry_out entry; @@ -138,8 +146,17 @@ union fuse_payloads_in { fuse_access_in access; fuse_bmap_in bmap; - /* value is from fuse_kern_chan.c in fusefs-libs */ - uint8_t bytes[0x21000 - sizeof(struct fuse_in_header)]; + /* + * In fusefs-libs 3.4.2 and below the buffer size is fixed at 0x21000 + * minus the header sizes. fusefs-libs 3.4.3 (and FUSE Protocol 7.29) + * add a FUSE_MAX_PAGES option that allows it to be greater. + * + * See fuse_kern_chan.c in fusefs-libs 2.9.9 and below, or + * FUSE_DEFAULT_MAX_PAGES_PER_REQ in fusefs-libs 3.4.3 and above. + */ + uint8_t bytes[ + max_max_write + 0x1000 - sizeof(struct fuse_in_header) + ]; fuse_create_in create; fuse_flush_in flush; fuse_fsync_in fsync; Index: tests/sys/fs/fusefs/mockfs.cc =================================================================== --- tests/sys/fs/fusefs/mockfs.cc +++ tests/sys/fs/fusefs/mockfs.cc @@ -365,7 +365,7 @@ m_daemon_id = NULL; m_kernel_minor_version = kernel_minor_version; m_maxreadahead = max_readahead; - m_maxwrite = max_write; + m_maxwrite = MIN(max_write, max_max_write); m_nready = -1; m_pm = pm; m_time_gran = time_gran; Index: tests/sys/fs/fusefs/utils.hh =================================================================== --- tests/sys/fs/fusefs/utils.hh +++ tests/sys/fs/fusefs/utils.hh @@ -55,7 +55,6 @@ bool is_unsafe_aio_enabled(void); extern const uint32_t libfuse_max_write; -extern const uint32_t default_max_write; class FuseTest : public ::testing::Test { protected: uint32_t m_maxreadahead; @@ -80,7 +79,7 @@ FuseTest(): m_maxreadahead(0), - m_maxwrite(default_max_write), + m_maxwrite(0), m_init_flags(0), m_allow_other(false), m_default_permissions(false), Index: tests/sys/fs/fusefs/utils.cc =================================================================== --- tests/sys/fs/fusefs/utils.cc +++ tests/sys/fs/fusefs/utils.cc @@ -59,13 +59,6 @@ */ 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() { @@ -156,6 +149,12 @@ ASSERT_EQ(0, sysctlbyname(maxphys_node, &val, &size, NULL, 0)) << strerror(errno); m_maxphys = val; + /* + * Set the default max_write to a distinct value from MAXPHYS to catch + * bugs that confuse the two. + */ + if (m_maxwrite == 0) + m_maxwrite = MIN(libfuse_max_write, (uint32_t)m_maxphys / 2); try { m_mock = new MockFS(m_maxreadahead, m_allow_other, Index: tests/sys/fs/fusefs/write.cc =================================================================== --- tests/sys/fs/fusefs/write.cc +++ tests/sys/fs/fusefs/write.cc @@ -166,6 +166,7 @@ public: virtual void SetUp() { m_async = true; + m_maxwrite = 65536; WriteBack::SetUp(); } }; @@ -194,6 +195,19 @@ } }; +/* Tests relating to the server's max_write property */ +class WriteMaxWrite: public Write { +public: +virtual void SetUp() { + /* + * For this test, m_maxwrite must be less than either m_maxbcachebuf or + * maxphys. + */ + m_maxwrite = 32768; + Write::SetUp(); +} +}; + void sigxfsz_handler(int __unused sig) { Write::s_sigxfsz = 1; } @@ -643,7 +657,7 @@ } /* fuse(4) should not issue writes of greater size than the daemon requests */ -TEST_F(Write, write_large) +TEST_F(WriteMaxWrite, write) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; @@ -653,6 +667,8 @@ ssize_t halfbufsize, bufsize; halfbufsize = m_mock->m_maxwrite; + if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys) + GTEST_SKIP() << "Must lower m_maxwrite for this test"; bufsize = halfbufsize * 2; contents = (int*)malloc(bufsize); ASSERT_NE(nullptr, contents);