Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RIoUring.hxx
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
3 * All rights reserved. *
4 * *
5 * For the licensing terms see $ROOTSYS/LICENSE. *
6 * For the list of contributors see $ROOTSYS/README/CREDITS. *
7 *************************************************************************/
8
9#ifndef ROOT_RIoUring
10#define ROOT_RIoUring
11
12#include <cerrno>
13#include <cstdint>
14#include <cstring>
15#include <stdexcept>
16#include <string>
17#include <utility>
18
19#include <liburing.h>
20#include <liburing/io_uring.h>
21
22#include "TError.h"
23
24namespace ROOT {
25namespace Internal {
26
27class RIoUring {
28private:
30 std::uint32_t fDepth = 0;
31
32public:
33 // Create an io_uring instance. The ring selects an appropriate queue depth. which can be queried
34 // afterwards using GetQueueDepth(). The depth is typically 1024 or lower. Throws an exception if
35 // ring setup fails.
37 std::uint32_t queueDepth = 1024;
38 int ret;
39 while (true) {
40 ret = io_uring_queue_init(queueDepth, &fRing, 0 /* no flags */);
41 if (ret == 0) {
43 break; // ring setup succeeded
44 }
45 if (ret != -ENOMEM) {
46 throw std::runtime_error("Error initializing io_uring: " + std::string(std::strerror(-ret)));
47 }
48 // try again with a smaller queue for ENOMEM
49 queueDepth /= 2;
50 if (queueDepth == 0) {
51 throw std::runtime_error("Failed to allocate memory for the smallest possible "
52 "io_uring instance. 'memlock' memory has been exhausted for this user");
53 }
54 }
55 }
56
57 // Create a io_uring instance that can hold at least `entriesHint` submission entries. The actual
58 // queue depth is rounded up to the next power of 2. Throws an exception if ring setup fails.
59 explicit RIoUring(std::uint32_t entriesHint) {
60 struct io_uring_params params = {}; /* zero initialize param struct, no flags */
62 if (ret != 0) {
63 throw std::runtime_error("Error initializing io_uring: " + std::string(std::strerror(-ret)));
64 }
65 fDepth = params.sq_entries;
66 }
67
68 RIoUring(const RIoUring&) = delete;
69 RIoUring& operator=(const RIoUring&) = delete;
70
72 // todo(max) try submitting any pending events before exiting
74 }
75
76 std::uint32_t GetQueueDepth() {
77 return fDepth;
78 }
79
80 /// Access the raw io_uring instance.
81 struct io_uring *GetRawRing() {
82 return &fRing;
83 }
84
85 /// Probes whether io_uring is actually usable at runtime. The presence of liburing at build
86 /// time does not imply kernel support: the running kernel may have been compiled without
87 /// CONFIG_IO_URING (e.g. older EL9 kernels) or the io_uring_setup system call may be blocked
88 /// by a seccomp profile. On failure, if errMsg is set, it contains a diagnostic string.
89 /// The kernel is probed only once per process; afterwards the cached result is returned.
90 static bool IsAvailable(std::string *errMsg = nullptr)
91 {
92 static const auto probeResult = [] {
93 // A queue depth of 1 keeps the probe cheap and does not exhaust the memlock limit.
94 struct io_uring ring;
95 int ret = io_uring_queue_init(1 /* queue depth */, &ring, 0 /* no flags */);
96 if (ret == 0) {
98 return std::pair<bool, std::string>(true, "");
99 }
100 std::string msg = std::strerror(-ret);
101 if (ret == -ENOSYS)
102 msg += ": the running kernel does not support io_uring (CONFIG_IO_URING is not set)";
103 else if (ret == -EPERM)
104 msg += ": the io_uring_setup system call is blocked, e.g. by a seccomp profile";
105 return std::pair<bool, std::string>(false, msg);
106 }();
107 if (errMsg)
108 *errMsg = probeResult.second;
109 return probeResult.first;
110 }
111
112 /// Basic read event composed of IO data and a target file descriptor.
113 struct RReadEvent {
114 /// The destination for reading
115 void *fBuffer = nullptr;
116 /// The file offset
117 std::uint64_t fOffset = 0;
118 /// The number of desired bytes
119 std::size_t fSize = 0;
120 /// The number of actually read bytes, set by the RIoUring instance
121 std::size_t fOutBytes = 0;
122 /// The file descriptor
123 int fFileDes = -1;
124 };
125
126 /// Submit a number of read events and wait for completion. Events are submitted in batches if
127 /// the number of events is larger than the submission queue depth.
129 unsigned int batch = 0;
130 unsigned int batchSize = fDepth;
131 unsigned int readPos = 0;
132
133 while (readPos < nReads) {
134 if (readPos + batchSize > nReads) {
135 batchSize = nReads - readPos;
136 }
137 // prep reads
138 struct io_uring_sqe *sqe;
139 for (std::size_t i = readPos; i < readPos + batchSize; ++i) {
141 if (!sqe) {
142 throw std::runtime_error("batch " + std::to_string(batch) + ": "
143 + "get SQE failed for read request '" + std::to_string(i)
144 + "', error: " + std::string(strerror(errno)));
145 }
146 if (readEvents[i].fFileDes == -1) {
147 throw std::runtime_error("batch " + std::to_string(batch) + ": "
148 + "bad fd (-1) for read request '" + std::to_string(i) + "'");
149 }
150 if (readEvents[i].fBuffer == nullptr) {
151 throw std::runtime_error("batch " + std::to_string(batch) + ": "
152 + "null read buffer for read request '" + std::to_string(i) + "'");
153 }
155 readEvents[i].fFileDes,
157 readEvents[i].fSize,
158 readEvents[i].fOffset
159 );
160 sqe->flags |= IOSQE_ASYNC; // maximize read event throughput
161 sqe->user_data = i;
162 }
163
164 // todo(max) check for any difference between submit vs. submit and wait for large nReq
165 int submitted = io_uring_submit_and_wait(&fRing, batchSize);
166 if (submitted <= 0) {
167 throw std::runtime_error("batch " + std::to_string(batch) + ": "
168 "ring submit failed, error: " + std::string(strerror(errno)));
169 }
170 if (submitted != static_cast<int>(batchSize)) {
171 throw std::runtime_error("ring submitted " + std::to_string(submitted) +
172 " events but requested " + std::to_string(batchSize));
173 }
174 // reap reads
175 struct io_uring_cqe *cqe;
176 int ret;
177 for (int i = 0; i < submitted; ++i) {
179 if (ret < 0) {
180 throw std::runtime_error("wait cqe failed, error: " + std::string(std::strerror(-ret)));
181 }
182 auto index = reinterpret_cast<std::size_t>(io_uring_cqe_get_data(cqe));
183 if (index >= nReads) {
184 throw std::runtime_error("bad cqe user data: " + std::to_string(index));
185 }
186 if (cqe->res < 0) {
187 throw std::runtime_error("batch " + std::to_string(batch) + ": "
188 + "read failed for ReadEvent[" + std::to_string(index) + "], "
189 "error: " + std::string(std::strerror(-cqe->res)));
190 }
191 readEvents[index].fOutBytes = static_cast<std::size_t>(cqe->res);
193 }
194 readPos += batchSize;
195 batch += 1;
196 }
197 return;
198 }
199};
200
201} // namespace Internal
202} // namespace ROOT
203
204#endif
fBuffer
dim_t fSize
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
struct io_uring fRing
Definition RIoUring.hxx:29
RIoUring(std::uint32_t entriesHint)
Definition RIoUring.hxx:59
std::uint32_t GetQueueDepth()
Definition RIoUring.hxx:76
RIoUring(const RIoUring &)=delete
RIoUring & operator=(const RIoUring &)=delete
static bool IsAvailable(std::string *errMsg=nullptr)
Probes whether io_uring is actually usable at runtime.
Definition RIoUring.hxx:90
void SubmitReadsAndWait(RReadEvent *readEvents, unsigned int nReads)
Submit a number of read events and wait for completion.
Definition RIoUring.hxx:128
struct io_uring * GetRawRing()
Access the raw io_uring instance.
Definition RIoUring.hxx:81
Basic read event composed of IO data and a target file descriptor.
Definition RIoUring.hxx:113
int fFileDes
The file descriptor.
Definition RIoUring.hxx:123
std::uint64_t fOffset
The file offset.
Definition RIoUring.hxx:117
void * fBuffer
The destination for reading.
Definition RIoUring.hxx:115
std::size_t fOutBytes
The number of actually read bytes, set by the RIoUring instance.
Definition RIoUring.hxx:121
std::size_t fSize
The number of desired bytes.
Definition RIoUring.hxx:119