Kelvin Zhang | 1bd2e5b | 2022-10-03 12:02:34 -0700 | [diff] [blame] | 1 | // |
| 2 | // Copyright (C) 2022 The Android Open Source Project |
| 3 | // |
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | // you may not use this file except in compliance with the License. |
| 6 | // You may obtain a copy of the License at |
| 7 | // |
| 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | // |
| 10 | // Unless required by applicable law or agreed to in writing, software |
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | // See the License for the specific language governing permissions and |
| 14 | // limitations under the License. |
| 15 | // |
| 16 | |
Elliott Hughes | 13bc0b8 | 2024-11-12 15:23:38 +0000 | [diff] [blame^] | 17 | #include <errno.h> |
Kelvin Zhang | 1bd2e5b | 2022-10-03 12:02:34 -0700 | [diff] [blame] | 18 | #include <liburing_cpp/IoUring.h> |
| 19 | #include <string.h> |
| 20 | |
| 21 | #include <algorithm> |
| 22 | #include <iostream> |
| 23 | #include <memory> |
| 24 | |
| 25 | #include "liburing.h" |
| 26 | #include "liburing_cpp/IoUringCQE.h" |
| 27 | |
| 28 | namespace io_uring_cpp { |
| 29 | |
| 30 | template <typename T> |
| 31 | bool IsZeroInitialized(const T& val) { |
| 32 | auto begin = reinterpret_cast<const char*>(&val); |
| 33 | auto end = begin + sizeof(val); |
| 34 | return std::all_of(begin, end, [](const auto& a) { return a == 0; }); |
| 35 | } |
| 36 | |
| 37 | class IoUring final : public IoUringInterface { |
| 38 | public: |
| 39 | ~IoUring() override { |
| 40 | if (!IsZeroInitialized(ring)) { |
| 41 | if (buffer_registered_) { |
| 42 | UnregisterBuffers(); |
| 43 | } |
| 44 | if (files_registered_) { |
| 45 | UnregisterFiles(); |
| 46 | } |
| 47 | io_uring_queue_exit(&ring); |
| 48 | } |
| 49 | } |
| 50 | IoUring(const IoUring&) = delete; |
| 51 | IoUring(IoUring&& rhs) { |
| 52 | ring = rhs.ring; |
| 53 | memset(&rhs.ring, 0, sizeof(rhs.ring)); |
| 54 | } |
| 55 | IoUring& operator=(IoUring&& rhs) { |
| 56 | std::swap(ring, rhs.ring); |
| 57 | return *this; |
| 58 | } |
| 59 | Errno RegisterBuffers(const struct iovec* iovecs, |
| 60 | size_t iovec_size) override { |
| 61 | const auto ret = |
| 62 | Errno(io_uring_register_buffers(&ring, iovecs, iovec_size)); |
| 63 | buffer_registered_ = ret.IsOk(); |
| 64 | return ret; |
| 65 | } |
| 66 | |
| 67 | Errno UnregisterBuffers() override { |
| 68 | const auto ret = Errno(io_uring_unregister_buffers(&ring)); |
| 69 | buffer_registered_ = !ret.IsOk(); |
| 70 | return ret; |
| 71 | } |
| 72 | |
| 73 | Errno RegisterFiles(const int* files, size_t files_size) override { |
| 74 | const auto ret = Errno(io_uring_register_files(&ring, files, files_size)); |
| 75 | files_registered_ = ret.IsOk(); |
| 76 | return ret; |
| 77 | } |
| 78 | |
| 79 | Errno UnregisterFiles() { |
| 80 | const auto ret = Errno(io_uring_unregister_files(&ring)); |
| 81 | files_registered_ = !ret.IsOk(); |
| 82 | return ret; |
| 83 | } |
| 84 | |
| 85 | IoUringSQE PrepRead(int fd, void* buf, unsigned nbytes, |
| 86 | uint64_t offset) override { |
| 87 | auto sqe = io_uring_get_sqe(&ring); |
| 88 | if (sqe == nullptr) { |
| 89 | return IoUringSQE{nullptr}; |
| 90 | } |
| 91 | io_uring_prep_read(sqe, fd, buf, nbytes, offset); |
| 92 | return IoUringSQE{static_cast<void*>(sqe)}; |
| 93 | } |
| 94 | IoUringSQE PrepWrite(int fd, const void* buf, unsigned nbytes, |
| 95 | uint64_t offset) override { |
| 96 | auto sqe = io_uring_get_sqe(&ring); |
| 97 | if (sqe == nullptr) { |
| 98 | return IoUringSQE{nullptr}; |
| 99 | } |
| 100 | io_uring_prep_write(sqe, fd, buf, nbytes, offset); |
| 101 | return IoUringSQE{static_cast<void*>(sqe)}; |
| 102 | } |
Kelvin Zhang | 301d172 | 2022-11-21 20:57:32 -0800 | [diff] [blame] | 103 | |
| 104 | size_t SQELeft() const override { return io_uring_sq_space_left(&ring); } |
| 105 | size_t SQEReady() const override { return io_uring_sq_ready(&ring); } |
| 106 | |
Kelvin Zhang | 1bd2e5b | 2022-10-03 12:02:34 -0700 | [diff] [blame] | 107 | IoUringSubmitResult Submit() override { |
| 108 | return IoUringSubmitResult{io_uring_submit(&ring)}; |
| 109 | } |
| 110 | |
| 111 | IoUringSubmitResult SubmitAndWait(size_t completions) override { |
| 112 | return IoUringSubmitResult{io_uring_submit_and_wait(&ring, completions)}; |
| 113 | } |
| 114 | |
| 115 | Result<Errno, std::vector<IoUringCQE>> PopCQE( |
| 116 | const unsigned int count) override { |
| 117 | std::vector<io_uring_cqe*> cqe_ptrs; |
| 118 | cqe_ptrs.resize(count); |
| 119 | const auto ret = io_uring_wait_cqe_nr(&ring, cqe_ptrs.data(), count); |
| 120 | if (ret != 0) { |
| 121 | return {Errno(ret)}; |
| 122 | } |
| 123 | const auto filled = io_uring_peek_batch_cqe(&ring, cqe_ptrs.data(), count); |
| 124 | if (filled != count) { |
| 125 | return {Errno(EAGAIN)}; |
| 126 | } |
| 127 | std::vector<IoUringCQE> cqes; |
| 128 | cqes.reserve(count); |
| 129 | for (const auto& cqe : cqe_ptrs) { |
| 130 | if (cqe == nullptr) { |
| 131 | return {Errno(EAGAIN)}; |
| 132 | } |
| 133 | cqes.push_back(IoUringCQE(cqe->res, cqe->flags, cqe->user_data)); |
| 134 | io_uring_cqe_seen(&ring, cqe); |
| 135 | } |
| 136 | return {cqes}; |
| 137 | } |
| 138 | |
| 139 | Result<Errno, IoUringCQE> PopCQE() override { |
| 140 | struct io_uring_cqe* ptr{}; |
| 141 | const auto ret = io_uring_wait_cqe(&ring, &ptr); |
| 142 | if (ret != 0) { |
| 143 | return {Errno(ret)}; |
| 144 | } |
| 145 | const auto cqe = IoUringCQE(ptr->res, ptr->flags, ptr->user_data); |
| 146 | io_uring_cqe_seen(&ring, ptr); |
| 147 | return {cqe}; |
| 148 | } |
| 149 | |
| 150 | Result<Errno, IoUringCQE> PeekCQE() override { |
| 151 | struct io_uring_cqe* ptr{}; |
| 152 | const auto ret = io_uring_peek_cqe(&ring, &ptr); |
| 153 | if (ret != 0) { |
| 154 | return {Errno(ret)}; |
| 155 | } |
| 156 | return {IoUringCQE(ptr->res, ptr->flags, ptr->user_data)}; |
| 157 | } |
| 158 | |
| 159 | IoUring(struct io_uring r) : ring(r) {} |
| 160 | |
| 161 | private: |
| 162 | struct io_uring ring {}; |
| 163 | bool buffer_registered_ = false; |
| 164 | bool files_registered_ = false; |
| 165 | std::atomic<size_t> request_id_{}; |
| 166 | }; |
| 167 | |
| 168 | const char* Errno::ErrMsg() { |
| 169 | if (error_code == 0) { |
| 170 | return nullptr; |
| 171 | } |
| 172 | return strerror(error_code); |
| 173 | } |
| 174 | |
| 175 | std::ostream& operator<<(std::ostream& out, Errno err) { |
| 176 | out << err.ErrCode() << ", " << err.ErrMsg(); |
| 177 | return out; |
| 178 | } |
| 179 | |
| 180 | std::unique_ptr<IoUringInterface> IoUringInterface::CreateLinuxIoUring( |
| 181 | int queue_depth, int flags) { |
| 182 | struct io_uring ring {}; |
| 183 | const auto err = io_uring_queue_init(queue_depth, &ring, flags); |
| 184 | if (err) { |
| 185 | errno = -err; |
| 186 | return {}; |
| 187 | } |
| 188 | return std::unique_ptr<IoUringInterface>(new IoUring(ring)); |
| 189 | } |
| 190 | |
| 191 | } // namespace io_uring_cpp |