16#include <sys/socket.h>
23template <
typename Sched,
typename Alloc,
typename Session>
24inline ex::task<void, TaskEnv<Sched, Alloc>>
25shm_session(
int client_fd, Session session, ex::counting_scope &scope) {
26 auto sched =
co_await ex::read_env(ex::get_start_scheduler);
28 auto d = defer([&]()
noexcept { close(client_fd); });
31 char control[CMSG_SPACE(
sizeof(
int))];
32 iovec iov = {.iov_base = &data, .iov_len =
sizeof(data)};
36 msg.msg_control = control;
37 msg.msg_controllen =
sizeof(control);
39 co_await condy::async_recvmsg(client_fd, &msg, 0);
42 for (cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg !=
nullptr;
43 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
44 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
45 std::memcpy(&received_fd, CMSG_DATA(cmsg),
sizeof(received_fd));
49 if (received_fd == -1) {
50 throw std::runtime_error(
"client did not pass a file descriptor");
52 auto d2 = defer([&]()
noexcept { close(received_fd); });
54 auto s = session.handle(received_fd, scope) |
56 std::error_code(ECANCELED, std::generic_category())) |
57 ex::upon_error([](
const auto &err)
noexcept {
58 return normalize_error(err, EIO);
60 ex::then([&](
auto r)
noexcept {
61 using R =
decltype(r);
62 if constexpr (std::same_as<R, std::error_code>) {
68 int32_t res =
co_await std::move(s);
70 auto d3 = defer([&]()
noexcept {
72 session.cleanup(sched, scope);
76 co_await condy::async_send(client_fd, condy::buffer(&res,
sizeof(res)), 0);
79 throw std::system_error(-res, std::generic_category());
82 auto r =
co_await condy::async_recv(client_fd,
83 condy::buffer(&data,
sizeof(data)), 0);
85 throw std::runtime_error(
"client sent data after result");
89template <
typename Sched,
typename Alloc,
typename Session>
90inline ex::task<void, TaskEnv<Sched, Alloc>>
91shm_server(
int server_fd, Session session, ex::counting_scope &scope) {
92 auto sched =
co_await ex::read_env(ex::get_start_scheduler);
94 sockaddr_un client_addr;
95 socklen_t client_len =
sizeof(client_addr);
96 int client_fd =
co_await condy::async_accept(
97 server_fd, (
struct sockaddr *)&client_addr, &client_len, 0);
99 auto task = shm_session<Sched, Alloc>(client_fd, session, scope);
100 auto s = ex::starts_on(sched, std::move(task)) |
101 ex::upon_error([](
const std::exception_ptr &)
noexcept {});
102 ex::spawn(std::move(s), scope.get_token());
106template <
typename Sched,
typename Alloc,
typename Session>
107inline ex::task<void, TaskEnv<Sched, Alloc>>
108shm_server_run(std::string_view path, Session session) {
109 int server_fd =
co_await condy::async_socket(AF_UNIX, SOCK_STREAM, 0, 0);
110 auto d = defer([&]()
noexcept { close(server_fd); });
112 sockaddr_un addr = {};
113 addr.sun_family = AF_UNIX;
114 if (path.size() >=
sizeof(addr.sun_path)) {
115 throw std::length_error(
"unix path too long");
117 std::memcpy(addr.sun_path, path.data(), path.size());
118 addr.sun_path[path.size()] =
'\0';
120 co_await (condy::async_unlink(addr.sun_path, 0) |
121 ex::upon_error([](std::error_code ec) {
122 if (ec.value() != ENOENT) {
123 throw std::system_error(ec,
"unlink");
128 co_await condy::async_bind(server_fd,
reinterpret_cast<sockaddr *
>(&addr),
130 auto d2 = defer([&]()
noexcept { unlink(addr.sun_path); });
131 co_await condy::async_listen(server_fd, 128);
133 ex::counting_scope scope;
134 auto stop_request = [&]()
noexcept { scope.request_stop(); };
135 ex::inplace_stop_callback<
decltype(stop_request)> cb{
136 co_await ex::read_env(ex::get_stop_token), std::move(stop_request)};
138 std::exception_ptr err;
139 auto sched =
co_await ex::read_env(ex::get_start_scheduler);
140 auto s = ex::starts_on(
141 sched, shm_server<Sched, Alloc>(server_fd, session, scope)) |
142 ex::upon_error([&](std::exception_ptr eptr)
noexcept {
143 scope.request_stop();
144 err = std::move(eptr);
146 ex::spawn(std::move(s), scope.get_token());
147 co_await scope.join();
149 std::rethrow_exception(err);
153template <
typename Sched,
typename Alloc, ShmHandler Handler>
class Session {
155 Session(
int control_fd, uint32_t dev_id, uint32_t flags, Handler &handler)
156 : control_fd_(control_fd), dev_id_(dev_id), flags_(flags),
159 ex::task<int32_t, TaskEnv<Sched, Alloc>>
160 handle(
int memfd, ex::counting_scope &scope)
noexcept {
161 auto sched =
co_await ex::read_env(ex::get_start_scheduler);
163 auto d = defer([&]()
noexcept {
165 cleanup(sched, scope);
170 co_await condy::async_statx(memfd,
"", AT_EMPTY_PATH, STATX_SIZE, &stx);
171 size_ = stx.stx_size;
173 int map_prot = PROT_READ | PROT_WRITE;
174 if (flags_ & UBLK_SHMEM_BUF_READ_ONLY) {
175 map_prot = PROT_READ;
178 mmap(
nullptr, size_, map_prot, MAP_SHARED | MAP_POPULATE, memfd, 0);
179 if (addr == MAP_FAILED) {
180 throw std::system_error(errno, std::generic_category(),
185 ublk_shmem_buf_reg buf_reg = {};
186 buf_reg.addr =
reinterpret_cast<uint64_t
>(base_);
188 buf_reg.flags = flags_;
189 index_ =
co_await control_register_shm_buf_t{}.invoke<Sched, Alloc>(
190 control_fd_, dev_id_, &buf_reg);
192 co_await handler_.handle_reg_shm(index_, base_, size_);
198 void cleanup(Sched sched, ex::counting_scope &scope)
noexcept {
199 Session self = *
this;
202 ex::let_value([self = std::move(self)]()
203 -> ex::task<
void, TaskEnv<Sched, Alloc>> {
204 if (self.index_ != -1) {
205 co_await ex::unstoppable(
206 control_unregister_shm_buf_t{}.invoke<Sched, Alloc>(
207 self.control_fd_, self.dev_id_, self.index_));
208 self.handler_.handle_unreg_shm(self.index_);
211 munmap(self.base_, self.size_);
215 ex::starts_on(sched, std::move(s)) |
216 ex::upon_error([](
const std::exception_ptr &)
noexcept {}),
226 void *base_ =
nullptr;
Implementation of ublk control command interface.
The std::execution backend namespace used by ublk-cpp.
Handler concepts for ublk server.
The main namespace of the ublk-cpp library.