zephyr/drivers/disk/nvme/nvme_disk.c

218 lines
4.9 KiB
C

/*
* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(nvme, CONFIG_NVME_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#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);
}