ublk-cpp v0.1
A ublk library for std::execution.
Loading...
Searching...
No Matches
queue.hpp
Go to the documentation of this file.
1
5
6#pragma once
7
10#include "ublk/detail/utils.hpp"
11#include "ublk/handler.hpp"
12#include "ublk/raw.hpp"
13#include "ublk/ublk_cmd.h"
14#include <atomic>
15#include <cerrno>
16#include <condy.hpp>
17#include <cstdint>
18#include <exception>
19#include <stdexcept>
20#include <sys/mman.h>
21#include <system_error>
22#include <unistd.h>
23#include <utility>
24
25namespace ublk {
26namespace detail {
27
28inline bool need_alloc_buf(uint64_t flags) noexcept {
29 return !(flags & (UBLK_F_SUPPORT_ZERO_COPY | UBLK_F_USER_COPY));
30}
31
32inline bool need_io_buf(uint64_t flags, uint32_t op_flags) noexcept {
33 bool zero_copy = flags & UBLK_F_SUPPORT_ZERO_COPY;
34 bool auto_reg = flags & UBLK_F_AUTO_BUF_REG;
35 bool need_reg_buf = op_flags & UBLK_IO_F_NEED_REG_BUF;
36 return need_reg_buf || (zero_copy && !auto_reg);
37}
38
39inline off_t io_desc_offset(size_t q_id) noexcept {
40 return UBLKSRV_CMD_BUF_OFFSET +
41 q_id * UBLK_MAX_QUEUE_DEPTH * sizeof(ublksrv_io_desc);
42}
43
44template <typename T>
45concept has_res = requires(T t) {
46 { t.res } -> std::convertible_to<int32_t>;
47};
48
49template <typename T>
50concept has_zone_lba = requires(T t) {
51 { t.zone_lba } -> std::convertible_to<uint64_t>;
52};
53
54inline void extract_io_result(auto &&r, int32_t &res,
55 uint64_t &zone_lba) noexcept {
56 using R = std::decay_t<decltype(r)>;
57 if constexpr (std::convertible_to<R, int32_t>) {
58 res = r;
59 zone_lba = 0;
60 } else {
61 static_assert(has_res<R>,
62 "returned result is neither int32_t nor has a "
63 "res field");
64 res = r.res;
65 zone_lba = 0;
66 if constexpr (has_zone_lba<R>) {
67 zone_lba = r.zone_lba;
68 }
69 }
70}
71
72inline std::error_code normalize_error(const auto &err, int v) noexcept {
73 using E = std::decay_t<decltype(err)>;
74 if constexpr (std::same_as<E, std::error_code>) {
75 return err;
76 } else if constexpr (std::same_as<E, std::exception_ptr>) {
77 try {
78 std::rethrow_exception(err);
79 } catch (const std::system_error &se) {
80 return se.code();
81 } catch (...) {
82 return std::error_code(v, std::generic_category());
83 }
84 } else {
85 return std::error_code(v, std::generic_category());
86 }
87}
88
89template <typename Sender>
90inline auto wrap_handle_io(Sender &&sender, int32_t &res,
91 uint64_t &zone_lba) noexcept {
92 return std::forward<Sender>(sender) |
93 ex::stopped_as_error(
94 std::error_code(ECANCELED, std::generic_category())) |
95 ex::upon_error([](const auto &err) noexcept {
96 return normalize_error(err, EIO);
97 }) |
98 ex::then([&](auto r) noexcept {
99 using R = decltype(r);
100 if constexpr (std::same_as<R, std::error_code>) {
101 res = -r.value();
102 zone_lba = 0;
103 } else {
104 extract_io_result(r, res, zone_lba);
105 }
106 return std::make_tuple(res, zone_lba);
107 });
108}
109
110template <typename Fd, IoHandler Handler, typename Alloc> class IoQueue {
111public:
112 IoQueue(Fd ublkc_fd, int ublkc_fd_raw, uint16_t q_id, uint64_t flags,
113 uint16_t queue_depth, uint32_t max_io_buf_bytes, Handler &handler,
114 const Alloc &alloc)
115 : ublkc_fd_(ublkc_fd), q_id_(q_id), flags_(flags),
116 queue_depth_(queue_depth), max_io_buf_bytes_(max_io_buf_bytes),
117 handler_(handler), alloc_(alloc) {
118 bool ok = false;
119 auto d = defer([&]() noexcept {
120 if (!ok) {
121 cleanup_();
122 }
123 });
124
125 size_t size = queue_depth_ * sizeof(ublksrv_io_desc);
126 auto off = io_desc_offset(q_id_);
127 void *addr = mmap(0, size, PROT_READ, MAP_SHARED | MAP_POPULATE,
128 ublkc_fd_raw, off);
129 if (addr == MAP_FAILED) {
130 throw std::system_error(errno, std::generic_category(),
131 "mmap io_desc");
132 }
133 iod_base_ = reinterpret_cast<ublksrv_io_desc *>(addr);
134
135 const size_t page = sysconf(_SC_PAGESIZE);
136 if (need_alloc_buf(flags_)) {
137 buf_bases_.reserve(queue_depth_);
138 for (uint16_t tag = 0; tag < queue_depth_; tag++) {
139 void *buf = alloc_aligned(alloc_, max_io_buf_bytes_, page);
140 buf_bases_.push_back(buf);
141 }
142 }
143
144 ok = true;
145 }
146
147 IoQueue(const IoQueue &) = delete;
148 IoQueue &operator=(const IoQueue &) = delete;
149 IoQueue(IoQueue &&) = delete;
150 IoQueue &operator=(IoQueue &&) = delete;
151
152 ~IoQueue() { cleanup_(); }
153
154public:
155 template <typename Sched> ex::task<void, TaskEnv<Sched, Alloc>> run() {
156 if constexpr (QueueHandler<Handler>) {
157 co_await handler_.init_queue(q_id_);
158 }
159
160 auto d = defer([&]() noexcept {
161 if constexpr (QueueHandler<Handler>) {
162 handler_.destroy_queue(q_id_);
163 }
164 });
165
166 ex::simple_counting_scope scope;
167 AllocVector<std::exception_ptr, decltype(alloc_)> errs(queue_depth_,
168 alloc_);
169 auto sched = co_await ex::read_env(ex::get_start_scheduler);
170 for (uint16_t tag = 0; tag < queue_depth_; tag++) {
171 auto task = worker_<Sched>(tag, iod_base_ + tag, get_buf_(tag));
172 auto s =
173 ex::starts_on(sched, std::move(task)) |
174 ex::upon_error([&, tag](const std::exception_ptr &ep) noexcept {
175 errs[tag] = ep;
176 });
177 ex::spawn(std::move(s), scope.get_token());
178 }
179
180 co_await scope.join();
181
182 for (auto &ep : errs) {
183 if (ep) {
184 std::rethrow_exception(ep);
185 }
186 }
187 }
188
189private:
190 void cleanup_() noexcept {
191 if (!buf_bases_.empty()) {
192 const size_t page = sysconf(_SC_PAGESIZE);
193 for (auto *buf : buf_bases_) {
194 free_aligned(alloc_, buf, max_io_buf_bytes_, page);
195 }
196 buf_bases_.clear();
197 }
198 if (iod_base_) {
199 size_t size = queue_depth_ * sizeof(ublksrv_io_desc);
200 munmap(iod_base_, size);
201 iod_base_ = nullptr;
202 }
203 }
204
205 void *get_buf_(uint16_t tag) const noexcept {
206 if (buf_bases_.empty()) {
207 return nullptr;
208 }
209 return buf_bases_[tag];
210 }
211
212 template <typename Sched>
213 ex::task<void, TaskEnv<Sched, Alloc>>
214 worker_(uint16_t tag, const ublksrv_io_desc *iod, void *buf) {
215 int32_t r;
216 bool zero_copy = flags_ & UBLK_F_SUPPORT_ZERO_COPY;
217 bool auto_reg = flags_ & UBLK_F_AUTO_BUF_REG;
218
219 uint64_t buf_addr = reinterpret_cast<uint64_t>(buf);
220 uint64_t sqe_addr = 0;
221 if (auto_reg) {
222 ublk_auto_buf_reg reg = {};
223 reg.index = tag;
224 reg.flags = UBLK_AUTO_BUF_REG_FALLBACK;
225 sqe_addr = ublk_auto_buf_reg_to_sqe_addr(&reg);
226 }
227
228 r = co_await (
229 raw::fetch_req(ublkc_fd_, q_id_, tag, buf_addr, sqe_addr) |
230 ex::upon_error(
231 [&](std::error_code ec) noexcept { return -ec.value(); }));
232 if (r == UBLK_IO_RES_NEED_GET_DATA) {
233 r = co_await (raw::need_get_data(ublkc_fd_, q_id_, tag, buf_addr) |
234 ex::upon_error([&](std::error_code ec) noexcept {
235 return -ec.value();
236 }));
237 }
238 if (r == UBLK_IO_RES_ABORT) {
239 co_return;
240 } else if (r < 0) {
241 throw std::system_error(-r, std::generic_category(), "fetch_req");
242 }
243
244 while (true) {
245 bool io_buf = need_io_buf(flags_, iod->op_flags);
246 if (io_buf) {
247 co_await raw::register_io_buf(ublkc_fd_, q_id_, tag, tag);
248 }
249
250 int32_t res;
251 uint64_t zone_lba;
252 const IoData io_data{q_id_, tag, iod, buf};
253 co_await wrap_handle_io(handler_.handle_io(io_data), res, zone_lba);
254
255 if (io_buf) {
256 co_await raw::unregister_io_buf(ublkc_fd_, q_id_, tag, tag);
257 }
258
259 uint64_t curr_buf_addr = buf_addr;
260 if (ublksrv_get_op(iod) == UBLK_IO_OP_ZONE_APPEND) {
261 assert(buf_addr == 0);
262 curr_buf_addr = zone_lba;
263 }
264 r = co_await (raw::commit_and_fetch_req(ublkc_fd_, q_id_, tag, res,
265 curr_buf_addr, sqe_addr) |
266 ex::upon_error([&](std::error_code ec) noexcept {
267 return -ec.value();
268 }));
269 if (r == UBLK_IO_RES_NEED_GET_DATA) {
270 r = co_await (
271 raw::need_get_data(ublkc_fd_, q_id_, tag, buf_addr) |
272 ex::upon_error([&](std::error_code ec) noexcept {
273 return -ec.value();
274 }));
275 }
276 if (r == UBLK_IO_RES_ABORT) {
277 co_return;
278 } else if (r < 0) {
279 throw std::system_error(-r, std::generic_category(),
280 "commit_and_fetch_req");
281 }
282 }
283 }
284
285private:
286 Fd ublkc_fd_;
287 uint16_t q_id_;
288 uint64_t flags_;
289 uint16_t queue_depth_;
290 uint32_t max_io_buf_bytes_;
291 Handler &handler_;
292 Alloc alloc_;
293 ublksrv_io_desc *iod_base_ = nullptr;
294 AllocVector<void *, Alloc> buf_bases_;
295};
296
297template <typename Fd, IoHandler Handler, typename Alloc> class BatchIoQueue {
298public:
299 BatchIoQueue(Fd ublkc_fd, int ublkc_fd_raw, uint16_t q_id, uint64_t flags,
300 uint16_t queue_depth, uint32_t max_io_buf_bytes,
301 Handler &handler, const Alloc &alloc)
302 : ublkc_fd_(ublkc_fd), q_id_(q_id), flags_(flags),
303 queue_depth_(queue_depth), max_io_buf_bytes_(max_io_buf_bytes),
304 handler_(handler), alloc_(alloc) {
305 bool ok = false;
306 auto d = defer([&]() noexcept {
307 if (!ok) {
308 cleanup_();
309 }
310 });
311
312 {
313 size_t size = queue_depth_ * sizeof(ublksrv_io_desc);
314 auto off = io_desc_offset(q_id_);
315 void *addr = mmap(0, size, PROT_READ, MAP_SHARED | MAP_POPULATE,
316 ublkc_fd_raw, off);
317 if (addr == MAP_FAILED) {
318 throw std::system_error(errno, std::generic_category(),
319 "mmap io_desc");
320 }
321 iod_base_ = reinterpret_cast<ublksrv_io_desc *>(addr);
322 }
323
324 const size_t page = sysconf(_SC_PAGESIZE);
325 {
326 size_t size = align_up(queue_depth_ * commit_element_size_(), page);
327 commit_buf_size_ = size;
328 commit_buf_[0] = alloc_aligned(alloc_, size * 2, page);
329 commit_buf_[1] = static_cast<char *>(commit_buf_[0]) + size;
330 if (mlock(commit_buf_[0], size * 2) < 0) {
331 throw std::system_error(errno, std::generic_category(),
332 "mlock commit_buf");
333 }
334 }
335
336 {
337 size_t size = align_up(queue_depth_ * sizeof(uint16_t), page);
338 fetch_buf_size_ = size;
339 fetch_buf_[0] = reinterpret_cast<uint16_t *>(
340 alloc_aligned(alloc_, size * 2, page));
341 fetch_buf_[1] = reinterpret_cast<uint16_t *>(
342 reinterpret_cast<char *>(fetch_buf_[0]) + size);
343 if (mlock(fetch_buf_[0], size * 2) < 0) {
344 throw std::system_error(errno, std::generic_category(),
345 "mlock fetch_buf");
346 }
347 }
348
349 if (need_alloc_buf(flags_)) {
350 buf_bases_.reserve(queue_depth_);
351 for (uint16_t tag = 0; tag < queue_depth_; tag++) {
352 void *buf = alloc_aligned(alloc_, max_io_buf_bytes_, page);
353 buf_bases_.push_back(buf);
354 }
355 }
356
357 ok = true;
358 }
359
360 BatchIoQueue(const BatchIoQueue &) = delete;
361 BatchIoQueue &operator=(const BatchIoQueue &) = delete;
362 BatchIoQueue(BatchIoQueue &&) = delete;
363 BatchIoQueue &operator=(BatchIoQueue &&) = delete;
364
365 ~BatchIoQueue() { cleanup_(); }
366
367public:
368 template <typename Sched> ex::task<void, TaskEnv<Sched, Alloc>> run() {
369 auto sched = co_await ex::read_env(ex::get_scheduler);
370 auto alloc = co_await ex::read_env(ex::get_allocator);
371
372 if constexpr (QueueHandler<Handler>) {
373 co_await handler_.init_queue(q_id_);
374 }
375
376 auto d = defer([&]() noexcept {
377 if constexpr (QueueHandler<Handler>) {
378 handler_.destroy_queue(q_id_);
379 }
380 });
381
382 for (uint16_t tag = 0; tag < queue_depth_; tag++) {
383 set_result_(0, tag, tag, 0);
384 }
385 co_await raw::prep_io_cmds(ublkc_fd_, q_id_, get_batch_flags_(),
386 queue_depth_, commit_element_size_(),
387 commit_buf_[0],
388 queue_depth_ * commit_element_size_());
389
390 FlusherState flusher;
391 AllocVector<WorkerState, decltype(alloc)> workers(queue_depth_, alloc);
392
393 ex::simple_counting_scope scope;
394 std::exception_ptr flusher_err;
395 AllocVector<std::exception_ptr, decltype(alloc)> worker_errs(
396 queue_depth_, alloc);
397 bool worker_stopped = false;
398 size_t running_workers = queue_depth_;
399
400 {
401 auto task = flusher_<Sched>(flusher, running_workers);
402 auto s = ex::starts_on(sched, std::move(task)) |
403 ex::upon_error([&](std::exception_ptr ep) noexcept {
404 flusher_err = std::move(ep);
405 });
406 ex::spawn(std::move(s), scope.get_token());
407 }
408 for (uint16_t tag = 0; tag < queue_depth_; tag++) {
409 auto task = worker_<Sched>(workers[tag], flusher, worker_stopped,
410 tag, iod_base_ + tag, get_buf_(tag));
411 auto s =
412 ex::starts_on(sched, std::move(task)) |
413 ex::upon_error([&, tag](const std::exception_ptr &ep) noexcept {
414 worker_errs[tag] = ep;
415 }) |
416 ex::then([&]() noexcept {
417 if (--running_workers == 0) {
418 flusher.futex.notify_one();
419 }
420 });
421 ex::spawn(std::move(s), scope.get_token());
422 }
423
424 condy::ProvidedBufferQueue queue(2, IOU_PBUF_RING_INC);
425 queue.push(condy::buffer(fetch_buf_[0], fetch_buf_size_));
426 queue.push(condy::buffer(fetch_buf_[1], fetch_buf_size_));
427
428 size_t off = 0;
429 // NOLINTNEXTLINE(bugprone-exception-escape)
430 auto fetch_cb = [&](std::pair<int32_t, condy::BufferInfo> r) noexcept {
431 auto &[res, info] = r;
432 auto bid = info.bid;
433 auto *fetch_buf = fetch_buf_[bid];
434 assert(res >= 0);
435 size_t nr_tags = static_cast<size_t>(res) / sizeof(uint16_t);
436 size_t end = off + nr_tags;
437 for (size_t j = off; j < end; j++) {
438 uint16_t tag = fetch_buf[j];
439 auto &worker = workers[tag];
440 write_once_(worker.flag, true);
441 worker.futex.notify_one();
442 }
443 off = end;
444 bool consumed = info.num_buffers;
445 if (consumed) {
446 auto r = queue.push(condy::buffer(fetch_buf, fetch_buf_size_));
447 assert(r == bid);
448 off = 0;
449 }
450 };
451
452 int32_t res = 0;
453
454 auto s =
455 raw::fetch_io_cmds(ublkc_fd_, queue, q_id_, fetch_cb) |
456 ex::then([](int32_t r, condy::BufferInfo) noexcept { return r; }) |
457 ex::upon_error(
458 [&](std::error_code ec) noexcept { return -ec.value(); }) |
459 ex::then([&](int32_t r) noexcept { res = r; }) |
460 ex::then([&]() noexcept {
461 worker_stopped = true;
462 for (auto &worker : workers) {
463 worker.futex.notify_one();
464 }
465 });
466
467 co_await ex::when_all(std::move(s), scope.join());
468
469 for (auto &ep : worker_errs) {
470 if (ep) {
471 std::rethrow_exception(ep);
472 }
473 }
474 if (flusher_err) {
475 std::rethrow_exception(flusher_err);
476 }
477 assert(res < 0);
478 if (res != UBLK_IO_RES_ABORT) {
479 throw std::system_error(-res, std::generic_category(),
480 "fetch_io_cmds");
481 }
482 }
483
484private:
485 void cleanup_() noexcept {
486 const size_t page = sysconf(_SC_PAGESIZE);
487 if (!buf_bases_.empty()) {
488 for (auto *buf : buf_bases_) {
489 free_aligned(alloc_, buf, max_io_buf_bytes_, page);
490 }
491 buf_bases_.clear();
492 }
493 if (fetch_buf_[0]) {
494 munlock(fetch_buf_[0], fetch_buf_size_ * 2);
495 free_aligned(alloc_, fetch_buf_[0], fetch_buf_size_ * 2, page);
496 }
497 if (commit_buf_[0]) {
498 munlock(commit_buf_[0], commit_buf_size_ * 2);
499 free_aligned(alloc_, commit_buf_[0], commit_buf_size_ * 2, page);
500 }
501 if (iod_base_) {
502 size_t size = queue_depth_ * sizeof(ublksrv_io_desc);
503 munmap(iod_base_, size);
504 iod_base_ = nullptr;
505 }
506 }
507
508 template <typename T>
509 static auto read_once_(const std::atomic<T> &a) noexcept {
510 return a.load(std::memory_order_relaxed);
511 }
512
513 template <typename T, typename V>
514 static void write_once_(std::atomic<T> &a, V v) noexcept {
515 a.store(v, std::memory_order_relaxed);
516 }
517
518 bool need_f_buf_addr_() noexcept {
519 return !(flags_ & UBLK_F_AUTO_BUF_REG) && need_alloc_buf(flags_);
520 }
521
522 bool need_f_zone_lba_() noexcept { return flags_ & UBLK_F_ZONED; }
523
524 uint16_t get_batch_flags_() noexcept {
525 uint16_t f = 0;
526 if (flags_ & UBLK_F_AUTO_BUF_REG) {
527 f |= UBLK_BATCH_F_AUTO_BUF_REG_FALLBACK;
528 } else if (need_f_buf_addr_()) {
529 f |= UBLK_BATCH_F_HAS_BUF_ADDR;
530 }
531 if (need_f_zone_lba_()) {
532 f |= UBLK_BATCH_F_HAS_ZONE_LBA;
533 }
534 return f;
535 }
536
537 size_t commit_element_size_() noexcept {
538 size_t size = sizeof(ublk_elem_header);
539 if (need_f_buf_addr_()) {
540 size += sizeof(uint64_t);
541 }
542 if (need_f_zone_lba_()) {
543 size += sizeof(uint64_t);
544 }
545 return size;
546 }
547
548 ublk_elem_header &commit_f_hdr(size_t index, size_t pos) noexcept {
549 return *reinterpret_cast<ublk_elem_header *>(
550 static_cast<char *>(commit_buf_[index]) +
551 pos * commit_element_size_());
552 }
553
554 uint64_t &commit_f_buf_addr(size_t index, size_t pos) noexcept {
555 assert(need_f_buf_addr_());
556 return *reinterpret_cast<uint64_t *>(
557 reinterpret_cast<char *>(&commit_f_hdr(index, pos)) +
558 sizeof(ublk_elem_header));
559 }
560
561 uint64_t &commit_f_zone_lba(size_t index, size_t pos) noexcept {
562 assert(need_f_zone_lba_());
563 size_t off = sizeof(ublk_elem_header);
564 if (need_f_buf_addr_()) {
565 off += sizeof(uint64_t);
566 }
567 return *reinterpret_cast<uint64_t *>(
568 reinterpret_cast<char *>(&commit_f_hdr(index, pos)) + off);
569 }
570
571 void set_result_(size_t index, size_t pos, uint16_t tag,
572 int32_t res) noexcept {
573 auto &hdr = commit_f_hdr(index, pos);
574 hdr.tag = tag;
575 hdr.result = res;
576 if (flags_ & UBLK_F_AUTO_BUF_REG) {
577 hdr.buf_index = tag;
578 } else if (need_f_buf_addr_()) {
579 commit_f_buf_addr(index, pos) =
580 reinterpret_cast<uint64_t>(buf_bases_[tag]);
581 }
582 }
583
584 void set_zone_lba_(size_t index, size_t pos, uint64_t lba) noexcept {
585 assert(need_f_zone_lba_());
586 commit_f_zone_lba(index, pos) = lba;
587 }
588
589 void *get_buf_(uint16_t tag) const noexcept {
590 if (buf_bases_.empty()) {
591 return nullptr;
592 }
593 return buf_bases_[tag];
594 }
595
596private:
597 struct FlusherState {
598 size_t index = 0;
599 std::atomic<uint16_t> pending = 0;
600 condy::Futex<uint16_t> futex{pending};
601 };
602
603 template <typename Sched>
604 ex::task<void, TaskEnv<Sched, Alloc>> flusher_(FlusherState &state,
605 size_t &running_workers) {
606 auto sched = co_await ex::read_env(ex::get_start_scheduler);
607 while (true) {
608 auto nr = read_once_(state.pending);
609 if (nr > 0) {
610 auto cur_index = state.index;
611 state.index = 1 - state.index;
612 write_once_(state.pending, 0);
613 auto s = raw::commit_io_cmds(
614 ublkc_fd_, q_id_, get_batch_flags_(), nr,
615 commit_element_size_(), commit_buf_[cur_index],
616 nr * commit_element_size_()) |
617 ex::then([&](int32_t r) {
618 if (r != nr * commit_element_size_()) {
619 throw std::runtime_error(
620 "commit_io_cmds returned unexpected byte "
621 "count");
622 }
623 });
624 co_await std::move(s);
625 co_await ex::schedule(sched);
626 } else if (running_workers == 0) {
627 co_return;
628 } else {
629 co_await state.futex.wait(0);
630 }
631 }
632 }
633
634 struct WorkerState {
635 std::atomic<bool> flag = false;
636 condy::Futex<bool> futex{flag};
637 };
638
639 template <typename Sched>
640 ex::task<void, TaskEnv<Sched, Alloc>>
641 worker_(WorkerState &state, FlusherState &flusher, bool &stopped,
642 uint16_t tag, const ublksrv_io_desc *iod, void *buf) {
643 while (true) {
644 if (!stopped && !read_once_(state.flag)) {
645 co_await state.futex.wait(false);
646 }
647 if (stopped) {
648 co_return;
649 }
650 assert(read_once_(state.flag));
651 write_once_(state.flag, false);
652
653 bool io_buf = need_io_buf(flags_, iod->op_flags);
654 if (io_buf) {
655 co_await raw::register_io_buf(ublkc_fd_, q_id_, tag, tag);
656 }
657
658 int32_t res;
659 uint64_t zone_lba;
660 const IoData io_data{q_id_, tag, iod, buf};
661 co_await wrap_handle_io(handler_.handle_io(io_data), res, zone_lba);
662
663 if (io_buf) {
664 co_await raw::unregister_io_buf(ublkc_fd_, q_id_, tag, tag);
665 }
666
667 auto cur_index = flusher.index;
668 auto slot = read_once_(flusher.pending);
669 set_result_(cur_index, slot, tag, res);
670 if (ublksrv_get_op(iod) == UBLK_IO_OP_ZONE_APPEND) {
671 set_zone_lba_(cur_index, slot, zone_lba);
672 }
673 write_once_(flusher.pending, slot + 1);
674 if (slot == 0) {
675 flusher.futex.notify_one();
676 }
677 }
678 }
679
680private:
681 Fd ublkc_fd_;
682 uint16_t q_id_;
683 uint64_t flags_;
684 uint16_t queue_depth_;
685 uint32_t max_io_buf_bytes_;
686 Handler &handler_;
687 Alloc alloc_;
688 ublksrv_io_desc *iod_base_ = nullptr;
689 size_t commit_buf_size_ = 0;
690 void *commit_buf_[2] = {};
691 size_t fetch_buf_size_ = 0;
692 uint16_t *fetch_buf_[2] = {};
693 AllocVector<void *, Alloc> buf_bases_;
694};
695
696} // namespace detail
697} // namespace ublk
The std::execution backend namespace used by ublk-cpp.
Handler concepts for ublk server.
auto need_get_data(Fd fd, uint16_t q_id, uint16_t tag, uint64_t buf_addr) noexcept
Copy the data of a write request into the user buffer after the driver returns UBLK_IO_RES_NEED_GET_D...
Definition raw.hpp:481
auto commit_and_fetch_req(Fd fd, uint16_t q_id, uint16_t tag, int32_t result, uint64_t buf_addr, uint64_t sqe_addr) noexcept
Overload of commit_and_fetch_req() with an explicit sqe->addr, used for automatic request buffer regi...
Definition raw.hpp:447
auto fetch_io_cmds(Fd fd, condy::ProvidedBufferQueue &buffers, uint16_t q_id, MultiShotFunc &&func) noexcept
Fetch I/O commands in multishot style into a provided buffer queue (UBLK_F_BATCH_IO).
Definition raw.hpp:575
auto commit_io_cmds(Fd fd, uint16_t q_id, uint16_t flags, uint16_t nr_elem, uint8_t elem_bytes, const void *elems, uint32_t elems_len) noexcept
Commit a batch of completed I/O commands for a queue (UBLK_F_BATCH_IO).
Definition raw.hpp:555
auto unregister_io_buf(Fd fd, uint16_t q_id, uint16_t tag, uint64_t buf_index) noexcept
Unregister the buffer of an in-flight request from the io_uring buffer table after zero-copy I/O comp...
Definition raw.hpp:515
auto register_io_buf(Fd fd, uint16_t q_id, uint16_t tag, uint64_t buf_index) noexcept
Register the buffer of an in-flight request into the io_uring buffer table for zero-copy I/O (UBLK_F_...
Definition raw.hpp:498
auto prep_io_cmds(Fd fd, uint16_t q_id, uint16_t flags, uint16_t nr_elem, uint8_t elem_bytes, const void *elems, uint32_t elems_len) noexcept
Prepare a batch of I/O commands for a queue (UBLK_F_BATCH_IO).
Definition raw.hpp:533
auto fetch_req(Fd fd, uint16_t q_id, uint16_t tag, uint64_t buf_addr, uint64_t sqe_addr) noexcept
Overload of fetch_req() with an explicit sqe->addr, used for automatic request buffer registration (U...
Definition raw.hpp:422
The main namespace of the ublk-cpp library.
Definition ublk.hpp:21
Low-level io_uring command senders for ublk control and I/O commands.
Task environment utils.
Small utility helpers.