1e21c39aaSAnton Nayshtut /* SPDX-License-Identifier: BSD-3-Clause 2e21c39aaSAnton Nayshtut * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3e21c39aaSAnton Nayshtut */ 4e21c39aaSAnton Nayshtut #include "spdk/stdinc.h" 5e21c39aaSAnton Nayshtut #include "spdk/util.h" 6e21c39aaSAnton Nayshtut #include "spdk/log.h" 7e21c39aaSAnton Nayshtut #include "aio_mgr.h" 8e21c39aaSAnton Nayshtut 9e21c39aaSAnton Nayshtut #define REQS_PER_AIO 10 10e21c39aaSAnton Nayshtut 11e21c39aaSAnton Nayshtut struct spdk_aio_mgr_req { 12e21c39aaSAnton Nayshtut struct aiocb io; 13e21c39aaSAnton Nayshtut TAILQ_ENTRY(spdk_aio_mgr_req) link; 14e21c39aaSAnton Nayshtut }; 15e21c39aaSAnton Nayshtut 16e21c39aaSAnton Nayshtut struct spdk_aio_mgr_io { 17e21c39aaSAnton Nayshtut TAILQ_ENTRY(spdk_aio_mgr_io) link; 18e21c39aaSAnton Nayshtut TAILQ_HEAD(, spdk_aio_mgr_req) reqs; 19e21c39aaSAnton Nayshtut struct spdk_aio_mgr *mgr; 20e21c39aaSAnton Nayshtut fsdev_aio_done_cb clb; 21e21c39aaSAnton Nayshtut void *ctx; 22e21c39aaSAnton Nayshtut uint32_t data_size; 23e21c39aaSAnton Nayshtut int err; 24e21c39aaSAnton Nayshtut }; 25e21c39aaSAnton Nayshtut 26e21c39aaSAnton Nayshtut struct spdk_aio_mgr { 27e21c39aaSAnton Nayshtut TAILQ_HEAD(, spdk_aio_mgr_io) in_flight; 28e21c39aaSAnton Nayshtut struct { 29e21c39aaSAnton Nayshtut struct spdk_aio_mgr_req *arr; 30e21c39aaSAnton Nayshtut uint32_t size; 31e21c39aaSAnton Nayshtut TAILQ_HEAD(, spdk_aio_mgr_req) pool; 32e21c39aaSAnton Nayshtut } reqs; 33e21c39aaSAnton Nayshtut struct { 34e21c39aaSAnton Nayshtut struct spdk_aio_mgr_io *arr; 35e21c39aaSAnton Nayshtut uint32_t size; 36e21c39aaSAnton Nayshtut TAILQ_HEAD(, spdk_aio_mgr_io) pool; 37e21c39aaSAnton Nayshtut } aios; 38e21c39aaSAnton Nayshtut }; 39e21c39aaSAnton Nayshtut 40e21c39aaSAnton Nayshtut static inline struct spdk_aio_mgr_req * 41e21c39aaSAnton Nayshtut aio_mgr_get_aio_req(struct spdk_aio_mgr *mgr) 42e21c39aaSAnton Nayshtut { 43e21c39aaSAnton Nayshtut struct spdk_aio_mgr_req *req = TAILQ_FIRST(&mgr->reqs.pool); 44e21c39aaSAnton Nayshtut 45e21c39aaSAnton Nayshtut if (req) { 46e21c39aaSAnton Nayshtut TAILQ_REMOVE(&mgr->reqs.pool, req, link); 47e21c39aaSAnton Nayshtut } 48e21c39aaSAnton Nayshtut 49e21c39aaSAnton Nayshtut return req; 50e21c39aaSAnton Nayshtut } 51e21c39aaSAnton Nayshtut 52e21c39aaSAnton Nayshtut static inline void 53e21c39aaSAnton Nayshtut aio_mgr_put_aio_req(struct spdk_aio_mgr *mgr, struct spdk_aio_mgr_req *req) 54e21c39aaSAnton Nayshtut { 55e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&mgr->reqs.pool, req, link); 56e21c39aaSAnton Nayshtut } 57e21c39aaSAnton Nayshtut 58e21c39aaSAnton Nayshtut static uint32_t 59e21c39aaSAnton Nayshtut fsdev_aio_submit(struct spdk_aio_mgr_io *aio, int fd, uint64_t offs, uint32_t size, 60e21c39aaSAnton Nayshtut struct iovec *iovs, 61e21c39aaSAnton Nayshtut uint32_t iovcnt, int (*aio_submit_func)(struct aiocb *), const char *aio_type) 62e21c39aaSAnton Nayshtut { 63e21c39aaSAnton Nayshtut uint32_t bytes_handled = 0; 64e21c39aaSAnton Nayshtut uint32_t iov_idx = 0; 65e21c39aaSAnton Nayshtut 66e21c39aaSAnton Nayshtut assert(aio->mgr); 67e21c39aaSAnton Nayshtut assert(!aio->err); 68e21c39aaSAnton Nayshtut assert(iovs != NULL); 69e21c39aaSAnton Nayshtut assert(iovcnt != 0); 70e21c39aaSAnton Nayshtut 71e21c39aaSAnton Nayshtut while (size && iov_idx < iovcnt) { 72e21c39aaSAnton Nayshtut struct spdk_aio_mgr_req *req; 73e21c39aaSAnton Nayshtut struct iovec *iov = &iovs[iov_idx]; 74e21c39aaSAnton Nayshtut size_t ho_handle = spdk_min(iov->iov_len, size); 75e21c39aaSAnton Nayshtut 76e21c39aaSAnton Nayshtut req = aio_mgr_get_aio_req(aio->mgr); 77e21c39aaSAnton Nayshtut if (!req) { 78e21c39aaSAnton Nayshtut SPDK_ERRLOG("cannot get aio req\n"); 79e21c39aaSAnton Nayshtut aio->err = EINVAL; 80e21c39aaSAnton Nayshtut break; 81e21c39aaSAnton Nayshtut } 82e21c39aaSAnton Nayshtut 83e21c39aaSAnton Nayshtut memset(&req->io, 0, sizeof(req->io)); 84e21c39aaSAnton Nayshtut 85e21c39aaSAnton Nayshtut req->io.aio_nbytes = ho_handle; 86e21c39aaSAnton Nayshtut req->io.aio_buf = iov->iov_base; 87e21c39aaSAnton Nayshtut req->io.aio_offset = offs + bytes_handled; 88e21c39aaSAnton Nayshtut req->io.aio_fildes = fd; 89e21c39aaSAnton Nayshtut 90e21c39aaSAnton Nayshtut SPDK_DEBUGLOG(spdk_aio_mgr_io, 91e21c39aaSAnton Nayshtut "aio to %s: aio=%p req=%p aio_nbytes=%zu aio_buf=%p aio_offset=%" PRIu64 " aio_fildes=%d\n", 92e21c39aaSAnton Nayshtut aio_type, aio, req, req->io.aio_nbytes, req->io.aio_buf, (uint64_t)req->io.aio_offset, 93e21c39aaSAnton Nayshtut req->io.aio_fildes); 94e21c39aaSAnton Nayshtut 95e21c39aaSAnton Nayshtut if (aio_submit_func(&req->io)) { 96e21c39aaSAnton Nayshtut aio->err = errno; 97e21c39aaSAnton Nayshtut SPDK_ERRLOG("aio_%s of io[%" PRIu32 "] at offset %" PRIu64 " failed with err=%d\n", 98e21c39aaSAnton Nayshtut aio_type, iov_idx, offs, aio->err); 99e21c39aaSAnton Nayshtut aio_mgr_put_aio_req(aio->mgr, req); 100e21c39aaSAnton Nayshtut break; 101e21c39aaSAnton Nayshtut } 102e21c39aaSAnton Nayshtut 103e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&aio->reqs, req, link); 104e21c39aaSAnton Nayshtut 105e21c39aaSAnton Nayshtut bytes_handled += ho_handle; 106e21c39aaSAnton Nayshtut size -= ho_handle; 107e21c39aaSAnton Nayshtut 108e21c39aaSAnton Nayshtut iov_idx++; 109e21c39aaSAnton Nayshtut } 110e21c39aaSAnton Nayshtut 111e21c39aaSAnton Nayshtut return bytes_handled; 112e21c39aaSAnton Nayshtut } 113e21c39aaSAnton Nayshtut 114e21c39aaSAnton Nayshtut static void 115e21c39aaSAnton Nayshtut fsdev_aio_cancel(struct spdk_aio_mgr_io *aio) 116e21c39aaSAnton Nayshtut { 117e21c39aaSAnton Nayshtut struct spdk_aio_mgr_req *req; 118e21c39aaSAnton Nayshtut TAILQ_FOREACH(req, &aio->reqs, link) { 119e21c39aaSAnton Nayshtut aio_cancel(req->io.aio_fildes, &req->io); 120e21c39aaSAnton Nayshtut } 121e21c39aaSAnton Nayshtut } 122e21c39aaSAnton Nayshtut 123e21c39aaSAnton Nayshtut static struct spdk_aio_mgr_io * 124e21c39aaSAnton Nayshtut aio_mgr_get_aio(struct spdk_aio_mgr *mgr, fsdev_aio_done_cb clb, void *ctx) 125e21c39aaSAnton Nayshtut { 126e21c39aaSAnton Nayshtut struct spdk_aio_mgr_io *aio = TAILQ_FIRST(&mgr->aios.pool); 127e21c39aaSAnton Nayshtut 128e21c39aaSAnton Nayshtut if (aio) { 129e21c39aaSAnton Nayshtut aio->mgr = mgr; 130e21c39aaSAnton Nayshtut aio->clb = clb; 131e21c39aaSAnton Nayshtut aio->ctx = ctx; 132e21c39aaSAnton Nayshtut aio->err = 0; 133e21c39aaSAnton Nayshtut aio->data_size = 0; 134e21c39aaSAnton Nayshtut TAILQ_INIT(&aio->reqs); 135e21c39aaSAnton Nayshtut TAILQ_REMOVE(&mgr->aios.pool, aio, link); 136e21c39aaSAnton Nayshtut } 137e21c39aaSAnton Nayshtut 138e21c39aaSAnton Nayshtut return aio; 139e21c39aaSAnton Nayshtut } 140e21c39aaSAnton Nayshtut 141e21c39aaSAnton Nayshtut static inline void 142e21c39aaSAnton Nayshtut aio_mgr_put_aio(struct spdk_aio_mgr *mgr, struct spdk_aio_mgr_io *aio) 143e21c39aaSAnton Nayshtut { 144e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&aio->mgr->aios.pool, aio, link); 145e21c39aaSAnton Nayshtut } 146e21c39aaSAnton Nayshtut 147e21c39aaSAnton Nayshtut static struct spdk_aio_mgr_io * 148e21c39aaSAnton Nayshtut spdk_aio_mgr_submit_io(struct spdk_aio_mgr *mgr, fsdev_aio_done_cb clb, void *ctx, 149e21c39aaSAnton Nayshtut int fd, uint64_t offs, uint32_t size, struct iovec *iovs, uint32_t iovcnt, 150e21c39aaSAnton Nayshtut int (*aio_submit_func)(struct aiocb *), const char *aio_type) 151e21c39aaSAnton Nayshtut { 152e21c39aaSAnton Nayshtut struct spdk_aio_mgr_io *aio; 153e21c39aaSAnton Nayshtut uint32_t bytes_handled; 154e21c39aaSAnton Nayshtut 155e21c39aaSAnton Nayshtut SPDK_DEBUGLOG(spdk_aio_mgr_io, "%s: fd=%d offs=%" PRIu64 " size=%" PRIu32 " iovcnt=%" PRIu32 "\n", 156e21c39aaSAnton Nayshtut aio_type, fd, offs, size, iovcnt); 157e21c39aaSAnton Nayshtut 158e21c39aaSAnton Nayshtut aio = aio_mgr_get_aio(mgr, clb, ctx); 159e21c39aaSAnton Nayshtut if (!aio) { 160e21c39aaSAnton Nayshtut SPDK_ERRLOG("Cannot get aio\n"); 161e21c39aaSAnton Nayshtut clb(ctx, 0, EFAULT); 162e21c39aaSAnton Nayshtut return NULL; 163e21c39aaSAnton Nayshtut } 164e21c39aaSAnton Nayshtut 165e21c39aaSAnton Nayshtut bytes_handled = fsdev_aio_submit(aio, fd, offs, size, iovs, iovcnt, aio_submit_func, aio_type); 166e21c39aaSAnton Nayshtut SPDK_DEBUGLOG(spdk_aio_mgr_io, "%s: aio=%p: handled %" PRIu32 " bytes\n", aio_type, aio, 167e21c39aaSAnton Nayshtut bytes_handled); 168e21c39aaSAnton Nayshtut if (bytes_handled) { 169e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&aio->mgr->in_flight, aio, link); 170e21c39aaSAnton Nayshtut return aio; 171e21c39aaSAnton Nayshtut } else { 172e21c39aaSAnton Nayshtut aio->clb(aio->ctx, 0, aio->err); 173e21c39aaSAnton Nayshtut aio_mgr_put_aio(mgr, aio); 174e21c39aaSAnton Nayshtut return NULL; 175e21c39aaSAnton Nayshtut } 176e21c39aaSAnton Nayshtut } 177e21c39aaSAnton Nayshtut 178e21c39aaSAnton Nayshtut struct spdk_aio_mgr * 179e21c39aaSAnton Nayshtut spdk_aio_mgr_create(uint32_t max_aios) 180e21c39aaSAnton Nayshtut { 181e21c39aaSAnton Nayshtut struct spdk_aio_mgr *mgr; 182e21c39aaSAnton Nayshtut uint32_t i; 183e21c39aaSAnton Nayshtut 184e21c39aaSAnton Nayshtut mgr = calloc(1, sizeof(*mgr)); 185e21c39aaSAnton Nayshtut if (!mgr) { 186e21c39aaSAnton Nayshtut SPDK_ERRLOG("cannot alloc mgr of %zu bytes\n", sizeof(*mgr)); 187e21c39aaSAnton Nayshtut return NULL; 188e21c39aaSAnton Nayshtut } 189e21c39aaSAnton Nayshtut 190e21c39aaSAnton Nayshtut mgr->reqs.arr = calloc(max_aios * REQS_PER_AIO, sizeof(mgr->reqs.arr[0])); 191e21c39aaSAnton Nayshtut if (!mgr->reqs.arr) { 192e21c39aaSAnton Nayshtut SPDK_ERRLOG("cannot alloc req pool of %" PRIu32 " * %d\n", max_aios, REQS_PER_AIO); 193e21c39aaSAnton Nayshtut free(mgr); 194e21c39aaSAnton Nayshtut return NULL; 195e21c39aaSAnton Nayshtut } 196e21c39aaSAnton Nayshtut 197e21c39aaSAnton Nayshtut mgr->aios.arr = calloc(max_aios, sizeof(mgr->aios.arr[0])); 198e21c39aaSAnton Nayshtut if (!mgr->aios.arr) { 199e21c39aaSAnton Nayshtut SPDK_ERRLOG("cannot alloc aios pool of %" PRIu32 "\n", max_aios); 200e21c39aaSAnton Nayshtut free(mgr->reqs.arr); 201e21c39aaSAnton Nayshtut free(mgr); 202e21c39aaSAnton Nayshtut return NULL; 203e21c39aaSAnton Nayshtut } 204e21c39aaSAnton Nayshtut 205e21c39aaSAnton Nayshtut TAILQ_INIT(&mgr->in_flight); 206e21c39aaSAnton Nayshtut TAILQ_INIT(&mgr->reqs.pool); 207e21c39aaSAnton Nayshtut TAILQ_INIT(&mgr->aios.pool); 208e21c39aaSAnton Nayshtut 209e21c39aaSAnton Nayshtut for (i = 0; i < max_aios * REQS_PER_AIO; i++) { 210e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&mgr->reqs.pool, &mgr->reqs.arr[i], link); 211e21c39aaSAnton Nayshtut } 212e21c39aaSAnton Nayshtut 213e21c39aaSAnton Nayshtut for (i = 0; i < max_aios; i++) { 214e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&mgr->aios.pool, &mgr->aios.arr[i], link); 215e21c39aaSAnton Nayshtut } 216e21c39aaSAnton Nayshtut 217e21c39aaSAnton Nayshtut return mgr; 218e21c39aaSAnton Nayshtut } 219e21c39aaSAnton Nayshtut 220e21c39aaSAnton Nayshtut struct spdk_aio_mgr_io * 221e21c39aaSAnton Nayshtut spdk_aio_mgr_read(struct spdk_aio_mgr *mgr, fsdev_aio_done_cb clb, void *ctx, 222e21c39aaSAnton Nayshtut int fd, uint64_t offs, uint32_t size, struct iovec *iovs, uint32_t iovcnt) 223e21c39aaSAnton Nayshtut { 224e21c39aaSAnton Nayshtut return spdk_aio_mgr_submit_io(mgr, clb, ctx, fd, offs, size, iovs, iovcnt, aio_read, "read"); 225e21c39aaSAnton Nayshtut } 226e21c39aaSAnton Nayshtut 227e21c39aaSAnton Nayshtut struct spdk_aio_mgr_io * 228e21c39aaSAnton Nayshtut spdk_aio_mgr_write(struct spdk_aio_mgr *mgr, fsdev_aio_done_cb clb, void *ctx, 229e21c39aaSAnton Nayshtut int fd, uint64_t offs, uint32_t size, const struct iovec *iovs, uint32_t iovcnt) 230e21c39aaSAnton Nayshtut { 231e21c39aaSAnton Nayshtut return spdk_aio_mgr_submit_io(mgr, clb, ctx, fd, offs, size, (struct iovec *)iovs, iovcnt, 232e21c39aaSAnton Nayshtut aio_write, "write"); 233e21c39aaSAnton Nayshtut } 234e21c39aaSAnton Nayshtut 235e21c39aaSAnton Nayshtut 236e21c39aaSAnton Nayshtut void 237e21c39aaSAnton Nayshtut spdk_aio_mgr_cancel(struct spdk_aio_mgr *mgr, struct spdk_aio_mgr_io *aio) 238e21c39aaSAnton Nayshtut { 239e21c39aaSAnton Nayshtut assert(mgr == aio->mgr); 240e21c39aaSAnton Nayshtut 241e21c39aaSAnton Nayshtut SPDK_DEBUGLOG(spdk_aio_mgr_io, "aio=%p cancelled\n", aio); 242e21c39aaSAnton Nayshtut fsdev_aio_cancel(aio); 243e21c39aaSAnton Nayshtut } 244e21c39aaSAnton Nayshtut 245*a29d7fdfSAnton Nayshtut bool 246e21c39aaSAnton Nayshtut spdk_aio_mgr_poll(struct spdk_aio_mgr *mgr) 247e21c39aaSAnton Nayshtut { 248e21c39aaSAnton Nayshtut struct spdk_aio_mgr_io *aio, *tmp_aio; 249*a29d7fdfSAnton Nayshtut uint32_t num_completions = 0; 250e21c39aaSAnton Nayshtut TAILQ_FOREACH_SAFE(aio, &mgr->in_flight, link, tmp_aio) { 251e21c39aaSAnton Nayshtut struct spdk_aio_mgr_req *req, *tmp_req; 252e21c39aaSAnton Nayshtut TAILQ_FOREACH_SAFE(req, &aio->reqs, link, tmp_req) { 253e21c39aaSAnton Nayshtut ssize_t ret; 254e21c39aaSAnton Nayshtut int err = aio_error(&req->io); 255e21c39aaSAnton Nayshtut if (err == EINPROGRESS) { /* the request has not been completed yet */ 256e21c39aaSAnton Nayshtut break; /* stop checking completions for this aio */ 257e21c39aaSAnton Nayshtut } 258e21c39aaSAnton Nayshtut 259*a29d7fdfSAnton Nayshtut if (!err) { /* the request completed successfully */ 260e21c39aaSAnton Nayshtut ; 261e21c39aaSAnton Nayshtut } else if (err == ECANCELED) { /* the request was canceled */ 262e21c39aaSAnton Nayshtut SPDK_WARNLOG("aio processing was cancelled\n"); 263e21c39aaSAnton Nayshtut aio->err = EAGAIN; 264e21c39aaSAnton Nayshtut } else { 265e21c39aaSAnton Nayshtut SPDK_ERRLOG("aio processing failed with err=%d\n", err); 266e21c39aaSAnton Nayshtut aio->err = err; 267e21c39aaSAnton Nayshtut } 268e21c39aaSAnton Nayshtut 269e21c39aaSAnton Nayshtut ret = aio_return(&req->io); 270e21c39aaSAnton Nayshtut if (ret > 0) { 271e21c39aaSAnton Nayshtut aio->data_size += ret; 272e21c39aaSAnton Nayshtut } 273e21c39aaSAnton Nayshtut 274e21c39aaSAnton Nayshtut SPDK_DEBUGLOG(spdk_aio_mgr_io, "aio completed: aio=%p req=%p err=%d ret=%zd\n", aio, req, err, ret); 275e21c39aaSAnton Nayshtut 276e21c39aaSAnton Nayshtut /* the request processing is done */ 277e21c39aaSAnton Nayshtut TAILQ_REMOVE(&aio->reqs, req, link); /* remove the req from the aio */ 278e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&mgr->reqs.pool, req, link); /* return the req to the pool */ 279*a29d7fdfSAnton Nayshtut num_completions++; 280e21c39aaSAnton Nayshtut if (TAILQ_EMPTY(&aio->reqs)) { /* all the aio's requests have been processed */ 281e21c39aaSAnton Nayshtut SPDK_DEBUGLOG(spdk_aio_mgr_io, "aio=%p is done (data_size=%" PRIu32 ")\n", aio, aio->data_size); 282e21c39aaSAnton Nayshtut aio->clb(aio->ctx, aio->data_size, aio->err); /* call the user's callback */ 283e21c39aaSAnton Nayshtut TAILQ_REMOVE(&mgr->in_flight, aio, link); /* remove the aio from the in_flight aios list */ 284e21c39aaSAnton Nayshtut TAILQ_INSERT_TAIL(&mgr->aios.pool, aio, link); /* return the aio to the pool */ 285e21c39aaSAnton Nayshtut } 286e21c39aaSAnton Nayshtut } 287e21c39aaSAnton Nayshtut } 288*a29d7fdfSAnton Nayshtut 289*a29d7fdfSAnton Nayshtut return num_completions ? true : false; 290e21c39aaSAnton Nayshtut } 291e21c39aaSAnton Nayshtut 292e21c39aaSAnton Nayshtut void 293e21c39aaSAnton Nayshtut spdk_aio_mgr_delete(struct spdk_aio_mgr *mgr) 294e21c39aaSAnton Nayshtut { 295e21c39aaSAnton Nayshtut assert(TAILQ_EMPTY(&mgr->in_flight)); 296e21c39aaSAnton Nayshtut free(mgr->aios.arr); 297e21c39aaSAnton Nayshtut free(mgr->reqs.arr); 298e21c39aaSAnton Nayshtut free(mgr); 299e21c39aaSAnton Nayshtut } 300e21c39aaSAnton Nayshtut 301e21c39aaSAnton Nayshtut SPDK_LOG_REGISTER_COMPONENT(spdk_aio_mgr_io) 302