lib/nvme: NVMe Boot Partition Read / Write support

Signed-off-by: Krishna Kanth Reddy <krish.reddy@samsung.com>
Change-Id: I44a7f41553db2f622b14bd4a20cad7f014801a65
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8631
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
This commit is contained in:
Krishna Kanth Reddy 2021-07-02 17:02:29 +05:30 committed by Tomasz Zawadzki
parent 6e5d6032a0
commit fec55c842c
5 changed files with 333 additions and 0 deletions

View File

@ -56,6 +56,11 @@ have been added. These functions accept `spdk_nvme_ns_cmd_ext_io_opts` structure
options, e.g. DMA memory domain which describes data that may belong to another memory domain and options, e.g. DMA memory domain which describes data that may belong to another memory domain and
can't be accessed directly. can't be accessed directly.
Added a new function `spdk_nvme_ctrlr_get_regs_bpinfo` to get boot partition info of a controller.
Added new functions `spdk_nvme_ctrlr_write_boot_partition`,
`spdk_nvme_ctrlr_read_boot_partition_start` and `spdk_nvme_ctrlr_read_boot_partition_poll`
to write and read the boot partitions of a controller.
### dpdk ### dpdk
Updated DPDK submodule to DPDK 21.08. Updated DPDK submodule to DPDK 21.08.

View File

