• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <errno.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>
IsZeroInitialized(const T & val)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:
~IoUring()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;
IoUring(IoUring && rhs)51   IoUring(IoUring&& rhs) {
52     ring = rhs.ring;
53     memset(&rhs.ring, 0, sizeof(rhs.ring));
54   }
operator =(IoUring && rhs)55   IoUring& operator=(IoUring&& rhs) {
56     std::swap(ring, rhs.ring);
57     return *this;
58   }
RegisterBuffers(const struct iovec * iovecs,size_t iovec_size)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 
UnregisterBuffers()67   Errno UnregisterBuffers() override {
68     const auto ret = Errno(io_uring_unregister_buffers(&ring));
69     buffer_registered_ = !ret.IsOk();
70     return ret;
71   }
72 
RegisterFiles(const int * files,size_t files_size)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 
UnregisterFiles()79   Errno UnregisterFiles() {
80     const auto ret = Errno(io_uring_unregister_files(&ring));
81     files_registered_ = !ret.IsOk();
82     return ret;
83   }
84 
PrepReadFixed(int fd,void * buf,unsigned nbytes,uint64_t offset,int buf_index)85   IoUringSQE PrepReadFixed(int fd,
86                            void* buf,
87                            unsigned nbytes,
88                            uint64_t offset,
89                            int buf_index) override {
90     auto sqe = io_uring_get_sqe(&ring);
91     if (sqe == nullptr) {
92       return IoUringSQE{nullptr};
93     }
94     io_uring_prep_read_fixed(sqe, fd, buf, nbytes, offset, buf_index);
95     return IoUringSQE{static_cast<void*>(sqe)};
96   }
97 
PrepRead(int fd,void * buf,unsigned nbytes,uint64_t offset)98   IoUringSQE PrepRead(int fd, void* buf, unsigned nbytes,
99                       uint64_t offset) override {
100     auto sqe = io_uring_get_sqe(&ring);
101     if (sqe == nullptr) {
102       return IoUringSQE{nullptr};
103     }
104     io_uring_prep_read(sqe, fd, buf, nbytes, offset);
105     return IoUringSQE{static_cast<void*>(sqe)};
106   }
PrepWrite(int fd,const void * buf,unsigned nbytes,uint64_t offset)107   IoUringSQE PrepWrite(int fd, const void* buf, unsigned nbytes,
108                        uint64_t offset) override {
109     auto sqe = io_uring_get_sqe(&ring);
110     if (sqe == nullptr) {
111       return IoUringSQE{nullptr};
112     }
113     io_uring_prep_write(sqe, fd, buf, nbytes, offset);
114     return IoUringSQE{static_cast<void*>(sqe)};
115   }
116 
SQELeft() const117   size_t SQELeft() const override { return io_uring_sq_space_left(&ring); }
SQEReady() const118   size_t SQEReady() const override { return io_uring_sq_ready(&ring); }
119 
Submit()120   IoUringSubmitResult Submit() override {
121     return IoUringSubmitResult{io_uring_submit(&ring)};
122   }
123 
SubmitAndWait(size_t completions)124   IoUringSubmitResult SubmitAndWait(size_t completions) override {
125     return IoUringSubmitResult{io_uring_submit_and_wait(&ring, completions)};
126   }
127 
PopCQE(const unsigned int count)128   Result<Errno, std::vector<IoUringCQE>> PopCQE(
129       const unsigned int count) override {
130     std::vector<io_uring_cqe*> cqe_ptrs;
131     cqe_ptrs.resize(count);
132     const auto ret = io_uring_wait_cqe_nr(&ring, cqe_ptrs.data(), count);
133     if (ret != 0) {
134       return {Errno(ret)};
135     }
136     const auto filled = io_uring_peek_batch_cqe(&ring, cqe_ptrs.data(), count);
137     if (filled != count) {
138       return {Errno(EAGAIN)};
139     }
140     std::vector<IoUringCQE> cqes;
141     cqes.reserve(count);
142     for (const auto& cqe : cqe_ptrs) {
143       if (cqe == nullptr) {
144         return {Errno(EAGAIN)};
145       }
146       cqes.push_back(IoUringCQE(cqe->res, cqe->flags, cqe->user_data));
147       io_uring_cqe_seen(&ring, cqe);
148     }
149     return {cqes};
150   }
151 
PopCQE()152   Result<Errno, IoUringCQE> PopCQE() override {
153     struct io_uring_cqe* ptr{};
154     const auto ret = io_uring_wait_cqe(&ring, &ptr);
155     if (ret != 0) {
156       return {Errno(ret)};
157     }
158     const auto cqe = IoUringCQE(ptr->res, ptr->flags, ptr->user_data);
159     io_uring_cqe_seen(&ring, ptr);
160     return {cqe};
161   }
162 
PeekCQE()163   Result<Errno, IoUringCQE> PeekCQE() override {
164     struct io_uring_cqe* ptr{};
165     const auto ret = io_uring_peek_cqe(&ring, &ptr);
166     if (ret != 0) {
167       return {Errno(ret)};
168     }
169     return {IoUringCQE(ptr->res, ptr->flags, ptr->user_data)};
170   }
171 
IoUring(struct io_uring r)172   IoUring(struct io_uring r) : ring(r) {}
173 
174  private:
175   struct io_uring ring {};
176   bool buffer_registered_ = false;
177   bool files_registered_ = false;
178   std::atomic<size_t> request_id_{};
179 };
180 
ErrMsg()181 const char* Errno::ErrMsg() {
182   if (error_code == 0) {
183     return nullptr;
184   }
185   return strerror(error_code);
186 }
187 
operator <<(std::ostream & out,Errno err)188 std::ostream& operator<<(std::ostream& out, Errno err) {
189   out << err.ErrCode() << ", " << err.ErrMsg();
190   return out;
191 }
192 
CreateLinuxIoUring(int queue_depth,int flags)193 std::unique_ptr<IoUringInterface> IoUringInterface::CreateLinuxIoUring(
194     int queue_depth, int flags) {
195   struct io_uring ring {};
196   const auto err = io_uring_queue_init(queue_depth, &ring, flags);
197   if (err) {
198     errno = -err;
199     return {};
200   }
201   return std::unique_ptr<IoUringInterface>(new IoUring(ring));
202 }
203 
204 }  // namespace io_uring_cpp
205