Author: np Date: Tue Feb 26 20:35:54 2013 New Revision: 247347 URL: http://svnweb.freebsd.org/changeset/base/247347
Log: cxgbe(4): Consider all the API versions of the interfaces exported by the firmware (instead of just the main firmware version) when evaluating firmware compatibility. Document the new "hw.cxgbe.fw_install" knob being introduced here. This should fix kern/173584 too. Setting hw.cxgbe.fw_install=2 will mostly do what was requested in the PR but it's a bit more intelligent in that it won't reinstall the same firmware repeatedly if the knob is left set. PR: kern/173584 MFC after: 5 days Modified: head/share/man/man4/cxgbe.4 head/sys/dev/cxgbe/common/common.h head/sys/dev/cxgbe/t4_main.c Modified: head/share/man/man4/cxgbe.4 ============================================================================== --- head/share/man/man4/cxgbe.4 Tue Feb 26 20:35:40 2013 (r247346) +++ head/share/man/man4/cxgbe.4 Tue Feb 26 20:35:54 2013 (r247347) @@ -178,6 +178,15 @@ Bit 0 represents INTx (line interrupts), The default is 7 (all allowed). The driver will select the best possible type out of the allowed types by itself. +.It Va hw.cxgbe.fw_install +0 prohibits the driver from installing a firmware on the card. +1 allows the driver to install a new firmware if internal driver +heuristics indicate that the new firmware is preferable to the one +already on the card. +2 instructs the driver to always install the new firmware on the card as +long as it is compatible with the driver and is a different version than +the one already on the card. +The default is 1. .It Va hw.cxgbe.config_file Select a pre-packaged device configuration file. A configuration file contains a recipe for partitioning and configuring the Modified: head/sys/dev/cxgbe/common/common.h ============================================================================== --- head/sys/dev/cxgbe/common/common.h Tue Feb 26 20:35:40 2013 (r247346) +++ head/sys/dev/cxgbe/common/common.h Tue Feb 26 20:35:54 2013 (r247347) @@ -68,6 +68,11 @@ enum { #define FW_VERSION_MICRO 4 #define FW_VERSION_BUILD 0 +#define FW_VERSION (V_FW_HDR_FW_VER_MAJOR(FW_VERSION_MAJOR) | \ + V_FW_HDR_FW_VER_MINOR(FW_VERSION_MINOR) | \ + V_FW_HDR_FW_VER_MICRO(FW_VERSION_MICRO) | \ + V_FW_HDR_FW_VER_BUILD(FW_VERSION_BUILD)) + struct port_stats { u64 tx_octets; /* total # of octets in good frames */ u64 tx_frames; /* all good frames */ Modified: head/sys/dev/cxgbe/t4_main.c ============================================================================== --- head/sys/dev/cxgbe/t4_main.c Tue Feb 26 20:35:40 2013 (r247346) +++ head/sys/dev/cxgbe/t4_main.c Tue Feb 26 20:35:54 2013 (r247347) @@ -213,6 +213,13 @@ static char t4_cfg_file[32] = "default"; TUNABLE_STR("hw.cxgbe.config_file", t4_cfg_file, sizeof(t4_cfg_file)); /* + * Firmware auto-install by driver during attach (0, 1, 2 = prohibited, allowed, + * encouraged respectively). + */ +static unsigned int t4_fw_install = 1; +TUNABLE_INT("hw.cxgbe.fw_install", &t4_fw_install); + +/* * ASIC features that will be used. Disable the ones you don't want so that the * chip resources aren't wasted on features that will not be used. */ @@ -1503,6 +1510,33 @@ allocate: } /* + * Is the given firmware compatible with the one the driver was compiled with? + */ +static int +fw_compatible(const struct fw_hdr *hdr) +{ + + if (hdr->fw_ver == htonl(FW_VERSION)) + return (1); + + /* + * XXX: Is this too conservative? Perhaps I should limit this to the + * features that are supported in the driver. + */ + if (hdr->intfver_nic == FW_HDR_INTFVER_NIC && + hdr->intfver_vnic == FW_HDR_INTFVER_VNIC && + hdr->intfver_ofld == FW_HDR_INTFVER_OFLD && + hdr->intfver_ri == FW_HDR_INTFVER_RI && + hdr->intfver_iscsipdu == FW_HDR_INTFVER_ISCSIPDU && + hdr->intfver_iscsi == FW_HDR_INTFVER_ISCSI && + hdr->intfver_fcoepdu == FW_HDR_INTFVER_FCOEPDU && + hdr->intfver_fcoe == FW_HDR_INTFVER_FCOEPDU) + return (1); + + return (0); +} + +/* * Install a compatible firmware (if required), establish contact with it (by * saying hello), and reset the device. If we end up as the master driver, * partition adapter resources by providing a configuration file to the @@ -1512,84 +1546,99 @@ static int prep_firmware(struct adapter *sc) { const struct firmware *fw = NULL, *cfg = NULL, *default_cfg; - int rc; + int rc, card_fw_usable, kld_fw_usable; enum dev_state state; + struct fw_hdr *card_fw; + const struct fw_hdr *kld_fw; default_cfg = firmware_get(T4_CFGNAME); - /* Check firmware version and install a different one if necessary */ - rc = t4_check_fw_version(sc); - snprintf(sc->fw_version, sizeof(sc->fw_version), "%u.%u.%u.%u", - G_FW_HDR_FW_VER_MAJOR(sc->params.fw_vers), - G_FW_HDR_FW_VER_MINOR(sc->params.fw_vers), - G_FW_HDR_FW_VER_MICRO(sc->params.fw_vers), - G_FW_HDR_FW_VER_BUILD(sc->params.fw_vers)); - if (rc != 0) { - uint32_t v = 0; - - fw = firmware_get(T4_FWNAME); - if (fw != NULL) { - const struct fw_hdr *hdr = (const void *)fw->data; + /* Read the header of the firmware on the card */ + card_fw = malloc(sizeof(*card_fw), M_CXGBE, M_ZERO | M_WAITOK); + rc = -t4_read_flash(sc, FLASH_FW_START, + sizeof (*card_fw) / sizeof (uint32_t), (uint32_t *)card_fw, 1); + if (rc == 0) + card_fw_usable = fw_compatible((const void*)card_fw); + else { + device_printf(sc->dev, + "Unable to read card's firmware header: %d\n", rc); + card_fw_usable = 0; + } - v = ntohl(hdr->fw_ver); + /* This is the firmware in the KLD */ + fw = firmware_get(T4_FWNAME); + if (fw != NULL) { + kld_fw = (const void *)fw->data; + kld_fw_usable = fw_compatible(kld_fw); + } else { + kld_fw = NULL; + kld_fw_usable = 0; + } - /* - * The firmware module will not be used if it isn't the - * same major version as what the driver was compiled - * with. - */ - if (G_FW_HDR_FW_VER_MAJOR(v) != FW_VERSION_MAJOR) { - device_printf(sc->dev, - "Found firmware image but version %d " - "can not be used with this driver (%d)\n", - G_FW_HDR_FW_VER_MAJOR(v), FW_VERSION_MAJOR); + /* + * Short circuit for the common case: the firmware on the card is an + * exact match and the KLD is an exact match too, or it's + * absent/incompatible, or we're prohibited from using it. Note that + * t4_fw_install = 2 is ignored here -- use cxgbetool loadfw if you want + * to reinstall the same firmware as the one on the card. + */ + if (card_fw_usable && card_fw->fw_ver == htonl(FW_VERSION) && + (!kld_fw_usable || kld_fw->fw_ver == htonl(FW_VERSION) || + t4_fw_install == 0)) + goto hello; + + if (kld_fw_usable && (!card_fw_usable || + ntohl(kld_fw->fw_ver) > ntohl(card_fw->fw_ver) || + (t4_fw_install == 2 && kld_fw->fw_ver != card_fw->fw_ver))) { + uint32_t v = ntohl(kld_fw->fw_ver); - firmware_put(fw, FIRMWARE_UNLOAD); - fw = NULL; - } - } + device_printf(sc->dev, + "installing firmware %d.%d.%d.%d on card.\n", + G_FW_HDR_FW_VER_MAJOR(v), G_FW_HDR_FW_VER_MINOR(v), + G_FW_HDR_FW_VER_MICRO(v), G_FW_HDR_FW_VER_BUILD(v)); - if (fw == NULL && rc < 0) { - device_printf(sc->dev, "No usable firmware. " - "card has %d.%d.%d, driver compiled with %d.%d.%d", - G_FW_HDR_FW_VER_MAJOR(sc->params.fw_vers), - G_FW_HDR_FW_VER_MINOR(sc->params.fw_vers), - G_FW_HDR_FW_VER_MICRO(sc->params.fw_vers), - FW_VERSION_MAJOR, FW_VERSION_MINOR, - FW_VERSION_MICRO); - rc = EAGAIN; + rc = -t4_load_fw(sc, fw->data, fw->datasize); + if (rc != 0) { + device_printf(sc->dev, + "failed to install firmware: %d\n", rc); goto done; } - /* - * Always upgrade, even for minor/micro/build mismatches. - * Downgrade only for a major version mismatch or if - * force_firmware_install was specified. - */ - if (fw != NULL && (rc < 0 || v > sc->params.fw_vers)) { - device_printf(sc->dev, - "installing firmware %d.%d.%d.%d on card.\n", - G_FW_HDR_FW_VER_MAJOR(v), G_FW_HDR_FW_VER_MINOR(v), - G_FW_HDR_FW_VER_MICRO(v), G_FW_HDR_FW_VER_BUILD(v)); - - rc = -t4_load_fw(sc, fw->data, fw->datasize); - if (rc != 0) { - device_printf(sc->dev, - "failed to install firmware: %d\n", rc); - goto done; - } else { - /* refresh */ - (void) t4_check_fw_version(sc); - snprintf(sc->fw_version, - sizeof(sc->fw_version), "%u.%u.%u.%u", - G_FW_HDR_FW_VER_MAJOR(sc->params.fw_vers), - G_FW_HDR_FW_VER_MINOR(sc->params.fw_vers), - G_FW_HDR_FW_VER_MICRO(sc->params.fw_vers), - G_FW_HDR_FW_VER_BUILD(sc->params.fw_vers)); - } - } + /* Installed successfully, update the cached header too. */ + memcpy(card_fw, kld_fw, sizeof(*card_fw)); + card_fw_usable = 1; + } + + if (!card_fw_usable) { + uint32_t c, k; + + c = ntohl(card_fw->fw_ver); + k = kld_fw ? ntohl(kld_fw->fw_ver) : 0; + + device_printf(sc->dev, "Cannot find a usable firmware: " + "fw_install %d, driver compiled with %d.%d.%d.%d, " + "card has %d.%d.%d.%d, KLD has %d.%d.%d.%d\n", + t4_fw_install, + G_FW_HDR_FW_VER_MAJOR(FW_VERSION), + G_FW_HDR_FW_VER_MINOR(FW_VERSION), + G_FW_HDR_FW_VER_MICRO(FW_VERSION), + G_FW_HDR_FW_VER_BUILD(FW_VERSION), + G_FW_HDR_FW_VER_MAJOR(c), G_FW_HDR_FW_VER_MINOR(c), + G_FW_HDR_FW_VER_MICRO(c), G_FW_HDR_FW_VER_BUILD(c), + G_FW_HDR_FW_VER_MAJOR(k), G_FW_HDR_FW_VER_MINOR(k), + G_FW_HDR_FW_VER_MICRO(k), G_FW_HDR_FW_VER_BUILD(k)); + goto done; } +hello: + /* We're using whatever's on the card and it's known to be good. */ + sc->params.fw_vers = ntohl(card_fw->fw_ver); + snprintf(sc->fw_version, sizeof(sc->fw_version), "%u.%u.%u.%u", + G_FW_HDR_FW_VER_MAJOR(sc->params.fw_vers), + G_FW_HDR_FW_VER_MINOR(sc->params.fw_vers), + G_FW_HDR_FW_VER_MICRO(sc->params.fw_vers), + G_FW_HDR_FW_VER_BUILD(sc->params.fw_vers)); + /* Contact firmware. */ rc = t4_fw_hello(sc, sc->mbox, sc->mbox, MASTER_MAY, &state); if (rc < 0) { @@ -1639,6 +1688,7 @@ prep_firmware(struct adapter *sc) sc->flags |= FW_OK; done: + free(card_fw, M_CXGBE); if (fw != NULL) firmware_put(fw, FIRMWARE_UNLOAD); if (cfg != NULL) _______________________________________________ svn-src-head@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"