@ -1196,6 +1196,15 @@ union spdk_nvme_cmbsz_register spdk_nvme_ctrlr_get_regs_cmbsz(struct spdk_nvme_c
*/ */
union spdk_nvme_pmrcap_register spdk_nvme_ctrlr_get_regs_pmrcap(struct spdk_nvme_ctrlr *ctrlr); union spdk_nvme_pmrcap_register spdk_nvme_ctrlr_get_regs_pmrcap(struct spdk_nvme_ctrlr *ctrlr);
/**
* Get the NVMe controller BPINFO (Boot Partition Information) register.
*
* \param ctrlr Opaque handle to NVMe controller.
*
* \return the NVMe controller BPINFO (Boot Partition Information) register.
*/
union spdk_nvme_bpinfo_register spdk_nvme_ctrlr_get_regs_bpinfo(struct spdk_nvme_ctrlr *ctrlr);
/** /**
* Get the NVMe controller PMR size. * Get the NVMe controller PMR size.
* *
@ -2301,6 +2310,78 @@ int spdk_nvme_ctrlr_update_firmware(struct spdk_nvme_ctrlr *ctrlr, void *payload
int slot, enum spdk_nvme_fw_commit_action commit_action, int slot, enum spdk_nvme_fw_commit_action commit_action,
struct spdk_nvme_status *completion_status); struct spdk_nvme_status *completion_status);
/**
* Start the Read from a Boot Partition.
*
* This function is thread safe and can be called at any point after spdk_nvme_probe().
*
* \param ctrlr NVMe controller to perform the Boot Partition read.
* \param payload The data buffer for Boot Partition read.
* \param bprsz Read size in multiples of 4 KiB to copy into the Boot Partition Memory Buffer.
* \param bprof Boot Partition offset to read from in 4 KiB units.
* \param bpid Boot Partition identifier for the Boot Partition read operation.
*
* \return 0 if Boot Partition read is successful. Negated errno on the following error conditions:
* -ENOMEM: if resources could not be allocated.
* -ENOTSUP: Boot Partition is not supported by the Controller.
* -EIO: Registers access failure.
* -EINVAL: Parameters are invalid.
* -EFAULT: Invalid address was specified as part of payload.
* -EALREADY: Boot Partition read already initiated.
*/
int spdk_nvme_ctrlr_read_boot_partition_start(struct spdk_nvme_ctrlr *ctrlr, void *payload,
uint32_t bprsz, uint32_t bprof, uint32_t bpid);
/**
* Poll the status of the Read from a Boot Partition.
*
* This function is thread safe and can be called at any point after spdk_nvme_probe().
*
* \param ctrlr NVMe controller to perform the Boot Partition read.
*
* \return 0 if Boot Partition read is successful. Negated errno on the following error conditions:
* -EIO: Registers access failure.
* -EINVAL: Invalid read status or the Boot Partition read is not initiated yet.
* -EAGAIN: If the read is still in progress; users must call
* spdk_nvme_ctrlr_read_boot_partition_poll again to check the read status.
*/
int spdk_nvme_ctrlr_read_boot_partition_poll(struct spdk_nvme_ctrlr *ctrlr);
/**
* Write to a Boot Partition.
*
* This function is thread safe and can be called at any point after spdk_nvme_probe().
* Users will get the completion after the data is downloaded, image is replaced and
* Boot Partition is activated or when the sequence encounters an error.
*
* \param ctrlr NVMe controller to perform the Boot Partition write.
* \param payload The data buffer for Boot Partition write.
* \param size Data size to write to the Boot Partition.
* \param bpid Boot Partition identifier for the Boot Partition write operation.
* \param cb_fn Callback function to invoke when the operation is completed.
* \param cb_arg Argument to pass to the callback function.
*
* \return 0 if Boot Partition write submit is successful. Negated errno on the following error conditions:
* -ENOMEM: if resources could not be allocated.
* -ENOTSUP: Boot Partition is not supported by the Controller.
* -EIO: Registers access failure.
* -EINVAL: Parameters are invalid.
*/
int spdk_nvme_ctrlr_write_boot_partition(struct spdk_nvme_ctrlr *ctrlr, void *payload,
uint32_t size, uint32_t bpid, spdk_nvme_cmd_cb cb_fn, void *cb_arg);
/**
* Return virtual address of PCIe NVM I/O registers
*
* This function returns a pointer to the PCIe I/O registers for a controller
* or NULL if unsupported for this transport.
*
* \param ctrlr Controller whose registers are to be accessed.
*
* \return Pointer to virtual address of register bank, or NULL.
*/
volatile struct spdk_nvme_registers *spdk_nvme_ctrlr_get_registers(struct spdk_nvme_ctrlr *ctrlr);
/** /**
* Reserve the controller memory buffer for data transfer use. * Reserve the controller memory buffer for data transfer use.
* *

View File

@ -124,6 +124,27 @@ nvme_ctrlr_get_pmrcap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_pmrcap_regi
&pmrcap->raw); &pmrcap->raw);
} }
int
nvme_ctrlr_get_bpinfo(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bpinfo_register *bpinfo)
{
return nvme_transport_ctrlr_get_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, bpinfo.raw),
&bpinfo->raw);
}
int
nvme_ctrlr_set_bprsel(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bprsel_register *bprsel)
{
return nvme_transport_ctrlr_set_reg_4(ctrlr, offsetof(struct spdk_nvme_registers, bprsel.raw),
bprsel->raw);
}
int
nvme_ctrlr_set_bpmbl(struct spdk_nvme_ctrlr *ctrlr, uint64_t bpmbl_value)
{
return nvme_transport_ctrlr_set_reg_8(ctrlr, offsetof(struct spdk_nvme_registers, bpmbl),
bpmbl_value);
}
static int static int
nvme_ctrlr_set_nssr(struct spdk_nvme_ctrlr *ctrlr, uint32_t nssr_value) nvme_ctrlr_set_nssr(struct spdk_nvme_ctrlr *ctrlr, uint32_t nssr_value)
{ {
@ -3959,6 +3980,17 @@ union spdk_nvme_pmrcap_register spdk_nvme_ctrlr_get_regs_pmrcap(struct spdk_nvme
return pmrcap; return pmrcap;
} }
union spdk_nvme_bpinfo_register spdk_nvme_ctrlr_get_regs_bpinfo(struct spdk_nvme_ctrlr *ctrlr)
{
union spdk_nvme_bpinfo_register bpinfo;
if (nvme_ctrlr_get_bpinfo(ctrlr, &bpinfo)) {
bpinfo.raw = 0;
}
return bpinfo;
}
uint64_t uint64_t
spdk_nvme_ctrlr_get_pmrsz(struct spdk_nvme_ctrlr *ctrlr) spdk_nvme_ctrlr_get_pmrsz(struct spdk_nvme_ctrlr *ctrlr)
{ {
@ -4513,6 +4545,194 @@ spdk_nvme_ctrlr_unmap_pmr(struct spdk_nvme_ctrlr *ctrlr)
return rc; return rc;
} }
int spdk_nvme_ctrlr_read_boot_partition_start(struct spdk_nvme_ctrlr *ctrlr, void *payload,
uint32_t bprsz, uint32_t bprof, uint32_t bpid)
{
union spdk_nvme_bprsel_register bprsel;
union spdk_nvme_bpinfo_register bpinfo;
uint64_t bpmbl, bpmb_size;
if (ctrlr->cap.bits.bps == 0) {
return -ENOTSUP;
}
if (nvme_ctrlr_get_bpinfo(ctrlr, &bpinfo)) {
NVME_CTRLR_ERRLOG(ctrlr, "get bpinfo failed\n");
return -EIO;
}
if (bpinfo.bits.brs == SPDK_NVME_BRS_READ_IN_PROGRESS) {
NVME_CTRLR_ERRLOG(ctrlr, "Boot Partition read already initiated\n");
return -EALREADY;
}
nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
bpmb_size = bprsz * 4096;
bpmbl = spdk_vtophys(payload, &bpmb_size);
if (bpmbl == SPDK_VTOPHYS_ERROR) {
NVME_CTRLR_ERRLOG(ctrlr, "spdk_vtophys of bpmbl failed\n");
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
return -EFAULT;
}
if (bpmb_size != bprsz * 4096) {
NVME_CTRLR_ERRLOG(ctrlr, "Boot Partition buffer is not physically contiguous\n");
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
return -EFAULT;
}
if (nvme_ctrlr_set_bpmbl(ctrlr, bpmbl)) {
NVME_CTRLR_ERRLOG(ctrlr, "set_bpmbl() failed\n");
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
return -EIO;
}
bprsel.bits.bpid = bpid;
bprsel.bits.bprof = bprof;
bprsel.bits.bprsz = bprsz;
if (nvme_ctrlr_set_bprsel(ctrlr, &bprsel)) {
NVME_CTRLR_ERRLOG(ctrlr, "set_bprsel() failed\n");
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
return -EIO;
}
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
return 0;
}
int spdk_nvme_ctrlr_read_boot_partition_poll(struct spdk_nvme_ctrlr *ctrlr)
{
int rc = 0;
union spdk_nvme_bpinfo_register bpinfo;
if (nvme_ctrlr_get_bpinfo(ctrlr, &bpinfo)) {
NVME_CTRLR_ERRLOG(ctrlr, "get bpinfo failed\n");
return -EIO;
}
switch (bpinfo.bits.brs) {
case SPDK_NVME_BRS_NO_READ:
NVME_CTRLR_ERRLOG(ctrlr, "Boot Partition read not initiated\n");
rc = -EINVAL;
break;
case SPDK_NVME_BRS_READ_IN_PROGRESS:
NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition read in progress\n");
rc = -EAGAIN;
break;
case SPDK_NVME_BRS_READ_ERROR:
NVME_CTRLR_ERRLOG(ctrlr, "Error completing Boot Partition read\n");
rc = -EIO;
break;
case SPDK_NVME_BRS_READ_SUCCESS:
NVME_CTRLR_INFOLOG(ctrlr, "Boot Partition read completed successfully\n");
break;
default:
NVME_CTRLR_ERRLOG(ctrlr, "Invalid Boot Partition read status\n");
rc = -EINVAL;
}
return rc;
}
static void
nvme_write_boot_partition_cb(void *arg, const struct spdk_nvme_cpl *cpl)
{
int res;
struct spdk_nvme_ctrlr *ctrlr = arg;
struct spdk_nvme_fw_commit fw_commit;
struct spdk_nvme_cpl err_cpl =
{.status = {.sct = SPDK_NVME_SCT_GENERIC, .sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR }};
if (spdk_nvme_cpl_is_error(cpl)) {
NVME_CTRLR_ERRLOG(ctrlr, "Write Boot Partition failed\n");
ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, cpl);
return;
}
if (ctrlr->bp_ws == SPDK_NVME_BP_WS_DOWNLOADING) {
NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Downloading at Offset %d Success\n", ctrlr->fw_offset);
ctrlr->fw_payload += ctrlr->fw_transfer_size;
ctrlr->fw_offset += ctrlr->fw_transfer_size;
ctrlr->fw_size_remaining -= ctrlr->fw_transfer_size;
ctrlr->fw_transfer_size = spdk_min(ctrlr->fw_size_remaining, ctrlr->min_page_size);
res = nvme_ctrlr_cmd_fw_image_download(ctrlr, ctrlr->fw_transfer_size, ctrlr->fw_offset,
ctrlr->fw_payload, nvme_write_boot_partition_cb, ctrlr);
if (res) {
NVME_CTRLR_ERRLOG(ctrlr, "nvme_ctrlr_cmd_fw_image_download failed!\n");
ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl);
return;
}
if (ctrlr->fw_transfer_size < ctrlr->min_page_size) {
ctrlr->bp_ws = SPDK_NVME_BP_WS_DOWNLOADED;
}
} else if (ctrlr->bp_ws == SPDK_NVME_BP_WS_DOWNLOADED) {
NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Download Success\n");
memset(&fw_commit, 0, sizeof(struct spdk_nvme_fw_commit));
fw_commit.bpid = ctrlr->bpid;
fw_commit.ca = SPDK_NVME_FW_COMMIT_REPLACE_BOOT_PARTITION;
res = nvme_ctrlr_cmd_fw_commit(ctrlr, &fw_commit,
nvme_write_boot_partition_cb, ctrlr);
if (res) {
NVME_CTRLR_ERRLOG(ctrlr, "nvme_ctrlr_cmd_fw_commit failed!\n");
NVME_CTRLR_ERRLOG(ctrlr, "commit action: %d\n", fw_commit.ca);
ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl);
return;
}
ctrlr->bp_ws = SPDK_NVME_BP_WS_REPLACE;
} else if (ctrlr->bp_ws == SPDK_NVME_BP_WS_REPLACE) {
NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Replacement Success\n");
memset(&fw_commit, 0, sizeof(struct spdk_nvme_fw_commit));
fw_commit.bpid = ctrlr->bpid;
fw_commit.ca = SPDK_NVME_FW_COMMIT_ACTIVATE_BOOT_PARTITION;
res = nvme_ctrlr_cmd_fw_commit(ctrlr, &fw_commit,
nvme_write_boot_partition_cb, ctrlr);
if (res) {
NVME_CTRLR_ERRLOG(ctrlr, "nvme_ctrlr_cmd_fw_commit failed!\n");
NVME_CTRLR_ERRLOG(ctrlr, "commit action: %d\n", fw_commit.ca);
ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl);
return;
}
ctrlr->bp_ws = SPDK_NVME_BP_WS_ACTIVATE;
} else if (ctrlr->bp_ws == SPDK_NVME_BP_WS_ACTIVATE) {
NVME_CTRLR_DEBUGLOG(ctrlr, "Boot Partition Activation Success\n");
ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, cpl);
} else {
NVME_CTRLR_ERRLOG(ctrlr, "Invalid Boot Partition write state\n");
ctrlr->bp_write_cb_fn(ctrlr->bp_write_cb_arg, &err_cpl);
return;
}
}
int spdk_nvme_ctrlr_write_boot_partition(struct spdk_nvme_ctrlr *ctrlr,
void *payload, uint32_t size, uint32_t bpid,
spdk_nvme_cmd_cb cb_fn, void *cb_arg)
{
int res;
if (ctrlr->cap.bits.bps == 0) {
return -ENOTSUP;
}
ctrlr->bp_ws = SPDK_NVME_BP_WS_DOWNLOADING;
ctrlr->bpid = bpid;
ctrlr->bp_write_cb_fn = cb_fn;
ctrlr->bp_write_cb_arg = cb_arg;
ctrlr->fw_offset = 0;
ctrlr->fw_size_remaining = size;
ctrlr->fw_payload = payload;
ctrlr->fw_transfer_size = spdk_min(ctrlr->fw_size_remaining, ctrlr->min_page_size);
res = nvme_ctrlr_cmd_fw_image_download(ctrlr, ctrlr->fw_transfer_size, ctrlr->fw_offset,
ctrlr->fw_payload, nvme_write_boot_partition_cb, ctrlr);
return res;
}
bool bool
spdk_nvme_ctrlr_is_discovery(struct spdk_nvme_ctrlr *ctrlr) spdk_nvme_ctrlr_is_discovery(struct spdk_nvme_ctrlr *ctrlr)
{ {

View File

@ -216,6 +216,14 @@ enum nvme_payload_type {
NVME_PAYLOAD_TYPE_SGL, NVME_PAYLOAD_TYPE_SGL,
}; };
/** Boot partition write states */
enum nvme_bp_write_state {
SPDK_NVME_BP_WS_DOWNLOADING = 0x0,
SPDK_NVME_BP_WS_DOWNLOADED = 0x1,
SPDK_NVME_BP_WS_REPLACE = 0x2,
SPDK_NVME_BP_WS_ACTIVATE = 0x3,
};
/** /**
* Descriptor for a request data payload. * Descriptor for a request data payload.
*/ */
@ -918,6 +926,18 @@ struct spdk_nvme_ctrlr {
/* PMR size in bytes */ /* PMR size in bytes */
uint64_t pmr_size; uint64_t pmr_size;
/* Boot Partition Info */
enum nvme_bp_write_state bp_ws;
uint32_t bpid;
spdk_nvme_cmd_cb bp_write_cb_fn;
void *bp_write_cb_arg;
/* Firmware Download */
void *fw_payload;
unsigned int fw_size_remaining;
unsigned int fw_offset;
unsigned int fw_transfer_size;
}; };
struct spdk_nvme_probe_ctx { struct spdk_nvme_probe_ctx {
@ -1085,6 +1105,9 @@ int nvme_ctrlr_get_cap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_cap_regist
int nvme_ctrlr_get_vs(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_vs_register *vs); int nvme_ctrlr_get_vs(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_vs_register *vs);
int nvme_ctrlr_get_cmbsz(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_cmbsz_register *cmbsz); int nvme_ctrlr_get_cmbsz(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_cmbsz_register *cmbsz);
int nvme_ctrlr_get_pmrcap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_pmrcap_register *pmrcap); int nvme_ctrlr_get_pmrcap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_pmrcap_register *pmrcap);
int nvme_ctrlr_get_bpinfo(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bpinfo_register *bpinfo);
int nvme_ctrlr_set_bprsel(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_bprsel_register *bprsel);
int nvme_ctrlr_set_bpmbl(struct spdk_nvme_ctrlr *ctrlr, uint64_t bpmbl_value);
bool nvme_ctrlr_multi_iocs_enabled(struct spdk_nvme_ctrlr *ctrlr); bool nvme_ctrlr_multi_iocs_enabled(struct spdk_nvme_ctrlr *ctrlr);
void nvme_ctrlr_process_async_event(struct spdk_nvme_ctrlr *ctrlr, void nvme_ctrlr_process_async_event(struct spdk_nvme_ctrlr *ctrlr,
const struct spdk_nvme_cpl *cpl); const struct spdk_nvme_cpl *cpl);

