From b262c464a675a5272f924e915bb588c44ed995b0 Mon Sep 17 00:00:00 2001 From: dab Date: Mon, 21 Sep 2020 15:45:49 +0000 Subject: [PATCH] Honor the FWUG value of some drives in nvmecontrol nvmecontrol tries to upload firmware in chunks as large as it thinks the device permits. It fails to take into account the FWUG value used by some drives to advertise the size and alignment limits for firmware chunks. - Use the firwmare update granularity value from the - If the granularity is not reported or not restricted, fall back to the previously existing logic that calculates the max transfer size based on MDTS. - Add firmware update granularity to the identify-controller output. Reviewed by: imp (previous version), chuck Obtained from: Dell EMC Isilon MFC after: 1 week Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D26390 --- sbin/nvmecontrol/firmware.c | 20 ++++++++++++++------ sbin/nvmecontrol/identify_ext.c | 9 +++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sbin/nvmecontrol/firmware.c b/sbin/nvmecontrol/firmware.c index bcdd13fbcbb..371ed5fc67f 100644 --- a/sbin/nvmecontrol/firmware.c +++ b/sbin/nvmecontrol/firmware.c @@ -155,21 +155,29 @@ read_image_file(const char *path, void **buf, int32_t *size) } static void -update_firmware(int fd, uint8_t *payload, int32_t payload_size) +update_firmware(int fd, uint8_t *payload, int32_t payload_size, uint8_t fwug) { struct nvme_pt_command pt; + uint64_t max_xfer_size; int32_t off, resid, size; void *chunk; off = 0; resid = payload_size; - if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL) - errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); + if (fwug != 0 && fwug != 0xFF) + max_xfer_size = ((uint64_t)fwug << 12); + else if (ioctl(fd, NVME_GET_MAX_XFER_SIZE, &max_xfer_size) < 0) + err(1, "query max transfer size failed"); + if (max_xfer_size > NVME_MAX_XFER_SIZE) + max_xfer_size = NVME_MAX_XFER_SIZE; + + if ((chunk = aligned_alloc(PAGE_SIZE, max_xfer_size)) == NULL) + errx(1, "unable to malloc %zd bytes", (size_t)max_xfer_size); while (resid > 0) { - size = (resid >= NVME_MAX_XFER_SIZE) ? - NVME_MAX_XFER_SIZE : resid; + size = (resid >= (int32_t)max_xfer_size) ? + max_xfer_size : resid; memcpy(chunk, payload + off, size); memset(&pt, 0, sizeof(pt)); @@ -333,7 +341,7 @@ firmware(const struct cmd *f, int argc, char *argv[]) } if (opt.fw_img != NULL) { - update_firmware(fd, buf, size); + update_firmware(fd, buf, size, cdata.fwug); if (opt.activate) activate_action = NVME_AA_REPLACE_ACTIVATE; else diff --git a/sbin/nvmecontrol/identify_ext.c b/sbin/nvmecontrol/identify_ext.c index 93afcf3595c..6e30ef0487a 100644 --- a/sbin/nvmecontrol/identify_ext.c +++ b/sbin/nvmecontrol/identify_ext.c @@ -56,6 +56,7 @@ nvme_print_controller(struct nvme_controller_data *cdata) uint8_t ns_smart; uint8_t sqes_max, sqes_min; uint8_t cqes_max, cqes_min; + uint8_t fwug; oncs = cdata->oncs; compare = (oncs >> NVME_CTRLR_DATA_ONCS_COMPARE_SHIFT) & @@ -79,6 +80,7 @@ nvme_print_controller(struct nvme_controller_data *cdata) NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; fw_slot1_ro = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) & NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK; + fwug = cdata->fwug; ns_smart = (cdata->lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & NVME_CTRLR_DATA_LPA_NS_SMART_MASK; @@ -192,6 +194,13 @@ nvme_print_controller(struct nvme_controller_data *cdata) uint128_to_str(to128(cdata->untncap.unvmcap), cbuf, sizeof(cbuf))); } + printf("Firmware Update Granularity: %02x ", fwug); + if (fwug == 0) + printf("(Not Reported)\n"); + else if (fwug == 0xFF) + printf("(No Granularity)\n"); + else + printf("(%d bytes)\n", ((uint32_t)fwug << 12)); printf("Host Buffer Preferred Size: %llu bytes\n", (long long unsigned)cdata->hmpre * 4096); printf("Host Buffer Minimum Size: %llu bytes\n", -- 2.45.0