218 lines
4.9 KiB
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);
|
|
}
|