Commit fec55c84 authored by Krishna Kanth Reddy's avatar Krishna Kanth Reddy Committed by Tomasz Zawadzki
Browse files

lib/nvme: NVMe Boot Partition Read / Write support



Signed-off-by: default avatarKrishna 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: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarPaul Luse <paul.e.luse@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
parent 6e5d6032
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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.
+81 −0
Original line number Diff line number Diff line
@@ -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.
 *
+220 −0
Original line number Diff line number Diff line
@@ -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)
{
+23 −0
Original line number Diff line number Diff line
@@ -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);
+4 −0
Original line number Diff line number Diff line
@@ -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;