From 93ff343528ce034ef35e71d3b1d506df3cf85282 Mon Sep 17 00:00:00 2001 From: Pavan Chebbi Date: Sun, 13 Dec 2020 06:51:42 -0500 Subject: bnxt_en: Refactor bnxt_flash_nvram. Refactor bnxt_flash_nvram() into __bnxt_flash_nvram() that takes an additional dir_item_len parameter. The new function will be used in subsequent patches with the dir_item_len parameter set to create the UPDATE directory during flashing. Signed-off-by: Pavan Chebbi Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 51 ++++++++++++++--------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 7b444fcb6289..11edf4998de7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2100,19 +2100,16 @@ static int bnxt_find_nvram_item(struct net_device *dev, u16 type, u16 ordinal, u16 ext, u16 *index, u32 *item_length, u32 *data_length); -static int bnxt_flash_nvram(struct net_device *dev, - u16 dir_type, - u16 dir_ordinal, - u16 dir_ext, - u16 dir_attr, - const u8 *data, - size_t data_len) +static int __bnxt_flash_nvram(struct net_device *dev, u16 dir_type, + u16 dir_ordinal, u16 dir_ext, u16 dir_attr, + u32 dir_item_len, const u8 *data, + size_t data_len) { struct bnxt *bp = netdev_priv(dev); int rc; struct hwrm_nvm_write_input req = {0}; dma_addr_t dma_handle; - u8 *kmem; + u8 *kmem = NULL; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_WRITE, -1, -1); @@ -2120,26 +2117,42 @@ static int bnxt_flash_nvram(struct net_device *dev, req.dir_ordinal = cpu_to_le16(dir_ordinal); req.dir_ext = cpu_to_le16(dir_ext); req.dir_attr = cpu_to_le16(dir_attr); - req.dir_data_length = cpu_to_le32(data_len); + req.dir_item_length = cpu_to_le32(dir_item_len); + if (data_len && data) { + req.dir_data_length = cpu_to_le32(data_len); - kmem = dma_alloc_coherent(&bp->pdev->dev, data_len, &dma_handle, - GFP_KERNEL); - if (!kmem) { - netdev_err(dev, "dma_alloc_coherent failure, length = %u\n", - (unsigned)data_len); - return -ENOMEM; + kmem = dma_alloc_coherent(&bp->pdev->dev, data_len, &dma_handle, + GFP_KERNEL); + if (!kmem) + return -ENOMEM; + + memcpy(kmem, data, data_len); + req.host_src_addr = cpu_to_le64(dma_handle); } - memcpy(kmem, data, data_len); - req.host_src_addr = cpu_to_le64(dma_handle); - rc = hwrm_send_message(bp, &req, sizeof(req), FLASH_NVRAM_TIMEOUT); - dma_free_coherent(&bp->pdev->dev, data_len, kmem, dma_handle); + rc = _hwrm_send_message(bp, &req, sizeof(req), FLASH_NVRAM_TIMEOUT); + if (kmem) + dma_free_coherent(&bp->pdev->dev, data_len, kmem, dma_handle); if (rc == -EACCES) bnxt_print_admin_err(bp); return rc; } +static int bnxt_flash_nvram(struct net_device *dev, u16 dir_type, + u16 dir_ordinal, u16 dir_ext, u16 dir_attr, + const u8 *data, size_t data_len) +{ + struct bnxt *bp = netdev_priv(dev); + int rc; + + mutex_lock(&bp->hwrm_cmd_lock); + rc = __bnxt_flash_nvram(dev, dir_type, dir_ordinal, dir_ext, dir_attr, + 0, data, data_len); + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + static int bnxt_hwrm_firmware_reset(struct net_device *dev, u8 proc_type, u8 self_reset, u8 flags) { -- cgit v1.2.3 From a9094ba6072bfabe93f93b641a3858d9c91c2c86 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 13 Dec 2020 06:51:43 -0500 Subject: bnxt_en: Rearrange the logic in bnxt_flash_package_from_fw_obj(). This function will be modified in the next patch to retry flashing the firmware in a loop. To facilate that, we rearrange the code so that the steps that only need to be done once before the loop will be moved to the top of the function. Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 63 +++++++++++------------ 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 11edf4998de7..7635ff84b928 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2435,15 +2435,32 @@ static int bnxt_flash_firmware_from_file(struct net_device *dev, int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware *fw, u32 install_type) { - struct bnxt *bp = netdev_priv(dev); - struct hwrm_nvm_install_update_output *resp = bp->hwrm_cmd_resp_addr; struct hwrm_nvm_install_update_input install = {0}; + struct hwrm_nvm_install_update_output resp = {0}; + struct hwrm_nvm_modify_input modify = {0}; + struct bnxt *bp = netdev_priv(dev); + dma_addr_t dma_handle; + u8 *kmem = NULL; u32 item_len; int rc = 0; u16 index; bnxt_hwrm_fw_set_time(bp); + bnxt_hwrm_cmd_hdr_init(bp, &modify, HWRM_NVM_MODIFY, -1, -1); + + kmem = dma_alloc_coherent(&bp->pdev->dev, fw->size, &dma_handle, + GFP_KERNEL); + if (!kmem) + return -ENOMEM; + + modify.host_src_addr = cpu_to_le64(dma_handle); + + bnxt_hwrm_cmd_hdr_init(bp, &install, HWRM_NVM_INSTALL_UPDATE, -1, -1); + if ((install_type & 0xffff) == 0) + install_type >>= 16; + install.install_type = cpu_to_le32(install_type); + rc = bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE, BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE, &index, &item_len, NULL); @@ -2457,65 +2474,45 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware (unsigned long)fw->size); rc = -EFBIG; } else { - dma_addr_t dma_handle; - u8 *kmem; - struct hwrm_nvm_modify_input modify = {0}; - - bnxt_hwrm_cmd_hdr_init(bp, &modify, HWRM_NVM_MODIFY, -1, -1); - modify.dir_idx = cpu_to_le16(index); modify.len = cpu_to_le32(fw->size); - kmem = dma_alloc_coherent(&bp->pdev->dev, fw->size, - &dma_handle, GFP_KERNEL); - if (!kmem) { - netdev_err(dev, - "dma_alloc_coherent failure, length = %u\n", - (unsigned int)fw->size); - rc = -ENOMEM; - } else { - memcpy(kmem, fw->data, fw->size); - modify.host_src_addr = cpu_to_le64(dma_handle); - - rc = hwrm_send_message(bp, &modify, sizeof(modify), - FLASH_PACKAGE_TIMEOUT); - dma_free_coherent(&bp->pdev->dev, fw->size, kmem, - dma_handle); - } + memcpy(kmem, fw->data, fw->size); + rc = hwrm_send_message(bp, &modify, sizeof(modify), + FLASH_PACKAGE_TIMEOUT); } if (rc) goto err_exit; - if ((install_type & 0xffff) == 0) - install_type >>= 16; - bnxt_hwrm_cmd_hdr_init(bp, &install, HWRM_NVM_INSTALL_UPDATE, -1, -1); - install.install_type = cpu_to_le32(install_type); - mutex_lock(&bp->hwrm_cmd_lock); rc = _hwrm_send_message(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); + memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp)); + if (rc) { - u8 error_code = ((struct hwrm_err_output *)resp)->cmd_err; + u8 error_code = ((struct hwrm_err_output *)&resp)->cmd_err; - if (resp->error_code && error_code == + if (resp.error_code && error_code == NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { install.flags |= cpu_to_le16( NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG); rc = _hwrm_send_message(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); + memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp)); } if (rc) goto flash_pkg_exit; } - if (resp->result) { + if (resp.result) { netdev_err(dev, "PKG install error = %d, problem_item = %d\n", - (s8)resp->result, (int)resp->problem_item); + (s8)resp.result, (int)resp.problem_item); rc = -ENOPKG; } flash_pkg_exit: mutex_unlock(&bp->hwrm_cmd_lock); err_exit: + dma_free_coherent(&bp->pdev->dev, fw->size, kmem, dma_handle); if (rc == -EACCES) bnxt_print_admin_err(bp); return rc; -- cgit v1.2.3 From 2e5fb428a61ce58f9db6ceccdeb6dc292248f1dc Mon Sep 17 00:00:00 2001 From: Pavan Chebbi Date: Sun, 13 Dec 2020 06:51:44 -0500 Subject: bnxt_en: Restructure bnxt_flash_package_from_fw_obj() to execute in a loop. On NICs with a smaller NVRAM, FW installation may fail after multiple updates due to fragmentation. The driver can retry when FW returns a special error code. To faciliate the retry, we restructure the logic that performs the flashing in a loop. The actual retry logic will be added in the next patch. Signed-off-by: Pavan Chebbi Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 60 +++++++++++------------ 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 7635ff84b928..fa4f9941498e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2461,58 +2461,54 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware install_type >>= 16; install.install_type = cpu_to_le32(install_type); - rc = bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE, - BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE, - &index, &item_len, NULL); - if (rc) { - netdev_err(dev, "PKG update area not created in nvram\n"); - return rc; - } + do { + rc = bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE, + BNX_DIR_ORDINAL_FIRST, + BNX_DIR_EXT_NONE, + &index, &item_len, NULL); + if (rc) { + netdev_err(dev, "PKG update area not created in nvram\n"); + break; + } + if (fw->size > item_len) { + netdev_err(dev, "PKG insufficient update area in nvram: %lu\n", + (unsigned long)fw->size); + rc = -EFBIG; + break; + } - if (fw->size > item_len) { - netdev_err(dev, "PKG insufficient update area in nvram: %lu\n", - (unsigned long)fw->size); - rc = -EFBIG; - } else { modify.dir_idx = cpu_to_le16(index); modify.len = cpu_to_le32(fw->size); memcpy(kmem, fw->data, fw->size); rc = hwrm_send_message(bp, &modify, sizeof(modify), FLASH_PACKAGE_TIMEOUT); - } - if (rc) - goto err_exit; - - mutex_lock(&bp->hwrm_cmd_lock); - rc = _hwrm_send_message(bp, &install, sizeof(install), - INSTALL_PACKAGE_TIMEOUT); - memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp)); + if (rc) + break; - if (rc) { - u8 error_code = ((struct hwrm_err_output *)&resp)->cmd_err; + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &install, sizeof(install), + INSTALL_PACKAGE_TIMEOUT); + memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp)); - if (resp.error_code && error_code == + if (rc && ((struct hwrm_err_output *)&resp)->cmd_err == NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { - install.flags |= cpu_to_le16( - NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG); + install.flags |= + cpu_to_le16(NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG); + rc = _hwrm_send_message(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp)); } - if (rc) - goto flash_pkg_exit; - } + mutex_unlock(&bp->hwrm_cmd_lock); + } while (false); + dma_free_coherent(&bp->pdev->dev, fw->size, kmem, dma_handle); if (resp.result) { netdev_err(dev, "PKG install error = %d, problem_item = %d\n", (s8)resp.result, (int)resp.problem_item); rc = -ENOPKG; } -flash_pkg_exit: - mutex_unlock(&bp->hwrm_cmd_lock); -err_exit: - dma_free_coherent(&bp->pdev->dev, fw->size, kmem, dma_handle); if (rc == -EACCES) bnxt_print_admin_err(bp); return rc; -- cgit v1.2.3 From 1432c3f6a6ca091db10b60c7b9078f34f4c5268d Mon Sep 17 00:00:00 2001 From: Pavan Chebbi Date: Sun, 13 Dec 2020 06:51:45 -0500 Subject: bnxt_en: Retry installing FW package under NO_SPACE error condition. In bnxt_flash_package_from_fw_obj(), if firmware returns the NO_SPACE error, call __bnxt_flash_nvram() to create the UPDATE directory and then loop back and retry one more time. Since the first try may fail, we use the silent version to send the firmware commands. Reviewed-by: Vasundhara Volam Reviewed-by: Edwin Peer Signed-off-by: Pavan Chebbi Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 37 ++++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index fa4f9941498e..38ab882715c4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2439,6 +2439,7 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware struct hwrm_nvm_install_update_output resp = {0}; struct hwrm_nvm_modify_input modify = {0}; struct bnxt *bp = netdev_priv(dev); + bool defrag_attempted = false; dma_addr_t dma_handle; u8 *kmem = NULL; u32 item_len; @@ -2487,21 +2488,47 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware break; mutex_lock(&bp->hwrm_cmd_lock); - rc = _hwrm_send_message(bp, &install, sizeof(install), - INSTALL_PACKAGE_TIMEOUT); + rc = _hwrm_send_message_silent(bp, &install, sizeof(install), + INSTALL_PACKAGE_TIMEOUT); memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp)); + if (defrag_attempted) { + /* We have tried to defragment already in the previous + * iteration. Return with the result for INSTALL_UPDATE + */ + mutex_unlock(&bp->hwrm_cmd_lock); + break; + } + if (rc && ((struct hwrm_err_output *)&resp)->cmd_err == NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { install.flags |= cpu_to_le16(NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG); - rc = _hwrm_send_message(bp, &install, sizeof(install), - INSTALL_PACKAGE_TIMEOUT); + rc = _hwrm_send_message_silent(bp, &install, + sizeof(install), + INSTALL_PACKAGE_TIMEOUT); memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp)); + + if (rc && ((struct hwrm_err_output *)&resp)->cmd_err == + NVM_INSTALL_UPDATE_CMD_ERR_CODE_NO_SPACE) { + /* FW has cleared NVM area, driver will create + * UPDATE directory and try the flash again + */ + defrag_attempted = true; + rc = __bnxt_flash_nvram(bp->dev, + BNX_DIR_TYPE_UPDATE, + BNX_DIR_ORDINAL_FIRST, + 0, 0, item_len, NULL, + 0); + } else if (rc) { + netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure rc :%x\n", rc); + } + } else if (rc) { + netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure rc :%x\n", rc); } mutex_unlock(&bp->hwrm_cmd_lock); - } while (false); + } while (defrag_attempted && !rc); dma_free_coherent(&bp->pdev->dev, fw->size, kmem, dma_handle); if (resp.result) { -- cgit v1.2.3 From a86b313e18178b39fcca4850d4dfeb5af1e3dc7e Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 13 Dec 2020 06:51:46 -0500 Subject: bnxt_en: Enable batch mode when using HWRM_NVM_MODIFY to flash packages. The current scheme allocates a DMA buffer as big as the requested firmware package file and DMAs the contents to firmware in one operation. The buffer size can be several hundred kilo bytes and the driver may not be able to allocate the memory. This will cause firmware upgrade to fail. Improve the scheme by using smaller DMA blocks and calling firmware to DMA each block in a batch mode. Older firmware can cause excessive NVRAM erases if the block size is too small so we try to allocate a 256K buffer to begin with and size it down successively if we cannot allocate the memory. Reviewed-by: Edwin Peer Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 49 ++++++++++++++++++----- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 38ab882715c4..9ff79d5d14c4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2432,6 +2432,10 @@ static int bnxt_flash_firmware_from_file(struct net_device *dev, return rc; } +#define BNXT_PKG_DMA_SIZE 0x40000 +#define BNXT_NVM_MORE_FLAG (cpu_to_le16(NVM_MODIFY_REQ_FLAGS_BATCH_MODE)) +#define BNXT_NVM_LAST_FLAG (cpu_to_le16(NVM_MODIFY_REQ_FLAGS_BATCH_LAST)) + int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware *fw, u32 install_type) { @@ -2442,6 +2446,7 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware bool defrag_attempted = false; dma_addr_t dma_handle; u8 *kmem = NULL; + u32 modify_len; u32 item_len; int rc = 0; u16 index; @@ -2450,8 +2455,19 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware bnxt_hwrm_cmd_hdr_init(bp, &modify, HWRM_NVM_MODIFY, -1, -1); - kmem = dma_alloc_coherent(&bp->pdev->dev, fw->size, &dma_handle, - GFP_KERNEL); + /* Try allocating a large DMA buffer first. Older fw will + * cause excessive NVRAM erases when using small blocks. + */ + modify_len = roundup_pow_of_two(fw->size); + modify_len = min_t(u32, modify_len, BNXT_PKG_DMA_SIZE); + while (1) { + kmem = dma_alloc_coherent(&bp->pdev->dev, modify_len, + &dma_handle, GFP_KERNEL); + if (!kmem && modify_len > PAGE_SIZE) + modify_len /= 2; + else + break; + } if (!kmem) return -ENOMEM; @@ -2463,6 +2479,8 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware install.install_type = cpu_to_le32(install_type); do { + u32 copied = 0, len = modify_len; + rc = bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE, BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE, @@ -2479,14 +2497,26 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware } modify.dir_idx = cpu_to_le16(index); - modify.len = cpu_to_le32(fw->size); - memcpy(kmem, fw->data, fw->size); - rc = hwrm_send_message(bp, &modify, sizeof(modify), - FLASH_PACKAGE_TIMEOUT); - if (rc) - break; + if (fw->size > modify_len) + modify.flags = BNXT_NVM_MORE_FLAG; + while (copied < fw->size) { + u32 balance = fw->size - copied; + if (balance <= modify_len) { + len = balance; + if (copied) + modify.flags |= BNXT_NVM_LAST_FLAG; + } + memcpy(kmem, fw->data + copied, len); + modify.len = cpu_to_le32(len); + modify.offset = cpu_to_le32(copied); + rc = hwrm_send_message(bp, &modify, sizeof(modify), + FLASH_PACKAGE_TIMEOUT); + if (rc) + goto pkg_abort; + copied += len; + } mutex_lock(&bp->hwrm_cmd_lock); rc = _hwrm_send_message_silent(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); @@ -2530,7 +2560,8 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware mutex_unlock(&bp->hwrm_cmd_lock); } while (defrag_attempted && !rc); - dma_free_coherent(&bp->pdev->dev, fw->size, kmem, dma_handle); +pkg_abort: + dma_free_coherent(&bp->pdev->dev, modify_len, kmem, dma_handle); if (resp.result) { netdev_err(dev, "PKG install error = %d, problem_item = %d\n", (s8)resp.result, (int)resp.problem_item); -- cgit v1.2.3