diff --git a/sys/dev/ixl/if_ixl.c b/sys/dev/ixl/if_ixl.c --- a/sys/dev/ixl/if_ixl.c +++ b/sys/dev/ixl/if_ixl.c @@ -770,6 +770,12 @@ ixl_update_stats_counters(pf); ixl_add_hw_stats(pf); + /* + * Driver may have been reloaded. Ensure that the link state + * is consistent with current settings. + */ + ixl_set_link(pf, (pf->state & IXL_PF_STATE_LINK_ACTIVE_ON_DOWN) != 0); + hw->phy.get_link_info = true; i40e_get_link_status(hw, &pf->link_up); ixl_update_link_status(pf); @@ -961,6 +967,8 @@ return; } + ixl_set_link(pf, true); + /* Reconfigure multicast filters in HW */ ixl_if_multi_set(ctx); @@ -1003,6 +1011,7 @@ ixl_if_stop(if_ctx_t ctx) { struct ixl_pf *pf = iflib_get_softc(ctx); + struct ifnet *ifp = iflib_get_ifp(ctx); struct ixl_vsi *vsi = &pf->vsi; INIT_DEBUGOUT("ixl_if_stop: begin\n"); @@ -1019,6 +1028,15 @@ ixl_disable_rings_intr(vsi); ixl_disable_rings(pf, vsi, &pf->qtag); + + /* + * Don't set link state if only reconfiguring + * e.g. on MTU change. + */ + if ((if_getflags(ifp) & IFF_UP) == 0 && + (atomic_load_acq_32(&pf->state) & + IXL_PF_STATE_LINK_ACTIVE_ON_DOWN) == 0) + ixl_set_link(pf, false); } static int diff --git a/sys/dev/ixl/ixl_pf.h b/sys/dev/ixl/ixl_pf.h --- a/sys/dev/ixl/ixl_pf.h +++ b/sys/dev/ixl/ixl_pf.h @@ -88,6 +88,7 @@ IXL_PF_STATE_EMP_RESET_REQ = (1 << 8), IXL_PF_STATE_FW_LLDP_DISABLED = (1 << 9), IXL_PF_STATE_EEE_ENABLED = (1 << 10), + IXL_PF_STATE_LINK_ACTIVE_ON_DOWN = (1 << 11), }; #define IXL_PF_IN_RECOVERY_MODE(pf) \ @@ -229,6 +230,11 @@ "\t0 - disable\n" \ "\t1 - enable\n" +#define IXL_SYSCTL_HELP_SET_LINK_ACTIVE \ +"\nKeep link active after setting interface down:\n" \ +"\t0 - disable\n" \ +"\t1 - enable\n" + #define IXL_SYSCTL_HELP_READ_I2C \ "\nRead a byte from I2C bus\n" \ "Input: 32-bit value\n" \ @@ -351,6 +357,7 @@ void ixl_get_bus_info(struct ixl_pf *pf); int ixl_aq_get_link_status(struct ixl_pf *, struct i40e_aqc_get_link_status *); +void ixl_set_link(struct ixl_pf *, bool); int ixl_handle_nvmupd_cmd(struct ixl_pf *, struct ifdrv *); int ixl_handle_i2c_eeprom_read_cmd(struct ixl_pf *, struct ifreq *ifr); diff --git a/sys/dev/ixl/ixl_pf_iflib.c b/sys/dev/ixl/ixl_pf_iflib.c --- a/sys/dev/ixl/ixl_pf_iflib.c +++ b/sys/dev/ixl/ixl_pf_iflib.c @@ -413,6 +413,8 @@ /* Print out message if an unqualified module is found */ if ((status->link_info & I40E_AQ_MEDIA_AVAILABLE) && (pf->advertised_speed) && + (atomic_load_32(&pf->state) & + IXL_PF_STATE_LINK_ACTIVE_ON_DOWN) != 0 && (!(status->an_info & I40E_AQ_QUALIFIED_MODULE)) && (!(status->link_info & I40E_AQ_LINK_UP))) device_printf(dev, "Link failed because " diff --git a/sys/dev/ixl/ixl_pf_main.c b/sys/dev/ixl/ixl_pf_main.c --- a/sys/dev/ixl/ixl_pf_main.c +++ b/sys/dev/ixl/ixl_pf_main.c @@ -62,6 +62,7 @@ static int ixl_sysctl_pf_rx_itr(SYSCTL_HANDLER_ARGS); static int ixl_sysctl_eee_enable(SYSCTL_HANDLER_ARGS); +static int ixl_sysctl_set_link_active(SYSCTL_HANDLER_ARGS); /* Debug Sysctls */ static int ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS); @@ -385,6 +386,9 @@ break; } + /* Keep link active by default */ + atomic_set_32(&pf->state, IXL_PF_STATE_LINK_ACTIVE_ON_DOWN); + /* Print a subset of the capability information. */ device_printf(dev, "PF-ID[%d]: VFs %d, MSI-X %d, VF MSI-X %d, QPs %d, %s\n", @@ -2499,6 +2503,12 @@ CTLFLAG_RD | CTLFLAG_MPSAFE, &pf->stats.rx_lpi_count, "RX LPI count"); + SYSCTL_ADD_PROC(ctx, ctx_list, OID_AUTO, + "link_active_on_if_down", + CTLTYPE_INT | CTLFLAG_RWTUN, + pf, 0, ixl_sysctl_set_link_active, "I", + IXL_SYSCTL_HELP_SET_LINK_ACTIVE); + /* Add sysctls meant to print debug information, but don't list them * in "sysctl -a" output. */ debug_node = SYSCTL_ADD_NODE(ctx, ctx_list, @@ -2519,6 +2529,11 @@ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, pf, 0, ixl_sysctl_link_status, "A", IXL_SYSCTL_HELP_LINK_STATUS); + SYSCTL_ADD_PROC(ctx, debug_list, + OID_AUTO, "phy_abilities_init", + CTLTYPE_STRING | CTLFLAG_RD, + pf, 1, ixl_sysctl_phy_abilities, "A", "Initial PHY Abilities"); + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "phy_abilities", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, @@ -3107,6 +3122,95 @@ return (-1); } +void +ixl_set_link(struct ixl_pf *pf, bool enable) +{ + struct i40e_hw *hw = &pf->hw; + device_t dev = pf->dev; + struct i40e_aq_get_phy_abilities_resp abilities; + struct i40e_aq_set_phy_config config; + enum i40e_status_code aq_error = 0; + u32 phy_type, phy_type_ext; + + /* Get initial capability information */ + aq_error = i40e_aq_get_phy_capabilities(hw, + FALSE, TRUE, &abilities, NULL); + if (aq_error) { + device_printf(dev, + "%s: Error getting phy capabilities %d," + " aq error: %d\n", __func__, aq_error, + hw->aq.asq_last_status); + return; + } + + phy_type = abilities.phy_type; + phy_type_ext = abilities.phy_type_ext; + + /* Get current capability information */ + aq_error = i40e_aq_get_phy_capabilities(hw, + FALSE, FALSE, &abilities, NULL); + if (aq_error) { + device_printf(dev, + "%s: Error getting phy capabilities %d," + " aq error: %d\n", __func__, aq_error, + hw->aq.asq_last_status); + return; + } + + /* Prepare new config */ + memset(&config, 0, sizeof(config)); + config.link_speed = abilities.link_speed; + config.abilities = abilities.abilities; + config.eee_capability = abilities.eee_capability; + config.eeer = abilities.eeer_val; + config.low_power_ctrl = abilities.d3_lpan; + config.fec_config = abilities.fec_cfg_curr_mod_ext_info + & I40E_AQ_PHY_FEC_CONFIG_MASK; + config.phy_type = 0; + config.phy_type_ext = 0; + + if (enable) { + config.phy_type = phy_type; + config.phy_type_ext = phy_type_ext; + + config.abilities &= ~(I40E_AQ_PHY_FLAG_PAUSE_TX | + I40E_AQ_PHY_FLAG_PAUSE_RX); + + switch (pf->fc) { + case I40E_FC_FULL: + config.abilities |= I40E_AQ_PHY_FLAG_PAUSE_TX | + I40E_AQ_PHY_FLAG_PAUSE_RX; + break; + case I40E_FC_RX_PAUSE: + config.abilities |= I40E_AQ_PHY_FLAG_PAUSE_RX; + break; + case I40E_FC_TX_PAUSE: + config.abilities |= I40E_AQ_PHY_FLAG_PAUSE_TX; + break; + default: + break; + } + } + + aq_error = i40e_aq_set_phy_config(hw, &config, NULL); + if (aq_error) { + device_printf(dev, + "%s: Error setting new phy config %d," + " aq error: %d\n", __func__, aq_error, + hw->aq.asq_last_status); + return; + } + + aq_error = i40e_aq_set_link_restart_an(hw, enable, NULL); + if (aq_error) { + device_printf(dev, + "%s: Error set link config %d," + " aq error: %d\n", __func__, aq_error, + hw->aq.asq_last_status); + return; + } +} + static char * ixl_phy_type_string(u32 bit_pos, bool ext) { @@ -3265,7 +3369,7 @@ } status = i40e_aq_get_phy_capabilities(hw, - FALSE, FALSE, &abilities, NULL); + FALSE, arg2 != 0, &abilities, NULL); if (status) { device_printf(dev, "%s: i40e_aq_get_phy_capabilities() status %s, aq error %s\n", @@ -4447,6 +4551,28 @@ return (0); } +static int +ixl_sysctl_set_link_active(SYSCTL_HANDLER_ARGS) +{ + struct ixl_pf *pf = (struct ixl_pf *)arg1; + int error, state; + + state = !!(atomic_load_acq_32(&pf->state) & + IXL_PF_STATE_LINK_ACTIVE_ON_DOWN); + + error = sysctl_handle_int(oidp, &state, 0, req); + if ((error) || (req->newptr == NULL)) + return (error); + + if (state == 0) + atomic_clear_32(&pf->state, IXL_PF_STATE_LINK_ACTIVE_ON_DOWN); + else + atomic_set_32(&pf->state, IXL_PF_STATE_LINK_ACTIVE_ON_DOWN); + + return (0); +} + + int ixl_attach_get_link_status(struct ixl_pf *pf) {