1a87e0831SAlan Somers /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3a87e0831SAlan Somers * 4a87e0831SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 5a87e0831SAlan Somers * 6a87e0831SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 7a87e0831SAlan Somers * from the FreeBSD Foundation. 8a87e0831SAlan Somers * 9a87e0831SAlan Somers * Redistribution and use in source and binary forms, with or without 10a87e0831SAlan Somers * modification, are permitted provided that the following conditions 11a87e0831SAlan Somers * are met: 12a87e0831SAlan Somers * 1. Redistributions of source code must retain the above copyright 13a87e0831SAlan Somers * notice, this list of conditions and the following disclaimer. 14a87e0831SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 15a87e0831SAlan Somers * notice, this list of conditions and the following disclaimer in the 16a87e0831SAlan Somers * documentation and/or other materials provided with the distribution. 17a87e0831SAlan Somers * 18a87e0831SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19a87e0831SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20a87e0831SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21a87e0831SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22a87e0831SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23a87e0831SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24a87e0831SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25a87e0831SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26a87e0831SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27a87e0831SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28a87e0831SAlan Somers * SUCH DAMAGE. 29a87e0831SAlan Somers */ 30a87e0831SAlan Somers 31a87e0831SAlan Somers extern "C" { 3248417ae0SAlan Somers #include <sys/types.h> 3317575badSAlan Somers #include <sys/mman.h> 3448417ae0SAlan Somers #include <sys/sysctl.h> 3517575badSAlan Somers 36a87e0831SAlan Somers #include <fcntl.h> 37a87e0831SAlan Somers #include <stdlib.h> 38a87e0831SAlan Somers #include <unistd.h> 39a87e0831SAlan Somers } 40a87e0831SAlan Somers 41a87e0831SAlan Somers #include "mockfs.hh" 42a87e0831SAlan Somers #include "utils.hh" 43a87e0831SAlan Somers 44a87e0831SAlan Somers /* 45a87e0831SAlan Somers * For testing I/O like fsx does, but deterministically and without a real 46a87e0831SAlan Somers * underlying file system 47a87e0831SAlan Somers */ 48a87e0831SAlan Somers 49a87e0831SAlan Somers using namespace testing; 50a87e0831SAlan Somers 51a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 52a87e0831SAlan Somers const char RELPATH[] = "some_file.txt"; 53a87e0831SAlan Somers const uint64_t ino = 42; 54a87e0831SAlan Somers 55dff3a6b4SAlan Somers static void compare(const void *tbuf, const void *controlbuf, off_t baseofs, 56dff3a6b4SAlan Somers ssize_t size) 57dff3a6b4SAlan Somers { 58dff3a6b4SAlan Somers int i; 59dff3a6b4SAlan Somers 60dff3a6b4SAlan Somers for (i = 0; i < size; i++) { 61dff3a6b4SAlan Somers if (((const char*)tbuf)[i] != ((const char*)controlbuf)[i]) { 62dff3a6b4SAlan Somers off_t ofs = baseofs + i; 63dff3a6b4SAlan Somers FAIL() << "miscompare at offset " 64dff3a6b4SAlan Somers << std::hex 65dff3a6b4SAlan Somers << std::showbase 66dff3a6b4SAlan Somers << ofs 67dff3a6b4SAlan Somers << ". expected = " 68dff3a6b4SAlan Somers << std::setw(2) 69dff3a6b4SAlan Somers << (unsigned)((const uint8_t*)controlbuf)[i] 70dff3a6b4SAlan Somers << " got = " 71dff3a6b4SAlan Somers << (unsigned)((const uint8_t*)tbuf)[i]; 72dff3a6b4SAlan Somers } 73dff3a6b4SAlan Somers } 74dff3a6b4SAlan Somers } 75dff3a6b4SAlan Somers 761c909c30SAlan Somers typedef tuple<bool, uint32_t, cache_mode, uint32_t> IoParam; 77c51f519bSAlan Somers 78c51f519bSAlan Somers class Io: public FuseTest, public WithParamInterface<IoParam> { 79a87e0831SAlan Somers public: 80a87e0831SAlan Somers int m_backing_fd, m_control_fd, m_test_fd; 8117575badSAlan Somers off_t m_filesize; 82c51f519bSAlan Somers bool m_direct_io; 83a87e0831SAlan Somers 849c9634d1SAlan Somers Io(): m_backing_fd(-1), m_control_fd(-1), m_test_fd(-1), m_filesize(0), 859c9634d1SAlan Somers m_direct_io(false) {}; 86a87e0831SAlan Somers 87a87e0831SAlan Somers void SetUp() 88a87e0831SAlan Somers { 8993c0c1d4SAlan Somers m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC, 0644); 90a87e0831SAlan Somers if (m_backing_fd < 0) 91a87e0831SAlan Somers FAIL() << strerror(errno); 9293c0c1d4SAlan Somers m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC, 0644); 93a87e0831SAlan Somers if (m_control_fd < 0) 94a87e0831SAlan Somers FAIL() << strerror(errno); 95a87e0831SAlan Somers srandom(22'9'1982); // Seed with my birthday 960482ec3eSAlan Somers 97f8ebf1cdSAlan Somers if (get<0>(GetParam())) 98f8ebf1cdSAlan Somers m_init_flags |= FUSE_ASYNC_READ; 990482ec3eSAlan Somers m_maxwrite = get<1>(GetParam()); 100c51f519bSAlan Somers switch (get<2>(GetParam())) { 101c51f519bSAlan Somers case Uncached: 102c51f519bSAlan Somers m_direct_io = true; 103c51f519bSAlan Somers break; 104c51f519bSAlan Somers case WritebackAsync: 105c51f519bSAlan Somers m_async = true; 106c51f519bSAlan Somers /* FALLTHROUGH */ 107c51f519bSAlan Somers case Writeback: 108f8ebf1cdSAlan Somers m_init_flags |= FUSE_WRITEBACK_CACHE; 109c51f519bSAlan Somers /* FALLTHROUGH */ 110c51f519bSAlan Somers case Writethrough: 111c51f519bSAlan Somers break; 112c51f519bSAlan Somers default: 113c51f519bSAlan Somers FAIL() << "Unknown cache mode"; 114c51f519bSAlan Somers } 1151c909c30SAlan Somers m_kernel_minor_version = get<3>(GetParam()); 11691972cfcSAlan Somers m_noatime = true; // To prevent SETATTR for atime on close 1170482ec3eSAlan Somers 118a87e0831SAlan Somers FuseTest::SetUp(); 119a87e0831SAlan Somers if (IsSkipped()) 120a87e0831SAlan Somers return; 121a87e0831SAlan Somers 122c51f519bSAlan Somers if (verbosity > 0) { 123c51f519bSAlan Somers printf("Test Parameters: init_flags=%#x maxwrite=%#x " 1241c909c30SAlan Somers "%sasync cache=%s kernel_minor_version=%d\n", 125c51f519bSAlan Somers m_init_flags, m_maxwrite, m_async? "" : "no", 1261c909c30SAlan Somers cache_mode_to_s(get<2>(GetParam())), 1271c909c30SAlan Somers m_kernel_minor_version); 128c51f519bSAlan Somers } 129c51f519bSAlan Somers 130a87e0831SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 131c51f519bSAlan Somers expect_open(ino, m_direct_io ? FOPEN_DIRECT_IO : 0, 1); 132a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 133a87e0831SAlan Somers ResultOf([=](auto in) { 134a87e0831SAlan Somers return (in.header.opcode == FUSE_WRITE && 135a87e0831SAlan Somers in.header.nodeid == ino); 136a87e0831SAlan Somers }, Eq(true)), 137a87e0831SAlan Somers _) 138a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 139a87e0831SAlan Somers const char *buf = (const char*)in.body.bytes + 140a87e0831SAlan Somers sizeof(struct fuse_write_in); 141a87e0831SAlan Somers ssize_t isize = in.body.write.size; 142a87e0831SAlan Somers off_t iofs = in.body.write.offset; 143a87e0831SAlan Somers 1440c9df4afSAlan Somers assert((size_t)isize <= sizeof(in.body.bytes) - 1450c9df4afSAlan Somers sizeof(struct fuse_write_in)); 146a87e0831SAlan Somers ASSERT_EQ(isize, pwrite(m_backing_fd, buf, isize, iofs)) 147a87e0831SAlan Somers << strerror(errno); 148a87e0831SAlan Somers SET_OUT_HEADER_LEN(out, write); 149a87e0831SAlan Somers out.body.write.size = isize; 150a87e0831SAlan Somers }))); 151a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 152a87e0831SAlan Somers ResultOf([=](auto in) { 153a87e0831SAlan Somers return (in.header.opcode == FUSE_READ && 154a87e0831SAlan Somers in.header.nodeid == ino); 155a87e0831SAlan Somers }, Eq(true)), 156a87e0831SAlan Somers _) 157a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 158a87e0831SAlan Somers ssize_t isize = in.body.write.size; 159a87e0831SAlan Somers off_t iofs = in.body.write.offset; 160a87e0831SAlan Somers void *buf = out.body.bytes; 16117575badSAlan Somers ssize_t osize; 162a87e0831SAlan Somers 1630c9df4afSAlan Somers assert((size_t)isize <= sizeof(out.body.bytes)); 16417575badSAlan Somers osize = pread(m_backing_fd, buf, isize, iofs); 16517575badSAlan Somers ASSERT_LE(0, osize) << strerror(errno); 16617575badSAlan Somers out.header.len = sizeof(struct fuse_out_header) + osize; 167a87e0831SAlan Somers }))); 168a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 169a87e0831SAlan Somers ResultOf([=](auto in) { 170a87e0831SAlan Somers return (in.header.opcode == FUSE_SETATTR && 171a87e0831SAlan Somers in.header.nodeid == ino && 172788af953SAlan Somers (in.body.setattr.valid & FATTR_SIZE)); 173788af953SAlan Somers 174a87e0831SAlan Somers }, Eq(true)), 175a87e0831SAlan Somers _) 176a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 177a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_backing_fd, in.body.setattr.size)) 178a87e0831SAlan Somers << strerror(errno); 179a87e0831SAlan Somers SET_OUT_HEADER_LEN(out, attr); 180a87e0831SAlan Somers out.body.attr.attr.ino = ino; 181a87e0831SAlan Somers out.body.attr.attr.mode = S_IFREG | 0755; 182a87e0831SAlan Somers out.body.attr.attr.size = in.body.setattr.size; 183a87e0831SAlan Somers out.body.attr.attr_valid = UINT64_MAX; 184a87e0831SAlan Somers }))); 185a87e0831SAlan Somers /* Any test that close()s will send FUSE_FLUSH and FUSE_RELEASE */ 186a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 187a87e0831SAlan Somers ResultOf([=](auto in) { 188a87e0831SAlan Somers return (in.header.opcode == FUSE_FLUSH && 189a87e0831SAlan Somers in.header.nodeid == ino); 190a87e0831SAlan Somers }, Eq(true)), 191a87e0831SAlan Somers _) 192a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 193a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 194a87e0831SAlan Somers ResultOf([=](auto in) { 195a87e0831SAlan Somers return (in.header.opcode == FUSE_RELEASE && 196a87e0831SAlan Somers in.header.nodeid == ino); 197a87e0831SAlan Somers }, Eq(true)), 198a87e0831SAlan Somers _) 199a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 2001c909c30SAlan Somers EXPECT_CALL(*m_mock, process( 2011c909c30SAlan Somers ResultOf([=](auto in) { 2021c909c30SAlan Somers return (in.header.opcode == FUSE_COPY_FILE_RANGE && 2031c909c30SAlan Somers in.header.nodeid == ino && 2041c909c30SAlan Somers in.body.copy_file_range.nodeid_out == ino && 2051c909c30SAlan Somers in.body.copy_file_range.flags == 0); 2061c909c30SAlan Somers }, Eq(true)), 2071c909c30SAlan Somers _) 2081c909c30SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 2091c909c30SAlan Somers off_t off_in = in.body.copy_file_range.off_in; 2101c909c30SAlan Somers off_t off_out = in.body.copy_file_range.off_out; 2111c909c30SAlan Somers ASSERT_EQ((ssize_t)in.body.copy_file_range.len, 2121c909c30SAlan Somers copy_file_range(m_backing_fd, &off_in, m_backing_fd, 2131c909c30SAlan Somers &off_out, in.body.copy_file_range.len, 0)); 2141c909c30SAlan Somers SET_OUT_HEADER_LEN(out, write); 2151c909c30SAlan Somers out.body.write.size = in.body.copy_file_range.len; 2161c909c30SAlan Somers }))); 2171c909c30SAlan Somers /* Claim that we don't support FUSE_LSEEK */ 2181c909c30SAlan Somers EXPECT_CALL(*m_mock, process( 2191c909c30SAlan Somers ResultOf([=](auto in) { 2201c909c30SAlan Somers return (in.header.opcode == FUSE_LSEEK); 2211c909c30SAlan Somers }, Eq(true)), 2221c909c30SAlan Somers _) 2231c909c30SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(ENOSYS))); 224a87e0831SAlan Somers 225a87e0831SAlan Somers m_test_fd = open(FULLPATH, O_RDWR ); 226a87e0831SAlan Somers EXPECT_LE(0, m_test_fd) << strerror(errno); 227a87e0831SAlan Somers } 228a87e0831SAlan Somers 229a87e0831SAlan Somers void TearDown() 230a87e0831SAlan Somers { 23148417ae0SAlan Somers if (m_test_fd >= 0) 23248417ae0SAlan Somers close(m_test_fd); 233a87e0831SAlan Somers if (m_backing_fd >= 0) 234a87e0831SAlan Somers close(m_backing_fd); 235a87e0831SAlan Somers if (m_control_fd >= 0) 236a87e0831SAlan Somers close(m_control_fd); 237a87e0831SAlan Somers FuseTest::TearDown(); 2387fc0921dSAlan Somers leak(m_test_fd); 239a87e0831SAlan Somers } 240a87e0831SAlan Somers 24148417ae0SAlan Somers void do_closeopen() 24248417ae0SAlan Somers { 24348417ae0SAlan Somers ASSERT_EQ(0, close(m_test_fd)) << strerror(errno); 24448417ae0SAlan Somers m_test_fd = open("backing_file", O_RDWR); 24548417ae0SAlan Somers ASSERT_LE(0, m_test_fd) << strerror(errno); 24648417ae0SAlan Somers 24748417ae0SAlan Somers ASSERT_EQ(0, close(m_control_fd)) << strerror(errno); 24848417ae0SAlan Somers m_control_fd = open("control", O_RDWR); 24948417ae0SAlan Somers ASSERT_LE(0, m_control_fd) << strerror(errno); 25048417ae0SAlan Somers } 25148417ae0SAlan Somers 2521c909c30SAlan Somers void do_copy_file_range(off_t off_in, off_t off_out, size_t size) 2531c909c30SAlan Somers { 2541c909c30SAlan Somers ssize_t r; 2551c909c30SAlan Somers off_t test_off_in = off_in; 2561c909c30SAlan Somers off_t test_off_out = off_out; 2571c909c30SAlan Somers off_t test_size = size; 2581c909c30SAlan Somers off_t control_off_in = off_in; 2591c909c30SAlan Somers off_t control_off_out = off_out; 2601c909c30SAlan Somers off_t control_size = size; 2611c909c30SAlan Somers 2621c909c30SAlan Somers while (test_size > 0) { 2631c909c30SAlan Somers r = copy_file_range(m_test_fd, &test_off_in, m_test_fd, 2641c909c30SAlan Somers &test_off_out, test_size, 0); 2651c909c30SAlan Somers ASSERT_GT(r, 0) << strerror(errno); 2661c909c30SAlan Somers test_size -= r; 2671c909c30SAlan Somers } 2681c909c30SAlan Somers while (control_size > 0) { 2691c909c30SAlan Somers r = copy_file_range(m_control_fd, &control_off_in, m_control_fd, 2701c909c30SAlan Somers &control_off_out, control_size, 0); 2711c909c30SAlan Somers ASSERT_GT(r, 0) << strerror(errno); 2721c909c30SAlan Somers control_size -= r; 2731c909c30SAlan Somers } 2741c909c30SAlan Somers m_filesize = std::max(m_filesize, off_out + (off_t)size); 2751c909c30SAlan Somers } 2761c909c30SAlan Somers 277a87e0831SAlan Somers void do_ftruncate(off_t offs) 278a87e0831SAlan Somers { 279a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_test_fd, offs)) << strerror(errno); 280a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_control_fd, offs)) << strerror(errno); 28117575badSAlan Somers m_filesize = offs; 28217575badSAlan Somers } 28317575badSAlan Somers 284*daf26f93SAlan Somers void do_mapread(off_t offs, ssize_t size) 28517575badSAlan Somers { 28617575badSAlan Somers void *control_buf, *p; 28717575badSAlan Somers off_t pg_offset, page_mask; 28817575badSAlan Somers size_t map_size; 28917575badSAlan Somers 29017575badSAlan Somers page_mask = getpagesize() - 1; 29117575badSAlan Somers pg_offset = offs & page_mask; 29217575badSAlan Somers map_size = pg_offset + size; 29317575badSAlan Somers 29417575badSAlan Somers p = mmap(NULL, map_size, PROT_READ, MAP_FILE | MAP_SHARED, m_test_fd, 29517575badSAlan Somers offs - pg_offset); 29617575badSAlan Somers ASSERT_NE(p, MAP_FAILED) << strerror(errno); 29717575badSAlan Somers 29817575badSAlan Somers control_buf = malloc(size); 2995a0b9a27SAlan Somers ASSERT_NE(nullptr, control_buf) << strerror(errno); 30017575badSAlan Somers 30117575badSAlan Somers ASSERT_EQ(size, pread(m_control_fd, control_buf, size, offs)) 30217575badSAlan Somers << strerror(errno); 30317575badSAlan Somers 30417575badSAlan Somers compare((void*)((char*)p + pg_offset), control_buf, offs, size); 30517575badSAlan Somers 30617575badSAlan Somers ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno); 30717575badSAlan Somers free(control_buf); 308a87e0831SAlan Somers } 309a87e0831SAlan Somers 310*daf26f93SAlan Somers void do_read(off_t offs, ssize_t size) 311a87e0831SAlan Somers { 312a87e0831SAlan Somers void *test_buf, *control_buf; 31317575badSAlan Somers ssize_t r; 314a87e0831SAlan Somers 315a87e0831SAlan Somers test_buf = malloc(size); 3165a0b9a27SAlan Somers ASSERT_NE(nullptr, test_buf) << strerror(errno); 317a87e0831SAlan Somers control_buf = malloc(size); 3185a0b9a27SAlan Somers ASSERT_NE(nullptr, control_buf) << strerror(errno); 319a87e0831SAlan Somers 32017575badSAlan Somers errno = 0; 32117575badSAlan Somers r = pread(m_test_fd, test_buf, size, offs); 32217575badSAlan Somers ASSERT_NE(-1, r) << strerror(errno); 32317575badSAlan Somers ASSERT_EQ(size, r) << "unexpected short read"; 32417575badSAlan Somers r = pread(m_control_fd, control_buf, size, offs); 32517575badSAlan Somers ASSERT_NE(-1, r) << strerror(errno); 32617575badSAlan Somers ASSERT_EQ(size, r) << "unexpected short read"; 327a87e0831SAlan Somers 328dff3a6b4SAlan Somers compare(test_buf, control_buf, offs, size); 329a87e0831SAlan Somers 330a87e0831SAlan Somers free(control_buf); 331a87e0831SAlan Somers free(test_buf); 332a87e0831SAlan Somers } 333a87e0831SAlan Somers 334*daf26f93SAlan Somers void do_mapwrite(off_t offs, ssize_t size) 33517575badSAlan Somers { 33617575badSAlan Somers char *buf; 33717575badSAlan Somers void *p; 33817575badSAlan Somers off_t pg_offset, page_mask; 33917575badSAlan Somers size_t map_size; 34017575badSAlan Somers long i; 34117575badSAlan Somers 34217575badSAlan Somers page_mask = getpagesize() - 1; 34317575badSAlan Somers pg_offset = offs & page_mask; 34417575badSAlan Somers map_size = pg_offset + size; 34517575badSAlan Somers 34617575badSAlan Somers buf = (char*)malloc(size); 3475a0b9a27SAlan Somers ASSERT_NE(nullptr, buf) << strerror(errno); 34817575badSAlan Somers for (i=0; i < size; i++) 34917575badSAlan Somers buf[i] = random(); 35017575badSAlan Somers 35117575badSAlan Somers if (offs + size > m_filesize) { 35217575badSAlan Somers /* 35317575badSAlan Somers * Must manually extend. vm_mmap_vnode will not implicitly 35417575badSAlan Somers * extend a vnode 35517575badSAlan Somers */ 35617575badSAlan Somers do_ftruncate(offs + size); 35717575badSAlan Somers } 35817575badSAlan Somers 35917575badSAlan Somers p = mmap(NULL, map_size, PROT_READ | PROT_WRITE, 36017575badSAlan Somers MAP_FILE | MAP_SHARED, m_test_fd, offs - pg_offset); 36117575badSAlan Somers ASSERT_NE(p, MAP_FAILED) << strerror(errno); 36217575badSAlan Somers 36317575badSAlan Somers bcopy(buf, (char*)p + pg_offset, size); 36417575badSAlan Somers ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs)) 36517575badSAlan Somers << strerror(errno); 36617575badSAlan Somers 36717575badSAlan Somers free(buf); 36817575badSAlan Somers ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno); 36917575badSAlan Somers } 37017575badSAlan Somers 371*daf26f93SAlan Somers void do_write(off_t offs, ssize_t size) 372a87e0831SAlan Somers { 373a87e0831SAlan Somers char *buf; 374a87e0831SAlan Somers long i; 375a87e0831SAlan Somers 376a87e0831SAlan Somers buf = (char*)malloc(size); 3775a0b9a27SAlan Somers ASSERT_NE(nullptr, buf) << strerror(errno); 378a87e0831SAlan Somers for (i=0; i < size; i++) 379a87e0831SAlan Somers buf[i] = random(); 380a87e0831SAlan Somers 381a87e0831SAlan Somers ASSERT_EQ(size, pwrite(m_test_fd, buf, size, offs )) 382a87e0831SAlan Somers << strerror(errno); 383a87e0831SAlan Somers ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs)) 384a87e0831SAlan Somers << strerror(errno); 38517575badSAlan Somers m_filesize = std::max(m_filesize, offs + size); 38617575badSAlan Somers 38717575badSAlan Somers free(buf); 388a87e0831SAlan Somers } 389a87e0831SAlan Somers 390a87e0831SAlan Somers }; 391a87e0831SAlan Somers 39248417ae0SAlan Somers class IoCacheable: public Io { 39348417ae0SAlan Somers public: 39448417ae0SAlan Somers virtual void SetUp() { 39548417ae0SAlan Somers Io::SetUp(); 39648417ae0SAlan Somers } 39748417ae0SAlan Somers }; 39848417ae0SAlan Somers 3991c909c30SAlan Somers class IoCopyFileRange: public Io { 4001c909c30SAlan Somers public: 4011c909c30SAlan Somers virtual void SetUp() { 4021c909c30SAlan Somers Io::SetUp(); 4031c909c30SAlan Somers } 4041c909c30SAlan Somers }; 4051c909c30SAlan Somers 406a87e0831SAlan Somers /* 407a87e0831SAlan Somers * Extend a file with dirty data in the last page of the last block. 408a87e0831SAlan Somers * 409a87e0831SAlan Somers * fsx -WR -P /tmp -S8 -N3 fsx.bin 410a87e0831SAlan Somers */ 4110482ec3eSAlan Somers TEST_P(Io, extend_from_dirty_page) 412a87e0831SAlan Somers { 413a87e0831SAlan Somers off_t wofs = 0x21a0; 414a87e0831SAlan Somers ssize_t wsize = 0xf0a8; 415a87e0831SAlan Somers off_t rofs = 0xb284; 416a87e0831SAlan Somers ssize_t rsize = 0x9b22; 417a87e0831SAlan Somers off_t truncsize = 0x28702; 418a87e0831SAlan Somers 419*daf26f93SAlan Somers do_write(wofs, wsize); 420a87e0831SAlan Somers do_ftruncate(truncsize); 421*daf26f93SAlan Somers do_read(rofs, rsize); 422a87e0831SAlan Somers } 423a87e0831SAlan Somers 424a87e0831SAlan Somers /* 42517575badSAlan Somers * mapwrite into a newly extended part of a file. 42617575badSAlan Somers * 42717575badSAlan Somers * fsx -c 100 -i 100 -l 524288 -o 131072 -N5 -P /tmp -S19 fsx.bin 42817575badSAlan Somers */ 42948417ae0SAlan Somers TEST_P(IoCacheable, extend_by_mapwrite) 43017575badSAlan Somers { 431*daf26f93SAlan Somers do_mapwrite(0x29a3a, 0x849e); /* [0x29a3a, 0x31ed7] */ 432*daf26f93SAlan Somers do_mapwrite(0x3c7d8, 0x3994); /* [0x3c7d8, 0x4016b] */ 433*daf26f93SAlan Somers do_read(0x30c16, 0xf556); /* [0x30c16, 0x4016b] */ 43417575badSAlan Somers } 43517575badSAlan Somers 43617575badSAlan Somers /* 437a87e0831SAlan Somers * When writing the last page of a file, it must be written synchronously. 438a87e0831SAlan Somers * Otherwise the cached page can become invalid by a subsequent extend 439a87e0831SAlan Somers * operation. 440a87e0831SAlan Somers * 441a87e0831SAlan Somers * fsx -WR -P /tmp -S642 -N3 fsx.bin 442a87e0831SAlan Somers */ 4430482ec3eSAlan Somers TEST_P(Io, last_page) 444a87e0831SAlan Somers { 445*daf26f93SAlan Somers do_write(0x1134f, 0xcc77); /* [0x1134f, 0x1dfc5] */ 446*daf26f93SAlan Somers do_write(0x2096a, 0xdfa7); /* [0x2096a, 0x2e910] */ 447*daf26f93SAlan Somers do_read(0x1a3aa, 0xb5b7); /* [0x1a3aa, 0x25960] */ 44817575badSAlan Somers } 449a87e0831SAlan Somers 45017575badSAlan Somers /* 45117575badSAlan Somers * Read a hole using mmap 45217575badSAlan Somers * 45317575badSAlan Somers * fsx -c 100 -i 100 -l 524288 -o 131072 -N11 -P /tmp -S14 fsx.bin 45417575badSAlan Somers */ 45548417ae0SAlan Somers TEST_P(IoCacheable, mapread_hole) 45617575badSAlan Somers { 457*daf26f93SAlan Somers do_write(0xf205, 0x123b7); /* [0xf205, 0x215bb] */ 458*daf26f93SAlan Somers do_mapread(0x2f4c, 0xeeea); /* [0x2f4c, 0x11e35] */ 459a87e0831SAlan Somers } 460a87e0831SAlan Somers 461a87e0831SAlan Somers /* 462a87e0831SAlan Somers * Read a hole from a block that contains some cached data. 463a87e0831SAlan Somers * 464a87e0831SAlan Somers * fsx -WR -P /tmp -S55 fsx.bin 465a87e0831SAlan Somers */ 4660482ec3eSAlan Somers TEST_P(Io, read_hole_from_cached_block) 467a87e0831SAlan Somers { 468a87e0831SAlan Somers off_t wofs = 0x160c5; 469a87e0831SAlan Somers ssize_t wsize = 0xa996; 470a87e0831SAlan Somers off_t rofs = 0x472e; 471a87e0831SAlan Somers ssize_t rsize = 0xd8d5; 472a87e0831SAlan Somers 473*daf26f93SAlan Somers do_write(wofs, wsize); 474*daf26f93SAlan Somers do_read(rofs, rsize); 475a87e0831SAlan Somers } 476a87e0831SAlan Somers 477a87e0831SAlan Somers /* 47893c0c1d4SAlan Somers * Truncating a file into a dirty buffer should not causing anything untoward 47993c0c1d4SAlan Somers * to happen when that buffer is eventually flushed. 480a87e0831SAlan Somers * 481a87e0831SAlan Somers * fsx -WR -P /tmp -S839 -d -N6 fsx.bin 482a87e0831SAlan Somers */ 4830482ec3eSAlan Somers TEST_P(Io, truncate_into_dirty_buffer) 484a87e0831SAlan Somers { 485a87e0831SAlan Somers off_t wofs0 = 0x3bad7; 486a87e0831SAlan Somers ssize_t wsize0 = 0x4529; 487a87e0831SAlan Somers off_t wofs1 = 0xc30d; 488a87e0831SAlan Somers ssize_t wsize1 = 0x5f77; 489a87e0831SAlan Somers off_t truncsize0 = 0x10916; 490a87e0831SAlan Somers off_t rofs = 0xdf17; 491a87e0831SAlan Somers ssize_t rsize = 0x29ff; 492a87e0831SAlan Somers off_t truncsize1 = 0x152b4; 493a87e0831SAlan Somers 494*daf26f93SAlan Somers do_write(wofs0, wsize0); 495*daf26f93SAlan Somers do_write(wofs1, wsize1); 496a87e0831SAlan Somers do_ftruncate(truncsize0); 497*daf26f93SAlan Somers do_read(rofs, rsize); 498a87e0831SAlan Somers do_ftruncate(truncsize1); 499a87e0831SAlan Somers close(m_test_fd); 500a87e0831SAlan Somers } 50193c0c1d4SAlan Somers 50293c0c1d4SAlan Somers /* 50393c0c1d4SAlan Somers * Truncating a file into a dirty buffer should not causing anything untoward 50493c0c1d4SAlan Somers * to happen when that buffer is eventually flushed, even when the buffer's 50593c0c1d4SAlan Somers * dirty_off is > 0. 50693c0c1d4SAlan Somers * 50793c0c1d4SAlan Somers * Based on this command with a few steps removed: 50893c0c1d4SAlan Somers * fsx -WR -P /tmp -S677 -d -N8 fsx.bin 50993c0c1d4SAlan Somers */ 5100482ec3eSAlan Somers TEST_P(Io, truncate_into_dirty_buffer2) 51193c0c1d4SAlan Somers { 51293c0c1d4SAlan Somers off_t truncsize0 = 0x344f3; 51393c0c1d4SAlan Somers off_t wofs = 0x2790c; 51493c0c1d4SAlan Somers ssize_t wsize = 0xd86a; 51593c0c1d4SAlan Somers off_t truncsize1 = 0x2de38; 51693c0c1d4SAlan Somers off_t rofs2 = 0x1fd7a; 51793c0c1d4SAlan Somers ssize_t rsize2 = 0xc594; 51893c0c1d4SAlan Somers off_t truncsize2 = 0x31e71; 51993c0c1d4SAlan Somers 52093c0c1d4SAlan Somers /* Sets the file size to something larger than the next write */ 52193c0c1d4SAlan Somers do_ftruncate(truncsize0); 52293c0c1d4SAlan Somers /* 52393c0c1d4SAlan Somers * Creates a dirty buffer. The part in lbn 2 doesn't flush 52493c0c1d4SAlan Somers * synchronously. 52593c0c1d4SAlan Somers */ 526*daf26f93SAlan Somers do_write(wofs, wsize); 52793c0c1d4SAlan Somers /* Truncates part of the dirty buffer created in step 2 */ 52893c0c1d4SAlan Somers do_ftruncate(truncsize1); 52993c0c1d4SAlan Somers /* XXX ?I don't know why this is necessary? */ 530*daf26f93SAlan Somers do_read(rofs2, rsize2); 53193c0c1d4SAlan Somers /* Truncates the dirty buffer */ 53293c0c1d4SAlan Somers do_ftruncate(truncsize2); 53393c0c1d4SAlan Somers close(m_test_fd); 53493c0c1d4SAlan Somers } 535dff3a6b4SAlan Somers 536dff3a6b4SAlan Somers /* 537dff3a6b4SAlan Somers * Regression test for a bug introduced in r348931 538dff3a6b4SAlan Somers * 539dff3a6b4SAlan Somers * Sequence of operations: 540dff3a6b4SAlan Somers * 1) The first write reads lbn so it can modify it 541dff3a6b4SAlan Somers * 2) The first write flushes lbn 3 immediately because it's the end of file 542dff3a6b4SAlan Somers * 3) The first write then flushes lbn 4 because it's the end of the file 543dff3a6b4SAlan Somers * 4) The second write modifies the cached versions of lbn 3 and 4 544dff3a6b4SAlan Somers * 5) The third write's getblkx invalidates lbn 4's B_CACHE because it's 545dff3a6b4SAlan Somers * extending the buffer. Then it flushes lbn 4 because B_DELWRI was set but 546dff3a6b4SAlan Somers * B_CACHE was clear. 547dff3a6b4SAlan Somers * 6) fuse_write_biobackend erroneously called vfs_bio_clrbuf, putting the 548dff3a6b4SAlan Somers * buffer into a weird write-only state. All read operations would return 549dff3a6b4SAlan Somers * 0. Writes were apparently still processed, because the buffer's contents 550dff3a6b4SAlan Somers * were correct when examined in a core dump. 551dff3a6b4SAlan Somers * 7) The third write reads lbn 4 because cache is clear 552dff3a6b4SAlan Somers * 9) uiomove dutifully copies new data into the buffer 553dff3a6b4SAlan Somers * 10) The buffer's dirty is flushed to lbn 4 554dff3a6b4SAlan Somers * 11) The read returns all zeros because of step 6. 555dff3a6b4SAlan Somers * 556dff3a6b4SAlan Somers * Based on: 557dff3a6b4SAlan Somers * fsx -WR -l 524388 -o 131072 -P /tmp -S6456 -q fsx.bin 558dff3a6b4SAlan Somers */ 5590482ec3eSAlan Somers TEST_P(Io, resize_a_valid_buffer_while_extending) 560dff3a6b4SAlan Somers { 561*daf26f93SAlan Somers do_write(0x36ee6, 0x14530); /* [0x36ee6, 0x4b415] */ 562*daf26f93SAlan Somers do_write(0x33256, 0x1507c); /* [0x33256, 0x482d1] */ 563*daf26f93SAlan Somers do_write(0x4c03d, 0x175c); /* [0x4c03d, 0x4d798] */ 564*daf26f93SAlan Somers do_read(0x3599c, 0xe277); /* [0x3599c, 0x43c12] */ 565dff3a6b4SAlan Somers close(m_test_fd); 566dff3a6b4SAlan Somers } 5670482ec3eSAlan Somers 5686b1c5349SAlan Somers /* 5696b1c5349SAlan Somers * mmap of a suitable region could trigger a panic. I'm not sure what 5706b1c5349SAlan Somers * combination of size and offset counts as "suitable". Regression test for 5716b1c5349SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=276191 5726b1c5349SAlan Somers */ 5736b1c5349SAlan Somers TEST_P(IoCacheable, vnode_pager_generic_putpage_clean_block_at_eof) 5746b1c5349SAlan Somers { 575*daf26f93SAlan Somers do_mapwrite(0x3b4e0, 0x1bbc3); 5766b1c5349SAlan Somers } 5776b1c5349SAlan Somers 5781c909c30SAlan Somers /* 5791c909c30SAlan Somers * A copy_file_range that follows an mmap write to the input area needs to 5801c909c30SAlan Somers * flush the mmap buffer first. 5811c909c30SAlan Somers */ 5821c909c30SAlan Somers TEST_P(IoCopyFileRange, copy_file_range_from_mapped_write) 5831c909c30SAlan Somers { 584*daf26f93SAlan Somers do_mapwrite(0, 0x1000); 5851c909c30SAlan Somers do_copy_file_range(0, 0x1000, 0x1000); 5861c909c30SAlan Somers do_read(0x1000, 0x1000); 5871c909c30SAlan Somers } 5881c909c30SAlan Somers 5891c909c30SAlan Somers 590811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(Io, Io, 591f8ebf1cdSAlan Somers Combine(Bool(), /* async read */ 5920482ec3eSAlan Somers Values(0x1000, 0x10000, 0x20000), /* m_maxwrite */ 5931c909c30SAlan Somers Values(Uncached, Writethrough, Writeback, WritebackAsync), 5941c909c30SAlan Somers Values(28) /* kernel_minor_vers */ 595c51f519bSAlan Somers ) 596c51f519bSAlan Somers ); 59748417ae0SAlan Somers 598811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(Io, IoCacheable, 599f8ebf1cdSAlan Somers Combine(Bool(), /* async read */ 60048417ae0SAlan Somers Values(0x1000, 0x10000, 0x20000), /* m_maxwrite */ 6011c909c30SAlan Somers Values(Writethrough, Writeback, WritebackAsync), 6021c909c30SAlan Somers Values(28) /* kernel_minor_vers */ 6031c909c30SAlan Somers ) 6041c909c30SAlan Somers ); 6051c909c30SAlan Somers 6061c909c30SAlan Somers INSTANTIATE_TEST_SUITE_P(Io, IoCopyFileRange, 6071c909c30SAlan Somers Combine(Values(true), /* async read */ 6081c909c30SAlan Somers Values(0x10000), /* m_maxwrite */ 6091c909c30SAlan Somers Values(Writethrough, Writeback, WritebackAsync), 6101c909c30SAlan Somers Values(27, 28) /* kernel_minor_vers */ 611c51f519bSAlan Somers ) 612c51f519bSAlan Somers ); 613