| 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 |  | 
|  | 17 | #include <asm-generic/errno-base.h> | 
|  | 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 |