|  | // | 
|  | // Copyright (C) 2022 The Android Open Source Project | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  | // | 
|  |  | 
|  | #include <asm-generic/errno-base.h> | 
|  | #include <liburing_cpp/IoUring.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <iostream> | 
|  | #include <memory> | 
|  |  | 
|  | #include "liburing.h" | 
|  | #include "liburing_cpp/IoUringCQE.h" | 
|  |  | 
|  | namespace io_uring_cpp { | 
|  |  | 
|  | template <typename T> | 
|  | bool IsZeroInitialized(const T& val) { | 
|  | auto begin = reinterpret_cast<const char*>(&val); | 
|  | auto end = begin + sizeof(val); | 
|  | return std::all_of(begin, end, [](const auto& a) { return a == 0; }); | 
|  | } | 
|  |  | 
|  | class IoUring final : public IoUringInterface { | 
|  | public: | 
|  | ~IoUring() override { | 
|  | if (!IsZeroInitialized(ring)) { | 
|  | if (buffer_registered_) { | 
|  | UnregisterBuffers(); | 
|  | } | 
|  | if (files_registered_) { | 
|  | UnregisterFiles(); | 
|  | } | 
|  | io_uring_queue_exit(&ring); | 
|  | } | 
|  | } | 
|  | IoUring(const IoUring&) = delete; | 
|  | IoUring(IoUring&& rhs) { | 
|  | ring = rhs.ring; | 
|  | memset(&rhs.ring, 0, sizeof(rhs.ring)); | 
|  | } | 
|  | IoUring& operator=(IoUring&& rhs) { | 
|  | std::swap(ring, rhs.ring); | 
|  | return *this; | 
|  | } | 
|  | Errno RegisterBuffers(const struct iovec* iovecs, | 
|  | size_t iovec_size) override { | 
|  | const auto ret = | 
|  | Errno(io_uring_register_buffers(&ring, iovecs, iovec_size)); | 
|  | buffer_registered_ = ret.IsOk(); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | Errno UnregisterBuffers() override { | 
|  | const auto ret = Errno(io_uring_unregister_buffers(&ring)); | 
|  | buffer_registered_ = !ret.IsOk(); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | Errno RegisterFiles(const int* files, size_t files_size) override { | 
|  | const auto ret = Errno(io_uring_register_files(&ring, files, files_size)); | 
|  | files_registered_ = ret.IsOk(); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | Errno UnregisterFiles() { | 
|  | const auto ret = Errno(io_uring_unregister_files(&ring)); | 
|  | files_registered_ = !ret.IsOk(); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | IoUringSQE PrepRead(int fd, void* buf, unsigned nbytes, | 
|  | uint64_t offset) override { | 
|  | auto sqe = io_uring_get_sqe(&ring); | 
|  | if (sqe == nullptr) { | 
|  | return IoUringSQE{nullptr}; | 
|  | } | 
|  | io_uring_prep_read(sqe, fd, buf, nbytes, offset); | 
|  | return IoUringSQE{static_cast<void*>(sqe)}; | 
|  | } | 
|  | IoUringSQE PrepWrite(int fd, const void* buf, unsigned nbytes, | 
|  | uint64_t offset) override { | 
|  | auto sqe = io_uring_get_sqe(&ring); | 
|  | if (sqe == nullptr) { | 
|  | return IoUringSQE{nullptr}; | 
|  | } | 
|  | io_uring_prep_write(sqe, fd, buf, nbytes, offset); | 
|  | return IoUringSQE{static_cast<void*>(sqe)}; | 
|  | } | 
|  |  | 
|  | size_t SQELeft() const override { return io_uring_sq_space_left(&ring); } | 
|  | size_t SQEReady() const override { return io_uring_sq_ready(&ring); } | 
|  |  | 
|  | IoUringSubmitResult Submit() override { | 
|  | return IoUringSubmitResult{io_uring_submit(&ring)}; | 
|  | } | 
|  |  | 
|  | IoUringSubmitResult SubmitAndWait(size_t completions) override { | 
|  | return IoUringSubmitResult{io_uring_submit_and_wait(&ring, completions)}; | 
|  | } | 
|  |  | 
|  | Result<Errno, std::vector<IoUringCQE>> PopCQE( | 
|  | const unsigned int count) override { | 
|  | std::vector<io_uring_cqe*> cqe_ptrs; | 
|  | cqe_ptrs.resize(count); | 
|  | const auto ret = io_uring_wait_cqe_nr(&ring, cqe_ptrs.data(), count); | 
|  | if (ret != 0) { | 
|  | return {Errno(ret)}; | 
|  | } | 
|  | const auto filled = io_uring_peek_batch_cqe(&ring, cqe_ptrs.data(), count); | 
|  | if (filled != count) { | 
|  | return {Errno(EAGAIN)}; | 
|  | } | 
|  | std::vector<IoUringCQE> cqes; | 
|  | cqes.reserve(count); | 
|  | for (const auto& cqe : cqe_ptrs) { | 
|  | if (cqe == nullptr) { | 
|  | return {Errno(EAGAIN)}; | 
|  | } | 
|  | cqes.push_back(IoUringCQE(cqe->res, cqe->flags, cqe->user_data)); | 
|  | io_uring_cqe_seen(&ring, cqe); | 
|  | } | 
|  | return {cqes}; | 
|  | } | 
|  |  | 
|  | Result<Errno, IoUringCQE> PopCQE() override { | 
|  | struct io_uring_cqe* ptr{}; | 
|  | const auto ret = io_uring_wait_cqe(&ring, &ptr); | 
|  | if (ret != 0) { | 
|  | return {Errno(ret)}; | 
|  | } | 
|  | const auto cqe = IoUringCQE(ptr->res, ptr->flags, ptr->user_data); | 
|  | io_uring_cqe_seen(&ring, ptr); | 
|  | return {cqe}; | 
|  | } | 
|  |  | 
|  | Result<Errno, IoUringCQE> PeekCQE() override { | 
|  | struct io_uring_cqe* ptr{}; | 
|  | const auto ret = io_uring_peek_cqe(&ring, &ptr); | 
|  | if (ret != 0) { | 
|  | return {Errno(ret)}; | 
|  | } | 
|  | return {IoUringCQE(ptr->res, ptr->flags, ptr->user_data)}; | 
|  | } | 
|  |  | 
|  | IoUring(struct io_uring r) : ring(r) {} | 
|  |  | 
|  | private: | 
|  | struct io_uring ring {}; | 
|  | bool buffer_registered_ = false; | 
|  | bool files_registered_ = false; | 
|  | std::atomic<size_t> request_id_{}; | 
|  | }; | 
|  |  | 
|  | const char* Errno::ErrMsg() { | 
|  | if (error_code == 0) { | 
|  | return nullptr; | 
|  | } | 
|  | return strerror(error_code); | 
|  | } | 
|  |  | 
|  | std::ostream& operator<<(std::ostream& out, Errno err) { | 
|  | out << err.ErrCode() << ", " << err.ErrMsg(); | 
|  | return out; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<IoUringInterface> IoUringInterface::CreateLinuxIoUring( | 
|  | int queue_depth, int flags) { | 
|  | struct io_uring ring {}; | 
|  | const auto err = io_uring_queue_init(queue_depth, &ring, flags); | 
|  | if (err) { | 
|  | errno = -err; | 
|  | return {}; | 
|  | } | 
|  | return std::unique_ptr<IoUringInterface>(new IoUring(ring)); | 
|  | } | 
|  |  | 
|  | }  // namespace io_uring_cpp |