gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/io_uring_util.h (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 #ifndef GVISOR_TEST_UTIL_IOURING_UTIL_H_ 16 #define GVISOR_TEST_UTIL_IOURING_UTIL_H_ 17 18 #include <linux/fs.h> 19 #include <sys/ioctl.h> 20 #include <sys/mman.h> 21 22 #include <atomic> 23 #include <cerrno> 24 #include <cstdint> 25 26 #include "test/util/file_descriptor.h" 27 #include "test/util/posix_error.h" 28 #include "test/util/save_util.h" 29 30 namespace gvisor { 31 namespace testing { 32 33 #define __NR_io_uring_setup 425 34 #define __NR_io_uring_enter 426 35 36 // io_uring_setup(2) flags. 37 #define IORING_SETUP_SQPOLL (1U << 1) 38 #define IORING_SETUP_CQSIZE (1U << 3) 39 40 // io_uring_enter(2) flags 41 #define IORING_ENTER_GETEVENTS (1U << 0) 42 43 #define IORING_FEAT_SINGLE_MMAP (1U << 0) 44 45 #define IORING_OFF_SQ_RING 0ULL 46 #define IORING_OFF_CQ_RING 0x8000000ULL 47 #define IORING_OFF_SQES 0x10000000ULL 48 49 // IO_URING operation codes. 50 #define IORING_OP_NOP 0 51 #define IORING_OP_READV 1 52 53 #define BLOCK_SZ kPageSize 54 55 struct io_sqring_offsets { 56 uint32_t head; 57 uint32_t tail; 58 uint32_t ring_mask; 59 uint32_t ring_entries; 60 uint32_t flags; 61 uint32_t dropped; 62 uint32_t array; 63 uint32_t resv1; 64 uint64_t resv2; 65 }; 66 67 struct io_cqring_offsets { 68 uint32_t head; 69 uint32_t tail; 70 uint32_t ring_mask; 71 uint32_t ring_entries; 72 uint32_t overflow; 73 uint32_t cqes; 74 uint32_t flags; 75 uint32_t resv1; 76 uint64_t resv2; 77 }; 78 79 struct io_uring_params { 80 uint32_t sq_entries; 81 uint32_t cq_entries; 82 uint32_t flags; 83 uint32_t sq_thread_cpu; 84 uint32_t sq_thread_idle; 85 uint32_t features; 86 uint32_t wq_fd; 87 uint32_t resv[3]; 88 struct io_sqring_offsets sq_off; 89 struct io_cqring_offsets cq_off; 90 }; 91 92 struct io_uring_cqe { 93 uint64_t user_data; 94 int32_t res; 95 uint32_t flags; 96 }; 97 98 struct io_uring_sqe { 99 uint8_t opcode; 100 uint8_t flags; 101 uint16_t ioprio; 102 int32_t fd; 103 union { 104 uint64_t off; 105 uint64_t addr2; 106 struct { 107 uint32_t cmd_op; 108 uint32_t __pad1; 109 }; 110 }; 111 union { 112 uint64_t addr; 113 uint64_t splice_off_in; 114 }; 115 uint32_t len; 116 union { 117 __kernel_rwf_t rw_flags; 118 uint32_t fsync_flags; 119 uint16_t poll_events; 120 uint32_t poll32_events; 121 uint32_t sync_range_flags; 122 uint32_t msg_flags; 123 uint32_t timeout_flags; 124 uint32_t accept_flags; 125 uint32_t cancel_flags; 126 uint32_t open_flags; 127 uint32_t statx_flags; 128 uint32_t fadvise_advice; 129 uint32_t splice_flags; 130 uint32_t rename_flags; 131 uint32_t unlink_flags; 132 uint32_t hardlink_flags; 133 uint32_t xattr_flags; 134 }; 135 uint64_t user_data; 136 union { 137 uint16_t buf_index; 138 uint16_t buf_group; 139 } __attribute__((packed)); 140 uint16_t personality; 141 union { 142 int32_t splice_fd_in; 143 uint32_t file_index; 144 }; 145 union { 146 struct { 147 uint64_t addr3; 148 uint64_t __pad2[1]; 149 }; 150 uint8_t cmd[0]; 151 }; 152 }; 153 154 using IOSqringOffsets = struct io_sqring_offsets; 155 using ICqringOffsets = struct io_cqring_offsets; 156 using IOUringCqe = struct io_uring_cqe; 157 using IOUringParams = struct io_uring_params; 158 using IOUringSqe = struct io_uring_sqe; 159 160 // Helper class for IO_URING 161 class IOUring { 162 public: 163 IOUring() = delete; 164 IOUring(FileDescriptor &&fd, unsigned int entries, IOUringParams ¶ms); 165 ~IOUring(); 166 167 static PosixErrorOr<std::unique_ptr<IOUring>> InitIOUring( 168 unsigned int entries, IOUringParams ¶ms); 169 170 uint32_t load_cq_head(); 171 uint32_t load_cq_tail(); 172 uint32_t load_sq_head(); 173 uint32_t load_sq_tail(); 174 uint32_t load_cq_overflow(); 175 uint32_t load_sq_dropped(); 176 void store_cq_head(uint32_t cq_head_val); 177 void store_sq_tail(uint32_t sq_tail_val); 178 int Enter(unsigned int to_submit, unsigned int min_complete, 179 unsigned int flags, sigset_t *sig); 180 181 IOUringCqe *get_cqes(); 182 IOUringSqe *get_sqes(); 183 uint32_t get_sq_mask(); 184 unsigned *get_sq_array(); 185 186 int Fd() { return iouringfd_.get(); } 187 188 private: 189 IOUringCqe *cqes_ = nullptr; 190 FileDescriptor iouringfd_; 191 size_t cring_sz_; 192 size_t sring_sz_; 193 size_t sqes_sz_; 194 uint32_t sq_mask_; 195 unsigned *sq_array_ = nullptr; 196 uint32_t *cq_head_ptr_ = nullptr; 197 uint32_t *cq_tail_ptr_ = nullptr; 198 uint32_t *sq_head_ptr_ = nullptr; 199 uint32_t *sq_tail_ptr_ = nullptr; 200 uint32_t *cq_overflow_ptr_ = nullptr; 201 uint32_t *sq_dropped_ptr_ = nullptr; 202 void *sq_ptr_ = nullptr; 203 void *cq_ptr_ = nullptr; 204 void *sqe_ptr_ = nullptr; 205 }; 206 207 // This is a wrapper for the io_uring_setup(2) system call. 208 inline int IOUringSetup(uint32_t entries, IOUringParams *params) { 209 return syscall(__NR_io_uring_setup, entries, params); 210 } 211 212 // This is a wrapper for the io_uring_enter(2) system call. 213 inline int IOUringEnter(unsigned int fd, unsigned int to_submit, 214 unsigned int min_complete, unsigned int flags, 215 sigset_t *sig) { 216 return syscall(__NR_io_uring_enter, fd, to_submit, min_complete, flags, sig); 217 } 218 219 // Returns a new iouringfd with the given number of entries. 220 inline PosixErrorOr<FileDescriptor> NewIOUringFD(uint32_t entries, 221 IOUringParams ¶ms) { 222 memset(¶ms, 0, sizeof(params)); 223 int fd = IOUringSetup(entries, ¶ms); 224 MaybeSave(); 225 if (fd < 0) { 226 return PosixError(errno, "io_uring_setup"); 227 } 228 return FileDescriptor(fd); 229 } 230 231 template <typename T> 232 static inline void io_uring_atomic_write(T *p, T v) { 233 std::atomic_store_explicit(reinterpret_cast<std::atomic<T> *>(p), v, 234 std::memory_order_release); 235 } 236 237 template <typename T> 238 static inline T io_uring_atomic_read(const T *p) { 239 return std::atomic_load_explicit(reinterpret_cast<const std::atomic<T> *>(p), 240 std::memory_order_acquire); 241 } 242 243 } // namespace testing 244 } // namespace gvisor 245 246 #endif // GVISOR_TEST_UTIL_IOURING_UTIL_H_