Condy v1.8
C++ Asynchronous System Call Layer for Linux
Loading...
Searching...
No Matches
zcrx.hpp
1#pragma once
2
7#include "condy/runtime.hpp"
8#include <bit>
9#include <sys/mman.h>
10
11namespace condy {
12
13#if !IO_URING_CHECK_VERSION(2, 15) // >= 2.15
14
16
25class ZeroCopyRxBuffer : public detail::ManagedBuffer<ZeroCopyRxBufferPool> {
26public:
27 using Base = detail::ManagedBuffer<ZeroCopyRxBufferPool>;
28 using Base::Base;
29};
30
37 void *addr = nullptr;
39 size_t size;
40};
41
49 size_t offset;
51 size_t size;
52};
53
68public:
76 ZeroCopyRxBufferPool(uint32_t if_idx, uint32_t if_rxq, uint32_t rq_entries,
77 const ZeroCopyRxArea &area)
78 : ZeroCopyRxBufferPool(*detail::Context::current().runtime(), if_idx,
79 if_rxq, rq_entries, area) {}
80
89 ZeroCopyRxBufferPool(Runtime &runtime, uint32_t if_idx, uint32_t if_rxq,
90 uint32_t rq_entries, const ZeroCopyRxArea &area)
91 : ZeroCopyRxBufferPool(runtime.ring_internal(), if_idx, if_rxq,
92 rq_entries, area, 0) {}
93
94 // Device-less constructor, DO NOT use this in production code if you don't
95 // know what you are doing.
96 ZeroCopyRxBufferPool(Runtime &runtime, uint32_t rq_entries,
97 const ZeroCopyRxArea &area)
98 : ZeroCopyRxBufferPool(runtime.ring_internal(), 0, 0, rq_entries, area,
99 ZCRX_REG_NODEV) {}
100
108 ZeroCopyRxBufferPool(uint32_t if_idx, uint32_t if_rxq, uint32_t rq_entries,
109 const ZeroCopyRxDMABufArea &area)
110 : ZeroCopyRxBufferPool(*detail::Context::current().runtime(), if_idx,
111 if_rxq, rq_entries, area) {}
112
121 ZeroCopyRxBufferPool(Runtime &runtime, uint32_t if_idx, uint32_t if_rxq,
122 uint32_t rq_entries, const ZeroCopyRxDMABufArea &area)
123 : ring_(&runtime.ring_internal()), flags_(0) {
124 area_size_ = 0;
125 area_ptr_ = nullptr;
126
127 io_uring_zcrx_area_reg area_reg = {};
128 area_reg.addr = area.offset;
129 area_reg.len = area.size;
130 area_reg.flags = IORING_ZCRX_AREA_DMABUF;
131
132 register_ifq_(if_idx, if_rxq, rq_entries, area_reg,
133 sysconf(_SC_PAGESIZE));
134 }
135
137 [[maybe_unused]] int r;
138 if (area_size_ > 0) {
139 assert(area_ptr_ != nullptr);
140 r = munmap(area_ptr_, area_size_);
141 assert(r == 0);
142 }
143 assert(rq_ring_.ring_ptr != nullptr);
144 r = munmap(rq_ring_.ring_ptr, ring_size_);
145 assert(r == 0);
146 // TODO: Unregister ifq. For now there's no way to unregister ifq, so we
147 // just leak the registration.
148 }
149
150 CONDY_DELETE_COPY_MOVE(ZeroCopyRxBufferPool);
151
152private:
153 ZeroCopyRxBufferPool(detail::Ring &ring, uint32_t if_idx, uint32_t if_rxq,
154 uint32_t rq_entries, const ZeroCopyRxArea &area,
155 uint32_t flags)
156 : ring_(&ring), flags_(flags) {
157 const size_t page_size = sysconf(_SC_PAGESIZE);
158
159 if (area.addr == nullptr) {
160 area_size_ = detail::align_up(area.size, page_size);
161 area_ptr_ = mmap(nullptr, area_size_, PROT_READ | PROT_WRITE,
162 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
163 if (area_ptr_ == MAP_FAILED) {
164 throw detail::make_system_error("mmap");
165 }
166 auto d = detail::defer([&]() { munmap(area_ptr_, area_size_); });
167
168 io_uring_zcrx_area_reg area_reg = {};
169 area_reg.addr = reinterpret_cast<uint64_t>(area_ptr_);
170 area_reg.len = area_size_;
171 area_reg.flags = 0;
172
173 register_ifq_(if_idx, if_rxq, rq_entries, area_reg, page_size);
174
175 d.dismiss();
176 } else {
177 // Not owned, so we don't track the size for unmapping
178 area_size_ = 0;
179 area_ptr_ = area.addr;
180
181 io_uring_zcrx_area_reg area_reg = {};
182 area_reg.addr = reinterpret_cast<uint64_t>(area_ptr_);
183 area_reg.len = area.size;
184 area_reg.flags = 0;
185
186 register_ifq_(if_idx, if_rxq, rq_entries, area_reg, page_size);
187 }
188 }
189
190public:
191 uint32_t zcrx_id() const noexcept { return zcrx_id_; }
192
193 ZeroCopyRxBuffer handle_finish(io_uring_cqe *cqe) noexcept {
194 assert(ring_->check_cqe32(cqe) && "Expected big CQE for ZeroCopyRx");
195
196 if (cqe->res < 0) {
197 return ZeroCopyRxBuffer();
198 }
199 io_uring_zcrx_cqe *rcqe =
200 reinterpret_cast<io_uring_zcrx_cqe *>(cqe->big_cqe);
201 void *data = static_cast<char *>(area_ptr_) +
202 (rcqe->off & ~IORING_ZCRX_AREA_MASK);
203 size_t size = static_cast<size_t>(cqe->res);
204 return ZeroCopyRxBuffer(data, size, this);
205 }
206
207 void add_buffer_back(void *ptr, size_t size) noexcept {
208 rq_enqueue_(ptr, size);
209 maybe_flush_rq_();
210 }
211
212private:
213 void register_ifq_(uint32_t if_idx, uint32_t if_rxq, uint32_t rq_entries,
214 io_uring_zcrx_area_reg &area_reg, size_t page_size) {
215 rq_entries = std::bit_ceil(rq_entries);
216 io_uring_region_desc region_reg = {};
217 ring_size_ = get_refill_ring_size_(rq_entries, page_size);
218 region_reg.user_addr = 0;
219 region_reg.size = ring_size_;
220 region_reg.flags = 0;
221
222 io_uring_zcrx_ifq_reg reg = {};
223 reg.if_idx = if_idx;
224 reg.if_rxq = if_rxq;
225 reg.rq_entries = rq_entries;
226 reg.area_ptr = reinterpret_cast<uint64_t>(&area_reg);
227 reg.region_ptr = reinterpret_cast<uint64_t>(&region_reg);
228 reg.flags = flags_;
229
230 int r = io_uring_register_ifq(ring_->ring(), &reg);
231 if (r != 0) {
232 throw detail::make_system_error("io_uring_register_ifq", -r);
233 }
234 // TODO: Unregister ifq if any exception. For now there's no way to
235 // unregister ifq.
236
237 void *ring_ptr = mmap(nullptr, ring_size_, PROT_READ | PROT_WRITE,
238 MAP_SHARED | MAP_POPULATE, ring_->ring()->ring_fd,
239 static_cast<off_t>(region_reg.mmap_offset));
240 if (ring_ptr == MAP_FAILED) {
241 throw detail::make_system_error("mmap");
242 }
243 rq_ring_.khead = (unsigned int *)((char *)ring_ptr + reg.offsets.head);
244 rq_ring_.ktail = (unsigned int *)((char *)ring_ptr + reg.offsets.tail);
245 rq_ring_.rqes =
246 (struct io_uring_zcrx_rqe *)((char *)ring_ptr + reg.offsets.rqes);
247 rq_ring_.rq_tail = 0;
248 rq_ring_.ring_entries = reg.rq_entries;
249 rq_ring_.ring_ptr = ring_ptr;
250
251 zcrx_id_ = reg.zcrx_id;
252 area_token_ = area_reg.rq_area_token;
253 }
254
255 static size_t get_refill_ring_size_(uint32_t rq_entries,
256 size_t page_size) noexcept {
257 size_t ring_size = rq_entries * sizeof(io_uring_zcrx_rqe);
258 ring_size += page_size;
259 ring_size = detail::align_up(ring_size, page_size);
260 return ring_size;
261 }
262
263 size_t rq_nr_queued_() const noexcept {
264 return rq_ring_.rq_tail - io_uring_smp_load_acquire(rq_ring_.khead);
265 }
266
267 void rq_enqueue_(void *ptr, size_t size) noexcept {
268 assert(rq_nr_queued_() < rq_ring_.ring_entries);
269 io_uring_zcrx_rqe *rqe;
270 unsigned rq_mask = rq_ring_.ring_entries - 1;
271 rqe = &rq_ring_.rqes[rq_ring_.rq_tail & rq_mask];
272 rqe->off = (static_cast<char *>(ptr) - static_cast<char *>(area_ptr_)) |
273 area_token_;
274 rqe->len = static_cast<uint32_t>(size);
275 io_uring_smp_store_release(rq_ring_.ktail, ++rq_ring_.rq_tail);
276 }
277
278 void flush_rq_() noexcept {
279 zcrx_ctrl ctrl = {};
280 ctrl.zcrx_id = zcrx_id_;
281 ctrl.op = ZCRX_CTRL_FLUSH_RQ;
282 [[maybe_unused]] int r =
283 io_uring_register_zcrx_ctrl(ring_->ring(), &ctrl);
284 assert(r == 0);
285 }
286
287 void maybe_flush_rq_() noexcept {
288 if (rq_nr_queued_() >= rq_ring_.ring_entries ||
289 (flags_ & ZCRX_REG_NODEV)) {
290 flush_rq_();
291 }
292 }
293
294private:
295 detail::Ring *ring_;
296 void *area_ptr_;
297 size_t area_size_;
298 size_t ring_size_;
299 io_uring_zcrx_rq rq_ring_;
300 uint32_t zcrx_id_;
301 uint64_t area_token_;
302 uint32_t flags_;
303};
304
305#endif
306
307} // namespace condy
The event loop runtime for executing asynchronous.
Definition runtime.hpp:36
Buffer pool for zero-copy receive buffers.
Definition zcrx.hpp:67
ZeroCopyRxBufferPool(uint32_t if_idx, uint32_t if_rxq, uint32_t rq_entries, const ZeroCopyRxDMABufArea &area)
Construct a new Zero Copy Rx Buffer Pool object.
Definition zcrx.hpp:108
ZeroCopyRxBufferPool(Runtime &runtime, uint32_t if_idx, uint32_t if_rxq, uint32_t rq_entries, const ZeroCopyRxArea &area)
Construct a new Zero Copy Rx Buffer Pool object.
Definition zcrx.hpp:89
ZeroCopyRxBufferPool(Runtime &runtime, uint32_t if_idx, uint32_t if_rxq, uint32_t rq_entries, const ZeroCopyRxDMABufArea &area)
Construct a new Zero Copy Rx Buffer Pool object.
Definition zcrx.hpp:121
ZeroCopyRxBufferPool(uint32_t if_idx, uint32_t if_rxq, uint32_t rq_entries, const ZeroCopyRxArea &area)
Construct a new Zero Copy Rx Buffer Pool object.
Definition zcrx.hpp:76
Buffer from a ZeroCopyRxBufferPool.
Definition zcrx.hpp:25
Basic buffer types and conversion utilities.
The main namespace for the Condy library.
Definition condy.hpp:37
Wrapper classes for liburing interfaces.
Runtime type for running the io_uring event loop.
Area for zero-copy receive buffers.
Definition zcrx.hpp:34
size_t size
Size of the buffer area in bytes.
Definition zcrx.hpp:39
void * addr
Starting address of the buffer area. If null, the pool will allocate memory internally.
Definition zcrx.hpp:37
Area for zero-copy receive buffers using DMA-BUF.
Definition zcrx.hpp:45
int dmabuf_fd
File descriptor of the DMA-BUF to use.
Definition zcrx.hpp:47
size_t offset
Offset into the DMA-BUF where the buffer area starts.
Definition zcrx.hpp:49
size_t size
Size of the buffer area within the DMA-BUF, in bytes.
Definition zcrx.hpp:51
Internal utility classes and functions used by Condy.