diff --git a/lib/nvme/nvme_cuse.c b/lib/nvme/nvme_cuse.c index 588b3d4d1..7f927fb61 100644 --- a/lib/nvme/nvme_cuse.c +++ b/lib/nvme/nvme_cuse.c @@ -61,6 +61,194 @@ struct cuse_device { static TAILQ_HEAD(, cuse_device) g_ctrlr_ctx_head = TAILQ_HEAD_INITIALIZER(g_ctrlr_ctx_head); static int g_controllers_found = 0; +struct cuse_io_ctx { + struct spdk_nvme_cmd nvme_cmd; + + uint64_t lba; + uint32_t lba_count; + + void *data; + int data_len; + + fuse_req_t req; +}; + +static void +cuse_io_ctx_free(struct cuse_io_ctx *ctx) +{ + spdk_free(ctx->data); + free(ctx); +} + +#define FUSE_REPLY_CHECK_BUFFER(req, arg, out_bufsz, val) \ + if (out_bufsz == 0) { \ + struct iovec out_iov; \ + out_iov.iov_base = (void *)arg; \ + out_iov.iov_len = sizeof(val); \ + fuse_reply_ioctl_retry(req, NULL, 0, &out_iov, 1); \ + return; \ + } + +static void +cuse_nvme_admin_cmd_cb(void *arg, const struct spdk_nvme_cpl *cpl) +{ + struct cuse_io_ctx *ctx = arg; + struct iovec out_iov[2]; + struct spdk_nvme_cpl _cpl; + + memcpy(&_cpl, cpl, sizeof(struct spdk_nvme_cpl)); + + out_iov[0].iov_base = &_cpl.cdw0; + out_iov[0].iov_len = sizeof(_cpl.cdw0); + if (ctx->data_len > 0) { + out_iov[1].iov_base = ctx->data; + out_iov[1].iov_len = ctx->data_len; + fuse_reply_ioctl_iov(ctx->req, 0, out_iov, 2); + } else { + fuse_reply_ioctl_iov(ctx->req, 0, out_iov, 1); + } + + cuse_io_ctx_free(ctx); +} + +static void +cuse_nvme_admin_cmd_execute(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg) +{ + int rc; + struct cuse_io_ctx *ctx = arg; + + rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &ctx->nvme_cmd, ctx->data, ctx->data_len, + cuse_nvme_admin_cmd_cb, (void *)ctx); + if (rc < 0) { + fuse_reply_err(ctx->req, EINVAL); + cuse_io_ctx_free(ctx); + } +} + +static void +cuse_nvme_admin_cmd(fuse_req_t req, int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, + const void *in_buf, size_t in_bufsz, size_t out_bufsz) +{ + struct nvme_admin_cmd *admin_cmd; + struct iovec in_iov, out_iov[2]; + struct cuse_io_ctx *ctx; + int rv; + struct cuse_device *cuse_device = fuse_req_userdata(req); + + in_iov.iov_base = (void *)arg; + in_iov.iov_len = sizeof(*admin_cmd); + if (in_bufsz == 0) { + fuse_reply_ioctl_retry(req, &in_iov, 1, NULL, 0); + return; + } + + admin_cmd = (struct nvme_admin_cmd *)in_buf; + + switch (spdk_nvme_opc_get_data_transfer(admin_cmd->opcode)) { + case SPDK_NVME_DATA_NONE: + SPDK_ERRLOG("SPDK_NVME_DATA_NONE not implemented\n"); + fuse_reply_err(req, EINVAL); + return; + case SPDK_NVME_DATA_HOST_TO_CONTROLLER: + SPDK_ERRLOG("SPDK_NVME_DATA_HOST_TO_CONTROLLER not implemented\n"); + fuse_reply_err(req, EINVAL); + return; + case SPDK_NVME_DATA_CONTROLLER_TO_HOST: + if (out_bufsz == 0) { + out_iov[0].iov_base = &((struct nvme_admin_cmd *)arg)->result; + out_iov[0].iov_len = sizeof(uint32_t); + if (admin_cmd->data_len > 0) { + out_iov[1].iov_base = (void *)admin_cmd->addr; + out_iov[1].iov_len = admin_cmd->data_len; + fuse_reply_ioctl_retry(req, &in_iov, 1, out_iov, 2); + } else { + fuse_reply_ioctl_retry(req, &in_iov, 1, out_iov, 1); + } + return; + } + + ctx = (struct cuse_io_ctx *)calloc(1, sizeof(struct cuse_io_ctx)); + if (!ctx) { + SPDK_ERRLOG("Cannot allocate memory for cuse_io_ctx\n"); + fuse_reply_err(req, ENOMEM); + return; + } + + ctx->req = req; + + memset(&ctx->nvme_cmd, 0, sizeof(ctx->nvme_cmd)); + ctx->nvme_cmd.opc = admin_cmd->opcode; + ctx->nvme_cmd.nsid = admin_cmd->nsid; + ctx->nvme_cmd.cdw10 = admin_cmd->cdw10; + ctx->nvme_cmd.cdw11 = admin_cmd->cdw11; + ctx->nvme_cmd.cdw12 = admin_cmd->cdw12; + ctx->nvme_cmd.cdw13 = admin_cmd->cdw13; + ctx->nvme_cmd.cdw14 = admin_cmd->cdw14; + ctx->nvme_cmd.cdw15 = admin_cmd->cdw15; + + ctx->data_len = admin_cmd->data_len; + if (ctx->data_len > 0) { + ctx->data = spdk_malloc(ctx->data_len, 0, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); + if (!ctx->data) { + SPDK_ERRLOG("Cannot allocate memory for data\n"); + fuse_reply_err(req, ENOMEM); + free(ctx); + return; + } + } + + break; + case SPDK_NVME_DATA_BIDIRECTIONAL: + fuse_reply_err(req, EINVAL); + return; + } + + rv = nvme_io_msg_send(cuse_device->ctrlr, 0, cuse_nvme_admin_cmd_execute, ctx); + if (rv) { + SPDK_ERRLOG("Cannot send io msg to the controller\n"); + fuse_reply_err(req, -rv); + cuse_io_ctx_free(ctx); + return; + } +} + +static void +cuse_nvme_reset_execute(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg) +{ + int rc; + fuse_req_t req = arg; + + rc = spdk_nvme_ctrlr_reset(ctrlr); + if (rc) { + fuse_reply_err(req, rc); + return; + } + + fuse_reply_ioctl_iov(req, 0, NULL, 0); +} + +static void +cuse_nvme_reset(fuse_req_t req, int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, + const void *in_buf, size_t in_bufsz, size_t out_bufsz) +{ + int rv; + struct cuse_device *cuse_device = fuse_req_userdata(req); + + if (cuse_device->nsid) { + SPDK_ERRLOG("Namespace reset not supported\n"); + fuse_reply_err(req, EINVAL); + return; + } + + rv = nvme_io_msg_send(cuse_device->ctrlr, cuse_device->nsid, cuse_nvme_reset_execute, (void *)req); + if (rv) { + SPDK_ERRLOG("Cannot send reset\n"); + fuse_reply_err(req, EINVAL); + } +} + static void cuse_ctrlr_ioctl(fuse_req_t req, int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, @@ -71,8 +259,19 @@ cuse_ctrlr_ioctl(fuse_req_t req, int cmd, void *arg, return; } - SPDK_ERRLOG("Unsupported IOCTL 0x%X.\n", cmd); - fuse_reply_err(req, EINVAL); + switch (cmd) { + case NVME_IOCTL_ADMIN_CMD: + cuse_nvme_admin_cmd(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz); + break; + + case NVME_IOCTL_RESET: + cuse_nvme_reset(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz); + break; + + default: + SPDK_ERRLOG("Unsupported IOCTL 0x%X.\n", cmd); + fuse_reply_err(req, EINVAL); + } } static void