From mboxrd@z Thu Jan 1 00:00:00 1970 From: Felix Manlunas Subject: [PATCH V2 net-next] liquidio: fix kernel panic when NIC firmware is older than 1.7.2 Date: Fri, 6 Jul 2018 11:27:07 -0700 Message-ID: <20180706182707.GA1720@felix-thinkpad.cavium.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Cc: netdev@vger.kernel.org, raghu.vatsavayi@cavium.com, derek.chickles@cavium.com, satananda.burla@cavium.com, ricardo.farrington@cavium.com, felix.manlunas@cavium.com To: davem@davemloft.net Return-path: Received: from mail-dm3nam03on0067.outbound.protection.outlook.com ([104.47.41.67]:32648 "EHLO NAM03-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S934100AbeGFS1M (ORCPT ); Fri, 6 Jul 2018 14:27:12 -0400 Content-Disposition: inline Sender: netdev-owner@vger.kernel.org List-ID: From: Rick Farrington Pre-1.7.2 NIC firmware does not support (and does not respond to) the "get speed" command which is sent by the 1.7.2 driver (for CN23XX-225 cards only) during modprobe. Due to a bug in older firmware (with respect to unknown commands), this unsupported command causes a cascade of errors that ends in a kernel panic. Fix it by making the sending of the "get speed" command conditional on the firmware version. Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas --- Patch Change Log: V1 -> V2: * In the if-statement that checks the firmware version, replace the boolean expression that calls strcmp() (which is not suitable when the firmware micro version has more than one digit) with a boolean expression that works in all cases. * In the patch description, specify which types of liquidio cards are afffected. drivers/net/ethernet/cavium/liquidio/lio_main.c | 26 ++++++++++++++++++++-- .../net/ethernet/cavium/liquidio/octeon_device.h | 9 ++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 7cb4e75..ebda6ef 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3299,7 +3299,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) { struct lio *lio = NULL; struct net_device *netdev; - u8 mac[6], i, j, *fw_ver; + u8 mac[6], i, j, *fw_ver, *micro_ver; + unsigned long micro; + u32 cur_ver; struct octeon_soft_command *sc; struct liquidio_if_cfg_context *ctx; struct liquidio_if_cfg_resp *resp; @@ -3429,6 +3431,14 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) fw_ver); } + /* extract micro version field; point past '..' */ + micro_ver = fw_ver + strlen(LIQUIDIO_BASE_VERSION) + 1; + if (kstrtoul(micro_ver, 10, µ) != 0) + micro = 0; + octeon_dev->fw_info.ver.maj = LIQUIDIO_BASE_MAJOR_VERSION; + octeon_dev->fw_info.ver.min = LIQUIDIO_BASE_MINOR_VERSION; + octeon_dev->fw_info.ver.rev = micro; + octeon_swap_8B_data((u64 *)(&resp->cfg_info), (sizeof(struct liquidio_if_cfg_info)) >> 3); @@ -3671,7 +3681,19 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) OCTEON_CN2350_25GB_SUBSYS_ID || octeon_dev->subsystem_id == OCTEON_CN2360_25GB_SUBSYS_ID) { - liquidio_get_speed(lio); + cur_ver = OCT_FW_VER(octeon_dev->fw_info.ver.maj, + octeon_dev->fw_info.ver.min, + octeon_dev->fw_info.ver.rev); + + /* speed control unsupported in f/w older than 1.7.2 */ + if (cur_ver < OCT_FW_VER(1, 7, 2)) { + dev_info(&octeon_dev->pci_dev->dev, + "speed setting not supported by f/w."); + octeon_dev->speed_setting = 25; + octeon_dev->no_speed_setting = 1; + } else { + liquidio_get_speed(lio); + } if (octeon_dev->speed_setting == 0) { octeon_dev->speed_setting = 25; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 94a4ed88d..d99ca6b 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -288,8 +288,17 @@ struct oct_fw_info { */ u32 app_mode; char liquidio_firmware_version[32]; + /* Fields extracted from legacy string 'liquidio_firmware_version' */ + struct { + u8 maj; + u8 min; + u8 rev; + } ver; }; +#define OCT_FW_VER(maj, min, rev) \ + (((u32)(maj) << 16) | ((u32)(min) << 8) | ((u32)(rev))) + /* wrappers around work structs */ struct cavium_wk { struct delayed_work work;