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(&params, 0, sizeof(params));
   108    params.resv[1] = 1;
   109    ASSERT_THAT(IOUringSetup(1, &params), 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, &params), 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, &params), 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(&params, 0, sizeof(params));
   137    params.flags |= IORING_SETUP_SQPOLL;
   138    ASSERT_THAT(IOUringSetup(1, &params), 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