xref: /freebsd/tests/sys/fs/fusefs/io.cc (revision 0077477f215c851fe15c9ea12cfb005125c4238a)
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 
41*0077477fSEnji Cooper #include <iomanip>
42*0077477fSEnji Cooper 
43a87e0831SAlan Somers #include "mockfs.hh"
44a87e0831SAlan Somers #include "utils.hh"
45a87e0831SAlan Somers 
46a87e0831SAlan Somers /*
47a87e0831SAlan Somers  * For testing I/O like fsx does, but deterministically and without a real
48a87e0831SAlan Somers  * underlying file system
49a87e0831SAlan Somers  */
50a87e0831SAlan Somers 
51a87e0831SAlan Somers using namespace testing;
52a87e0831SAlan Somers 
53a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
54a87e0831SAlan Somers const char RELPATH[] = "some_file.txt";
55a87e0831SAlan Somers const uint64_t ino = 42;
56a87e0831SAlan Somers 
57dff3a6b4SAlan Somers static void compare(const void *tbuf, const void *controlbuf, off_t baseofs,
58dff3a6b4SAlan Somers 	ssize_t size)
59dff3a6b4SAlan Somers {
60dff3a6b4SAlan Somers 	int i;
61dff3a6b4SAlan Somers 
62dff3a6b4SAlan Somers 	for (i = 0; i < size; i++) {
63dff3a6b4SAlan Somers 		if (((const char*)tbuf)[i] != ((const char*)controlbuf)[i]) {
64dff3a6b4SAlan Somers 			off_t ofs = baseofs + i;
65dff3a6b4SAlan Somers 			FAIL() << "miscompare at offset "
66dff3a6b4SAlan Somers 			       << std::hex
67dff3a6b4SAlan Somers 			       << std::showbase
68dff3a6b4SAlan Somers 			       << ofs
69dff3a6b4SAlan Somers 			       << ".  expected = "
70dff3a6b4SAlan Somers 			       << std::setw(2)
71dff3a6b4SAlan Somers 			       << (unsigned)((const uint8_t*)controlbuf)[i]
72dff3a6b4SAlan Somers 			       << " got = "
73dff3a6b4SAlan Somers 			       << (unsigned)((const uint8_t*)tbuf)[i];
74dff3a6b4SAlan Somers 		}
75dff3a6b4SAlan Somers 	}
76dff3a6b4SAlan Somers }
77dff3a6b4SAlan Somers 
781c909c30SAlan Somers typedef tuple<bool, uint32_t, cache_mode, uint32_t> IoParam;
79c51f519bSAlan Somers 
80c51f519bSAlan Somers class Io: public FuseTest, public WithParamInterface<IoParam> {
81a87e0831SAlan Somers public:
82a87e0831SAlan Somers int m_backing_fd, m_control_fd, m_test_fd;
8317575badSAlan Somers off_t m_filesize;
84c51f519bSAlan Somers bool m_direct_io;
85a87e0831SAlan Somers 
869c9634d1SAlan Somers Io(): m_backing_fd(-1), m_control_fd(-1), m_test_fd(-1), m_filesize(0),
879c9634d1SAlan Somers 	m_direct_io(false) {};
88a87e0831SAlan Somers 
89a87e0831SAlan Somers void SetUp()
90a87e0831SAlan Somers {
9193c0c1d4SAlan Somers 	m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC, 0644);
92a87e0831SAlan Somers 	if (m_backing_fd < 0)
93a87e0831SAlan Somers 		FAIL() << strerror(errno);
9493c0c1d4SAlan Somers 	m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC, 0644);
95a87e0831SAlan Somers 	if (m_control_fd < 0)
96a87e0831SAlan Somers 		FAIL() << strerror(errno);
97a87e0831SAlan Somers 	srandom(22'9'1982);	// Seed with my birthday
980482ec3eSAlan Somers 
99f8ebf1cdSAlan Somers 	if (get<0>(GetParam()))
100f8ebf1cdSAlan Somers 		m_init_flags |= FUSE_ASYNC_READ;
1010482ec3eSAlan Somers 	m_maxwrite = get<1>(GetParam());
102c51f519bSAlan Somers 	switch (get<2>(GetParam())) {
103c51f519bSAlan Somers 		case Uncached:
104c51f519bSAlan Somers 			m_direct_io = true;
105c51f519bSAlan Somers 			break;
106c51f519bSAlan Somers 		case WritebackAsync:
107c51f519bSAlan Somers 			m_async = true;
108c51f519bSAlan Somers 			/* FALLTHROUGH */
109c51f519bSAlan Somers 		case Writeback:
110f8ebf1cdSAlan Somers 			m_init_flags |= FUSE_WRITEBACK_CACHE;
111c51f519bSAlan Somers 			/* FALLTHROUGH */
112c51f519bSAlan Somers 		case Writethrough:
113c51f519bSAlan Somers 			break;
114c51f519bSAlan Somers 		default:
115c51f519bSAlan Somers 			FAIL() << "Unknown cache mode";
116c51f519bSAlan Somers 	}
1171c909c30SAlan Somers 	m_kernel_minor_version = get<3>(GetParam());
11891972cfcSAlan Somers 	m_noatime = true;	// To prevent SETATTR for atime on close
1190482ec3eSAlan Somers 
120a87e0831SAlan Somers 	FuseTest::SetUp();
121a87e0831SAlan Somers 	if (IsSkipped())
122a87e0831SAlan Somers 		return;
123a87e0831SAlan Somers 
124c51f519bSAlan Somers 	if (verbosity > 0) {
125c51f519bSAlan Somers 		printf("Test Parameters: init_flags=%#x maxwrite=%#x "
1261c909c30SAlan Somers 		    "%sasync cache=%s kernel_minor_version=%d\n",
127c51f519bSAlan Somers 		    m_init_flags, m_maxwrite, m_async? "" : "no",
1281c909c30SAlan Somers 		    cache_mode_to_s(get<2>(GetParam())),
1291c909c30SAlan Somers 		    m_kernel_minor_version);
130c51f519bSAlan Somers 	}
131c51f519bSAlan Somers 
132a87e0831SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
133c51f519bSAlan Somers 	expect_open(ino, m_direct_io ? FOPEN_DIRECT_IO : 0, 1);
134a87e0831SAlan Somers 	EXPECT_CALL(*m_mock, process(
135a87e0831SAlan Somers 		ResultOf([=](auto in) {
136a87e0831SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
137a87e0831SAlan Somers 				in.header.nodeid == ino);
138a87e0831SAlan Somers 		}, Eq(true)),
139a87e0831SAlan Somers 		_)
140a87e0831SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
141a87e0831SAlan Somers 		const char *buf = (const char*)in.body.bytes +
142a87e0831SAlan Somers 			sizeof(struct fuse_write_in);
143a87e0831SAlan Somers 		ssize_t isize = in.body.write.size;
144a87e0831SAlan Somers 		off_t iofs = in.body.write.offset;
145a87e0831SAlan Somers 
1460c9df4afSAlan Somers 		assert((size_t)isize <= sizeof(in.body.bytes) -
1470c9df4afSAlan Somers 			sizeof(struct fuse_write_in));
148a87e0831SAlan Somers 		ASSERT_EQ(isize, pwrite(m_backing_fd, buf, isize, iofs))
149a87e0831SAlan Somers 			<< strerror(errno);
150a87e0831SAlan Somers 		SET_OUT_HEADER_LEN(out, write);
151a87e0831SAlan Somers 		out.body.write.size = isize;
152a87e0831SAlan Somers 	})));
153a87e0831SAlan Somers 	EXPECT_CALL(*m_mock, process(
154a87e0831SAlan Somers 		ResultOf([=](auto in) {
155a87e0831SAlan Somers 			return (in.header.opcode == FUSE_READ &&
156a87e0831SAlan Somers 				in.header.nodeid == ino);
157a87e0831SAlan Somers 		}, Eq(true)),
158a87e0831SAlan Somers 		_)
159a87e0831SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
160a87e0831SAlan Somers 		ssize_t isize = in.body.write.size;
161a87e0831SAlan Somers 		off_t iofs = in.body.write.offset;
162a87e0831SAlan Somers 		void *buf = out.body.bytes;
16317575badSAlan Somers 		ssize_t osize;
164a87e0831SAlan Somers 
1650c9df4afSAlan Somers 		assert((size_t)isize <= sizeof(out.body.bytes));
16617575badSAlan Somers 		osize = pread(m_backing_fd, buf, isize, iofs);
16717575badSAlan Somers 		ASSERT_LE(0, osize) << strerror(errno);
16817575badSAlan Somers 		out.header.len = sizeof(struct fuse_out_header) + osize;
169a87e0831SAlan Somers 	})));
170a87e0831SAlan Somers 	EXPECT_CALL(*m_mock, process(
171a87e0831SAlan Somers 		ResultOf([=](auto in) {
172a87e0831SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
173a87e0831SAlan Somers 				in.header.nodeid == ino &&
174788af953SAlan Somers 				(in.body.setattr.valid & FATTR_SIZE));
175788af953SAlan Somers 
176a87e0831SAlan Somers 		}, Eq(true)),
177a87e0831SAlan Somers 		_)
178a87e0831SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
179a87e0831SAlan Somers 		ASSERT_EQ(0, ftruncate(m_backing_fd, in.body.setattr.size))
180a87e0831SAlan Somers 			<< strerror(errno);
181a87e0831SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
182a87e0831SAlan Somers 		out.body.attr.attr.ino = ino;
183a87e0831SAlan Somers 		out.body.attr.attr.mode = S_IFREG | 0755;
184a87e0831SAlan Somers 		out.body.attr.attr.size = in.body.setattr.size;
185a87e0831SAlan Somers 		out.body.attr.attr_valid = UINT64_MAX;
186a87e0831SAlan Somers 	})));
187a87e0831SAlan Somers 	/* Any test that close()s will send FUSE_FLUSH and FUSE_RELEASE */
188a87e0831SAlan Somers 	EXPECT_CALL(*m_mock, process(
189a87e0831SAlan Somers 		ResultOf([=](auto in) {
190a87e0831SAlan Somers 			return (in.header.opcode == FUSE_FLUSH &&
191a87e0831SAlan Somers 				in.header.nodeid == ino);
192a87e0831SAlan Somers 		}, Eq(true)),
193a87e0831SAlan Somers 		_)
194a87e0831SAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(0)));
195a87e0831SAlan Somers 	EXPECT_CALL(*m_mock, process(
196a87e0831SAlan Somers 		ResultOf([=](auto in) {
197a87e0831SAlan Somers 			return (in.header.opcode == FUSE_RELEASE &&
198a87e0831SAlan Somers 				in.header.nodeid == ino);
199a87e0831SAlan Somers 		}, Eq(true)),
200a87e0831SAlan Somers 		_)
201a87e0831SAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(0)));
2021c909c30SAlan Somers 	EXPECT_CALL(*m_mock, process(
2031c909c30SAlan Somers 		ResultOf([=](auto in) {
2041c909c30SAlan Somers 			return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
2051c909c30SAlan Somers 				in.header.nodeid == ino &&
2061c909c30SAlan Somers 				in.body.copy_file_range.nodeid_out == ino &&
2071c909c30SAlan Somers 				in.body.copy_file_range.flags == 0);
2081c909c30SAlan Somers 		}, Eq(true)),
2091c909c30SAlan Somers 		_)
2101c909c30SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
2111c909c30SAlan Somers 		off_t off_in = in.body.copy_file_range.off_in;
2121c909c30SAlan Somers 		off_t off_out = in.body.copy_file_range.off_out;
2131c909c30SAlan Somers 		ASSERT_EQ((ssize_t)in.body.copy_file_range.len,
2141c909c30SAlan Somers 		    copy_file_range(m_backing_fd, &off_in, m_backing_fd,
2151c909c30SAlan Somers 			    &off_out, in.body.copy_file_range.len, 0));
2161c909c30SAlan Somers 		SET_OUT_HEADER_LEN(out, write);
2171c909c30SAlan Somers 		out.body.write.size = in.body.copy_file_range.len;
2181c909c30SAlan Somers 	})));
2191c909c30SAlan Somers 	/* Claim that we don't support FUSE_LSEEK */
2201c909c30SAlan Somers 	EXPECT_CALL(*m_mock, process(
2211c909c30SAlan Somers 		ResultOf([=](auto in) {
2221c909c30SAlan Somers 			return (in.header.opcode == FUSE_LSEEK);
2231c909c30SAlan Somers 		}, Eq(true)),
2241c909c30SAlan Somers 		_)
2251c909c30SAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
226a87e0831SAlan Somers 
227a87e0831SAlan Somers 	m_test_fd = open(FULLPATH, O_RDWR );
228a87e0831SAlan Somers 	EXPECT_LE(0, m_test_fd) << strerror(errno);
229a87e0831SAlan Somers }
230a87e0831SAlan Somers 
231a87e0831SAlan Somers void TearDown()
232a87e0831SAlan Somers {
23348417ae0SAlan Somers 	if (m_test_fd >= 0)
23448417ae0SAlan Somers 		close(m_test_fd);
235a87e0831SAlan Somers 	if (m_backing_fd >= 0)
236a87e0831SAlan Somers 		close(m_backing_fd);
237a87e0831SAlan Somers 	if (m_control_fd >= 0)
238a87e0831SAlan Somers 		close(m_control_fd);
239a87e0831SAlan Somers 	FuseTest::TearDown();
2407fc0921dSAlan Somers 	leak(m_test_fd);
241a87e0831SAlan Somers }
242a87e0831SAlan Somers 
24348417ae0SAlan Somers void do_closeopen()
24448417ae0SAlan Somers {
24548417ae0SAlan Somers 	ASSERT_EQ(0, close(m_test_fd)) << strerror(errno);
24648417ae0SAlan Somers 	m_test_fd = open("backing_file", O_RDWR);
24748417ae0SAlan Somers 	ASSERT_LE(0, m_test_fd) << strerror(errno);
24848417ae0SAlan Somers 
24948417ae0SAlan Somers 	ASSERT_EQ(0, close(m_control_fd)) << strerror(errno);
25048417ae0SAlan Somers 	m_control_fd = open("control", O_RDWR);
25148417ae0SAlan Somers 	ASSERT_LE(0, m_control_fd) << strerror(errno);
25248417ae0SAlan Somers }
25348417ae0SAlan Somers 
2541c909c30SAlan Somers void do_copy_file_range(off_t off_in, off_t off_out, size_t size)
2551c909c30SAlan Somers {
2561c909c30SAlan Somers 	ssize_t r;
2571c909c30SAlan Somers 	off_t test_off_in = off_in;
2581c909c30SAlan Somers 	off_t test_off_out = off_out;
2591c909c30SAlan Somers 	off_t test_size = size;
2601c909c30SAlan Somers 	off_t control_off_in = off_in;
2611c909c30SAlan Somers 	off_t control_off_out = off_out;
2621c909c30SAlan Somers 	off_t control_size = size;
2631c909c30SAlan Somers 
2641c909c30SAlan Somers 	while (test_size > 0) {
2651c909c30SAlan Somers 		r = copy_file_range(m_test_fd, &test_off_in, m_test_fd,
2661c909c30SAlan Somers 				&test_off_out, test_size, 0);
2671c909c30SAlan Somers 		ASSERT_GT(r, 0) << strerror(errno);
2681c909c30SAlan Somers 		test_size -= r;
2691c909c30SAlan Somers 	}
2701c909c30SAlan Somers 	while (control_size > 0) {
2711c909c30SAlan Somers 		r = copy_file_range(m_control_fd, &control_off_in, m_control_fd,
2721c909c30SAlan Somers 				&control_off_out, control_size, 0);
2731c909c30SAlan Somers 		ASSERT_GT(r, 0) << strerror(errno);
2741c909c30SAlan Somers 		control_size -= r;
2751c909c30SAlan Somers 	}
2761c909c30SAlan Somers 	m_filesize = std::max(m_filesize, off_out + (off_t)size);
2771c909c30SAlan Somers }
2781c909c30SAlan Somers 
279a87e0831SAlan Somers void do_ftruncate(off_t offs)
280a87e0831SAlan Somers {
281a87e0831SAlan Somers 	ASSERT_EQ(0, ftruncate(m_test_fd, offs)) << strerror(errno);
282a87e0831SAlan Somers 	ASSERT_EQ(0, ftruncate(m_control_fd, offs)) << strerror(errno);
28317575badSAlan Somers 	m_filesize = offs;
28417575badSAlan Somers }
28517575badSAlan Somers 
286daf26f93SAlan Somers void do_mapread(off_t offs, ssize_t size)
28717575badSAlan Somers {
2888bae22bbSAlan Somers 	char *control_buf;
2898bae22bbSAlan Somers 	void *p;
29017575badSAlan Somers 	off_t pg_offset, page_mask;
29117575badSAlan Somers 	size_t map_size;
29217575badSAlan Somers 
29317575badSAlan Somers 	page_mask = getpagesize() - 1;
29417575badSAlan Somers 	pg_offset = offs & page_mask;
29517575badSAlan Somers 	map_size = pg_offset + size;
29617575badSAlan Somers 
29717575badSAlan Somers 	p = mmap(NULL, map_size, PROT_READ, MAP_FILE | MAP_SHARED, m_test_fd,
29817575badSAlan Somers 	    offs - pg_offset);
29917575badSAlan Somers 	ASSERT_NE(p, MAP_FAILED) << strerror(errno);
30017575badSAlan Somers 
3018bae22bbSAlan Somers 	control_buf = new char[size];
30217575badSAlan Somers 
30317575badSAlan Somers 	ASSERT_EQ(size, pread(m_control_fd, control_buf, size, offs))
30417575badSAlan Somers 		<< strerror(errno);
30517575badSAlan Somers 
30617575badSAlan Somers 	compare((void*)((char*)p + pg_offset), control_buf, offs, size);
30717575badSAlan Somers 
30817575badSAlan Somers 	ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno);
3098bae22bbSAlan Somers 	delete[] control_buf;
310a87e0831SAlan Somers }
311a87e0831SAlan Somers 
312daf26f93SAlan Somers void do_read(off_t offs, ssize_t size)
313a87e0831SAlan Somers {
3148bae22bbSAlan Somers 	char *test_buf, *control_buf;
31517575badSAlan Somers 	ssize_t r;
316a87e0831SAlan Somers 
3178bae22bbSAlan Somers 	test_buf = new char[size];
3188bae22bbSAlan Somers 	control_buf = new char[size];
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 
3308bae22bbSAlan Somers 	delete[] control_buf;
3318bae22bbSAlan Somers 	delete[] test_buf;
332a87e0831SAlan Somers }
333a87e0831SAlan Somers 
334daf26f93SAlan 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 
3468bae22bbSAlan Somers 	buf = new char[size];
34717575badSAlan Somers 	for (i=0; i < size; i++)
34817575badSAlan Somers 		buf[i] = random();
34917575badSAlan Somers 
35017575badSAlan Somers 	if (offs + size > m_filesize) {
35117575badSAlan Somers 		/*
35217575badSAlan Somers 		 * Must manually extend.  vm_mmap_vnode will not implicitly
35317575badSAlan Somers 		 * extend a vnode
35417575badSAlan Somers 		 */
35517575badSAlan Somers 		do_ftruncate(offs + size);
35617575badSAlan Somers 	}
35717575badSAlan Somers 
35817575badSAlan Somers 	p = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
35917575badSAlan Somers 	    MAP_FILE | MAP_SHARED, m_test_fd, offs - pg_offset);
36017575badSAlan Somers 	ASSERT_NE(p, MAP_FAILED) << strerror(errno);
36117575badSAlan Somers 
36217575badSAlan Somers 	bcopy(buf, (char*)p + pg_offset, size);
36317575badSAlan Somers 	ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs))
36417575badSAlan Somers 		<< strerror(errno);
36517575badSAlan Somers 
3668bae22bbSAlan Somers 	delete[] buf;
36717575badSAlan Somers 	ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno);
36817575badSAlan Somers }
36917575badSAlan Somers 
370daf26f93SAlan Somers void do_write(off_t offs, ssize_t size)
371a87e0831SAlan Somers {
372a87e0831SAlan Somers 	char *buf;
373a87e0831SAlan Somers 	long i;
374a87e0831SAlan Somers 
3758bae22bbSAlan Somers 	buf = new char[size];
376a87e0831SAlan Somers 	for (i=0; i < size; i++)
377a87e0831SAlan Somers 		buf[i] = random();
378a87e0831SAlan Somers 
379a87e0831SAlan Somers 	ASSERT_EQ(size, pwrite(m_test_fd, buf, size, offs ))
380a87e0831SAlan Somers 		<< strerror(errno);
381a87e0831SAlan Somers 	ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs))
382a87e0831SAlan Somers 		<< strerror(errno);
38317575badSAlan Somers 	m_filesize = std::max(m_filesize, offs + size);
38417575badSAlan Somers 
3858bae22bbSAlan Somers 	delete[] buf;
386a87e0831SAlan Somers }
387a87e0831SAlan Somers 
388a87e0831SAlan Somers };
389a87e0831SAlan Somers 
39048417ae0SAlan Somers class IoCacheable: public Io {
39148417ae0SAlan Somers public:
39248417ae0SAlan Somers virtual void SetUp() {
39348417ae0SAlan Somers 	Io::SetUp();
39448417ae0SAlan Somers }
39548417ae0SAlan Somers };
39648417ae0SAlan Somers 
3971c909c30SAlan Somers class IoCopyFileRange: public Io {
3981c909c30SAlan Somers public:
3991c909c30SAlan Somers virtual void SetUp() {
4001c909c30SAlan Somers 	Io::SetUp();
4011c909c30SAlan Somers }
4021c909c30SAlan Somers };
4031c909c30SAlan Somers 
404a87e0831SAlan Somers /*
405a87e0831SAlan Somers  * Extend a file with dirty data in the last page of the last block.
406a87e0831SAlan Somers  *
407a87e0831SAlan Somers  * fsx -WR -P /tmp -S8 -N3 fsx.bin
408a87e0831SAlan Somers  */
4090482ec3eSAlan Somers TEST_P(Io, extend_from_dirty_page)
410a87e0831SAlan Somers {
411a87e0831SAlan Somers 	off_t wofs = 0x21a0;
412a87e0831SAlan Somers 	ssize_t wsize = 0xf0a8;
413a87e0831SAlan Somers 	off_t rofs = 0xb284;
414a87e0831SAlan Somers 	ssize_t rsize = 0x9b22;
415a87e0831SAlan Somers 	off_t truncsize = 0x28702;
416a87e0831SAlan Somers 
417daf26f93SAlan Somers 	do_write(wofs, wsize);
418a87e0831SAlan Somers 	do_ftruncate(truncsize);
419daf26f93SAlan Somers 	do_read(rofs, rsize);
420a87e0831SAlan Somers }
421a87e0831SAlan Somers 
422a87e0831SAlan Somers /*
42317575badSAlan Somers  * mapwrite into a newly extended part of a file.
42417575badSAlan Somers  *
42517575badSAlan Somers  * fsx -c 100 -i 100 -l 524288 -o 131072 -N5 -P /tmp -S19 fsx.bin
42617575badSAlan Somers  */
42748417ae0SAlan Somers TEST_P(IoCacheable, extend_by_mapwrite)
42817575badSAlan Somers {
429daf26f93SAlan Somers 	do_mapwrite(0x29a3a, 0x849e);	/* [0x29a3a, 0x31ed7] */
430daf26f93SAlan Somers 	do_mapwrite(0x3c7d8, 0x3994);	/* [0x3c7d8, 0x4016b] */
431daf26f93SAlan Somers 	do_read(0x30c16, 0xf556);	/* [0x30c16, 0x4016b] */
43217575badSAlan Somers }
43317575badSAlan Somers 
43417575badSAlan Somers /*
435a87e0831SAlan Somers  * When writing the last page of a file, it must be written synchronously.
436a87e0831SAlan Somers  * Otherwise the cached page can become invalid by a subsequent extend
437a87e0831SAlan Somers  * operation.
438a87e0831SAlan Somers  *
439a87e0831SAlan Somers  * fsx -WR -P /tmp -S642 -N3 fsx.bin
440a87e0831SAlan Somers  */
4410482ec3eSAlan Somers TEST_P(Io, last_page)
442a87e0831SAlan Somers {
443daf26f93SAlan Somers 	do_write(0x1134f, 0xcc77);	/* [0x1134f, 0x1dfc5] */
444daf26f93SAlan Somers 	do_write(0x2096a, 0xdfa7);	/* [0x2096a, 0x2e910] */
445daf26f93SAlan Somers 	do_read(0x1a3aa, 0xb5b7);	/* [0x1a3aa, 0x25960] */
44617575badSAlan Somers }
447a87e0831SAlan Somers 
44817575badSAlan Somers /*
44917575badSAlan Somers  * Read a hole using mmap
45017575badSAlan Somers  *
45117575badSAlan Somers  * fsx -c 100 -i 100 -l 524288 -o 131072 -N11 -P /tmp  -S14 fsx.bin
45217575badSAlan Somers  */
45348417ae0SAlan Somers TEST_P(IoCacheable, mapread_hole)
45417575badSAlan Somers {
455daf26f93SAlan Somers 	do_write(0xf205, 0x123b7);	/* [0xf205, 0x215bb] */
456daf26f93SAlan Somers 	do_mapread(0x2f4c, 0xeeea);	/* [0x2f4c, 0x11e35] */
457a87e0831SAlan Somers }
458a87e0831SAlan Somers 
459a87e0831SAlan Somers /*
460a87e0831SAlan Somers  * Read a hole from a block that contains some cached data.
461a87e0831SAlan Somers  *
462a87e0831SAlan Somers  * fsx -WR -P /tmp -S55  fsx.bin
463a87e0831SAlan Somers  */
4640482ec3eSAlan Somers TEST_P(Io, read_hole_from_cached_block)
465a87e0831SAlan Somers {
466a87e0831SAlan Somers 	off_t wofs = 0x160c5;
467a87e0831SAlan Somers 	ssize_t wsize = 0xa996;
468a87e0831SAlan Somers 	off_t rofs = 0x472e;
469a87e0831SAlan Somers 	ssize_t rsize = 0xd8d5;
470a87e0831SAlan Somers 
471daf26f93SAlan Somers 	do_write(wofs, wsize);
472daf26f93SAlan Somers 	do_read(rofs, rsize);
473a87e0831SAlan Somers }
474a87e0831SAlan Somers 
475a87e0831SAlan Somers /*
47693c0c1d4SAlan Somers  * Truncating a file into a dirty buffer should not causing anything untoward
47793c0c1d4SAlan Somers  * to happen when that buffer is eventually flushed.
478a87e0831SAlan Somers  *
479a87e0831SAlan Somers  * fsx -WR -P /tmp -S839 -d -N6 fsx.bin
480a87e0831SAlan Somers  */
4810482ec3eSAlan Somers TEST_P(Io, truncate_into_dirty_buffer)
482a87e0831SAlan Somers {
483a87e0831SAlan Somers 	off_t wofs0 = 0x3bad7;
484a87e0831SAlan Somers 	ssize_t wsize0 = 0x4529;
485a87e0831SAlan Somers 	off_t wofs1 = 0xc30d;
486a87e0831SAlan Somers 	ssize_t wsize1 = 0x5f77;
487a87e0831SAlan Somers 	off_t truncsize0 = 0x10916;
488a87e0831SAlan Somers 	off_t rofs = 0xdf17;
489a87e0831SAlan Somers 	ssize_t rsize = 0x29ff;
490a87e0831SAlan Somers 	off_t truncsize1 = 0x152b4;
491a87e0831SAlan Somers 
492daf26f93SAlan Somers 	do_write(wofs0, wsize0);
493daf26f93SAlan Somers 	do_write(wofs1, wsize1);
494a87e0831SAlan Somers 	do_ftruncate(truncsize0);
495daf26f93SAlan Somers 	do_read(rofs, rsize);
496a87e0831SAlan Somers 	do_ftruncate(truncsize1);
497a87e0831SAlan Somers 	close(m_test_fd);
498a87e0831SAlan Somers }
49993c0c1d4SAlan Somers 
50093c0c1d4SAlan Somers /*
50193c0c1d4SAlan Somers  * Truncating a file into a dirty buffer should not causing anything untoward
50293c0c1d4SAlan Somers  * to happen when that buffer is eventually flushed, even when the buffer's
50393c0c1d4SAlan Somers  * dirty_off is > 0.
50493c0c1d4SAlan Somers  *
50593c0c1d4SAlan Somers  * Based on this command with a few steps removed:
50693c0c1d4SAlan Somers  * fsx -WR -P /tmp -S677 -d -N8 fsx.bin
50793c0c1d4SAlan Somers  */
5080482ec3eSAlan Somers TEST_P(Io, truncate_into_dirty_buffer2)
50993c0c1d4SAlan Somers {
51093c0c1d4SAlan Somers 	off_t truncsize0 = 0x344f3;
51193c0c1d4SAlan Somers 	off_t wofs = 0x2790c;
51293c0c1d4SAlan Somers 	ssize_t wsize = 0xd86a;
51393c0c1d4SAlan Somers 	off_t truncsize1 = 0x2de38;
51493c0c1d4SAlan Somers 	off_t rofs2 = 0x1fd7a;
51593c0c1d4SAlan Somers 	ssize_t rsize2 = 0xc594;
51693c0c1d4SAlan Somers 	off_t truncsize2 = 0x31e71;
51793c0c1d4SAlan Somers 
51893c0c1d4SAlan Somers 	/* Sets the file size to something larger than the next write */
51993c0c1d4SAlan Somers 	do_ftruncate(truncsize0);
52093c0c1d4SAlan Somers 	/*
52193c0c1d4SAlan Somers 	 * Creates a dirty buffer.  The part in lbn 2 doesn't flush
52293c0c1d4SAlan Somers 	 * synchronously.
52393c0c1d4SAlan Somers 	 */
524daf26f93SAlan Somers 	do_write(wofs, wsize);
52593c0c1d4SAlan Somers 	/* Truncates part of the dirty buffer created in step 2 */
52693c0c1d4SAlan Somers 	do_ftruncate(truncsize1);
52793c0c1d4SAlan Somers 	/* XXX ?I don't know why this is necessary? */
528daf26f93SAlan Somers 	do_read(rofs2, rsize2);
52993c0c1d4SAlan Somers 	/* Truncates the dirty buffer */
53093c0c1d4SAlan Somers 	do_ftruncate(truncsize2);
53193c0c1d4SAlan Somers 	close(m_test_fd);
53293c0c1d4SAlan Somers }
533dff3a6b4SAlan Somers 
534dff3a6b4SAlan Somers /*
535dff3a6b4SAlan Somers  * Regression test for a bug introduced in r348931
536dff3a6b4SAlan Somers  *
537dff3a6b4SAlan Somers  * Sequence of operations:
538dff3a6b4SAlan Somers  * 1) The first write reads lbn so it can modify it
539dff3a6b4SAlan Somers  * 2) The first write flushes lbn 3 immediately because it's the end of file
540dff3a6b4SAlan Somers  * 3) The first write then flushes lbn 4 because it's the end of the file
541dff3a6b4SAlan Somers  * 4) The second write modifies the cached versions of lbn 3 and 4
542dff3a6b4SAlan Somers  * 5) The third write's getblkx invalidates lbn 4's B_CACHE because it's
543dff3a6b4SAlan Somers  *    extending the buffer.  Then it flushes lbn 4 because B_DELWRI was set but
544dff3a6b4SAlan Somers  *    B_CACHE was clear.
545dff3a6b4SAlan Somers  * 6) fuse_write_biobackend erroneously called vfs_bio_clrbuf, putting the
546dff3a6b4SAlan Somers  *    buffer into a weird write-only state.  All read operations would return
547dff3a6b4SAlan Somers  *    0.  Writes were apparently still processed, because the buffer's contents
548dff3a6b4SAlan Somers  *    were correct when examined in a core dump.
549dff3a6b4SAlan Somers  * 7) The third write reads lbn 4 because cache is clear
550dff3a6b4SAlan Somers  * 9) uiomove dutifully copies new data into the buffer
551dff3a6b4SAlan Somers  * 10) The buffer's dirty is flushed to lbn 4
552dff3a6b4SAlan Somers  * 11) The read returns all zeros because of step 6.
553dff3a6b4SAlan Somers  *
554dff3a6b4SAlan Somers  * Based on:
555dff3a6b4SAlan Somers  * fsx -WR -l 524388 -o 131072 -P /tmp -S6456 -q  fsx.bin
556dff3a6b4SAlan Somers  */
5570482ec3eSAlan Somers TEST_P(Io, resize_a_valid_buffer_while_extending)
558dff3a6b4SAlan Somers {
559daf26f93SAlan Somers 	do_write(0x36ee6, 0x14530);	/* [0x36ee6, 0x4b415] */
560daf26f93SAlan Somers 	do_write(0x33256, 0x1507c);	/* [0x33256, 0x482d1] */
561daf26f93SAlan Somers 	do_write(0x4c03d, 0x175c);	/* [0x4c03d, 0x4d798] */
562daf26f93SAlan Somers 	do_read(0x3599c, 0xe277);	/* [0x3599c, 0x43c12] */
563dff3a6b4SAlan Somers 	close(m_test_fd);
564dff3a6b4SAlan Somers }
5650482ec3eSAlan Somers 
5666b1c5349SAlan Somers /*
5676b1c5349SAlan Somers  * mmap of a suitable region could trigger a panic.  I'm not sure what
5686b1c5349SAlan Somers  * combination of size and offset counts as "suitable".  Regression test for
5696b1c5349SAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=276191
5706b1c5349SAlan Somers  */
5716b1c5349SAlan Somers TEST_P(IoCacheable, vnode_pager_generic_putpage_clean_block_at_eof)
5726b1c5349SAlan Somers {
573daf26f93SAlan Somers 	do_mapwrite(0x3b4e0, 0x1bbc3);
5746b1c5349SAlan Somers }
5756b1c5349SAlan Somers 
5761c909c30SAlan Somers /*
5771c909c30SAlan Somers  * A copy_file_range that follows an mmap write to the input area needs to
5781c909c30SAlan Somers  * flush the mmap buffer first.
5791c909c30SAlan Somers  */
5801c909c30SAlan Somers TEST_P(IoCopyFileRange, copy_file_range_from_mapped_write)
5811c909c30SAlan Somers {
582daf26f93SAlan Somers 	do_mapwrite(0, 0x1000);
5831c909c30SAlan Somers 	do_copy_file_range(0, 0x1000, 0x1000);
5841c909c30SAlan Somers 	do_read(0x1000, 0x1000);
5851c909c30SAlan Somers }
5861c909c30SAlan Somers 
5871c909c30SAlan Somers 
588811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(Io, Io,
589f8ebf1cdSAlan Somers 	Combine(Bool(),					/* async read */
5900482ec3eSAlan Somers 		Values(0x1000, 0x10000, 0x20000),	/* m_maxwrite */
5911c909c30SAlan Somers 		Values(Uncached, Writethrough, Writeback, WritebackAsync),
5921c909c30SAlan Somers 		Values(28)				/* kernel_minor_vers */
593c51f519bSAlan Somers 	)
594c51f519bSAlan Somers );
59548417ae0SAlan Somers 
596811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(Io, IoCacheable,
597f8ebf1cdSAlan Somers 	Combine(Bool(),					/* async read */
59848417ae0SAlan Somers 		Values(0x1000, 0x10000, 0x20000),	/* m_maxwrite */
5991c909c30SAlan Somers 		Values(Writethrough, Writeback, WritebackAsync),
6001c909c30SAlan Somers 		Values(28)				/* kernel_minor_vers */
6011c909c30SAlan Somers 	)
6021c909c30SAlan Somers );
6031c909c30SAlan Somers 
6041c909c30SAlan Somers INSTANTIATE_TEST_SUITE_P(Io, IoCopyFileRange,
6051c909c30SAlan Somers 	Combine(Values(true),				/* async read */
6061c909c30SAlan Somers 		Values(0x10000),			/* m_maxwrite */
6071c909c30SAlan Somers 		Values(Writethrough, Writeback, WritebackAsync),
6081c909c30SAlan Somers 		Values(27, 28)				/* kernel_minor_vers */
609c51f519bSAlan Somers 	)
610c51f519bSAlan Somers );
611