blob: f561d257f2851dfc45f530833b12797bf1b5fb4a [file] [log] [blame]
Kelvin Zhang1bd2e5b2022-10-03 12:02:34 -07001//
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
28namespace io_uring_cpp {
29
30template <typename T>
31bool 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
37class 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 Zhang301d1722022-11-21 20:57:32 -0800103
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 Zhang1bd2e5b2022-10-03 12:02:34 -0700107 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
168const char* Errno::ErrMsg() {
169 if (error_code == 0) {
170 return nullptr;
171 }
172 return strerror(error_code);
173}
174
175std::ostream& operator<<(std::ostream& out, Errno err) {
176 out << err.ErrCode() << ", " << err.ErrMsg();
177 return out;
178}
179
180std::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