View File

@ -48,6 +48,7 @@
spdk_nvme_ctrlr_get_regs_vs; spdk_nvme_ctrlr_get_regs_vs;
spdk_nvme_ctrlr_get_regs_cmbsz; spdk_nvme_ctrlr_get_regs_cmbsz;
spdk_nvme_ctrlr_get_regs_pmrcap; spdk_nvme_ctrlr_get_regs_pmrcap;
spdk_nvme_ctrlr_get_regs_bpinfo;
spdk_nvme_ctrlr_get_pmrsz; spdk_nvme_ctrlr_get_pmrsz;
spdk_nvme_ctrlr_get_num_ns; spdk_nvme_ctrlr_get_num_ns;
spdk_nvme_ctrlr_get_pci_device; spdk_nvme_ctrlr_get_pci_device;
@ -100,6 +101,9 @@
spdk_nvme_ctrlr_disable_pmr; spdk_nvme_ctrlr_disable_pmr;
spdk_nvme_ctrlr_map_pmr; spdk_nvme_ctrlr_map_pmr;
spdk_nvme_ctrlr_unmap_pmr; spdk_nvme_ctrlr_unmap_pmr;
spdk_nvme_ctrlr_read_boot_partition_start;
spdk_nvme_ctrlr_read_boot_partition_poll;
spdk_nvme_ctrlr_write_boot_partition;
spdk_nvme_ctrlr_get_transport_id; spdk_nvme_ctrlr_get_transport_id;
spdk_nvme_ctrlr_alloc_qid; spdk_nvme_ctrlr_alloc_qid;
spdk_nvme_ctrlr_free_qid; spdk_nvme_ctrlr_free_qid;