/* * Copyright (c) 2022 Intel Corporation * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_DECLARE(nvme, CONFIG_NVME_LOG_LEVEL); #include #include #include "nvme.h" static int nvme_disk_status(struct disk_info *disk) { return 0; } static int nvme_disk_read(struct disk_info *disk, uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector) { struct nvme_namespace *ns = CONTAINER_OF(disk->name, struct nvme_namespace, name[0]); struct nvme_completion_poll_status status = NVME_CPL_STATUS_POLL_INIT(status); struct nvme_request *request; uint32_t payload_size; int ret = 0; if (!NVME_IS_BUFFER_DWORD_ALIGNED(data_buf)) { LOG_WRN("Data buffer pointer needs to be 4-bytes aligned"); return -EINVAL; } nvme_lock(disk->dev); payload_size = num_sector * nvme_namespace_get_sector_size(ns); request = nvme_allocate_request_vaddr((void *)data_buf, payload_size, nvme_completion_poll_cb, &status); if (request == NULL) { ret = -ENOMEM; goto out; } nvme_namespace_read_cmd(&request->cmd, ns->id, start_sector, num_sector); /* We use only the first ioq atm * ToDo: use smp cpu id and use it to select ioq */ nvme_cmd_qpair_submit_request(ns->ctrlr->ioq, request); nvme_completion_poll(&status); if (nvme_cpl_status_is_error(&status)) { LOG_WRN("Reading at sector %u (count %d) on disk %s failed", start_sector, num_sector, ns->name); nvme_completion_print(&status.cpl); ret = -EIO; } out: nvme_unlock(disk->dev); return ret; } static int nvme_disk_write(struct disk_info *disk, const uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector) { struct nvme_namespace *ns = CONTAINER_OF(disk->name, struct nvme_namespace, name[0]); struct nvme_completion_poll_status status = NVME_CPL_STATUS_POLL_INIT(status); struct nvme_request *request; uint32_t payload_size; int ret = 0; if (!NVME_IS_BUFFER_DWORD_ALIGNED(data_buf)) { LOG_WRN("Data buffer pointer needs to be 4-bytes aligned"); return -EINVAL; } nvme_lock(disk->dev); payload_size = num_sector * nvme_namespace_get_sector_size(ns); request = nvme_allocate_request_vaddr((void *)data_buf, payload_size, nvme_completion_poll_cb, &status); if (request == NULL) { ret = -ENOMEM; goto out; } nvme_namespace_write_cmd(&request->cmd, ns->id, start_sector, num_sector); /* We use only the first ioq atm * ToDo: use smp cpu id and use it to select ioq */ nvme_cmd_qpair_submit_request(ns->ctrlr->ioq, request); nvme_completion_poll(&status); if (nvme_cpl_status_is_error(&status)) { LOG_WRN("Writing at sector %u (count %d) on disk %s failed", start_sector, num_sector, ns->name); nvme_completion_print(&status.cpl); ret = -EIO; } out: nvme_unlock(disk->dev); return ret; } static int nvme_disk_flush(struct nvme_namespace *ns) { struct nvme_completion_poll_status status = NVME_CPL_STATUS_POLL_INIT(status); struct nvme_request *request; request = nvme_allocate_request_null(nvme_completion_poll_cb, &status); if (request == NULL) { return -ENOMEM; } nvme_namespace_flush_cmd(&request->cmd, ns->id); /* We use only the first ioq * ToDo: use smp cpu id and use it to select ioq */ nvme_cmd_qpair_submit_request(ns->ctrlr->ioq, request); nvme_completion_poll(&status); if (nvme_cpl_status_is_error(&status)) { LOG_ERR("Flushing disk %s failed", ns->name); nvme_completion_print(&status.cpl); return -EIO; } return 0; } static int nvme_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff) { struct nvme_namespace *ns = CONTAINER_OF(disk->name, struct nvme_namespace, name[0]); int ret = 0; nvme_lock(disk->dev); switch (cmd) { case DISK_IOCTL_GET_SECTOR_COUNT: if (!buff) { ret = -EINVAL; break; } *(uint32_t *)buff = nvme_namespace_get_num_sectors(ns); break; case DISK_IOCTL_GET_SECTOR_SIZE: if (!buff) { ret = -EINVAL; break; } *(uint32_t *)buff = nvme_namespace_get_sector_size(ns); break; case DISK_IOCTL_GET_ERASE_BLOCK_SZ: if (!buff) { ret = -EINVAL; break; } *(uint32_t *)buff = nvme_namespace_get_sector_size(ns); break; case DISK_IOCTL_CTRL_DEINIT: case DISK_IOCTL_CTRL_SYNC: ret = nvme_disk_flush(ns); break; case DISK_IOCTL_CTRL_INIT: ret = 0; break; default: ret = -EINVAL; } nvme_unlock(disk->dev); return ret; } static int nvme_disk_init(struct disk_info *disk) { return nvme_disk_ioctl(disk, DISK_IOCTL_CTRL_INIT, NULL); } static const struct disk_operations nvme_disk_ops = { .init = nvme_disk_init, .status = nvme_disk_status, .read = nvme_disk_read, .write = nvme_disk_write, .ioctl = nvme_disk_ioctl, }; int nvme_namespace_disk_setup(struct nvme_namespace *ns, struct disk_info *disk) { disk->name = ns->name; disk->ops = &nvme_disk_ops; disk->dev = ns->ctrlr->dev; return disk_access_register(disk); }