gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/io_uring.cc (about) 1 // Copyright 2022 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <asm-generic/errno-base.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <pthread.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <sys/epoll.h> 23 #include <sys/mman.h> 24 #include <sys/stat.h> 25 #include <sys/types.h> 26 #include <unistd.h> 27 28 #include <cerrno> 29 #include <cstddef> 30 #include <cstdint> 31 32 #include "gtest/gtest.h" 33 #include "test/util/io_uring_util.h" 34 #include "test/util/memory_util.h" 35 #include "test/util/multiprocess_util.h" 36 #include "test/util/temp_path.h" 37 #include "test/util/test_util.h" 38 #include "test/util/thread_util.h" 39 40 namespace gvisor { 41 namespace testing { 42 43 namespace { 44 45 bool IOUringAvailable() { 46 if (IsRunningOnGvisor()) { 47 return true; 48 } 49 50 // io_uring is relatively new and may not be available on all kernels. Probe 51 // using an intentionally invalid call to io_uring_enter. 52 errno = 0; 53 int rc = syscall(__NR_io_uring_enter, -1, 1, 1, 0, nullptr); 54 if (rc != -1) { 55 // How did this succeed? 56 std::cerr << "Probe io_uring_enter(2) with invalid FD somehow succeeded..." 57 << std::endl; 58 return false; 59 } 60 return errno != ENOSYS; 61 } 62 63 // IOVecContainsString checks that a tuple argument of (struct iovec *, int) 64 // corresponding to an iovec array and its length, contains data that matches 65 // the string length strlen and the string value str. 66 MATCHER_P(IOVecContainsString, str, "") { 67 struct iovec *iovs = arg.first; 68 int len = strlen(str); 69 int niov = arg.second; 70 int offset = 0; 71 72 for (int i = 0; i < niov; i++) { 73 struct iovec iov = iovs[i]; 74 if (len < offset) { 75 *result_listener << "strlen " << len << " < offset " << offset; 76 return false; 77 } 78 if (strncmp(static_cast<char *>(iov.iov_base), &str[offset], iov.iov_len)) { 79 absl::string_view iovec_string(static_cast<char *>(iov.iov_base), 80 iov.iov_len); 81 *result_listener << iovec_string << " @offset " << offset; 82 return false; 83 } 84 offset += iov.iov_len; 85 } 86 if (offset != len) { 87 *result_listener << offset; 88 return false; 89 } 90 91 return true; 92 } 93 94 // Testing that io_uring_setup(2) successfully returns a valid file descriptor. 95 TEST(IOUringTest, ValidFD) { 96 SKIP_IF(!IOUringAvailable()); 97 98 IOUringParams params = {}; 99 FileDescriptor iouringfd = ASSERT_NO_ERRNO_AND_VALUE(NewIOUringFD(1, params)); 100 } 101 102 // Testing that io_uring_setup(2) fails with EINVAL on non-zero params. 103 TEST(IOUringTest, ParamsNonZeroResv) { 104 SKIP_IF(!IOUringAvailable()); 105 106 IOUringParams params = {}; 107 memset(¶ms, 0, sizeof(params)); 108 params.resv[1] = 1; 109 ASSERT_THAT(IOUringSetup(1, ¶ms), SyscallFailsWithErrno(EINVAL)); 110 } 111 112 TEST(IOUringTest, ZeroCQEntries) { 113 SKIP_IF(!IOUringAvailable()); 114 115 IOUringParams params = {}; 116 params.cq_entries = 0; 117 params.flags = IORING_SETUP_CQSIZE; 118 ASSERT_THAT(IOUringSetup(1, ¶ms), SyscallFailsWithErrno(EINVAL)); 119 } 120 121 TEST(IOUringTest, ZeroCQEntriesLessThanSQEntries) { 122 SKIP_IF(!IOUringAvailable()); 123 124 IOUringParams params = {}; 125 params.cq_entries = 16; 126 params.flags = IORING_SETUP_CQSIZE; 127 ASSERT_THAT(IOUringSetup(32, ¶ms), SyscallFailsWithErrno(EINVAL)); 128 } 129 130 // Testing that io_uring_setup(2) fails with EINVAL on unsupported flags. 131 TEST(IOUringTest, UnsupportedFlags) { 132 // Gvisor only test, since linux supports all flags. 133 SKIP_IF(!IsRunningOnGvisor()); 134 135 IOUringParams params = {}; 136 memset(¶ms, 0, sizeof(params)); 137 params.flags |= IORING_SETUP_SQPOLL; 138 ASSERT_THAT(IOUringSetup(1, ¶ms), SyscallFailsWithErrno(EINVAL)); 139 } 140 141 // Testing that both mmap and munmap calls succeed and subsequent access to 142 // unmapped memory results in SIGSEGV. 143 TEST(IOUringTest, MMapMUnMapWork) { 144 SKIP_IF(!IOUringAvailable()); 145 146 IOUringParams params = {}; 147 FileDescriptor iouringfd = ASSERT_NO_ERRNO_AND_VALUE(NewIOUringFD(1, params)); 148 149 void *ptr = nullptr; 150 int sring_sz = params.sq_off.array + params.sq_entries * sizeof(unsigned); 151 152 ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, 153 iouringfd.get(), IORING_OFF_SQ_RING); 154 155 EXPECT_NE(ptr, MAP_FAILED); 156 157 const auto rest = [&] { 158 // N.B. we must be in a single-threaded subprocess to ensure that another 159 // thread doesn't racily remap at ptr. 160 TEST_PCHECK_MSG(MunmapSafe(ptr, sring_sz) == 0, "munmap failed"); 161 // This should SIGSEGV. 162 *reinterpret_cast<volatile int *>(ptr) = 42; 163 }; 164 165 int child_exit_status = ASSERT_NO_ERRNO_AND_VALUE(InForkedProcess(rest)); 166 EXPECT_TRUE(WIFSIGNALED(child_exit_status) && 167 WTERMSIG(child_exit_status) == SIGSEGV) 168 << "exit status: " << child_exit_status; 169 } 170 171 // Testing that both mmap fails with EINVAL when an invalid offset is passed. 172 TEST(IOUringTest, MMapWrongOffset) { 173 SKIP_IF(!IOUringAvailable()); 174 175 IOUringParams params = {}; 176 FileDescriptor iouringfd = ASSERT_NO_ERRNO_AND_VALUE(NewIOUringFD(1, params)); 177 178 int sring_sz = params.sq_off.array + params.sq_entries * sizeof(unsigned); 179 180 EXPECT_THAT(reinterpret_cast<uintptr_t>( 181 mmap(0, sring_sz, PROT_READ | PROT_WRITE, 182 MAP_SHARED | MAP_POPULATE, iouringfd.get(), 66)), 183 SyscallFailsWithErrno(EINVAL)); 184 } 185 186 // Testing that mmap() handles all three IO_URING-specific offsets and that 187 // returned addresses are page aligned. 188 TEST(IOUringTest, MMapOffsets) { 189 SKIP_IF(!IOUringAvailable()); 190 191 IOUringParams params = {}; 192 FileDescriptor iouringfd = ASSERT_NO_ERRNO_AND_VALUE(NewIOUringFD(1, params)); 193 194 void *sq_ptr = nullptr; 195 void *cq_ptr = nullptr; 196 void *sqe_ptr = nullptr; 197 198 int sring_sz = params.sq_off.array + params.sq_entries * sizeof(unsigned); 199 int cring_sz = params.cq_off.cqes + params.cq_entries * sizeof(IOUringCqe); 200 201 sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, 202 iouringfd.get(), IORING_OFF_SQ_RING); 203 204 cq_ptr = mmap(0, cring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, 205 iouringfd.get(), IORING_OFF_CQ_RING); 206 207 sqe_ptr = mmap(0, sizeof(IOUringSqe), PROT_READ | PROT_WRITE, 208 MAP_SHARED | MAP_POPULATE, iouringfd.get(), IORING_OFF_SQES); 209 210 EXPECT_NE(sq_ptr, MAP_FAILED); 211 EXPECT_NE(cq_ptr, MAP_FAILED); 212 EXPECT_NE(sqe_ptr, MAP_FAILED); 213 214 EXPECT_EQ(reinterpret_cast<uintptr_t>(sq_ptr) % kPageSize, 0); 215 EXPECT_EQ(reinterpret_cast<uintptr_t>(cq_ptr) % kPageSize, 0); 216 EXPECT_EQ(reinterpret_cast<uintptr_t>(sqe_ptr) % kPageSize, 0); 217 218 ASSERT_THAT(munmap(sq_ptr, sring_sz), SyscallSucceeds()); 219 ASSERT_THAT(munmap(cq_ptr, cring_sz), SyscallSucceeds()); 220 ASSERT_THAT(munmap(sqe_ptr, sizeof(IOUringSqe)), SyscallSucceeds()); 221 } 222 223 // Testing that IOUringParams are populated with correct values. 224 TEST(IOUringTest, ReturnedParamsValues) { 225 SKIP_IF(!IsRunningOnGvisor()); 226 227 IOUringParams params = {}; 228 FileDescriptor iouringfd = ASSERT_NO_ERRNO_AND_VALUE(NewIOUringFD(1, params)); 229 230 EXPECT_EQ(params.sq_entries, 1); 231 EXPECT_EQ(params.cq_entries, 2); 232 233 EXPECT_EQ(params.sq_off.head, 0); 234 EXPECT_EQ(params.sq_off.tail, 64); 235 EXPECT_EQ(params.sq_off.ring_mask, 256); 236 EXPECT_EQ(params.sq_off.ring_entries, 264); 237 EXPECT_EQ(params.sq_off.flags, 276); 238 EXPECT_EQ(params.sq_off.dropped, 272); 239 EXPECT_EQ(params.sq_off.array, 384); 240 241 EXPECT_EQ(params.cq_off.head, 128); 242 EXPECT_EQ(params.cq_off.tail, 192); 243 EXPECT_EQ(params.cq_off.ring_mask, 260); 244 EXPECT_EQ(params.cq_off.ring_entries, 268); 245 EXPECT_EQ(params.cq_off.overflow, 284); 246 EXPECT_EQ(params.cq_off.cqes, 320); 247 EXPECT_EQ(params.cq_off.flags, 280); 248 249 // gVisor should support IORING_FEAT_SINGLE_MMAP. 250 EXPECT_NE((params.features & IORING_FEAT_SINGLE_MMAP), 0); 251 } 252 253 // Testing that offset of SQE indices array is cacheline aligned. 254 TEST(IOUringTest, SqeIndexArrayCacheAligned) { 255 SKIP_IF(!IOUringAvailable()); 256 257 IOUringParams params = {}; 258 for (uint32_t i = 1; i < 10; ++i) { 259 FileDescriptor iouringfd = 260 ASSERT_NO_ERRNO_AND_VALUE(NewIOUringFD(i, params)); 261 ASSERT_EQ(params.sq_off.array % 64, 0); 262 } 263 } 264 265 // Testing that io_uring_enter(2) successfully handles a single NOP operation. 266 TEST(IOUringTest, SingleNOPTest) { 267 SKIP_IF(!IOUringAvailable()); 268 269 IOUringParams params = {}; 270 std::unique_ptr<IOUring> io_uring = 271 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 272 273 ASSERT_EQ(params.sq_entries, 1); 274 ASSERT_EQ(params.cq_entries, 2); 275 276 uint32_t sq_head = io_uring->load_sq_head(); 277 ASSERT_EQ(sq_head, 0); 278 279 IOUringSqe *sqe = io_uring->get_sqes(); 280 sqe->opcode = IORING_OP_NOP; 281 sqe->user_data = 42; 282 283 uint32_t sq_tail = io_uring->load_sq_tail(); 284 io_uring->store_sq_tail(sq_tail + 1); 285 286 int ret = io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 287 ASSERT_EQ(ret, 1); 288 289 IOUringCqe *cqe = io_uring->get_cqes(); 290 291 sq_head = io_uring->load_sq_head(); 292 ASSERT_EQ(sq_head, 1); 293 294 uint32_t cq_tail = io_uring->load_cq_tail(); 295 ASSERT_EQ(cq_tail, 1); 296 297 ASSERT_EQ(cqe->user_data, 42); 298 ASSERT_EQ(cqe->res, 0); 299 300 uint32_t cq_head = io_uring->load_cq_head(); 301 io_uring->store_cq_head(cq_head + 1); 302 } 303 304 // Testing that io_uring_enter(2) successfully queueing NOP operations. 305 TEST(IOUringTest, QueueingNOPTest) { 306 SKIP_IF(!IOUringAvailable()); 307 308 IOUringParams params = {}; 309 std::unique_ptr<IOUring> io_uring = 310 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(4, params)); 311 312 uint32_t sq_head = io_uring->load_sq_head(); 313 ASSERT_EQ(sq_head, 0); 314 315 unsigned *sq_array = io_uring->get_sq_array(); 316 unsigned index = 0; 317 IOUringSqe *sqe = io_uring->get_sqes(); 318 for (size_t i = 0; i < 4; ++i) { 319 sqe[i].opcode = IORING_OP_NOP; 320 sqe[i].user_data = 42 + i; 321 index = i & io_uring->get_sq_mask(); 322 sq_array[index] = index; 323 } 324 325 uint32_t sq_tail = io_uring->load_sq_tail(); 326 ASSERT_EQ(sq_tail, 0); 327 io_uring->store_sq_tail(sq_tail + 4); 328 329 int ret = io_uring->Enter(2, 2, IORING_ENTER_GETEVENTS, nullptr); 330 ASSERT_EQ(ret, 2); 331 332 IOUringCqe *cqe = io_uring->get_cqes(); 333 334 sq_head = io_uring->load_sq_head(); 335 ASSERT_EQ(sq_head, 2); 336 337 uint32_t cq_tail = io_uring->load_cq_tail(); 338 ASSERT_EQ(cq_tail, 2); 339 340 for (size_t i = 0; i < 2; ++i) { 341 ASSERT_EQ(cqe[i].res, 0); 342 ASSERT_EQ(cqe[i].user_data, 42 + i); 343 } 344 345 uint32_t cq_head = io_uring->load_cq_head(); 346 io_uring->store_cq_head(cq_head + 2); 347 348 ret = io_uring->Enter(2, 2, IORING_ENTER_GETEVENTS, nullptr); 349 ASSERT_EQ(ret, 2); 350 351 sq_head = io_uring->load_sq_head(); 352 ASSERT_EQ(sq_head, 4); 353 354 cq_tail = io_uring->load_cq_tail(); 355 ASSERT_EQ(cq_tail, 4); 356 357 for (size_t i = 2; i < 4; ++i) { 358 ASSERT_EQ(cqe[i].res, 0); 359 ASSERT_EQ(cqe[i].user_data, 42 + i); 360 } 361 362 cq_head = io_uring->load_cq_head(); 363 io_uring->store_cq_head(cq_head + 2); 364 } 365 366 // Testing that io_uring_enter(2) successfully multiple NOP operations. 367 TEST(IOUringTest, MultipleNOPTest) { 368 SKIP_IF(!IOUringAvailable()); 369 370 IOUringParams params = {}; 371 std::unique_ptr<IOUring> io_uring = 372 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(4, params)); 373 374 uint32_t sq_head = io_uring->load_sq_head(); 375 ASSERT_EQ(sq_head, 0); 376 377 unsigned *sq_array = io_uring->get_sq_array(); 378 unsigned index = 0; 379 IOUringSqe *sqe = io_uring->get_sqes(); 380 for (size_t i = 0; i < 3; ++i) { 381 sqe[i].opcode = IORING_OP_NOP; 382 sqe[i].user_data = 42 + i; 383 index = i & io_uring->get_sq_mask(); 384 sq_array[index] = index; 385 } 386 387 uint32_t sq_tail = io_uring->load_sq_tail(); 388 ASSERT_EQ(sq_tail, 0); 389 io_uring->store_sq_tail(sq_tail + 3); 390 391 int ret = io_uring->Enter(3, 3, IORING_ENTER_GETEVENTS, nullptr); 392 ASSERT_EQ(ret, 3); 393 394 IOUringCqe *cqe = io_uring->get_cqes(); 395 396 sq_head = io_uring->load_sq_head(); 397 ASSERT_EQ(sq_head, 3); 398 399 uint32_t cq_tail = io_uring->load_cq_tail(); 400 ASSERT_EQ(cq_tail, 3); 401 402 for (size_t i = 0; i < 3; ++i) { 403 ASSERT_EQ(cqe[i].res, 0); 404 ASSERT_EQ(cqe[i].user_data, 42 + i); 405 } 406 407 uint32_t cq_head = io_uring->load_cq_head(); 408 io_uring->store_cq_head(cq_head + 3); 409 } 410 411 // Testing that io_uring_enter(2) successfully handles multiple threads 412 // submitting NOP operations. 413 TEST(IOUringTest, MultiThreadedNOPTest) { 414 SKIP_IF(!IOUringAvailable()); 415 416 IOUringParams params = {}; 417 std::unique_ptr<IOUring> io_uring = 418 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(4, params)); 419 420 uint32_t sq_head = io_uring->load_sq_head(); 421 ASSERT_EQ(sq_head, 0); 422 423 unsigned *sq_array = io_uring->get_sq_array(); 424 unsigned index = 0; 425 IOUringSqe *sqe = io_uring->get_sqes(); 426 for (size_t i = 0; i < 4; ++i) { 427 sqe[i].opcode = IORING_OP_NOP; 428 sqe[i].user_data = 42 + i; 429 index = i & io_uring->get_sq_mask(); 430 sq_array[index] = index; 431 } 432 433 uint32_t sq_tail = io_uring->load_sq_tail(); 434 io_uring->store_sq_tail(sq_tail + 4); 435 436 for (int i = 0; i < 4; i++) { 437 ScopedThread t([&] { 438 IOUring *io_uring_ptr = io_uring.get(); 439 int ret = io_uring_ptr->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 440 ASSERT_EQ(ret, 1); 441 }); 442 } 443 444 IOUringCqe *cqe = io_uring->get_cqes(); 445 446 sq_head = io_uring->load_sq_head(); 447 ASSERT_EQ(sq_head, 4); 448 449 uint32_t cq_tail = io_uring->load_cq_tail(); 450 ASSERT_EQ(cq_tail, 4); 451 452 for (size_t i = 0; i < 4; ++i) { 453 ASSERT_EQ(cqe[i].res, 0); 454 ASSERT_EQ(cqe[i].user_data, 42 + i); 455 } 456 457 uint32_t cq_head = io_uring->load_cq_head(); 458 io_uring->store_cq_head(cq_head + 4); 459 } 460 461 // Testing that io_uring_enter(2) successfully consumes submission with an 462 // invalid opcode and returned CQE contains EINVAL in its result field. 463 TEST(IOUringTest, InvalidOpCodeTest) { 464 SKIP_IF(!IOUringAvailable()); 465 466 IOUringParams params = {}; 467 std::unique_ptr<IOUring> io_uring = 468 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 469 470 uint32_t sq_head = io_uring->load_sq_head(); 471 ASSERT_EQ(sq_head, 0); 472 473 IOUringSqe *sqe = io_uring->get_sqes(); 474 sqe->opcode = 255; // maximum value for one-byte unsigned integer 475 sqe->user_data = 42; 476 477 uint32_t sq_tail = io_uring->load_sq_tail(); 478 io_uring->store_sq_tail(sq_tail + 1); 479 480 int ret = io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 481 ASSERT_EQ(ret, 1); 482 483 IOUringCqe *cqe = io_uring->get_cqes(); 484 485 sq_head = io_uring->load_sq_head(); 486 ASSERT_EQ(sq_head, 1); 487 488 uint32_t cq_tail = io_uring->load_cq_tail(); 489 ASSERT_EQ(cq_tail, 1); 490 491 ASSERT_EQ(cqe->user_data, 42); 492 ASSERT_EQ(cqe->res, -EINVAL); 493 494 uint32_t cq_head = io_uring->load_cq_head(); 495 io_uring->store_cq_head(cq_head + 1); 496 } 497 498 // Tests that filling the shared memory region with garbage data doesn't cause a 499 // kernel panic. 500 TEST(IOUringTest, CorruptRingHeader) { 501 SKIP_IF(!IOUringAvailable()); 502 503 const int kEntries = 64; 504 505 IOUringParams params = {}; 506 FileDescriptor iouringfd = 507 ASSERT_NO_ERRNO_AND_VALUE(NewIOUringFD(kEntries, params)); 508 509 int sring_sz = params.sq_off.array + params.sq_entries * sizeof(unsigned); 510 int cring_sz = params.cq_off.cqes + params.cq_entries * sizeof(IOUringCqe); 511 int sqes_sz = params.sq_entries * sizeof(IOUringSqe); 512 513 void *sq_ptr = 514 mmap(0, sring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, 515 iouringfd.get(), IORING_OFF_SQ_RING); 516 517 void *cq_ptr = 518 mmap(0, cring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, 519 iouringfd.get(), IORING_OFF_CQ_RING); 520 521 void *sqe_ptr = 522 mmap(0, sqes_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, 523 iouringfd.get(), IORING_OFF_SQES); 524 525 EXPECT_NE(sq_ptr, MAP_FAILED); 526 EXPECT_NE(cq_ptr, MAP_FAILED); 527 EXPECT_NE(sqe_ptr, MAP_FAILED); 528 529 // Corrupt all the buffers. 530 memset(sq_ptr, 0xff, sring_sz); 531 memset(cq_ptr, 0xff, cring_sz); 532 memset(sqe_ptr, 0xff, sqes_sz); 533 534 IOUringEnter(iouringfd.get(), 1, 0, IORING_ENTER_GETEVENTS, nullptr); 535 536 // If kernel hasn't panicked, the test succeeds. 537 538 EXPECT_THAT(munmap(sq_ptr, sring_sz), SyscallSucceeds()); 539 EXPECT_THAT(munmap(cq_ptr, cring_sz), SyscallSucceeds()); 540 EXPECT_THAT(munmap(sqe_ptr, sizeof(IOUringSqe)), SyscallSucceeds()); 541 } 542 543 // Testing that io_uring_enter(2) successfully consumes submission and SQE ring 544 // buffers wrap around. 545 TEST(IOUringTest, SQERingBuffersWrapAroundTest) { 546 SKIP_IF(!IOUringAvailable()); 547 548 IOUringParams params = {}; 549 std::unique_ptr<IOUring> io_uring = 550 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(4, params)); 551 552 uint32_t sq_head = io_uring->load_sq_head(); 553 ASSERT_EQ(sq_head, 0); 554 555 unsigned *sq_array = io_uring->get_sq_array(); 556 unsigned index = 0; 557 IOUringSqe *sqe = io_uring->get_sqes(); 558 for (size_t i = 0; i < 4; ++i) { 559 sqe[i].opcode = IORING_OP_NOP; 560 sqe[i].user_data = 42 + i; 561 index = i & io_uring->get_sq_mask(); 562 sq_array[index] = index; 563 } 564 565 uint32_t sq_tail = io_uring->load_sq_tail(); 566 io_uring->store_sq_tail(sq_tail + 4); 567 568 int ret = io_uring->Enter(4, 4, IORING_ENTER_GETEVENTS, nullptr); 569 ASSERT_EQ(ret, 4); 570 571 IOUringCqe *cqe = io_uring->get_cqes(); 572 573 sq_head = io_uring->load_sq_head(); 574 ASSERT_EQ(sq_head, 4); 575 576 uint32_t cq_tail = io_uring->load_cq_tail(); 577 ASSERT_EQ(cq_tail, 4); 578 579 for (size_t i = 0; i < 4; ++i) { 580 ASSERT_EQ(cqe[i].res, 0); 581 ASSERT_EQ(cqe[i].user_data, 42 + i); 582 } 583 584 uint32_t cq_head = io_uring->load_cq_head(); 585 io_uring->store_cq_head(cq_head + 4); 586 587 for (size_t i = 0; i < 4; ++i) { 588 sqe[i].user_data = 42 + 2 * (i + 1); 589 } 590 591 sq_tail = io_uring->load_sq_tail(); 592 io_uring->store_sq_tail(sq_tail + 4); 593 594 ret = io_uring->Enter(4, 4, IORING_ENTER_GETEVENTS, nullptr); 595 ASSERT_EQ(ret, 4); 596 597 sq_head = io_uring->load_sq_head(); 598 ASSERT_EQ(sq_head, 8); 599 600 cq_tail = io_uring->load_cq_tail(); 601 ASSERT_EQ(cq_tail, 8); 602 603 for (size_t i = 0; i < 4; ++i) { 604 ASSERT_EQ(cqe[4 + i].res, 0); 605 ASSERT_EQ(cqe[4 + i].user_data, 42 + 2 * (i + 1)); 606 } 607 608 cq_head = io_uring->load_cq_head(); 609 io_uring->store_cq_head(cq_head + 4); 610 } 611 612 // Testing that io_uring_enter(2) fails with EFAULT when non-null sigset_t has 613 // been passed as we currently don't support replacing signal mask. 614 TEST(IOUringTest, NonNullSigsetTest) { 615 SKIP_IF(!IsRunningOnGvisor()); 616 617 IOUringParams params = {}; 618 std::unique_ptr<IOUring> io_uring = 619 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 620 621 uint32_t sq_head = io_uring->load_sq_head(); 622 ASSERT_EQ(sq_head, 0); 623 624 IOUringSqe *sqe = io_uring->get_sqes(); 625 sqe->opcode = IORING_OP_NOP; 626 sqe->user_data = 42; 627 628 uint32_t sq_tail = io_uring->load_sq_tail(); 629 io_uring->store_sq_tail(sq_tail + 1); 630 631 sigset_t non_null_sigset; 632 EXPECT_THAT(io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, &non_null_sigset), 633 SyscallFailsWithErrno(EFAULT)); 634 } 635 636 // Testing that completion queue overflow counter is incremented when the 637 // completion queue is not drained by the user and completion queue entries are 638 // not overwritten. 639 TEST(IOUringTest, OverflowCQTest) { 640 // Gvisor's completion queue overflow behaviour is different from Linux. 641 SKIP_IF(!IsRunningOnGvisor()); 642 643 IOUringParams params = {}; 644 std::unique_ptr<IOUring> io_uring = 645 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(4, params)); 646 647 uint32_t sq_head = io_uring->load_sq_head(); 648 ASSERT_EQ(sq_head, 0); 649 650 unsigned *sq_array = io_uring->get_sq_array(); 651 unsigned index = 0; 652 IOUringSqe *sqe = io_uring->get_sqes(); 653 IOUringCqe *cqe = io_uring->get_cqes(); 654 655 for (size_t submission_round = 0; submission_round < 2; ++submission_round) { 656 for (size_t i = 0; i < 4; ++i) { 657 sqe[i].opcode = IORING_OP_NOP; 658 sqe[i].user_data = 42 + i + submission_round; 659 index = i & io_uring->get_sq_mask(); 660 sq_array[index] = index; 661 } 662 663 uint32_t sq_tail = io_uring->load_sq_tail(); 664 ASSERT_EQ(sq_tail, 4 * submission_round); 665 io_uring->store_sq_tail(sq_tail + 4); 666 667 int ret = io_uring->Enter(4, 4, IORING_ENTER_GETEVENTS, nullptr); 668 ASSERT_EQ(ret, 4); 669 670 sq_head = io_uring->load_sq_head(); 671 ASSERT_EQ(sq_head, 4 * (submission_round + 1)); 672 673 uint32_t dropped = io_uring->load_sq_dropped(); 674 ASSERT_EQ(dropped, 0); 675 676 uint32_t cq_overflow_counter = io_uring->load_cq_overflow(); 677 ASSERT_EQ(cq_overflow_counter, 0); 678 679 uint32_t cq_tail = io_uring->load_cq_tail(); 680 ASSERT_EQ(cq_tail, 4 * (submission_round + 1)); 681 682 for (size_t i = 0; i < 4; ++i) { 683 ASSERT_EQ(cqe[i + 4 * submission_round].res, 0); 684 ASSERT_EQ(cqe[i + 4 * submission_round].user_data, 685 42 + i + submission_round); 686 } 687 } 688 689 for (size_t i = 0; i < 2; ++i) { 690 sqe[i].opcode = IORING_OP_NOP; 691 sqe[i].user_data = 52 + i; 692 index = i & io_uring->get_sq_mask(); 693 sq_array[index] = index; 694 } 695 696 uint32_t sq_tail = io_uring->load_sq_tail(); 697 ASSERT_EQ(sq_tail, 8); 698 io_uring->store_sq_tail(sq_tail + 2); 699 700 int ret = io_uring->Enter(2, 2, IORING_ENTER_GETEVENTS, nullptr); 701 ASSERT_EQ(ret, 2); 702 703 sq_head = io_uring->load_sq_head(); 704 ASSERT_EQ(sq_head, 10); 705 706 uint32_t cq_tail = io_uring->load_cq_tail(); 707 ASSERT_EQ(cq_tail, 8); 708 709 ASSERT_EQ(cqe[0].res, 0); 710 ASSERT_EQ(cqe[0].user_data, 42); 711 ASSERT_EQ(cqe[1].res, 0); 712 ASSERT_EQ(cqe[1].user_data, 43); 713 714 uint32_t dropped = io_uring->load_sq_dropped(); 715 ASSERT_EQ(dropped, 0); 716 717 uint32_t cq_overflow_counter = io_uring->load_cq_overflow(); 718 ASSERT_EQ(cq_overflow_counter, 2); 719 } 720 721 // Testing that io_uring_enter(2) successfully handles single READV operation. 722 TEST(IOUringTest, SingleREADVTest) { 723 SKIP_IF(!IOUringAvailable()); 724 725 IOUringParams params = {}; 726 std::unique_ptr<IOUring> io_uring = 727 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 728 729 ASSERT_EQ(params.sq_entries, 1); 730 ASSERT_EQ(params.cq_entries, 2); 731 732 uint32_t sq_head = io_uring->load_sq_head(); 733 ASSERT_EQ(sq_head, 0); 734 735 std::string file_name = NewTempAbsPath(); 736 std::string contents("DEADBEEF"); 737 ASSERT_NO_ERRNO(CreateWithContents(file_name, contents, 0666)); 738 739 FileDescriptor filefd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDONLY)); 740 ASSERT_GE(filefd.get(), 0); 741 742 struct stat st = ASSERT_NO_ERRNO_AND_VALUE(Stat(file_name)); 743 off_t file_sz = st.st_size; 744 ASSERT_GT(file_sz, 0); 745 746 int num_blocks = (file_sz + BLOCK_SZ - 1) / BLOCK_SZ; 747 ASSERT_EQ(num_blocks, 1); 748 749 unsigned *sq_array = io_uring->get_sq_array(); 750 struct io_uring_sqe *sqe = io_uring->get_sqes(); 751 752 struct iovec iov; 753 iov.iov_len = file_sz; 754 void *buf; 755 ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); 756 iov.iov_base = buf; 757 758 sqe->flags = 0; 759 sqe->fd = filefd.get(); 760 sqe->opcode = IORING_OP_READV; 761 sqe->addr = reinterpret_cast<uint64_t>(&iov); 762 sqe->len = num_blocks; 763 sqe->off = 0; 764 sqe->user_data = reinterpret_cast<uint64_t>(&iov); 765 sq_array[0] = 0; 766 767 uint32_t sq_tail = io_uring->load_sq_tail(); 768 io_uring->store_sq_tail(sq_tail + 1); 769 770 int ret = io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 771 ASSERT_EQ(ret, 1); 772 773 struct io_uring_cqe *cqe = io_uring->get_cqes(); 774 775 sq_head = io_uring->load_sq_head(); 776 ASSERT_EQ(sq_head, 1); 777 778 uint32_t cq_tail = io_uring->load_cq_tail(); 779 ASSERT_EQ(cq_tail, 1); 780 781 ASSERT_EQ(cqe->res, file_sz); 782 783 struct iovec *fi = reinterpret_cast<struct iovec *>(cqe->user_data); 784 785 std::pair<struct iovec *, int> iovec_desc(fi, num_blocks); 786 EXPECT_THAT(iovec_desc, IOVecContainsString(contents.c_str())); 787 788 uint32_t cq_head = io_uring->load_cq_head(); 789 io_uring->store_cq_head(cq_head + 1); 790 } 791 792 // Tests that IORING_OP_READV handles EOF on an empty file correctly. 793 TEST(IOUringTest, ReadvEmptyFile) { 794 SKIP_IF(!IOUringAvailable()); 795 796 IOUringParams params = {}; 797 std::unique_ptr<IOUring> io_uring = 798 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 799 800 uint32_t sq_head = io_uring->load_sq_head(); 801 802 std::string file_name = NewTempAbsPath(); 803 ASSERT_NO_ERRNO(CreateWithContents(file_name, "", 0666)); 804 805 FileDescriptor filefd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDONLY)); 806 ASSERT_GE(filefd.get(), 0); 807 808 unsigned *sq_array = io_uring->get_sq_array(); 809 struct io_uring_sqe *sqe = io_uring->get_sqes(); 810 811 struct iovec iov; 812 iov.iov_len = 0; 813 void *buf; 814 ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); 815 iov.iov_base = buf; 816 817 sqe->flags = 0; 818 sqe->fd = filefd.get(); 819 sqe->opcode = IORING_OP_READV; 820 sqe->addr = reinterpret_cast<uint64_t>(&iov); 821 sqe->len = 1; 822 sqe->off = 0; 823 sqe->user_data = reinterpret_cast<uint64_t>(&iov); 824 sq_array[0] = 0; 825 826 uint32_t sq_tail = io_uring->load_sq_tail(); 827 io_uring->store_sq_tail(sq_tail + 1); 828 829 int ret = io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 830 ASSERT_EQ(ret, 1); 831 832 struct io_uring_cqe *cqe = io_uring->get_cqes(); 833 834 sq_head = io_uring->load_sq_head(); 835 ASSERT_EQ(sq_head, 1); 836 837 uint32_t cq_tail = io_uring->load_cq_tail(); 838 ASSERT_EQ(cq_tail, 1); 839 840 ASSERT_EQ(cqe->res, 0); // 0 length read, EOF. 841 842 uint32_t cq_head = io_uring->load_cq_head(); 843 io_uring->store_cq_head(cq_head + 1); 844 } 845 846 // Testing that io_uring_enter(2) successfully handles three READV operations 847 // from three different files submitted through a single invocation. 848 TEST(IOUringTest, ThreeREADVSingleEnterTest) { 849 SKIP_IF(!IOUringAvailable()); 850 851 IOUringParams params = {}; 852 std::unique_ptr<IOUring> io_uring = 853 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(4, params)); 854 855 ASSERT_EQ(params.sq_entries, 4); 856 ASSERT_EQ(params.cq_entries, 8); 857 858 uint32_t sq_head = io_uring->load_sq_head(); 859 ASSERT_EQ(sq_head, 0); 860 861 FileDescriptor filefd[3]; 862 unsigned *sq_array = io_uring->get_sq_array(); 863 struct io_uring_sqe *sqe = io_uring->get_sqes(); 864 off_t file_sz[3]; 865 int num_blocks[3]; 866 struct iovec iov[3]; 867 868 for (size_t i = 0; i < 3; i++) { 869 std::string file_name = NewTempAbsPath(); 870 std::string contents("DEADBEEF"); 871 for (size_t j = 0; j < i; ++j) { 872 contents.append(" DEADBEEF"); 873 } 874 ASSERT_NO_ERRNO(CreateWithContents(file_name, contents, 0666)); 875 876 filefd[i] = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDONLY)); 877 ASSERT_GE(filefd[i].get(), 0); 878 879 struct stat st = ASSERT_NO_ERRNO_AND_VALUE(Stat(file_name)); 880 file_sz[i] = st.st_size; 881 ASSERT_GT(file_sz[i], 0); 882 883 num_blocks[i] = (file_sz[i] + BLOCK_SZ - 1) / BLOCK_SZ; 884 ASSERT_EQ(num_blocks[i], 1); 885 886 iov[i].iov_len = file_sz[i]; 887 void *buf; 888 ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); 889 iov[i].iov_base = buf; 890 891 sqe[i].flags = 0; 892 sqe[i].fd = filefd[i].get(); 893 sqe[i].opcode = IORING_OP_READV; 894 sqe[i].addr = reinterpret_cast<uint64_t>(&iov[i]); 895 sqe[i].len = num_blocks[i]; 896 sqe[i].off = 0; 897 sqe[i].user_data = reinterpret_cast<uint64_t>(&iov[i]); 898 sq_array[i] = i; 899 900 uint32_t sq_tail = io_uring->load_sq_tail(); 901 io_uring->store_sq_tail(sq_tail + 1); 902 } 903 904 ASSERT_EQ(file_sz[0], 8); 905 ASSERT_EQ(file_sz[1], 17); 906 ASSERT_EQ(file_sz[2], 26); 907 908 int ret = io_uring->Enter(3, 3, IORING_ENTER_GETEVENTS, nullptr); 909 ASSERT_EQ(ret, 3); 910 911 struct io_uring_cqe *cqe = io_uring->get_cqes(); 912 913 sq_head = io_uring->load_sq_head(); 914 ASSERT_EQ(sq_head, 3); 915 916 uint32_t cq_tail = io_uring->load_cq_tail(); 917 ASSERT_EQ(cq_tail, 3); 918 919 ASSERT_EQ(cqe[0].res, file_sz[0]); 920 ASSERT_EQ(cqe[1].res, file_sz[1]); 921 ASSERT_EQ(cqe[2].res, file_sz[2]); 922 923 for (size_t i = 0; i < 3; i++) { 924 struct iovec *fi = reinterpret_cast<struct iovec *>(cqe->user_data); 925 926 std::string contents("DEADBEEF"); 927 for (size_t j = 0; j < i; ++j) { 928 contents.append(" DEADBEEF"); 929 } 930 931 std::pair<struct iovec *, int> iovec_desc(&fi[i], num_blocks[i]); 932 EXPECT_THAT(iovec_desc, IOVecContainsString(contents.c_str())); 933 934 uint32_t cq_head = io_uring->load_cq_head(); 935 io_uring->store_cq_head(cq_head + 1); 936 } 937 } 938 939 // Testing that io_uring_enter(2) works with closed FDs. 940 TEST(IOUringTest, ReadClosedFD) { 941 SKIP_IF(!IOUringAvailable()); 942 943 IOUringParams params = {}; 944 std::unique_ptr<IOUring> io_uring = 945 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 946 947 ASSERT_EQ(params.sq_entries, 1); 948 ASSERT_EQ(params.cq_entries, 2); 949 950 uint32_t sq_head = io_uring->load_sq_head(); 951 ASSERT_EQ(sq_head, 0); 952 953 auto file_name = NewTempAbsPath(); 954 std::string contents("DEADBEEF"); 955 ASSERT_NO_ERRNO(CreateWithContents(file_name, contents, 0666)); 956 auto filefd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDONLY)); 957 unsigned *sq_array = io_uring->get_sq_array(); 958 struct io_uring_sqe *sqe = io_uring->get_sqes(); 959 960 struct iovec iov; 961 iov.iov_len = contents.size(); 962 void *buf; 963 ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); 964 iov.iov_base = buf; 965 966 sqe[0].flags = 0; 967 sqe[0].fd = filefd.get(); 968 sqe[0].opcode = IORING_OP_READV; 969 sqe[0].addr = reinterpret_cast<uint64_t>(&iov); 970 sqe[0].len = 1; 971 sqe[0].off = 0; 972 sqe[0].user_data = reinterpret_cast<uint64_t>(&iov); 973 sq_array[0] = 0; 974 975 uint32_t sq_tail = io_uring->load_sq_tail(); 976 io_uring->store_sq_tail(sq_tail + 1); 977 978 filefd.reset(); 979 980 IOUring *io_uring_ptr = io_uring.get(); 981 int ret = io_uring_ptr->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 982 ASSERT_EQ(ret, 1); 983 984 struct io_uring_cqe *cqe = io_uring->get_cqes(); 985 986 sq_head = io_uring->load_sq_head(); 987 ASSERT_EQ(sq_head, 1); 988 989 uint32_t cq_tail = io_uring->load_cq_tail(); 990 ASSERT_EQ(cq_tail, 1); 991 992 ASSERT_TRUE(cqe[0].res == -EBADF); 993 uint32_t cq_head = io_uring->load_cq_head(); 994 io_uring->store_cq_head(cq_head + 1); 995 } 996 997 // Testing that io_uring_enter(2) successfully handles single READV operation 998 // with short read situation. 999 TEST(IOUringTest, ShortReadREADVTest) { 1000 SKIP_IF(!IOUringAvailable()); 1001 1002 IOUringParams params = {}; 1003 std::unique_ptr<IOUring> io_uring = 1004 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 1005 1006 ASSERT_EQ(params.sq_entries, 1); 1007 ASSERT_EQ(params.cq_entries, 2); 1008 1009 uint32_t sq_head = io_uring->load_sq_head(); 1010 ASSERT_EQ(sq_head, 0); 1011 1012 std::string file_name = NewTempAbsPath(); 1013 std::string contents("DEADBEEF"); 1014 ASSERT_NO_ERRNO(CreateWithContents(file_name, contents, 0666)); 1015 1016 FileDescriptor filefd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDONLY)); 1017 ASSERT_GE(filefd.get(), 0); 1018 1019 struct stat st = ASSERT_NO_ERRNO_AND_VALUE(Stat(file_name)); 1020 // Set file size to be twice of its actual size to mimic the short read. 1021 off_t file_sz = 2 * st.st_size; 1022 ASSERT_GT(file_sz, 0); 1023 1024 int num_blocks = (file_sz + BLOCK_SZ - 1) / BLOCK_SZ; 1025 ASSERT_EQ(num_blocks, 1); 1026 1027 unsigned *sq_array = io_uring->get_sq_array(); 1028 struct io_uring_sqe *sqe = io_uring->get_sqes(); 1029 1030 struct iovec iov; 1031 iov.iov_len = file_sz; 1032 void *buf; 1033 ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); 1034 iov.iov_base = buf; 1035 1036 sqe->flags = 0; 1037 sqe->fd = filefd.get(); 1038 sqe->opcode = IORING_OP_READV; 1039 sqe->addr = reinterpret_cast<uint64_t>(&iov); 1040 sqe->len = num_blocks; 1041 sqe->off = 0; 1042 sqe->user_data = reinterpret_cast<uint64_t>(&iov); 1043 sq_array[0] = 0; 1044 1045 uint32_t sq_tail = io_uring->load_sq_tail(); 1046 io_uring->store_sq_tail(sq_tail + 1); 1047 1048 int ret = io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 1049 ASSERT_EQ(ret, 1); 1050 1051 struct io_uring_cqe *cqe = io_uring->get_cqes(); 1052 1053 sq_head = io_uring->load_sq_head(); 1054 ASSERT_EQ(sq_head, 1); 1055 1056 uint32_t cq_tail = io_uring->load_cq_tail(); 1057 ASSERT_EQ(cq_tail, 1); 1058 1059 ASSERT_EQ(cqe->res, file_sz / 2); 1060 1061 struct iovec *fi = reinterpret_cast<struct iovec *>(cqe->user_data); 1062 fi->iov_len = file_sz / 2; 1063 1064 std::pair<struct iovec *, int> iovec_desc(fi, num_blocks); 1065 EXPECT_THAT(iovec_desc, IOVecContainsString(contents.c_str())); 1066 1067 uint32_t cq_head = io_uring->load_cq_head(); 1068 io_uring->store_cq_head(cq_head + 1); 1069 } 1070 1071 // Testing that io_uring_enter(2) successfully handles single READV operation 1072 // when there file does not have read permissions. 1073 TEST(IOUringTest, NoReadPermissionsREADVTest) { 1074 SKIP_IF(!IOUringAvailable()); 1075 1076 IOUringParams params = {}; 1077 std::unique_ptr<IOUring> io_uring = 1078 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 1079 1080 ASSERT_EQ(params.sq_entries, 1); 1081 ASSERT_EQ(params.cq_entries, 2); 1082 1083 uint32_t sq_head = io_uring->load_sq_head(); 1084 ASSERT_EQ(sq_head, 0); 1085 1086 std::string file_name = NewTempAbsPath(); 1087 std::string contents("DEADBEEF"); 1088 ASSERT_NO_ERRNO(CreateWithContents(file_name, contents, 0666)); 1089 1090 FileDescriptor filefd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_WRONLY)); 1091 ASSERT_GE(filefd.get(), 0); 1092 1093 struct stat st = ASSERT_NO_ERRNO_AND_VALUE(Stat(file_name)); 1094 off_t file_sz = st.st_size; 1095 ASSERT_GT(file_sz, 0); 1096 1097 int num_blocks = (file_sz + BLOCK_SZ - 1) / BLOCK_SZ; 1098 ASSERT_EQ(num_blocks, 1); 1099 1100 unsigned *sq_array = io_uring->get_sq_array(); 1101 struct io_uring_sqe *sqe = io_uring->get_sqes(); 1102 1103 struct iovec iov; 1104 iov.iov_len = file_sz; 1105 void *buf; 1106 ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); 1107 iov.iov_base = buf; 1108 1109 sqe->flags = 0; 1110 sqe->fd = filefd.get(); 1111 sqe->opcode = IORING_OP_READV; 1112 sqe->addr = reinterpret_cast<uint64_t>(&iov); 1113 sqe->len = num_blocks; 1114 sqe->off = 0; 1115 sqe->user_data = reinterpret_cast<uint64_t>(&iov); 1116 sq_array[0] = 0; 1117 1118 uint32_t sq_tail = io_uring->load_sq_tail(); 1119 io_uring->store_sq_tail(sq_tail + 1); 1120 1121 int ret = io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 1122 ASSERT_EQ(ret, 1); 1123 1124 struct io_uring_cqe *cqe = io_uring->get_cqes(); 1125 1126 sq_head = io_uring->load_sq_head(); 1127 ASSERT_EQ(sq_head, 1); 1128 1129 uint32_t cq_tail = io_uring->load_cq_tail(); 1130 ASSERT_EQ(cq_tail, 1); 1131 1132 ASSERT_EQ(cqe->res, -EBADF); 1133 1134 uint32_t cq_head = io_uring->load_cq_head(); 1135 io_uring->store_cq_head(cq_head + 1); 1136 } 1137 1138 struct SqeFieldsUT { 1139 uint16_t ioprio; 1140 uint16_t buf_index; 1141 }; 1142 1143 class IOUringSqeFieldsTest : public ::testing::Test, 1144 public ::testing::WithParamInterface<SqeFieldsUT> { 1145 }; 1146 1147 // Testing that io_uring_enter(2) successfully handles single READV operation 1148 // and returns EINVAL error in the CQE when ioprio is set. 1149 TEST(IOUringTest, READVWithInvalidSqeFieldValue) { 1150 SKIP_IF(!IOUringAvailable()); 1151 1152 IOUringParams params = {}; 1153 std::unique_ptr<IOUring> io_uring = 1154 ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); 1155 1156 ASSERT_EQ(params.sq_entries, 1); 1157 ASSERT_EQ(params.cq_entries, 2); 1158 1159 uint32_t sq_head = io_uring->load_sq_head(); 1160 ASSERT_EQ(sq_head, 0); 1161 1162 std::string file_name = NewTempAbsPath(); 1163 std::string contents("DEADBEEF"); 1164 ASSERT_NO_ERRNO(CreateWithContents(file_name, contents, 0666)); 1165 1166 FileDescriptor filefd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDONLY)); 1167 ASSERT_GE(filefd.get(), 0); 1168 1169 struct stat st = ASSERT_NO_ERRNO_AND_VALUE(Stat(file_name)); 1170 off_t file_sz = st.st_size; 1171 ASSERT_GT(file_sz, 0); 1172 1173 int num_blocks = (file_sz + BLOCK_SZ - 1) / BLOCK_SZ; 1174 ASSERT_EQ(num_blocks, 1); 1175 1176 unsigned *sq_array = io_uring->get_sq_array(); 1177 struct io_uring_sqe *sqe = io_uring->get_sqes(); 1178 1179 struct iovec iov; 1180 iov.iov_len = file_sz; 1181 void *buf; 1182 ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); 1183 iov.iov_base = buf; 1184 1185 sqe->flags = 0; 1186 sqe->fd = filefd.get(); 1187 sqe->opcode = IORING_OP_READV; 1188 sqe->addr = reinterpret_cast<uint64_t>(&iov); 1189 sqe->len = num_blocks; 1190 sqe->off = 0; 1191 sqe->user_data = reinterpret_cast<uint64_t>(&iov); 1192 sqe->ioprio = 1; 1193 sqe->buf_index = 0; 1194 sq_array[0] = 0; 1195 1196 uint32_t sq_tail = io_uring->load_sq_tail(); 1197 io_uring->store_sq_tail(sq_tail + 1); 1198 1199 int ret = io_uring->Enter(1, 1, IORING_ENTER_GETEVENTS, nullptr); 1200 ASSERT_EQ(ret, 1); 1201 1202 struct io_uring_cqe *cqe = io_uring->get_cqes(); 1203 1204 sq_head = io_uring->load_sq_head(); 1205 ASSERT_EQ(sq_head, 1); 1206 1207 uint32_t cq_tail = io_uring->load_cq_tail(); 1208 ASSERT_EQ(cq_tail, 1); 1209 1210 ASSERT_EQ(cqe->res, -EINVAL); 1211 1212 uint32_t cq_head = io_uring->load_cq_head(); 1213 io_uring->store_cq_head(cq_head + 1); 1214 } 1215 1216 } // namespace 1217 1218 } // namespace testing 1219 } // namespace gvisor