OSDN Git Service

liquidio: fix kernel panic when NIC firmware is older than 1.7.2
authorRick Farrington <ricardo.farrington@cavium.com>
Fri, 6 Jul 2018 18:27:07 +0000 (11:27 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 7 Jul 2018 02:10:35 +0000 (11:10 +0900)
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 <ricardo.farrington@cavium.com>
Signed-off-by: Felix Manlunas <felix.manlunas@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/cavium/liquidio/lio_main.c
drivers/net/ethernet/cavium/liquidio/octeon_device.h

index 7cb4e75..ebda6ef 100644 (file)
@@ -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 '<maj>.<min>.' */
+               micro_ver = fw_ver + strlen(LIQUIDIO_BASE_VERSION) + 1;
+               if (kstrtoul(micro_ver, 10, &micro) != 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;
index 94a4ed8..d99ca6b 100644 (file)
@@ -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;