blob: 8a9f2c0951cbe48ed6351179eba2bdc07f4abc73 [file] [log] [blame]
William A. Kennington III5c20da22021-06-18 16:44:55 -07001#include <poll.h>
2
William A. Kennington III43e38182021-08-19 17:29:48 -07003#include <array>
William A. Kennington IIIc25b3842022-08-24 16:42:43 -07004#include <charconv>
William A. Kennington III43e38182021-08-19 17:29:48 -07005#include <chrono>
William A. Kennington III2728f882022-08-24 15:44:53 -07006#include <fmt/format.h>
William A. Kennington IIIb95a1282021-08-19 17:46:58 -07007#include <optional>
William A. Kennington III5c20da22021-06-18 16:44:55 -07008#include <stdplus/io_uring.hpp>
William A. Kennington IIIc25b3842022-08-24 16:42:43 -07009#include <stdplus/util/cexec.hpp>
William A. Kennington IIIb95a1282021-08-19 17:46:58 -070010#include <string_view>
William A. Kennington IIIc25b3842022-08-24 16:42:43 -070011#include <sys/utsname.h>
William A. Kennington III5c20da22021-06-18 16:44:55 -070012
13#include <gmock/gmock.h>
14#include <gtest/gtest.h>
15
16namespace stdplus
17{
18
19using testing::_;
20
William A. Kennington IIIc25b3842022-08-24 16:42:43 -070021static std::string_view extractVerNum(std::string_view& str)
22{
23 auto ret = str.substr(0, str.find('.'));
24 str.remove_prefix(ret.size() + (ret.size() == str.size() ? 0 : 1));
25 return ret;
26}
27
28template <typename T>
29static T intFromStr(std::string_view str)
30{
31 T ret;
32 auto res = std::from_chars(str.data(), str.data() + str.size(), ret);
33 if (res.ec != std::errc{} || res.ptr != str.data() + str.size())
34 {
35 throw std::invalid_argument("Bad kernel version");
36 }
37 return ret;
38}
39
40static bool isKernelSafe(std::string_view ver, uint8_t smajor, uint8_t sminor)
41{
42 auto major = intFromStr<uint8_t>(extractVerNum(ver));
43 auto minor = intFromStr<uint8_t>(extractVerNum(ver));
44 return major > smajor || (major == smajor && minor >= sminor);
45}
46
47TEST(KernelInfo, VersionSafe)
48{
49 EXPECT_THROW(isKernelSafe("a", 5, 10), std::invalid_argument);
50 EXPECT_THROW(isKernelSafe("3a.3b.c", 5, 10), std::invalid_argument);
51 EXPECT_FALSE(isKernelSafe("4.11.20-nfd", 5, 10));
52 EXPECT_FALSE(isKernelSafe("4.11.20", 5, 10));
53 EXPECT_FALSE(isKernelSafe("4.11", 5, 10));
54 EXPECT_FALSE(isKernelSafe("5.9.0", 5, 10));
55 EXPECT_TRUE(isKernelSafe("5.10.1", 5, 10));
56 EXPECT_TRUE(isKernelSafe("6.0.0", 5, 10));
57 EXPECT_TRUE(isKernelSafe("6.0.0-abc", 5, 10));
58}
59
60static bool checkKernelSafe(uint8_t smajor, uint8_t sminor)
61{
62 utsname uts;
63 CHECK_ERRNO(uname(&uts), "uname");
64 return isKernelSafe(uts.release, smajor, sminor);
65}
66
William A. Kennington III2728f882022-08-24 15:44:53 -070067TEST(KernelInfo, Print)
68{
69 utsname uts;
70 ASSERT_NO_THROW(CHECK_ERRNO(uname(&uts), "uname"));
71 fmt::print("{} {} {} {} {}", uts.sysname, uts.nodename, uts.release,
72 uts.version, uts.machine);
73}
74
William A. Kennington III43e38182021-08-19 17:29:48 -070075TEST(Convert, ChronoToKTS)
76{
77 const auto ns = 700;
78 const auto s = 5;
79 const auto kts =
80 chronoToKTS(std::chrono::nanoseconds(ns) + std::chrono::seconds(s));
81 EXPECT_EQ(kts.tv_sec, s);
82 EXPECT_EQ(kts.tv_nsec, ns);
83}
84
William A. Kennington III5c20da22021-06-18 16:44:55 -070085class MockHandler : public IoUring::CQEHandler
86{
87 public:
88 MOCK_METHOD(void, handleCQE, (io_uring_cqe&), (noexcept, override));
89};
90
91class IoUringTest : public testing::Test
92{
93 protected:
94 static void SetUpTestCase()
95 {
96 io_uring r;
97 if (io_uring_queue_init(1, &r, 0) == -ENOSYS)
98 {
99 // Not supported, skip running this test
100 exit(77);
101 }
102 io_uring_queue_exit(&r);
103 }
104 std::array<testing::StrictMock<MockHandler>, 2> h;
105 IoUring ring;
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700106
107 void testFdWrite(int fd, int flags,
108 std::optional<int> expected_res = std::nullopt)
109 {
110 auto& sqe = ring.getSQE();
111 std::string_view data = "Test\n";
112 io_uring_prep_write(&sqe, fd, data.data(), data.size(), 0);
113 sqe.flags |= flags;
114 ring.setHandler(sqe, &h[0]);
115 ring.submit();
116 ring.wait();
117 EXPECT_CALL(h[0], handleCQE(_)).WillOnce([&](io_uring_cqe& cqe) {
118 EXPECT_EQ(cqe.res, expected_res.value_or(data.size()));
119 });
120 ring.process();
121 testing::Mock::VerifyAndClearExpectations(&h[0]);
122 }
William A. Kennington III5c20da22021-06-18 16:44:55 -0700123};
124
125TEST_F(IoUringTest, NullHandler)
126{
127 auto& sqe = ring.getSQE();
128 io_uring_prep_nop(&sqe);
129 ring.submit();
130 ring.process();
131}
132
133TEST_F(IoUringTest, HandlerCalled)
134{
135 {
136 auto& sqe = ring.getSQE();
137 io_uring_prep_nop(&sqe);
138 ring.setHandler(sqe, &h[0]);
139 }
140
141 // Nothing should happen without submission
142 ring.process();
143
144 {
145 auto& sqe = ring.getSQE();
146 io_uring_prep_nop(&sqe);
147 ring.setHandler(sqe, &h[1]);
148 }
149
150 // Handle all of the outstanding requests
151 ring.submit();
152 EXPECT_CALL(h[0], handleCQE(_));
153 EXPECT_CALL(h[1], handleCQE(_));
154 ring.process();
155 testing::Mock::VerifyAndClearExpectations(&h[0]);
156 testing::Mock::VerifyAndClearExpectations(&h[1]);
157}
158
159TEST_F(IoUringTest, HandlerReplacement)
160{
161 auto& sqe = ring.getSQE();
162 io_uring_prep_nop(&sqe);
163 ring.setHandler(sqe, &h[0]);
164
165 // Setting a new handler should cancel the old one
166 EXPECT_CALL(h[0], handleCQE(_));
167 ring.setHandler(sqe, &h[1]);
168 testing::Mock::VerifyAndClearExpectations(&h[0]);
169
170 // Setting a null handler should cancel the old one
171 EXPECT_CALL(h[1], handleCQE(_));
172 ring.setHandler(sqe, nullptr);
173 testing::Mock::VerifyAndClearExpectations(&h[1]);
174
175 // Set it back twice and make sure it isn't recognized idempotently
176 ring.setHandler(sqe, &h[1]);
177 ring.setHandler(sqe, &h[1]);
178
179 // Make sure it still works
180 ring.submit();
181 EXPECT_CALL(h[1], handleCQE(_));
182 ring.process();
183 testing::Mock::VerifyAndClearExpectations(&h[1]);
184}
185
186TEST_F(IoUringTest, EventFd)
187{
188 auto& efd = ring.getEventFd();
189
190 for (size_t i = 0; i < h.size(); ++i)
191 {
192 auto& sqe = ring.getSQE();
193 io_uring_prep_nop(&sqe);
194 ring.setHandler(sqe, &h[i]);
195 ring.submit();
196 }
197
198 // Our event fd should become ready
199 pollfd pfd;
200 pfd.fd = efd.get();
201 pfd.events = POLLIN;
202 ASSERT_EQ(1, poll(&pfd, 1, 100));
203
204 // Handle all of the outstanding requests
205 EXPECT_CALL(h[0], handleCQE(_));
206 EXPECT_CALL(h[1], handleCQE(_));
207 ring.processEvents();
208 testing::Mock::VerifyAndClearExpectations(&h[0]);
209 testing::Mock::VerifyAndClearExpectations(&h[1]);
210
211 // Our event fd should be empty
212 ASSERT_EQ(0, poll(&pfd, 1, 100));
213}
214
William A. Kennington III43e38182021-08-19 17:29:48 -0700215TEST_F(IoUringTest, Wait)
216{
217 auto& sqe = ring.getSQE();
218 auto kts = chronoToKTS(std::chrono::milliseconds(1));
219 io_uring_prep_timeout(&sqe, &kts, 0, 0);
220 ring.setHandler(sqe, &h[0]);
221 ring.submit();
222 ring.wait(std::chrono::seconds(1));
223 EXPECT_CALL(h[0], handleCQE(_));
224}
225
William A. Kennington III5c20da22021-06-18 16:44:55 -0700226TEST_F(IoUringTest, HandleCalledOnDestroy)
227{
228 auto& sqe = ring.getSQE();
229 io_uring_prep_nop(&sqe);
230 ring.setHandler(sqe, &h[0]);
231 EXPECT_CALL(h[0], handleCQE(_));
232}
233
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700234TEST_F(IoUringTest, RegisterFiles)
235{
William A. Kennington IIIc25b3842022-08-24 16:42:43 -0700236 // Earlier kernels had buggy support for registered files
237 if (!checkKernelSafe(5, 10))
238 {
239 GTEST_SKIP();
240 }
241
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700242 std::optional<IoUring::FileHandle> fh;
243
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700244 // Slots are always allocated linearly and re-used if invalidated
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700245 fh = ring.registerFile(STDERR_FILENO);
William A. Kennington III1afd7732021-08-20 00:49:10 -0700246 EXPECT_GT(ring.getFiles().size(), 1);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700247 EXPECT_EQ(*fh, 0);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700248 fh = ring.registerFile(STDOUT_FILENO);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700249 EXPECT_EQ(*fh, 1);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700250 EXPECT_GT(ring.getFiles().size(), 2);
251 EXPECT_EQ(ring.getFiles()[0], -1);
252 EXPECT_EQ(ring.getFiles()[1], STDOUT_FILENO);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700253
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700254 // The first handle should have dropped and can be replaced
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700255 fh = ring.registerFile(STDERR_FILENO);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700256 EXPECT_EQ(*fh, 0);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700257 EXPECT_GT(ring.getFiles().size(), 1);
258 EXPECT_EQ(ring.getFiles()[0], STDERR_FILENO);
259 EXPECT_EQ(ring.getFiles()[1], -1);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700260
261 // We should be able to write to stderr via the fixed file and regular fd
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700262 testFdWrite(STDERR_FILENO, 0);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700263 testFdWrite(*fh, IOSQE_FIXED_FILE);
264
265 // Without registration we should only be able to write to the regular fd
266 fh.reset();
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700267 testFdWrite(STDERR_FILENO, 0);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700268 testFdWrite(*fh, IOSQE_FIXED_FILE, -EBADF);
269
270 std::vector<IoUring::FileHandle> fhs;
William A. Kennington III1afd7732021-08-20 00:49:10 -0700271 EXPECT_LT(ring.getFiles().size(), 9);
272 ring.reserveFiles(9);
273 EXPECT_EQ(ring.getFiles().size(), 9);
274 for (size_t i = 0; i < 9; ++i)
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700275 {
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700276 fhs.emplace_back(ring.registerFile(STDERR_FILENO));
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700277 testFdWrite(fhs.back(), IOSQE_FIXED_FILE);
278 }
William A. Kennington III1afd7732021-08-20 00:49:10 -0700279 EXPECT_EQ(ring.getFiles().size(), 9);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700280 fhs.emplace_back(ring.registerFile(STDERR_FILENO));
William A. Kennington III1afd7732021-08-20 00:49:10 -0700281 testFdWrite(fhs.back(), IOSQE_FIXED_FILE);
282 EXPECT_GE(ring.getFiles().size(), 10);
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700283}
284
William A. Kennington III5c20da22021-06-18 16:44:55 -0700285} // namespace stdplus