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
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
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);
/**
* 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.
*
@ -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,
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.
*

View File

@ -124,6 +124,27 @@ nvme_ctrlr_get_pmrcap(struct spdk_nvme_ctrlr *ctrlr, union spdk_nvme_pmrcap_regi
&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
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;
}
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
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;
}
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
spdk_nvme_ctrlr_is_discovery(struct spdk_nvme_ctrlr *ctrlr)
{

View File

@ -216,6 +216,14 @@ enum nvme_payload_type {
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.
*/
@ -918,6 +926,18 @@ struct spdk_nvme_ctrlr {
/* PMR size in bytes */
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 {
@ -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_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_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);
void nvme_ctrlr_process_async_event(struct spdk_nvme_ctrlr *ctrlr,
const struct spdk_nvme_cpl *cpl);

View File

@ -48,6 +48,7 @@
spdk_nvme_ctrlr_get_regs_vs;
spdk_nvme_ctrlr_get_regs_cmbsz;
spdk_nvme_ctrlr_get_regs_pmrcap;
spdk_nvme_ctrlr_get_regs_bpinfo;
spdk_nvme_ctrlr_get_pmrsz;
spdk_nvme_ctrlr_get_num_ns;
spdk_nvme_ctrlr_get_pci_device;
@ -100,6 +101,9 @@
spdk_nvme_ctrlr_disable_pmr;
spdk_nvme_ctrlr_map_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_alloc_qid;
spdk_nvme_ctrlr_free_qid;