1a87e0831SAlan Somers /*- 2a87e0831SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * TODO: after fusefs gains the options to select cache mode for each mount 49a87e0831SAlan Somers * point, run each of these tests for all cache modes. 50a87e0831SAlan Somers */ 51a87e0831SAlan Somers 52a87e0831SAlan Somers using namespace testing; 53a87e0831SAlan Somers 54*c51f519bSAlan Somers enum cache_mode { 55*c51f519bSAlan Somers Uncached, 56*c51f519bSAlan Somers Writethrough, 57*c51f519bSAlan Somers Writeback, 58*c51f519bSAlan Somers WritebackAsync 59*c51f519bSAlan Somers }; 60*c51f519bSAlan Somers 61*c51f519bSAlan Somers const char *cache_mode_to_s(enum cache_mode cm) { 62*c51f519bSAlan Somers switch (cm) { 63*c51f519bSAlan Somers case Uncached: 64*c51f519bSAlan Somers return "Uncached"; 65*c51f519bSAlan Somers case Writethrough: 66*c51f519bSAlan Somers return "Writethrough"; 67*c51f519bSAlan Somers case Writeback: 68*c51f519bSAlan Somers return "Writeback"; 69*c51f519bSAlan Somers case WritebackAsync: 70*c51f519bSAlan Somers return "WritebackAsync"; 71*c51f519bSAlan Somers default: 72*c51f519bSAlan Somers return "Unknown"; 73*c51f519bSAlan Somers } 74*c51f519bSAlan Somers } 75*c51f519bSAlan Somers 76a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 77a87e0831SAlan Somers const char RELPATH[] = "some_file.txt"; 78a87e0831SAlan Somers const uint64_t ino = 42; 79a87e0831SAlan Somers 80dff3a6b4SAlan Somers static void compare(const void *tbuf, const void *controlbuf, off_t baseofs, 81dff3a6b4SAlan Somers ssize_t size) 82dff3a6b4SAlan Somers { 83dff3a6b4SAlan Somers int i; 84dff3a6b4SAlan Somers 85dff3a6b4SAlan Somers for (i = 0; i < size; i++) { 86dff3a6b4SAlan Somers if (((const char*)tbuf)[i] != ((const char*)controlbuf)[i]) { 87dff3a6b4SAlan Somers off_t ofs = baseofs + i; 88dff3a6b4SAlan Somers FAIL() << "miscompare at offset " 89dff3a6b4SAlan Somers << std::hex 90dff3a6b4SAlan Somers << std::showbase 91dff3a6b4SAlan Somers << ofs 92dff3a6b4SAlan Somers << ". expected = " 93dff3a6b4SAlan Somers << std::setw(2) 94dff3a6b4SAlan Somers << (unsigned)((const uint8_t*)controlbuf)[i] 95dff3a6b4SAlan Somers << " got = " 96dff3a6b4SAlan Somers << (unsigned)((const uint8_t*)tbuf)[i]; 97dff3a6b4SAlan Somers } 98dff3a6b4SAlan Somers } 99dff3a6b4SAlan Somers } 100dff3a6b4SAlan Somers 101*c51f519bSAlan Somers typedef tuple<bool, uint32_t, cache_mode> IoParam; 102*c51f519bSAlan Somers 103*c51f519bSAlan Somers class Io: public FuseTest, public WithParamInterface<IoParam> { 104a87e0831SAlan Somers public: 105a87e0831SAlan Somers int m_backing_fd, m_control_fd, m_test_fd; 10617575badSAlan Somers off_t m_filesize; 107*c51f519bSAlan Somers bool m_direct_io; 108a87e0831SAlan Somers 109*c51f519bSAlan Somers Io(): m_backing_fd(-1), m_control_fd(-1), m_direct_io(false) {}; 110a87e0831SAlan Somers 111a87e0831SAlan Somers void SetUp() 112a87e0831SAlan Somers { 11317575badSAlan Somers m_filesize = 0; 11493c0c1d4SAlan Somers m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC, 0644); 115a87e0831SAlan Somers if (m_backing_fd < 0) 116a87e0831SAlan Somers FAIL() << strerror(errno); 11793c0c1d4SAlan Somers m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC, 0644); 118a87e0831SAlan Somers if (m_control_fd < 0) 119a87e0831SAlan Somers FAIL() << strerror(errno); 120a87e0831SAlan Somers srandom(22'9'1982); // Seed with my birthday 1210482ec3eSAlan Somers 122f8ebf1cdSAlan Somers if (get<0>(GetParam())) 123f8ebf1cdSAlan Somers m_init_flags |= FUSE_ASYNC_READ; 1240482ec3eSAlan Somers m_maxwrite = get<1>(GetParam()); 125*c51f519bSAlan Somers switch (get<2>(GetParam())) { 126*c51f519bSAlan Somers case Uncached: 127*c51f519bSAlan Somers m_direct_io = true; 128*c51f519bSAlan Somers break; 129*c51f519bSAlan Somers case WritebackAsync: 130*c51f519bSAlan Somers m_async = true; 131*c51f519bSAlan Somers /* FALLTHROUGH */ 132*c51f519bSAlan Somers case Writeback: 133f8ebf1cdSAlan Somers m_init_flags |= FUSE_WRITEBACK_CACHE; 134*c51f519bSAlan Somers /* FALLTHROUGH */ 135*c51f519bSAlan Somers case Writethrough: 136*c51f519bSAlan Somers break; 137*c51f519bSAlan Somers default: 138*c51f519bSAlan Somers FAIL() << "Unknown cache mode"; 139*c51f519bSAlan Somers } 1400482ec3eSAlan Somers 141a87e0831SAlan Somers FuseTest::SetUp(); 142a87e0831SAlan Somers if (IsSkipped()) 143a87e0831SAlan Somers return; 144a87e0831SAlan Somers 145*c51f519bSAlan Somers if (verbosity > 0) { 146*c51f519bSAlan Somers printf("Test Parameters: init_flags=%#x maxwrite=%#x " 147*c51f519bSAlan Somers "%sasync cache=%s\n", 148*c51f519bSAlan Somers m_init_flags, m_maxwrite, m_async? "" : "no", 149*c51f519bSAlan Somers cache_mode_to_s(get<2>(GetParam()))); 150*c51f519bSAlan Somers } 151*c51f519bSAlan Somers 152a87e0831SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 153*c51f519bSAlan Somers expect_open(ino, m_direct_io ? FOPEN_DIRECT_IO : 0, 1); 154a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 155a87e0831SAlan Somers ResultOf([=](auto in) { 156a87e0831SAlan Somers return (in.header.opcode == FUSE_WRITE && 157a87e0831SAlan Somers in.header.nodeid == ino); 158a87e0831SAlan Somers }, Eq(true)), 159a87e0831SAlan Somers _) 160a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 161a87e0831SAlan Somers const char *buf = (const char*)in.body.bytes + 162a87e0831SAlan Somers sizeof(struct fuse_write_in); 163a87e0831SAlan Somers ssize_t isize = in.body.write.size; 164a87e0831SAlan Somers off_t iofs = in.body.write.offset; 165a87e0831SAlan Somers 166a87e0831SAlan Somers ASSERT_EQ(isize, pwrite(m_backing_fd, buf, isize, iofs)) 167a87e0831SAlan Somers << strerror(errno); 168a87e0831SAlan Somers SET_OUT_HEADER_LEN(out, write); 169a87e0831SAlan Somers out.body.write.size = isize; 170a87e0831SAlan Somers }))); 171a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 172a87e0831SAlan Somers ResultOf([=](auto in) { 173a87e0831SAlan Somers return (in.header.opcode == FUSE_READ && 174a87e0831SAlan Somers in.header.nodeid == ino); 175a87e0831SAlan Somers }, Eq(true)), 176a87e0831SAlan Somers _) 177a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 178a87e0831SAlan Somers ssize_t isize = in.body.write.size; 179a87e0831SAlan Somers off_t iofs = in.body.write.offset; 180a87e0831SAlan Somers void *buf = out.body.bytes; 18117575badSAlan Somers ssize_t osize; 182a87e0831SAlan Somers 18317575badSAlan Somers osize = pread(m_backing_fd, buf, isize, iofs); 18417575badSAlan Somers ASSERT_LE(0, osize) << strerror(errno); 18517575badSAlan Somers out.header.len = sizeof(struct fuse_out_header) + osize; 186a87e0831SAlan Somers }))); 187a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 188a87e0831SAlan Somers ResultOf([=](auto in) { 189a87e0831SAlan Somers return (in.header.opcode == FUSE_SETATTR && 190a87e0831SAlan Somers in.header.nodeid == ino && 191788af953SAlan Somers (in.body.setattr.valid & FATTR_SIZE)); 192788af953SAlan Somers 193a87e0831SAlan Somers }, Eq(true)), 194a87e0831SAlan Somers _) 195a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 196a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_backing_fd, in.body.setattr.size)) 197a87e0831SAlan Somers << strerror(errno); 198a87e0831SAlan Somers SET_OUT_HEADER_LEN(out, attr); 199a87e0831SAlan Somers out.body.attr.attr.ino = ino; 200a87e0831SAlan Somers out.body.attr.attr.mode = S_IFREG | 0755; 201a87e0831SAlan Somers out.body.attr.attr.size = in.body.setattr.size; 202a87e0831SAlan Somers out.body.attr.attr_valid = UINT64_MAX; 203a87e0831SAlan Somers }))); 204a87e0831SAlan Somers /* Any test that close()s will send FUSE_FLUSH and FUSE_RELEASE */ 205a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 206a87e0831SAlan Somers ResultOf([=](auto in) { 207a87e0831SAlan Somers return (in.header.opcode == FUSE_FLUSH && 208a87e0831SAlan Somers in.header.nodeid == ino); 209a87e0831SAlan Somers }, Eq(true)), 210a87e0831SAlan Somers _) 211a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 212a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 213a87e0831SAlan Somers ResultOf([=](auto in) { 214a87e0831SAlan Somers return (in.header.opcode == FUSE_RELEASE && 215a87e0831SAlan Somers in.header.nodeid == ino); 216a87e0831SAlan Somers }, Eq(true)), 217a87e0831SAlan Somers _) 218a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 219a87e0831SAlan Somers 220a87e0831SAlan Somers m_test_fd = open(FULLPATH, O_RDWR ); 221a87e0831SAlan Somers EXPECT_LE(0, m_test_fd) << strerror(errno); 222a87e0831SAlan Somers } 223a87e0831SAlan Somers 224a87e0831SAlan Somers void TearDown() 225a87e0831SAlan Somers { 22648417ae0SAlan Somers if (m_test_fd >= 0) 22748417ae0SAlan Somers close(m_test_fd); 228a87e0831SAlan Somers if (m_backing_fd >= 0) 229a87e0831SAlan Somers close(m_backing_fd); 230a87e0831SAlan Somers if (m_control_fd >= 0) 231a87e0831SAlan Somers close(m_control_fd); 232a87e0831SAlan Somers FuseTest::TearDown(); 233a87e0831SAlan Somers /* Deliberately leak test_fd */ 234a87e0831SAlan Somers } 235a87e0831SAlan Somers 23648417ae0SAlan Somers void do_closeopen() 23748417ae0SAlan Somers { 23848417ae0SAlan Somers ASSERT_EQ(0, close(m_test_fd)) << strerror(errno); 23948417ae0SAlan Somers m_test_fd = open("backing_file", O_RDWR); 24048417ae0SAlan Somers ASSERT_LE(0, m_test_fd) << strerror(errno); 24148417ae0SAlan Somers 24248417ae0SAlan Somers ASSERT_EQ(0, close(m_control_fd)) << strerror(errno); 24348417ae0SAlan Somers m_control_fd = open("control", O_RDWR); 24448417ae0SAlan Somers ASSERT_LE(0, m_control_fd) << strerror(errno); 24548417ae0SAlan Somers } 24648417ae0SAlan Somers 247a87e0831SAlan Somers void do_ftruncate(off_t offs) 248a87e0831SAlan Somers { 249a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_test_fd, offs)) << strerror(errno); 250a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_control_fd, offs)) << strerror(errno); 25117575badSAlan Somers m_filesize = offs; 25217575badSAlan Somers } 25317575badSAlan Somers 25417575badSAlan Somers void do_mapread(ssize_t size, off_t offs) 25517575badSAlan Somers { 25617575badSAlan Somers void *control_buf, *p; 25717575badSAlan Somers off_t pg_offset, page_mask; 25817575badSAlan Somers size_t map_size; 25917575badSAlan Somers 26017575badSAlan Somers page_mask = getpagesize() - 1; 26117575badSAlan Somers pg_offset = offs & page_mask; 26217575badSAlan Somers map_size = pg_offset + size; 26317575badSAlan Somers 26417575badSAlan Somers p = mmap(NULL, map_size, PROT_READ, MAP_FILE | MAP_SHARED, m_test_fd, 26517575badSAlan Somers offs - pg_offset); 26617575badSAlan Somers ASSERT_NE(p, MAP_FAILED) << strerror(errno); 26717575badSAlan Somers 26817575badSAlan Somers control_buf = malloc(size); 26917575badSAlan Somers ASSERT_NE(NULL, control_buf) << strerror(errno); 27017575badSAlan Somers 27117575badSAlan Somers ASSERT_EQ(size, pread(m_control_fd, control_buf, size, offs)) 27217575badSAlan Somers << strerror(errno); 27317575badSAlan Somers 27417575badSAlan Somers compare((void*)((char*)p + pg_offset), control_buf, offs, size); 27517575badSAlan Somers 27617575badSAlan Somers ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno); 27717575badSAlan Somers free(control_buf); 278a87e0831SAlan Somers } 279a87e0831SAlan Somers 280a87e0831SAlan Somers void do_read(ssize_t size, off_t offs) 281a87e0831SAlan Somers { 282a87e0831SAlan Somers void *test_buf, *control_buf; 28317575badSAlan Somers ssize_t r; 284a87e0831SAlan Somers 285a87e0831SAlan Somers test_buf = malloc(size); 286a87e0831SAlan Somers ASSERT_NE(NULL, test_buf) << strerror(errno); 287a87e0831SAlan Somers control_buf = malloc(size); 288a87e0831SAlan Somers ASSERT_NE(NULL, control_buf) << strerror(errno); 289a87e0831SAlan Somers 29017575badSAlan Somers errno = 0; 29117575badSAlan Somers r = pread(m_test_fd, test_buf, size, offs); 29217575badSAlan Somers ASSERT_NE(-1, r) << strerror(errno); 29317575badSAlan Somers ASSERT_EQ(size, r) << "unexpected short read"; 29417575badSAlan Somers r = pread(m_control_fd, control_buf, size, offs); 29517575badSAlan Somers ASSERT_NE(-1, r) << strerror(errno); 29617575badSAlan Somers ASSERT_EQ(size, r) << "unexpected short read"; 297a87e0831SAlan Somers 298dff3a6b4SAlan Somers compare(test_buf, control_buf, offs, size); 299a87e0831SAlan Somers 300a87e0831SAlan Somers free(control_buf); 301a87e0831SAlan Somers free(test_buf); 302a87e0831SAlan Somers } 303a87e0831SAlan Somers 30417575badSAlan Somers void do_mapwrite(ssize_t size, off_t offs) 30517575badSAlan Somers { 30617575badSAlan Somers char *buf; 30717575badSAlan Somers void *p; 30817575badSAlan Somers off_t pg_offset, page_mask; 30917575badSAlan Somers size_t map_size; 31017575badSAlan Somers long i; 31117575badSAlan Somers 31217575badSAlan Somers page_mask = getpagesize() - 1; 31317575badSAlan Somers pg_offset = offs & page_mask; 31417575badSAlan Somers map_size = pg_offset + size; 31517575badSAlan Somers 31617575badSAlan Somers buf = (char*)malloc(size); 31717575badSAlan Somers ASSERT_NE(NULL, buf) << strerror(errno); 31817575badSAlan Somers for (i=0; i < size; i++) 31917575badSAlan Somers buf[i] = random(); 32017575badSAlan Somers 32117575badSAlan Somers if (offs + size > m_filesize) { 32217575badSAlan Somers /* 32317575badSAlan Somers * Must manually extend. vm_mmap_vnode will not implicitly 32417575badSAlan Somers * extend a vnode 32517575badSAlan Somers */ 32617575badSAlan Somers do_ftruncate(offs + size); 32717575badSAlan Somers } 32817575badSAlan Somers 32917575badSAlan Somers p = mmap(NULL, map_size, PROT_READ | PROT_WRITE, 33017575badSAlan Somers MAP_FILE | MAP_SHARED, m_test_fd, offs - pg_offset); 33117575badSAlan Somers ASSERT_NE(p, MAP_FAILED) << strerror(errno); 33217575badSAlan Somers 33317575badSAlan Somers bcopy(buf, (char*)p + pg_offset, size); 33417575badSAlan Somers ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs)) 33517575badSAlan Somers << strerror(errno); 33617575badSAlan Somers 33717575badSAlan Somers free(buf); 33817575badSAlan Somers ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno); 33917575badSAlan Somers } 34017575badSAlan Somers 341a87e0831SAlan Somers void do_write(ssize_t size, off_t offs) 342a87e0831SAlan Somers { 343a87e0831SAlan Somers char *buf; 344a87e0831SAlan Somers long i; 345a87e0831SAlan Somers 346a87e0831SAlan Somers buf = (char*)malloc(size); 347a87e0831SAlan Somers ASSERT_NE(NULL, buf) << strerror(errno); 348a87e0831SAlan Somers for (i=0; i < size; i++) 349a87e0831SAlan Somers buf[i] = random(); 350a87e0831SAlan Somers 351a87e0831SAlan Somers ASSERT_EQ(size, pwrite(m_test_fd, buf, size, offs )) 352a87e0831SAlan Somers << strerror(errno); 353a87e0831SAlan Somers ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs)) 354a87e0831SAlan Somers << strerror(errno); 35517575badSAlan Somers m_filesize = std::max(m_filesize, offs + size); 35617575badSAlan Somers 35717575badSAlan Somers free(buf); 358a87e0831SAlan Somers } 359a87e0831SAlan Somers 360a87e0831SAlan Somers }; 361a87e0831SAlan Somers 36248417ae0SAlan Somers class IoCacheable: public Io { 36348417ae0SAlan Somers public: 36448417ae0SAlan Somers virtual void SetUp() { 36548417ae0SAlan Somers Io::SetUp(); 36648417ae0SAlan Somers } 36748417ae0SAlan Somers }; 36848417ae0SAlan Somers 369a87e0831SAlan Somers /* 370a87e0831SAlan Somers * Extend a file with dirty data in the last page of the last block. 371a87e0831SAlan Somers * 372a87e0831SAlan Somers * fsx -WR -P /tmp -S8 -N3 fsx.bin 373a87e0831SAlan Somers */ 3740482ec3eSAlan Somers TEST_P(Io, extend_from_dirty_page) 375a87e0831SAlan Somers { 376a87e0831SAlan Somers off_t wofs = 0x21a0; 377a87e0831SAlan Somers ssize_t wsize = 0xf0a8; 378a87e0831SAlan Somers off_t rofs = 0xb284; 379a87e0831SAlan Somers ssize_t rsize = 0x9b22; 380a87e0831SAlan Somers off_t truncsize = 0x28702; 381a87e0831SAlan Somers 382a87e0831SAlan Somers do_write(wsize, wofs); 383a87e0831SAlan Somers do_ftruncate(truncsize); 384a87e0831SAlan Somers do_read(rsize, rofs); 385a87e0831SAlan Somers } 386a87e0831SAlan Somers 387a87e0831SAlan Somers /* 38817575badSAlan Somers * mapwrite into a newly extended part of a file. 38917575badSAlan Somers * 39017575badSAlan Somers * fsx -c 100 -i 100 -l 524288 -o 131072 -N5 -P /tmp -S19 fsx.bin 39117575badSAlan Somers */ 39248417ae0SAlan Somers TEST_P(IoCacheable, extend_by_mapwrite) 39317575badSAlan Somers { 39417575badSAlan Somers do_mapwrite(0x849e, 0x29a3a); /* [0x29a3a, 0x31ed7] */ 39517575badSAlan Somers do_mapwrite(0x3994, 0x3c7d8); /* [0x3c7d8, 0x4016b] */ 39617575badSAlan Somers do_read(0xf556, 0x30c16); /* [0x30c16, 0x4016b] */ 39717575badSAlan Somers } 39817575badSAlan Somers 39917575badSAlan Somers /* 400a87e0831SAlan Somers * When writing the last page of a file, it must be written synchronously. 401a87e0831SAlan Somers * Otherwise the cached page can become invalid by a subsequent extend 402a87e0831SAlan Somers * operation. 403a87e0831SAlan Somers * 404a87e0831SAlan Somers * fsx -WR -P /tmp -S642 -N3 fsx.bin 405a87e0831SAlan Somers */ 4060482ec3eSAlan Somers TEST_P(Io, last_page) 407a87e0831SAlan Somers { 40817575badSAlan Somers do_write(0xcc77, 0x1134f); /* [0x1134f, 0x1dfc5] */ 40917575badSAlan Somers do_write(0xdfa7, 0x2096a); /* [0x2096a, 0x2e910] */ 41017575badSAlan Somers do_read(0xb5b7, 0x1a3aa); /* [0x1a3aa, 0x25960] */ 41117575badSAlan Somers } 412a87e0831SAlan Somers 41317575badSAlan Somers /* 41417575badSAlan Somers * Read a hole using mmap 41517575badSAlan Somers * 41617575badSAlan Somers * fsx -c 100 -i 100 -l 524288 -o 131072 -N11 -P /tmp -S14 fsx.bin 41717575badSAlan Somers */ 41848417ae0SAlan Somers TEST_P(IoCacheable, mapread_hole) 41917575badSAlan Somers { 42017575badSAlan Somers do_write(0x123b7, 0xf205); /* [0xf205, 0x215bb] */ 42117575badSAlan Somers do_mapread(0xeeea, 0x2f4c); /* [0x2f4c, 0x11e35] */ 422a87e0831SAlan Somers } 423a87e0831SAlan Somers 424a87e0831SAlan Somers /* 425a87e0831SAlan Somers * Read a hole from a block that contains some cached data. 426a87e0831SAlan Somers * 427a87e0831SAlan Somers * fsx -WR -P /tmp -S55 fsx.bin 428a87e0831SAlan Somers */ 4290482ec3eSAlan Somers TEST_P(Io, read_hole_from_cached_block) 430a87e0831SAlan Somers { 431a87e0831SAlan Somers off_t wofs = 0x160c5; 432a87e0831SAlan Somers ssize_t wsize = 0xa996; 433a87e0831SAlan Somers off_t rofs = 0x472e; 434a87e0831SAlan Somers ssize_t rsize = 0xd8d5; 435a87e0831SAlan Somers 436a87e0831SAlan Somers do_write(wsize, wofs); 437a87e0831SAlan Somers do_read(rsize, rofs); 438a87e0831SAlan Somers } 439a87e0831SAlan Somers 440a87e0831SAlan Somers /* 44193c0c1d4SAlan Somers * Truncating a file into a dirty buffer should not causing anything untoward 44293c0c1d4SAlan Somers * to happen when that buffer is eventually flushed. 443a87e0831SAlan Somers * 444a87e0831SAlan Somers * fsx -WR -P /tmp -S839 -d -N6 fsx.bin 445a87e0831SAlan Somers */ 4460482ec3eSAlan Somers TEST_P(Io, truncate_into_dirty_buffer) 447a87e0831SAlan Somers { 448a87e0831SAlan Somers off_t wofs0 = 0x3bad7; 449a87e0831SAlan Somers ssize_t wsize0 = 0x4529; 450a87e0831SAlan Somers off_t wofs1 = 0xc30d; 451a87e0831SAlan Somers ssize_t wsize1 = 0x5f77; 452a87e0831SAlan Somers off_t truncsize0 = 0x10916; 453a87e0831SAlan Somers off_t rofs = 0xdf17; 454a87e0831SAlan Somers ssize_t rsize = 0x29ff; 455a87e0831SAlan Somers off_t truncsize1 = 0x152b4; 456a87e0831SAlan Somers 457a87e0831SAlan Somers do_write(wsize0, wofs0); 458a87e0831SAlan Somers do_write(wsize1, wofs1); 459a87e0831SAlan Somers do_ftruncate(truncsize0); 460a87e0831SAlan Somers do_read(rsize, rofs); 461a87e0831SAlan Somers do_ftruncate(truncsize1); 462a87e0831SAlan Somers close(m_test_fd); 463a87e0831SAlan Somers } 46493c0c1d4SAlan Somers 46593c0c1d4SAlan Somers /* 46693c0c1d4SAlan Somers * Truncating a file into a dirty buffer should not causing anything untoward 46793c0c1d4SAlan Somers * to happen when that buffer is eventually flushed, even when the buffer's 46893c0c1d4SAlan Somers * dirty_off is > 0. 46993c0c1d4SAlan Somers * 47093c0c1d4SAlan Somers * Based on this command with a few steps removed: 47193c0c1d4SAlan Somers * fsx -WR -P /tmp -S677 -d -N8 fsx.bin 47293c0c1d4SAlan Somers */ 4730482ec3eSAlan Somers TEST_P(Io, truncate_into_dirty_buffer2) 47493c0c1d4SAlan Somers { 47593c0c1d4SAlan Somers off_t truncsize0 = 0x344f3; 47693c0c1d4SAlan Somers off_t wofs = 0x2790c; 47793c0c1d4SAlan Somers ssize_t wsize = 0xd86a; 47893c0c1d4SAlan Somers off_t truncsize1 = 0x2de38; 47993c0c1d4SAlan Somers off_t rofs2 = 0x1fd7a; 48093c0c1d4SAlan Somers ssize_t rsize2 = 0xc594; 48193c0c1d4SAlan Somers off_t truncsize2 = 0x31e71; 48293c0c1d4SAlan Somers 48393c0c1d4SAlan Somers /* Sets the file size to something larger than the next write */ 48493c0c1d4SAlan Somers do_ftruncate(truncsize0); 48593c0c1d4SAlan Somers /* 48693c0c1d4SAlan Somers * Creates a dirty buffer. The part in lbn 2 doesn't flush 48793c0c1d4SAlan Somers * synchronously. 48893c0c1d4SAlan Somers */ 48993c0c1d4SAlan Somers do_write(wsize, wofs); 49093c0c1d4SAlan Somers /* Truncates part of the dirty buffer created in step 2 */ 49193c0c1d4SAlan Somers do_ftruncate(truncsize1); 49293c0c1d4SAlan Somers /* XXX ?I don't know why this is necessary? */ 49393c0c1d4SAlan Somers do_read(rsize2, rofs2); 49493c0c1d4SAlan Somers /* Truncates the dirty buffer */ 49593c0c1d4SAlan Somers do_ftruncate(truncsize2); 49693c0c1d4SAlan Somers close(m_test_fd); 49793c0c1d4SAlan Somers } 498dff3a6b4SAlan Somers 499dff3a6b4SAlan Somers /* 500dff3a6b4SAlan Somers * Regression test for a bug introduced in r348931 501dff3a6b4SAlan Somers * 502dff3a6b4SAlan Somers * Sequence of operations: 503dff3a6b4SAlan Somers * 1) The first write reads lbn so it can modify it 504dff3a6b4SAlan Somers * 2) The first write flushes lbn 3 immediately because it's the end of file 505dff3a6b4SAlan Somers * 3) The first write then flushes lbn 4 because it's the end of the file 506dff3a6b4SAlan Somers * 4) The second write modifies the cached versions of lbn 3 and 4 507dff3a6b4SAlan Somers * 5) The third write's getblkx invalidates lbn 4's B_CACHE because it's 508dff3a6b4SAlan Somers * extending the buffer. Then it flushes lbn 4 because B_DELWRI was set but 509dff3a6b4SAlan Somers * B_CACHE was clear. 510dff3a6b4SAlan Somers * 6) fuse_write_biobackend erroneously called vfs_bio_clrbuf, putting the 511dff3a6b4SAlan Somers * buffer into a weird write-only state. All read operations would return 512dff3a6b4SAlan Somers * 0. Writes were apparently still processed, because the buffer's contents 513dff3a6b4SAlan Somers * were correct when examined in a core dump. 514dff3a6b4SAlan Somers * 7) The third write reads lbn 4 because cache is clear 515dff3a6b4SAlan Somers * 9) uiomove dutifully copies new data into the buffer 516dff3a6b4SAlan Somers * 10) The buffer's dirty is flushed to lbn 4 517dff3a6b4SAlan Somers * 11) The read returns all zeros because of step 6. 518dff3a6b4SAlan Somers * 519dff3a6b4SAlan Somers * Based on: 520dff3a6b4SAlan Somers * fsx -WR -l 524388 -o 131072 -P /tmp -S6456 -q fsx.bin 521dff3a6b4SAlan Somers */ 5220482ec3eSAlan Somers TEST_P(Io, resize_a_valid_buffer_while_extending) 523dff3a6b4SAlan Somers { 524dff3a6b4SAlan Somers do_write(0x14530, 0x36ee6); /* [0x36ee6, 0x4b415] */ 525dff3a6b4SAlan Somers do_write(0x1507c, 0x33256); /* [0x33256, 0x482d1] */ 526dff3a6b4SAlan Somers do_write(0x175c, 0x4c03d); /* [0x4c03d, 0x4d798] */ 527dff3a6b4SAlan Somers do_read(0xe277, 0x3599c); /* [0x3599c, 0x43c12] */ 528dff3a6b4SAlan Somers close(m_test_fd); 529dff3a6b4SAlan Somers } 5300482ec3eSAlan Somers 5310482ec3eSAlan Somers INSTANTIATE_TEST_CASE_P(Io, Io, 532f8ebf1cdSAlan Somers Combine(Bool(), /* async read */ 5330482ec3eSAlan Somers Values(0x1000, 0x10000, 0x20000), /* m_maxwrite */ 534*c51f519bSAlan Somers Values(Uncached, Writethrough, Writeback, WritebackAsync) 535*c51f519bSAlan Somers ) 536*c51f519bSAlan Somers ); 53748417ae0SAlan Somers 53848417ae0SAlan Somers INSTANTIATE_TEST_CASE_P(Io, IoCacheable, 539f8ebf1cdSAlan Somers Combine(Bool(), /* async read */ 54048417ae0SAlan Somers Values(0x1000, 0x10000, 0x20000), /* m_maxwrite */ 541*c51f519bSAlan Somers Values(Writethrough, Writeback, WritebackAsync) 542*c51f519bSAlan Somers ) 543*c51f519bSAlan Somers ); 544