summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornp <np@FreeBSD.org>2013-02-26 20:35:54 +0000
committernp <np@FreeBSD.org>2013-02-26 20:35:54 +0000
commit84af8e189deac5d2829f528c072b065d79a4414d (patch)
tree0e4f7417ccf0830c9776a2abeb39e601b599474a
parent5a60eaa26c6ef43c0f688295af51867a58dd3cb1 (diff)
downloadFreeBSD-src-84af8e189deac5d2829f528c072b065d79a4414d.zip
FreeBSD-src-84af8e189deac5d2829f528c072b065d79a4414d.tar.gz
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
-rw-r--r--share/man/man4/cxgbe.49
-rw-r--r--sys/dev/cxgbe/common/common.h5
-rw-r--r--sys/dev/cxgbe/t4_main.c178
3 files changed, 128 insertions, 64 deletions
diff --git a/share/man/man4/cxgbe.4 b/share/man/man4/cxgbe.4
index 882bcf7..25cc148 100644
--- a/share/man/man4/cxgbe.4
+++ b/share/man/man4/cxgbe.4
@@ -178,6 +178,15 @@ Bit 0 represents INTx (line interrupts), bit 1 MSI, bit 2 MSI-X.
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
diff --git a/sys/dev/cxgbe/common/common.h b/sys/dev/cxgbe/common/common.h
index abc9422..ccd1195 100644
--- a/sys/dev/cxgbe/common/common.h
+++ b/sys/dev/cxgbe/common/common.h
@@ -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 */
diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c
index da3accd..fcea547 100644
--- a/sys/dev/cxgbe/t4_main.c
+++ b/sys/dev/cxgbe/t4_main.c
@@ -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;
+ /* 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;
+ }
- fw = firmware_get(T4_FWNAME);
- if (fw != NULL) {
- const struct fw_hdr *hdr = (const void *)fw->data;
+ /* 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;
+ }
- v = ntohl(hdr->fw_ver);
+ /*
+ * 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;
- /*
- * 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);
+ 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)
OpenPOWER on IntegriCloud