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