On Mon, 29 Apr 2019 12:16:28 -0700, Jeff Kirsher wrote: > From: Alice Michael <alice.mich...@intel.com> > > This patch introduces "recovery mode" to the i40e driver. It is > part of a new Any2Any idea of upgrading the firmware. In this > approach, it is required for the driver to have support for > "transition firmware", that is used for migrating from structured > to flat firmware image. In this new, very basic mode, i40e driver > must be able to handle particular IOCTL calls from the NVM Update > Tool and run a small set of AQ commands.
Could you show us commands that get executed? I think that'd be much quicker for people to parse. > Signed-off-by: Alice Michael <alice.mich...@intel.com> > Signed-off-by: Piotr Marczak <piotr.marc...@intel.com> > Tested-by: Don Buchholz <donald.buchh...@intel.com> > Signed-off-by: Jeff Kirsher <jeffrey.t.kirs...@intel.com> From a cursory look it seems you create a "basic" netdev. Can this netdev pass traffic? I'd suggest you implement devlink "limp mode". Devlink can flash the device now. You can register a devlink instance without registering any "minimal" netdevs, and flash with devlink. > @@ -13904,6 +14007,134 @@ void i40e_set_fec_in_flags(u8 fec_cfg, u32 *flags) > *flags &= ~(I40E_FLAG_RS_FEC | I40E_FLAG_BASE_R_FEC); > } > > +/** > + * i40e_check_recovery_mode - check if we are running transition firmware > + * @pf: board private structure > + * > + * Check registers indicating the firmware runs in recovery mode. Sets the > + * appropriate driver state. > + * > + * Returns true if the recovery mode was detected, false otherwise > + **/ > +static bool i40e_check_recovery_mode(struct i40e_pf *pf) > +{ > + u32 val = rd32(&pf->hw, I40E_GL_FWSTS); > + > + if (val & I40E_GL_FWSTS_FWS1B_MASK) { > + dev_notice(&pf->pdev->dev, "Firmware recovery mode detected. > Limiting functionality.\n"); > + dev_notice(&pf->pdev->dev, "Refer to the Intel(R) Ethernet > Adapters and Devices User Guide for details on firmware recovery mode.\n"); > + set_bit(__I40E_RECOVERY_MODE, pf->state); > + > + return true; > + } > + if (test_and_clear_bit(__I40E_RECOVERY_MODE, pf->state)) > + dev_info(&pf->pdev->dev, "Reinitializing in normal mode with > full functionality.\n"); > + > + return false; > +} > + > +/** > + * i40e_init_recovery_mode - initialize subsystems needed in recovery mode > + * @pf: board private structure > + * @hw: ptr to the hardware info > + * > + * This function does a minimal setup of all subsystems needed for running > + * recovery mode. > + * > + * Returns 0 on success, negative on failure > + **/ > +static int i40e_init_recovery_mode(struct i40e_pf *pf, struct i40e_hw *hw) > +{ > + struct i40e_vsi *vsi; > + int err; > + int v_idx; > + > +#ifdef HAVE_PCI_ERS > + pci_save_state(pf->pdev); > +#endif > + > + /* set up periodic task facility */ > + timer_setup(&pf->service_timer, i40e_service_timer, 0); > + pf->service_timer_period = HZ; > + > + INIT_WORK(&pf->service_task, i40e_service_task); > + clear_bit(__I40E_SERVICE_SCHED, pf->state); > + > + err = i40e_init_interrupt_scheme(pf); > + if (err) > + goto err_switch_setup; > + > + /* The number of VSIs reported by the FW is the minimum guaranteed > + * to us; HW supports far more and we share the remaining pool with > + * the other PFs. We allocate space for more than the guarantee with > + * the understanding that we might not get them all later. > + */ > + if (pf->hw.func_caps.num_vsis < I40E_MIN_VSI_ALLOC) > + pf->num_alloc_vsi = I40E_MIN_VSI_ALLOC; > + else > + pf->num_alloc_vsi = pf->hw.func_caps.num_vsis; > + > + /* Set up the vsi struct and our local tracking of the MAIN PF vsi. */ > + pf->vsi = kcalloc(pf->num_alloc_vsi, sizeof(struct i40e_vsi *), > + GFP_KERNEL); > + if (!pf->vsi) { > + err = -ENOMEM; > + goto err_switch_setup; > + } > + > + /* We allocate one VSI which is needed as absolute minimum > + * in order to register the netdev > + */ > + v_idx = i40e_vsi_mem_alloc(pf, I40E_VSI_MAIN); > + if (v_idx < 0) > + goto err_switch_setup; > + pf->lan_vsi = v_idx; > + vsi = pf->vsi[v_idx]; > + if (!vsi) > + goto err_switch_setup; > + vsi->alloc_queue_pairs = 1; > + err = i40e_config_netdev(vsi); > + if (err) > + goto err_switch_setup; > + err = register_netdev(vsi->netdev); > + if (err) > + goto err_switch_setup; > + vsi->netdev_registered = true; > + i40e_dbg_pf_init(pf); > + > + err = i40e_setup_misc_vector_for_recovery_mode(pf); > + if (err) > + goto err_switch_setup; > + > + /* tell the firmware that we're starting */ > + i40e_send_version(pf); > + > + /* since everything's happy, start the service_task timer */ > + mod_timer(&pf->service_timer, > + round_jiffies(jiffies + pf->service_timer_period)); > + > + return 0; > + > +err_switch_setup: > + i40e_reset_interrupt_capability(pf); > + del_timer_sync(&pf->service_timer); > +#ifdef NOT_FOR_UPSTREAM Delightful :) > + dev_warn(&pf->pdev->dev, "previous errors forcing module to load in > debug mode\n"); > + i40e_dbg_pf_init(pf); > + set_bit(__I40E_DEBUG_MODE, pf->state); > + return 0; > +#else > + i40e_shutdown_adminq(hw); > + iounmap(hw->hw_addr); > + pci_disable_pcie_error_reporting(pf->pdev); > + pci_release_mem_regions(pf->pdev); > + pci_disable_device(pf->pdev); > + kfree(pf); > + > + return err; > +#endif > +} > + > /** > * i40e_probe - Device initialization routine > * @pdev: PCI device information struct