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
This commit is contained in:
David Bright 2020-09-21 15:45:49 +00:00
parent 624a7e1f4f
commit 16969d1448
2 changed files with 23 additions and 6 deletions

View File

@ -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

View File

@ -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",