#include "ice_iflib.h"
#include "ice_drv_info.h"
#include "ice_switch.h"
#include "ice_sched.h"
#ifdef PCI_IOV
#include "ice_iov.h"
#endif
#include <sys/module.h>
#include <sys/sockio.h>
#include <sys/smp.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
static void *ice_register(device_t);
static int ice_if_attach_pre(if_ctx_t);
static int ice_attach_pre_recovery_mode(struct ice_softc *sc);
static int ice_if_attach_post(if_ctx_t);
static void ice_attach_post_recovery_mode(struct ice_softc *sc);
static int ice_if_detach(if_ctx_t);
static int ice_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets);
static int ice_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nqs, int nqsets);
static int ice_if_msix_intr_assign(if_ctx_t ctx, int msix);
static void ice_if_queues_free(if_ctx_t ctx);
static int ice_if_mtu_set(if_ctx_t ctx, uint32_t mtu);
static void ice_if_intr_enable(if_ctx_t ctx);
static void ice_if_intr_disable(if_ctx_t ctx);
static int ice_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid);
static int ice_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid);
static int ice_if_promisc_set(if_ctx_t ctx, int flags);
static void ice_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr);
static int ice_if_media_change(if_ctx_t ctx);
static void ice_if_init(if_ctx_t ctx);
static void ice_if_timer(if_ctx_t ctx, uint16_t qid);
static void ice_if_update_admin_status(if_ctx_t ctx);
static void ice_if_multi_set(if_ctx_t ctx);
static void ice_if_vlan_register(if_ctx_t ctx, u16 vtag);
static void ice_if_vlan_unregister(if_ctx_t ctx, u16 vtag);
static void ice_if_stop(if_ctx_t ctx);
static uint64_t ice_if_get_counter(if_ctx_t ctx, ift_counter counter);
static int ice_if_priv_ioctl(if_ctx_t ctx, u_long command, caddr_t data);
static int ice_if_i2c_req(if_ctx_t ctx, struct ifi2creq *req);
static int ice_if_suspend(if_ctx_t ctx);
static int ice_if_resume(if_ctx_t ctx);
static bool ice_if_needs_restart(if_ctx_t ctx, enum iflib_restart_event event);
static void ice_init_link(struct ice_softc *sc);
#ifdef PCI_IOV
static int ice_if_iov_init(if_ctx_t ctx, uint16_t num_vfs, const nvlist_t *params);
static void ice_if_iov_uninit(if_ctx_t ctx);
static int ice_if_iov_vf_add(if_ctx_t ctx, uint16_t vfnum, const nvlist_t *params);
static void ice_if_vflr_handle(if_ctx_t ctx);
#endif
static int ice_setup_mirror_vsi(struct ice_mirr_if *mif);
static int ice_wire_mirror_intrs(struct ice_mirr_if *mif);
static void ice_free_irqvs_subif(struct ice_mirr_if *mif);
static void *ice_subif_register(device_t);
static void ice_subif_setup_scctx(struct ice_mirr_if *mif);
static int ice_subif_rebuild(struct ice_softc *sc);
static int ice_subif_rebuild_vsi_qmap(struct ice_softc *sc);
static int ice_subif_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs,
uint64_t *paddrs, int ntxqs, int ntxqsets);
static int ice_subif_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs,
uint64_t *paddrs, int nrxqs, int nrxqsets);
static int ice_subif_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid);
static int ice_subif_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid);
static void ice_subif_if_intr_enable(if_ctx_t ctx);
static int ice_subif_if_msix_intr_assign(if_ctx_t ctx, int msix);
static void ice_subif_if_init(if_ctx_t ctx);
static void ice_subif_if_stop(if_ctx_t ctx);
static void ice_subif_if_queues_free(if_ctx_t ctx);
static int ice_subif_if_attach_pre(if_ctx_t);
static int ice_subif_if_attach_post(if_ctx_t);
static void ice_subif_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr);
static int ice_subif_if_promisc_set(if_ctx_t ctx, int flags);
static int ice_msix_que(void *arg);
static int ice_msix_admin(void *arg);
static int ice_pci_mapping(struct ice_softc *sc);
static void ice_free_pci_mapping(struct ice_softc *sc);
static void ice_update_link_status(struct ice_softc *sc, bool update_media);
static void ice_init_device_features(struct ice_softc *sc);
static void ice_init_tx_tracking(struct ice_vsi *vsi);
static void ice_handle_reset_event(struct ice_softc *sc);
static void ice_handle_pf_reset_request(struct ice_softc *sc);
static void ice_prepare_for_reset(struct ice_softc *sc);
static int ice_rebuild_pf_vsi_qmap(struct ice_softc *sc);
static void ice_rebuild(struct ice_softc *sc);
static void ice_rebuild_recovery_mode(struct ice_softc *sc);
static void ice_free_irqvs(struct ice_softc *sc);
static void ice_update_rx_mbuf_sz(struct ice_softc *sc);
static void ice_poll_for_media_avail(struct ice_softc *sc);
static void ice_setup_scctx(struct ice_softc *sc);
static int ice_allocate_msix(struct ice_softc *sc);
static void ice_admin_timer(void *arg);
static void ice_transition_recovery_mode(struct ice_softc *sc);
static void ice_transition_safe_mode(struct ice_softc *sc);
static void ice_set_default_promisc_mask(ice_bitmap_t *promisc_mask);
static device_method_t ice_methods[] = {
DEVMETHOD(device_register, ice_register),
DEVMETHOD(device_probe, iflib_device_probe_vendor),
DEVMETHOD(device_attach, iflib_device_attach),
DEVMETHOD(device_detach, iflib_device_detach),
DEVMETHOD(device_shutdown, iflib_device_shutdown),
DEVMETHOD(device_suspend, iflib_device_suspend),
DEVMETHOD(device_resume, iflib_device_resume),
#ifdef PCI_IOV
DEVMETHOD(pci_iov_init, iflib_device_iov_init),
DEVMETHOD(pci_iov_uninit, iflib_device_iov_uninit),
DEVMETHOD(pci_iov_add_vf, iflib_device_iov_add_vf),
#endif
DEVMETHOD_END
};
static device_method_t ice_iflib_methods[] = {
DEVMETHOD(ifdi_attach_pre, ice_if_attach_pre),
DEVMETHOD(ifdi_attach_post, ice_if_attach_post),
DEVMETHOD(ifdi_detach, ice_if_detach),
DEVMETHOD(ifdi_tx_queues_alloc, ice_if_tx_queues_alloc),
DEVMETHOD(ifdi_rx_queues_alloc, ice_if_rx_queues_alloc),
DEVMETHOD(ifdi_msix_intr_assign, ice_if_msix_intr_assign),
DEVMETHOD(ifdi_queues_free, ice_if_queues_free),
DEVMETHOD(ifdi_mtu_set, ice_if_mtu_set),
DEVMETHOD(ifdi_intr_enable, ice_if_intr_enable),
DEVMETHOD(ifdi_intr_disable, ice_if_intr_disable),
DEVMETHOD(ifdi_rx_queue_intr_enable, ice_if_rx_queue_intr_enable),
DEVMETHOD(ifdi_tx_queue_intr_enable, ice_if_tx_queue_intr_enable),
DEVMETHOD(ifdi_promisc_set, ice_if_promisc_set),
DEVMETHOD(ifdi_media_status, ice_if_media_status),
DEVMETHOD(ifdi_media_change, ice_if_media_change),
DEVMETHOD(ifdi_init, ice_if_init),
DEVMETHOD(ifdi_stop, ice_if_stop),
DEVMETHOD(ifdi_timer, ice_if_timer),
DEVMETHOD(ifdi_update_admin_status, ice_if_update_admin_status),
DEVMETHOD(ifdi_multi_set, ice_if_multi_set),
DEVMETHOD(ifdi_vlan_register, ice_if_vlan_register),
DEVMETHOD(ifdi_vlan_unregister, ice_if_vlan_unregister),
DEVMETHOD(ifdi_get_counter, ice_if_get_counter),
DEVMETHOD(ifdi_priv_ioctl, ice_if_priv_ioctl),
DEVMETHOD(ifdi_i2c_req, ice_if_i2c_req),
DEVMETHOD(ifdi_suspend, ice_if_suspend),
DEVMETHOD(ifdi_resume, ice_if_resume),
DEVMETHOD(ifdi_needs_restart, ice_if_needs_restart),
#ifdef PCI_IOV
DEVMETHOD(ifdi_iov_vf_add, ice_if_iov_vf_add),
DEVMETHOD(ifdi_iov_init, ice_if_iov_init),
DEVMETHOD(ifdi_iov_uninit, ice_if_iov_uninit),
DEVMETHOD(ifdi_vflr_handle, ice_if_vflr_handle),
#endif
DEVMETHOD_END
};
static driver_t ice_driver = {
.name = "ice",
.methods = ice_methods,
.size = sizeof(struct ice_softc),
};
static driver_t ice_iflib_driver = {
.name = "ice",
.methods = ice_iflib_methods,
.size = sizeof(struct ice_softc),
};
extern struct if_txrx ice_txrx;
extern struct if_txrx ice_recovery_txrx;
static struct if_shared_ctx ice_sctx = {
.isc_magic = IFLIB_MAGIC,
.isc_q_align = PAGE_SIZE,
.isc_tx_maxsize = ICE_MAX_FRAME_SIZE,
.isc_tx_maxsegsize = ICE_MAX_FRAME_SIZE,
.isc_tso_maxsize = ICE_TSO_SIZE + sizeof(struct ether_vlan_header),
.isc_tso_maxsegsize = ICE_MAX_DMA_SEG_SIZE,
.isc_rx_maxsize = ICE_MAX_FRAME_SIZE,
.isc_rx_nsegments = ICE_MAX_RX_SEGS,
.isc_rx_maxsegsize = ICE_MAX_FRAME_SIZE,
.isc_nfl = 1,
.isc_ntxqs = 1,
.isc_nrxqs = 1,
.isc_admin_intrcnt = 1,
.isc_vendor_info = ice_vendor_info_array,
.isc_driver_version = __DECONST(char *, ice_driver_version),
.isc_driver = &ice_iflib_driver,
.isc_flags = IFLIB_NEED_SCRATCH | IFLIB_TSO_INIT_IP |
IFLIB_ADMIN_ALWAYS_RUN | IFLIB_SKIP_MSIX,
.isc_nrxd_min = {ICE_MIN_DESC_COUNT},
.isc_ntxd_min = {ICE_MIN_DESC_COUNT},
.isc_nrxd_max = {ICE_IFLIB_MAX_DESC_COUNT},
.isc_ntxd_max = {ICE_IFLIB_MAX_DESC_COUNT},
.isc_nrxd_default = {ICE_DEFAULT_DESC_COUNT},
.isc_ntxd_default = {ICE_DEFAULT_DESC_COUNT},
};
DRIVER_MODULE(ice, pci, ice_driver, ice_module_event_handler, NULL);
MODULE_VERSION(ice, 1);
MODULE_DEPEND(ice, pci, 1, 1, 1);
MODULE_DEPEND(ice, ether, 1, 1, 1);
MODULE_DEPEND(ice, iflib, 1, 1, 1);
IFLIB_PNP_INFO(pci, ice, ice_vendor_info_array);
#include "ice_iflib_sysctls.h"
static int
ice_pci_mapping(struct ice_softc *sc)
{
int rc;
rc = ice_map_bar(sc->dev, &sc->bar0, 0);
if (rc)
return rc;
return 0;
}
static void
ice_free_pci_mapping(struct ice_softc *sc)
{
ice_free_bar(sc->dev, &sc->bar0);
}
static void *
ice_register(device_t dev __unused)
{
return &ice_sctx;
}
static void
ice_setup_scctx(struct ice_softc *sc)
{
if_softc_ctx_t scctx = sc->scctx;
struct ice_hw *hw = &sc->hw;
device_t dev = sc->dev;
bool safe_mode, recovery_mode;
safe_mode = ice_is_bit_set(sc->feat_en, ICE_FEATURE_SAFE_MODE);
recovery_mode = ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE);
if (safe_mode || recovery_mode) {
scctx->isc_ntxqsets = scctx->isc_nrxqsets = 1;
scctx->isc_ntxqsets_max = 1;
scctx->isc_nrxqsets_max = 1;
} else {
sc->ifc_sysctl_ntxqs = scctx->isc_ntxqsets;
sc->ifc_sysctl_nrxqs = scctx->isc_nrxqsets;
if (scctx->isc_ntxqsets == 0)
scctx->isc_ntxqsets = hw->func_caps.common_cap.rss_table_size;
if (scctx->isc_nrxqsets == 0)
scctx->isc_nrxqsets = hw->func_caps.common_cap.rss_table_size;
scctx->isc_ntxqsets_max = hw->func_caps.common_cap.num_txq;
scctx->isc_nrxqsets_max = hw->func_caps.common_cap.num_rxq;
if (sc->ifc_sysctl_ntxqs > scctx->isc_ntxqsets_max)
sc->ifc_sysctl_ntxqs = scctx->isc_ntxqsets_max;
if (sc->ifc_sysctl_nrxqs > scctx->isc_nrxqsets_max)
sc->ifc_sysctl_nrxqs = scctx->isc_nrxqsets_max;
}
scctx->isc_txqsizes[0] = roundup2(scctx->isc_ntxd[0]
* sizeof(struct ice_tx_desc), DBA_ALIGN);
scctx->isc_rxqsizes[0] = roundup2(scctx->isc_nrxd[0]
* sizeof(union ice_32b_rx_flex_desc), DBA_ALIGN);
scctx->isc_tx_nsegments = ICE_MAX_TX_SEGS;
scctx->isc_tx_tso_segments_max = ICE_MAX_TSO_SEGS;
scctx->isc_tx_tso_size_max = ICE_TSO_SIZE;
scctx->isc_tx_tso_segsize_max = ICE_MAX_DMA_SEG_SIZE;
scctx->isc_msix_bar = pci_msix_table_bar(dev);
scctx->isc_rss_table_size = hw->func_caps.common_cap.rss_table_size;
if (recovery_mode)
scctx->isc_txrx = &ice_recovery_txrx;
else
scctx->isc_txrx = &ice_txrx;
if (safe_mode || recovery_mode) {
scctx->isc_capenable = ICE_SAFE_CAPS;
scctx->isc_tx_csum_flags = 0;
} else {
scctx->isc_capenable = ICE_FULL_CAPS;
scctx->isc_tx_csum_flags = ICE_CSUM_OFFLOAD;
}
scctx->isc_capabilities = scctx->isc_capenable;
}
static int
ice_if_attach_pre(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
enum ice_fw_modes fw_mode;
int status;
if_softc_ctx_t scctx;
struct ice_hw *hw;
device_t dev;
int err;
device_printf(iflib_get_dev(ctx), "Loading the iflib ice driver\n");
ice_set_state(&sc->state, ICE_STATE_ATTACHING);
sc->ctx = ctx;
sc->media = iflib_get_media(ctx);
sc->sctx = iflib_get_sctx(ctx);
sc->iflib_ctx_lock = iflib_ctx_lock_get(ctx);
sc->ifp = iflib_get_ifp(ctx);
dev = sc->dev = iflib_get_dev(ctx);
scctx = sc->scctx = iflib_get_softc_ctx(ctx);
hw = &sc->hw;
hw->back = sc;
snprintf(sc->admin_mtx_name, sizeof(sc->admin_mtx_name),
"%s:admin", device_get_nameunit(dev));
mtx_init(&sc->admin_mtx, sc->admin_mtx_name, NULL, MTX_DEF);
callout_init_mtx(&sc->admin_timer, &sc->admin_mtx, 0);
ASSERT_CTX_LOCKED(sc);
if (ice_pci_mapping(sc)) {
err = (ENXIO);
goto destroy_admin_timer;
}
ice_save_pci_info(hw, dev);
ice_add_device_tunables(sc);
ice_set_ctrlq_len(hw);
reinit_hw:
fw_mode = ice_get_fw_mode(hw);
if (fw_mode == ICE_FW_MODE_REC) {
device_printf(dev, "Firmware recovery mode detected. Limiting functionality. Refer to Intel(R) Ethernet Adapters and Devices User Guide for details on firmware recovery mode.\n");
err = ice_attach_pre_recovery_mode(sc);
if (err)
goto free_pci_mapping;
return (0);
}
status = ice_init_hw(hw);
if (status) {
if (status == ICE_ERR_FW_API_VER) {
err = ice_attach_pre_recovery_mode(sc);
if (err)
goto free_pci_mapping;
return (0);
} else {
err = EIO;
device_printf(dev, "Unable to initialize hw, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
}
goto free_pci_mapping;
}
ice_init_device_features(sc);
ice_set_state(&sc->state, ICE_STATE_LINK_ACTIVE_ON_DOWN);
err = ice_send_version(sc);
if (err)
goto deinit_hw;
err = ice_load_pkg_file(sc);
if (!err) {
ice_deinit_hw(hw);
goto reinit_hw;
}
err = ice_init_link_events(sc);
if (err) {
device_printf(dev, "ice_init_link_events failed: %s\n",
ice_err_str(err));
goto deinit_hw;
}
status = ice_set_vlan_mode(hw);
if (status) {
err = EIO;
device_printf(dev, "Unable to initialize VLAN mode, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
goto deinit_hw;
}
ice_print_nvm_version(sc);
iflib_set_mac(ctx, hw->port_info->mac.lan_addr);
ice_setup_scctx(sc);
err = ice_resmgr_init(&sc->tx_qmgr, hw->func_caps.common_cap.num_txq);
if (err) {
device_printf(dev, "Unable to initialize Tx queue manager: %s\n",
ice_err_str(err));
goto deinit_hw;
}
err = ice_resmgr_init(&sc->rx_qmgr, hw->func_caps.common_cap.num_rxq);
if (err) {
device_printf(dev, "Unable to initialize Rx queue manager: %s\n",
ice_err_str(err));
goto free_tx_qmgr;
}
err = ice_alloc_intr_tracking(sc);
if (err)
goto free_rx_qmgr;
sc->num_available_vsi = min(ICE_MAX_VSI_AVAILABLE,
hw->func_caps.guar_num_vsi);
if (!sc->num_available_vsi) {
err = EIO;
device_printf(dev, "No VSIs allocated to host\n");
goto free_intr_tracking;
}
sc->all_vsi = (struct ice_vsi **)
malloc(sizeof(struct ice_vsi *) * sc->num_available_vsi,
M_ICE, M_WAITOK | M_ZERO);
if (!sc->all_vsi) {
err = ENOMEM;
device_printf(dev, "Unable to allocate VSI array\n");
goto free_intr_tracking;
}
ice_setup_pf_vsi(sc);
ice_alloc_vsi_qmap(&sc->pf_vsi, scctx->isc_ntxqsets_max,
scctx->isc_nrxqsets_max);
err = ice_allocate_msix(sc);
if (err)
goto free_main_vsi;
return 0;
free_main_vsi:
ice_release_vsi(&sc->pf_vsi);
free(sc->all_vsi, M_ICE);
sc->all_vsi = NULL;
free_intr_tracking:
ice_free_intr_tracking(sc);
free_rx_qmgr:
ice_resmgr_destroy(&sc->rx_qmgr);
free_tx_qmgr:
ice_resmgr_destroy(&sc->tx_qmgr);
deinit_hw:
ice_deinit_hw(hw);
free_pci_mapping:
ice_free_pci_mapping(sc);
destroy_admin_timer:
mtx_lock(&sc->admin_mtx);
callout_stop(&sc->admin_timer);
mtx_unlock(&sc->admin_mtx);
mtx_destroy(&sc->admin_mtx);
return err;
}
static int
ice_attach_pre_recovery_mode(struct ice_softc *sc)
{
ice_set_state(&sc->state, ICE_STATE_RECOVERY_MODE);
ice_setup_scctx(sc);
sc->pf_vsi.sc = sc;
return ice_allocate_msix(sc);
}
static void
ice_update_link_status(struct ice_softc *sc, bool update_media)
{
struct ice_hw *hw = &sc->hw;
int status;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
if (!ice_testandset_state(&sc->state, ICE_STATE_LINK_STATUS_REPORTED)) {
if (sc->link_up) {
uint64_t baudrate = ice_aq_speed_to_rate(sc->hw.port_info);
if (!(hw->port_info->phy.link_info_old.link_info & ICE_AQ_LINK_UP))
ice_set_default_local_lldp_mib(sc);
iflib_link_state_change(sc->ctx, LINK_STATE_UP, baudrate);
ice_rdma_link_change(sc, LINK_STATE_UP, baudrate);
ice_link_up_msg(sc);
} else {
iflib_link_state_change(sc->ctx, LINK_STATE_DOWN, 0);
ice_rdma_link_change(sc, LINK_STATE_DOWN, 0);
}
#ifdef PCI_IOV
ice_vc_notify_all_vfs_link_state(sc);
#endif
update_media = true;
}
if (update_media && !ice_test_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET)) {
status = ice_add_media_types(sc, sc->media);
if (status)
device_printf(sc->dev, "Error adding device media types: %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
}
}
static int
ice_if_attach_post(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
if_t ifp = iflib_get_ifp(ctx);
int status;
int err;
ASSERT_CTX_LOCKED(sc);
if (sc->scctx->isc_intr != IFLIB_INTR_MSIX) {
device_printf(sc->dev, "The ice driver does not support loading without MSI-X\n");
return (ENOTSUP);
}
sc->scctx->isc_max_frame_size = if_getmtu(ifp) +
ETHER_HDR_LEN + ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE)) {
ice_attach_post_recovery_mode(sc);
return (0);
}
sc->pf_vsi.max_frame_size = sc->scctx->isc_max_frame_size;
err = ice_initialize_vsi(&sc->pf_vsi);
if (err) {
device_printf(sc->dev, "Unable to initialize Main VSI: %s\n",
ice_err_str(err));
return err;
}
ice_init_health_events(sc);
err = ice_config_rss(&sc->pf_vsi);
if (err) {
device_printf(sc->dev,
"Unable to configure RSS for the main VSI, err %s\n",
ice_err_str(err));
return err;
}
err = ice_cfg_pf_ethertype_filters(sc);
if (err)
return err;
ice_get_and_print_bus_info(sc);
ice_set_link_management_mode(sc);
ice_init_saved_phy_cfg(sc);
ice_cfg_pba_num(sc);
status = ice_aq_set_pfc_mode(&sc->hw, ICE_AQC_PFC_VLAN_BASED_PFC, NULL);
if (status)
device_printf(sc->dev, "Setting pfc mode failed, status %s\n", ice_status_str(status));
ice_add_device_sysctls(sc);
#ifdef PCI_IOV
if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_SRIOV)) {
err = ice_iov_attach(sc);
if (err == ENOMEM)
return (err);
}
#endif
ice_init_dcb_setup(sc);
ice_init_link(sc);
ice_configure_misc_interrupts(sc);
ice_enable_intr(&sc->hw, sc->irqvs[0].me);
err = ice_rdma_pf_attach(sc);
if (err)
return (err);
mtx_lock(&sc->admin_mtx);
callout_reset(&sc->admin_timer, hz/2, ice_admin_timer, sc);
mtx_unlock(&sc->admin_mtx);
if (ice_test_state(&sc->state, ICE_STATE_LINK_ACTIVE_ON_DOWN) &&
!ice_test_state(&sc->state, ICE_STATE_NO_MEDIA))
ice_set_state(&sc->state, ICE_STATE_FIRST_INIT_LINK);
ice_clear_state(&sc->state, ICE_STATE_ATTACHING);
return 0;
}
static void
ice_attach_post_recovery_mode(struct ice_softc *sc)
{
ice_configure_misc_interrupts(sc);
ice_enable_intr(&sc->hw, sc->irqvs[0].me);
mtx_lock(&sc->admin_mtx);
callout_reset(&sc->admin_timer, hz/2, ice_admin_timer, sc);
mtx_unlock(&sc->admin_mtx);
ice_clear_state(&sc->state, ICE_STATE_ATTACHING);
}
static void
ice_free_irqvs(struct ice_softc *sc)
{
struct ice_vsi *vsi = &sc->pf_vsi;
if_ctx_t ctx = sc->ctx;
int i;
if (sc->irqvs == NULL)
return;
for (i = 0; i < sc->num_irq_vectors; i++)
iflib_irq_free(ctx, &sc->irqvs[i].irq);
for (i = 0; i < vsi->num_rx_queues; i++)
vsi->rx_queues[i].irqv = NULL;
for (i = 0; i < vsi->num_tx_queues; i++)
vsi->tx_queues[i].irqv = NULL;
free(sc->irqvs, M_ICE);
sc->irqvs = NULL;
sc->num_irq_vectors = 0;
}
static int
ice_if_detach(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
int status;
int i;
ASSERT_CTX_LOCKED(sc);
ice_set_state(&sc->state, ICE_STATE_DETACHING);
mtx_lock(&sc->admin_mtx);
callout_stop(&sc->admin_timer);
mtx_unlock(&sc->admin_mtx);
mtx_destroy(&sc->admin_mtx);
if (sc->mirr_if)
ice_destroy_mirror_interface(sc);
ice_rdma_pf_detach(sc);
#ifdef PCI_IOV
if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_SRIOV))
ice_iov_detach(sc);
#endif
ifmedia_removeall(sc->media);
ice_vsi_del_txqs_ctx(vsi);
ice_vsi_del_rxqs_ctx(vsi);
ice_free_irqvs(sc);
for (i = 0; i < sc->num_available_vsi; i++) {
if (sc->all_vsi[i])
ice_release_vsi(sc->all_vsi[i]);
}
if (sc->all_vsi) {
free(sc->all_vsi, M_ICE);
sc->all_vsi = NULL;
}
pci_release_msi(sc->dev);
if (sc->msix_table != NULL) {
bus_release_resource(sc->dev, SYS_RES_MEMORY,
rman_get_rid(sc->msix_table),
sc->msix_table);
sc->msix_table = NULL;
}
ice_free_intr_tracking(sc);
ice_resmgr_destroy(&sc->tx_qmgr);
ice_resmgr_destroy(&sc->rx_qmgr);
if (!ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
ice_deinit_hw(&sc->hw);
IFLIB_CTX_UNLOCK(sc);
status = ice_reset(&sc->hw, ICE_RESET_PFR);
IFLIB_CTX_LOCK(sc);
if (status) {
device_printf(sc->dev, "device PF reset failed, err %s\n",
ice_status_str(status));
}
ice_free_pci_mapping(sc);
return 0;
}
static int
ice_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs,
int __invariant_only ntxqs, int ntxqsets)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
struct ice_tx_queue *txq;
int err, i, j;
MPASS(ntxqs == 1);
MPASS(sc->scctx->isc_ntxd[0] <= ICE_MAX_DESC_COUNT);
ASSERT_CTX_LOCKED(sc);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (0);
if (!(vsi->tx_queues =
(struct ice_tx_queue *) malloc(sizeof(struct ice_tx_queue) * ntxqsets, M_ICE, M_NOWAIT | M_ZERO))) {
device_printf(sc->dev, "Unable to allocate Tx queue memory\n");
return (ENOMEM);
}
for (i = 0, txq = vsi->tx_queues; i < ntxqsets; i++, txq++) {
if (!(txq->tx_rsq =
(uint16_t *) malloc(sizeof(uint16_t) * sc->scctx->isc_ntxd[0], M_ICE, M_NOWAIT))) {
device_printf(sc->dev, "Unable to allocate tx_rsq memory\n");
err = ENOMEM;
goto free_tx_queues;
}
for (j = 0; j < sc->scctx->isc_ntxd[0]; j++)
txq->tx_rsq[j] = QIDX_INVALID;
}
err = ice_resmgr_assign_contiguous(&sc->tx_qmgr, vsi->tx_qmap, ntxqsets);
if (err) {
device_printf(sc->dev, "Unable to assign PF queues: %s\n",
ice_err_str(err));
goto free_tx_queues;
}
vsi->qmap_type = ICE_RESMGR_ALLOC_CONTIGUOUS;
ice_vsi_add_txqs_ctx(vsi);
for (i = 0, txq = vsi->tx_queues; i < ntxqsets; i++, txq++) {
txq->me = txq->q_handle = i;
txq->vsi = vsi;
txq->desc_count = sc->scctx->isc_ntxd[0];
txq->tail = QTX_COMM_DBELL(vsi->tx_qmap[i]);
txq->tx_base = (struct ice_tx_desc *)vaddrs[i];
txq->tx_paddr = paddrs[i];
ice_add_txq_sysctls(txq);
}
vsi->num_tx_queues = ntxqsets;
return (0);
free_tx_queues:
for (i = 0, txq = vsi->tx_queues; i < ntxqsets; i++, txq++) {
if (txq->tx_rsq != NULL) {
free(txq->tx_rsq, M_ICE);
txq->tx_rsq = NULL;
}
}
free(vsi->tx_queues, M_ICE);
vsi->tx_queues = NULL;
return err;
}
static int
ice_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs,
int __invariant_only nrxqs, int nrxqsets)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
struct ice_rx_queue *rxq;
int err, i;
MPASS(nrxqs == 1);
MPASS(sc->scctx->isc_nrxd[0] <= ICE_MAX_DESC_COUNT);
ASSERT_CTX_LOCKED(sc);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (0);
if (!(vsi->rx_queues =
(struct ice_rx_queue *) malloc(sizeof(struct ice_rx_queue) * nrxqsets, M_ICE, M_NOWAIT | M_ZERO))) {
device_printf(sc->dev, "Unable to allocate Rx queue memory\n");
return (ENOMEM);
}
err = ice_resmgr_assign_contiguous(&sc->rx_qmgr, vsi->rx_qmap, nrxqsets);
if (err) {
device_printf(sc->dev, "Unable to assign PF queues: %s\n",
ice_err_str(err));
goto free_rx_queues;
}
vsi->qmap_type = ICE_RESMGR_ALLOC_CONTIGUOUS;
ice_vsi_add_rxqs_ctx(vsi);
for (i = 0, rxq = vsi->rx_queues; i < nrxqsets; i++, rxq++) {
rxq->me = i;
rxq->vsi = vsi;
rxq->desc_count = sc->scctx->isc_nrxd[0];
rxq->tail = QRX_TAIL(vsi->rx_qmap[i]);
rxq->rx_base = (union ice_32b_rx_flex_desc *)vaddrs[i];
rxq->rx_paddr = paddrs[i];
ice_add_rxq_sysctls(rxq);
}
vsi->num_rx_queues = nrxqsets;
return (0);
free_rx_queues:
free(vsi->rx_queues, M_ICE);
vsi->rx_queues = NULL;
return err;
}
static void
ice_if_queues_free(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
struct ice_tx_queue *txq;
int i;
ice_vsi_del_txqs_ctx(vsi);
ice_vsi_del_rxqs_ctx(vsi);
ice_free_irqvs(sc);
if (vsi->tx_queues != NULL) {
for (i = 0, txq = vsi->tx_queues; i < vsi->num_tx_queues; i++, txq++) {
if (txq->tx_rsq != NULL) {
free(txq->tx_rsq, M_ICE);
txq->tx_rsq = NULL;
}
}
free(vsi->tx_queues, M_ICE);
vsi->tx_queues = NULL;
vsi->num_tx_queues = 0;
}
if (vsi->rx_queues != NULL) {
free(vsi->rx_queues, M_ICE);
vsi->rx_queues = NULL;
vsi->num_rx_queues = 0;
}
}
static int
ice_msix_que(void *arg)
{
struct ice_rx_queue __unused *rxq = (struct ice_rx_queue *)arg;
return (FILTER_SCHEDULE_THREAD);
}
static int
ice_msix_admin(void *arg)
{
struct ice_softc *sc = (struct ice_softc *)arg;
struct ice_hw *hw = &sc->hw;
device_t dev = sc->dev;
u32 oicr;
oicr = rd32(hw, PFINT_OICR);
ice_set_state(&sc->state, ICE_STATE_CONTROLQ_EVENT_PENDING);
if (oicr & PFINT_OICR_VFLR_M) {
ice_set_state(&sc->state, ICE_STATE_VFLR_PENDING);
}
if (oicr & PFINT_OICR_MAL_DETECT_M) {
ice_set_state(&sc->state, ICE_STATE_MDD_PENDING);
}
if (oicr & PFINT_OICR_GRST_M) {
u32 reset;
reset = (rd32(hw, GLGEN_RSTAT) & GLGEN_RSTAT_RESET_TYPE_M) >>
GLGEN_RSTAT_RESET_TYPE_S;
if (reset == ICE_RESET_CORER)
sc->soft_stats.corer_count++;
else if (reset == ICE_RESET_GLOBR)
sc->soft_stats.globr_count++;
else
sc->soft_stats.empr_count++;
if (!ice_testandset_state(&sc->state, ICE_STATE_RESET_OICR_RECV)) {
hw->reset_ongoing = true;
if (if_getflags(sc->ifp) & IFF_UP)
ice_set_state(&sc->state, ICE_STATE_FIRST_INIT_LINK);
}
}
if (oicr & PFINT_OICR_ECC_ERR_M) {
device_printf(dev, "ECC Error detected!\n");
ice_set_state(&sc->state, ICE_STATE_RESET_PFR_REQ);
}
if (oicr & (PFINT_OICR_PE_CRITERR_M | PFINT_OICR_HMC_ERR_M)) {
if (oicr & PFINT_OICR_HMC_ERR_M)
ice_log_hmc_error(hw, dev);
ice_rdma_notify_pe_intr(sc, oicr);
}
if (oicr & PFINT_OICR_PCI_EXCEPTION_M) {
device_printf(dev, "PCI Exception detected!\n");
ice_set_state(&sc->state, ICE_STATE_RESET_PFR_REQ);
}
return (FILTER_SCHEDULE_THREAD);
}
static int
ice_allocate_msix(struct ice_softc *sc)
{
bool iflib_override_queue_count = false;
if_softc_ctx_t scctx = sc->scctx;
device_t dev = sc->dev;
cpuset_t cpus;
int bar, queues, vectors, requested;
int err = 0;
int rdma;
bar = scctx->isc_msix_bar;
sc->msix_table = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &bar, RF_ACTIVE);
if (!sc->msix_table) {
device_printf(dev, "Unable to map MSI-X table\n");
return (ENOMEM);
}
if (sc->ifc_sysctl_ntxqs || sc->ifc_sysctl_nrxqs)
iflib_override_queue_count = true;
err = bus_get_cpus(dev, INTR_CPUS, sizeof(cpus), &cpus);
if (err) {
device_printf(dev, "%s: Unable to fetch the CPU list: %s\n",
__func__, ice_err_str(err));
CPU_COPY(&all_cpus, &cpus);
}
if (iflib_override_queue_count) {
queues = mp_ncpus;
} else {
queues = CPU_COUNT(&cpus);
}
queues = imin(queues, rss_getnumbuckets());
queues = imin(queues, sc->ifc_sysctl_ntxqs ?: scctx->isc_ntxqsets);
queues = imin(queues, sc->ifc_sysctl_nrxqs ?: scctx->isc_nrxqsets);
if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_RDMA)) {
rdma = min(CPU_COUNT(&cpus), ICE_RDMA_MAX_MSIX);
rdma = min(rdma, ice_rdma_max_msix);
} else {
rdma = 0;
}
requested = rdma + queues + 1;
if_ctx_t ctx = sc->ctx;
u32 extra_vectors = iflib_get_extra_msix_vectors_sysctl(ctx);
requested += extra_vectors;
vectors = requested;
err = pci_alloc_msix(dev, &vectors);
if (err) {
device_printf(dev, "Failed to allocate %d MSI-X vectors, err %s\n",
vectors, ice_err_str(err));
goto err_free_msix_table;
}
if (vectors < requested) {
int diff = requested - vectors;
device_printf(dev, "Requested %d MSI-X vectors, but got only %d\n",
requested, vectors);
diff += extra_vectors;
extra_vectors = 0;
if (rdma >= diff) {
rdma -= diff;
diff = 0;
} else {
ice_clear_bit(ICE_FEATURE_RDMA, sc->feat_cap);
diff -= rdma;
rdma = 0;
}
if (queues <= diff) {
device_printf(dev, "Unable to allocate sufficient MSI-X vectors\n");
err = (ERANGE);
goto err_pci_release_msi;
}
queues -= diff;
}
device_printf(dev, "Using %d Tx and Rx queues\n", queues);
if (rdma)
device_printf(dev, "Reserving %d MSI-X interrupts for iRDMA\n",
rdma);
device_printf(dev, "Using MSI-X interrupts with %d vectors\n",
vectors);
scctx->isc_vectors = vectors;
scctx->isc_nrxqsets = queues;
scctx->isc_ntxqsets = queues;
scctx->isc_intr = IFLIB_INTR_MSIX;
sc->irdma_vectors = rdma;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (0);
sc->lan_vectors = vectors - rdma;
sc->lan_vectors -= extra_vectors;
err = ice_resmgr_assign_contiguous(&sc->dev_imgr, sc->pf_imap, sc->lan_vectors);
if (err) {
device_printf(dev, "Unable to assign PF interrupt mapping: %s\n",
ice_err_str(err));
goto err_pci_release_msi;
}
err = ice_resmgr_assign_contiguous(&sc->dev_imgr, sc->rdma_imap, rdma);
if (err) {
device_printf(dev, "Unable to assign PF RDMA interrupt mapping: %s\n",
ice_err_str(err));
goto err_release_pf_imap;
}
sc->extra_vectors = extra_vectors;
err = ice_resmgr_init(&sc->os_imgr, sc->extra_vectors);
if (err) {
device_printf(dev, "Unable to initialize OS extra interrupt manager: %s\n",
ice_err_str(err));
ice_resmgr_release_map(&sc->dev_imgr, sc->rdma_imap,
rdma);
goto err_release_pf_imap;
}
return (0);
err_release_pf_imap:
ice_resmgr_release_map(&sc->dev_imgr, sc->pf_imap,
sc->lan_vectors);
err_pci_release_msi:
pci_release_msi(dev);
err_free_msix_table:
if (sc->msix_table != NULL) {
bus_release_resource(sc->dev, SYS_RES_MEMORY,
rman_get_rid(sc->msix_table),
sc->msix_table);
sc->msix_table = NULL;
}
return (err);
}
static int
ice_if_msix_intr_assign(if_ctx_t ctx, int msix)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
int err, i, vector;
ASSERT_CTX_LOCKED(sc);
if (vsi->num_rx_queues != vsi->num_tx_queues) {
device_printf(sc->dev,
"iflib requested %d Tx queues, and %d Rx queues, but the driver isn't able to support a differing number of Tx and Rx queues\n",
vsi->num_tx_queues, vsi->num_rx_queues);
return (EOPNOTSUPP);
}
if (msix < (vsi->num_rx_queues + 1)) {
device_printf(sc->dev,
"Not enough MSI-X vectors to assign one vector to each queue pair\n");
return (EOPNOTSUPP);
}
sc->num_irq_vectors = vsi->num_rx_queues + 1;
if (!(sc->irqvs =
(struct ice_irq_vector *) malloc(sizeof(struct ice_irq_vector) * (sc->num_irq_vectors),
M_ICE, M_NOWAIT))) {
device_printf(sc->dev,
"Unable to allocate irqv memory\n");
return (ENOMEM);
}
err = iflib_irq_alloc_generic(ctx, &sc->irqvs[0].irq, 1, IFLIB_INTR_ADMIN,
ice_msix_admin, sc, 0, "admin");
if (err) {
device_printf(sc->dev,
"Failed to register Admin queue handler: %s\n",
ice_err_str(err));
goto free_irqvs;
}
sc->irqvs[0].me = 0;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (0);
int rid;
for (i = 0, vector = 1; i < vsi->num_rx_queues; i++, vector++) {
struct ice_rx_queue *rxq = &vsi->rx_queues[i];
struct ice_tx_queue *txq = &vsi->tx_queues[i];
char irq_name[16];
rid = vector + 1;
snprintf(irq_name, sizeof(irq_name), "rxq%d", i);
err = iflib_irq_alloc_generic(ctx, &sc->irqvs[vector].irq, rid,
IFLIB_INTR_RXTX, ice_msix_que,
rxq, rxq->me, irq_name);
if (err) {
device_printf(sc->dev,
"Failed to allocate q int %d err: %s\n",
i, ice_err_str(err));
vector--;
i--;
goto fail;
}
sc->irqvs[vector].me = vector;
rxq->irqv = &sc->irqvs[vector];
bzero(irq_name, sizeof(irq_name));
snprintf(irq_name, sizeof(irq_name), "txq%d", i);
iflib_softirq_alloc_generic(ctx, &sc->irqvs[vector].irq,
IFLIB_INTR_TX, txq,
txq->me, irq_name);
txq->irqv = &sc->irqvs[vector];
}
sc->last_rid = rid + sc->irdma_vectors;
#ifdef PCI_IOV
iflib_softirq_alloc_generic(ctx, NULL, IFLIB_INTR_IOV, sc, 0, "iov");
#endif
return (0);
fail:
for (; i >= 0; i--, vector--)
iflib_irq_free(ctx, &sc->irqvs[vector].irq);
iflib_irq_free(ctx, &sc->irqvs[0].irq);
free_irqvs:
free(sc->irqvs, M_ICE);
sc->irqvs = NULL;
return err;
}
static int
ice_if_mtu_set(if_ctx_t ctx, uint32_t mtu)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
ASSERT_CTX_LOCKED(sc);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (ENOSYS);
if (mtu < ICE_MIN_MTU || mtu > ICE_MAX_MTU)
return (EINVAL);
sc->scctx->isc_max_frame_size = mtu +
ETHER_HDR_LEN + ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN;
sc->pf_vsi.max_frame_size = sc->scctx->isc_max_frame_size;
return (0);
}
static void
ice_if_intr_enable(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
struct ice_hw *hw = &sc->hw;
ASSERT_CTX_LOCKED(sc);
ice_enable_intr(hw, sc->irqvs[0].me);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
for (int i = 0; i < vsi->num_rx_queues; i++)
ice_enable_intr(hw, vsi->rx_queues[i].irqv->me);
}
static void
ice_if_intr_disable(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_hw *hw = &sc->hw;
unsigned int i;
ASSERT_CTX_LOCKED(sc);
for (i = 1; i < hw->func_caps.common_cap.num_msix_vectors; i++)
ice_disable_intr(hw, i);
}
static int
ice_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
struct ice_hw *hw = &sc->hw;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (ENOSYS);
ice_enable_intr(hw, vsi->rx_queues[rxqid].irqv->me);
return (0);
}
static int
ice_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
struct ice_hw *hw = &sc->hw;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (ENOSYS);
ice_enable_intr(hw, vsi->tx_queues[txqid].irqv->me);
return (0);
}
static void
ice_set_default_promisc_mask(ice_bitmap_t *promisc_mask)
{
ice_zero_bitmap(promisc_mask, ICE_PROMISC_MAX);
ice_set_bit(ICE_PROMISC_UCAST_TX, promisc_mask);
ice_set_bit(ICE_PROMISC_UCAST_RX, promisc_mask);
ice_set_bit(ICE_PROMISC_MCAST_TX, promisc_mask);
ice_set_bit(ICE_PROMISC_MCAST_RX, promisc_mask);
}
static int
ice_if_promisc_set(if_ctx_t ctx, int flags)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_hw *hw = &sc->hw;
device_t dev = sc->dev;
int status;
bool promisc_enable = flags & IFF_PROMISC;
bool multi_enable = flags & IFF_ALLMULTI;
ice_declare_bitmap(promisc_mask, ICE_PROMISC_MAX);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (ENOSYS);
ice_set_default_promisc_mask(promisc_mask);
if (promisc_enable) {
status = ice_set_vsi_promisc(hw, sc->pf_vsi.idx,
promisc_mask, 0);
if (status && status != ICE_ERR_ALREADY_EXISTS) {
device_printf(dev,
"Failed to enable promiscuous mode for "
"PF VSI, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
return (EIO);
}
} else {
status = ice_clear_vsi_promisc(hw, sc->pf_vsi.idx,
promisc_mask, 0);
if (status) {
device_printf(dev,
"Failed to disable promiscuous mode for"
" PF VSI, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
return (EIO);
}
if (multi_enable) {
ice_clear_bit(ICE_PROMISC_UCAST_TX, promisc_mask);
ice_clear_bit(ICE_PROMISC_UCAST_RX, promisc_mask);
status = ice_set_vsi_promisc(hw, sc->pf_vsi.idx,
promisc_mask, 0);
if (status && status != ICE_ERR_ALREADY_EXISTS) {
device_printf(dev,
"Failed to enable allmulti mode "
"for PF VSI, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(
hw->adminq.sq_last_status));
return (EIO);
}
}
}
return (0);
}
static int
ice_if_media_change(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
device_printf(sc->dev, "Media change is not supported.\n");
return (ENODEV);
}
static void
ice_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ice_link_status *li = &sc->hw.port_info->phy.link_info;
ifmr->ifm_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
if (!sc->link_up)
return;
ifmr->ifm_status |= IFM_ACTIVE;
ifmr->ifm_active |= IFM_FDX;
if (li->phy_type_low)
ifmr->ifm_active |= ice_get_phy_type_low(li->phy_type_low);
else if (li->phy_type_high)
ifmr->ifm_active |= ice_get_phy_type_high(li->phy_type_high);
else
ifmr->ifm_active |= IFM_UNKNOWN;
if (li->an_info & ICE_AQ_LINK_PAUSE_TX)
ifmr->ifm_active |= IFM_ETH_TXPAUSE;
if (li->an_info & ICE_AQ_LINK_PAUSE_RX)
ifmr->ifm_active |= IFM_ETH_RXPAUSE;
}
static void
ice_init_tx_tracking(struct ice_vsi *vsi)
{
struct ice_tx_queue *txq;
size_t j;
int i;
for (i = 0, txq = vsi->tx_queues; i < vsi->num_tx_queues; i++, txq++) {
txq->tx_rs_cidx = txq->tx_rs_pidx = 0;
txq->tx_cidx_processed = txq->desc_count - 1;
for (j = 0; j < txq->desc_count; j++)
txq->tx_rsq[j] = QIDX_INVALID;
}
}
static void
ice_update_rx_mbuf_sz(struct ice_softc *sc)
{
uint32_t mbuf_sz = iflib_get_rx_mbuf_sz(sc->ctx);
struct ice_vsi *vsi = &sc->pf_vsi;
MPASS(mbuf_sz <= UINT16_MAX);
vsi->mbuf_sz = mbuf_sz;
}
static void
ice_if_init(if_ctx_t ctx)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
device_t dev = sc->dev;
int err;
ASSERT_CTX_LOCKED(sc);
if (ice_driver_is_detaching(sc))
return;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
if (ice_test_state(&sc->state, ICE_STATE_RESET_FAILED)) {
device_printf(sc->dev, "request to start interface cannot be completed as the device failed to reset\n");
return;
}
if (ice_test_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET)) {
device_printf(sc->dev, "request to start interface while device is prepared for impending reset\n");
return;
}
ice_update_rx_mbuf_sz(sc);
err = ice_update_laa_mac(sc);
if (err) {
device_printf(dev,
"LAA address change failed, err %s\n",
ice_err_str(err));
return;
}
ice_init_tx_tracking(&sc->pf_vsi);
err = ice_cfg_vsi_for_tx(&sc->pf_vsi);
if (err) {
device_printf(dev,
"Unable to configure the main VSI for Tx: %s\n",
ice_err_str(err));
return;
}
err = ice_cfg_vsi_for_rx(&sc->pf_vsi);
if (err) {
device_printf(dev,
"Unable to configure the main VSI for Rx: %s\n",
ice_err_str(err));
goto err_cleanup_tx;
}
err = ice_control_all_rx_queues(&sc->pf_vsi, true);
if (err) {
device_printf(dev,
"Unable to enable Rx rings for transmit: %s\n",
ice_err_str(err));
goto err_cleanup_tx;
}
err = ice_cfg_pf_default_mac_filters(sc);
if (err) {
device_printf(dev,
"Unable to configure default MAC filters: %s\n",
ice_err_str(err));
goto err_stop_rx;
}
ice_configure_all_rxq_interrupts(&sc->pf_vsi);
ice_configure_rx_itr(&sc->pf_vsi);
ice_if_promisc_set(ctx, if_getflags(sc->ifp));
if (!ice_testandclear_state(&sc->state, ICE_STATE_FIRST_INIT_LINK))
if (!sc->link_up && ((if_getflags(sc->ifp) & IFF_UP) ||
ice_test_state(&sc->state, ICE_STATE_LINK_ACTIVE_ON_DOWN)))
ice_set_link(sc, true);
ice_rdma_pf_init(sc);
ice_set_state(&sc->state, ICE_STATE_DRIVER_INITIALIZED);
if (sc->mirr_if && ice_testandclear_state(&mif->state, ICE_STATE_SUBIF_NEEDS_REINIT)) {
ice_clear_state(&mif->state, ICE_STATE_DRIVER_INITIALIZED);
iflib_request_reset(sc->mirr_if->subctx);
iflib_admin_intr_deferred(sc->mirr_if->subctx);
}
return;
err_stop_rx:
ice_control_all_rx_queues(&sc->pf_vsi, false);
err_cleanup_tx:
ice_vsi_disable_tx(&sc->pf_vsi);
}
static void
ice_poll_for_media_avail(struct ice_softc *sc)
{
struct ice_hw *hw = &sc->hw;
struct ice_port_info *pi = hw->port_info;
if (ice_is_e830(hw) &&
ice_test_state(&sc->state, ICE_STATE_PHY_FW_INIT_PENDING)) {
if (rd32(hw, GL_MNG_FWSM) & GL_MNG_FWSM_FW_LOADING_M)
ice_clear_state(&sc->state, ICE_STATE_PHY_FW_INIT_PENDING);
else
return;
}
if (ice_test_state(&sc->state, ICE_STATE_NO_MEDIA)) {
pi->phy.get_link_info = true;
ice_get_link_status(pi, &sc->link_up);
if (pi->phy.link_info.link_info & ICE_AQ_MEDIA_AVAILABLE) {
int status;
if (ice_test_state(&sc->state, ICE_STATE_LINK_ACTIVE_ON_DOWN) ||
(if_getflags(sc->ifp) & IFF_UP)) {
ice_apply_saved_phy_cfg(sc, ICE_APPLY_LS_FEC_FC);
status = ice_add_media_types(sc, sc->media);
if (status)
device_printf(sc->dev,
"Error adding device media types: %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
}
ice_clear_state(&sc->state, ICE_STATE_NO_MEDIA);
}
}
}
static void
ice_if_timer(if_ctx_t ctx, uint16_t qid)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
uint64_t prev_link_xoff_rx = sc->stats.cur.link_xoff_rx;
if (qid != 0)
return;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
ice_update_pf_stats(sc);
if (sc->stats.cur.link_xoff_rx != prev_link_xoff_rx)
sc->scctx->isc_pause_frames = 1;
ice_update_vsi_hw_stats(&sc->pf_vsi);
if (sc->mirr_if && sc->mirr_if->if_attached)
ice_update_vsi_hw_stats(sc->mirr_if->vsi);
}
static void
ice_admin_timer(void *arg)
{
struct ice_softc *sc = (struct ice_softc *)arg;
if (ice_driver_is_detaching(sc))
return;
iflib_admin_intr_deferred(sc->ctx);
callout_schedule(&sc->admin_timer, hz/2);
}
static void
ice_transition_recovery_mode(struct ice_softc *sc)
{
struct ice_vsi *vsi = &sc->pf_vsi;
int i;
device_printf(sc->dev, "Firmware recovery mode detected. Limiting functionality. Refer to Intel(R) Ethernet Adapters and Devices User Guide for details on firmware recovery mode.\n");
iflib_link_state_change(sc->ctx, LINK_STATE_DOWN, 0);
ice_request_stack_reinit(sc);
ice_rdma_pf_detach(sc);
ice_clear_bit(ICE_FEATURE_RDMA, sc->feat_cap);
#ifdef PCI_IOV
if (ice_test_and_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en))
ice_iov_detach(sc);
#else
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en);
#endif
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_cap);
ice_vsi_del_txqs_ctx(vsi);
ice_vsi_del_rxqs_ctx(vsi);
for (i = 0; i < sc->num_available_vsi; i++) {
if (sc->all_vsi[i])
ice_release_vsi(sc->all_vsi[i]);
}
sc->num_available_vsi = 0;
if (sc->all_vsi) {
free(sc->all_vsi, M_ICE);
sc->all_vsi = NULL;
}
ice_resmgr_destroy(&sc->dev_imgr);
ice_resmgr_destroy(&sc->tx_qmgr);
ice_resmgr_destroy(&sc->rx_qmgr);
ice_deinit_hw(&sc->hw);
}
static void
ice_transition_safe_mode(struct ice_softc *sc)
{
ice_set_bit(ICE_FEATURE_SAFE_MODE, sc->feat_cap);
ice_set_bit(ICE_FEATURE_SAFE_MODE, sc->feat_en);
ice_rdma_pf_detach(sc);
ice_clear_bit(ICE_FEATURE_RDMA, sc->feat_cap);
#ifdef PCI_IOV
if (ice_test_and_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en))
ice_iov_detach(sc);
#else
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en);
#endif
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_cap);
ice_clear_bit(ICE_FEATURE_RSS, sc->feat_cap);
ice_clear_bit(ICE_FEATURE_RSS, sc->feat_en);
}
static void
ice_if_update_admin_status(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
enum ice_fw_modes fw_mode;
bool reschedule = false;
u16 pending = 0;
ASSERT_CTX_LOCKED(sc);
fw_mode = ice_get_fw_mode(&sc->hw);
if (fw_mode == ICE_FW_MODE_REC) {
if (!ice_testandset_state(&sc->state, ICE_STATE_RECOVERY_MODE)) {
ice_transition_recovery_mode(sc);
}
} else if (fw_mode == ICE_FW_MODE_ROLLBACK) {
if (!ice_testandset_state(&sc->state, ICE_STATE_ROLLBACK_MODE)) {
ice_print_rollback_msg(&sc->hw);
}
}
ice_handle_reset_event(sc);
ice_handle_pf_reset_request(sc);
ice_handle_mdd_event(sc);
if (ice_test_state(&sc->state, ICE_STATE_RESET_FAILED) ||
ice_test_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET) ||
ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE)) {
;
} else if (ice_testandclear_state(&sc->state, ICE_STATE_CONTROLQ_EVENT_PENDING)) {
ice_process_ctrlq(sc, ICE_CTL_Q_ADMIN, &pending);
if (pending > 0)
reschedule = true;
if (ice_is_generic_mac(&sc->hw)) {
ice_process_ctrlq(sc, ICE_CTL_Q_SB, &pending);
if (pending > 0)
reschedule = true;
}
ice_process_ctrlq(sc, ICE_CTL_Q_MAILBOX, &pending);
if (pending > 0)
reschedule = true;
}
ice_poll_for_media_avail(sc);
ice_update_link_status(sc, false);
#ifdef PCI_IOV
if (ice_testandclear_state(&sc->state, ICE_STATE_VFLR_PENDING))
iflib_iov_intr_deferred(ctx);
#endif
if (reschedule) {
ice_set_state(&sc->state, ICE_STATE_CONTROLQ_EVENT_PENDING);
iflib_admin_intr_deferred(ctx);
} else {
ice_enable_intr(&sc->hw, sc->irqvs[0].me);
}
}
static void
ice_prepare_for_reset(struct ice_softc *sc)
{
struct ice_hw *hw = &sc->hw;
if (ice_testandset_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET))
return;
log(LOG_INFO, "%s: preparing to reset device logic\n", if_name(sc->ifp));
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
ice_rdma_notify_reset(sc);
ice_rdma_pf_stop(sc);
ice_resmgr_release_map(&sc->tx_qmgr, sc->pf_vsi.tx_qmap,
sc->pf_vsi.num_tx_queues);
ice_resmgr_release_map(&sc->rx_qmgr, sc->pf_vsi.rx_qmap,
sc->pf_vsi.num_rx_queues);
if (sc->mirr_if) {
ice_resmgr_release_map(&sc->tx_qmgr, sc->mirr_if->vsi->tx_qmap,
sc->mirr_if->num_irq_vectors);
ice_resmgr_release_map(&sc->rx_qmgr, sc->mirr_if->vsi->rx_qmap,
sc->mirr_if->num_irq_vectors);
}
ice_clear_hw_tbls(hw);
if (hw->port_info)
ice_sched_cleanup_all(hw);
ice_shutdown_all_ctrlq(hw, false);
}
static int
ice_rebuild_pf_vsi_qmap(struct ice_softc *sc)
{
struct ice_vsi *vsi = &sc->pf_vsi;
struct ice_tx_queue *txq;
struct ice_rx_queue *rxq;
int err, i;
err = ice_resmgr_assign_contiguous(&sc->tx_qmgr, vsi->tx_qmap,
vsi->num_tx_queues);
if (err) {
device_printf(sc->dev, "Unable to re-assign PF Tx queues: %s\n",
ice_err_str(err));
return (err);
}
err = ice_resmgr_assign_contiguous(&sc->rx_qmgr, vsi->rx_qmap,
vsi->num_rx_queues);
if (err) {
device_printf(sc->dev, "Unable to re-assign PF Rx queues: %s\n",
ice_err_str(err));
goto err_release_tx_queues;
}
vsi->qmap_type = ICE_RESMGR_ALLOC_CONTIGUOUS;
for (i = 0, txq = vsi->tx_queues; i < vsi->num_tx_queues; i++, txq++)
txq->tail = QTX_COMM_DBELL(vsi->tx_qmap[i]);
for (i = 0, rxq = vsi->rx_queues; i < vsi->num_rx_queues; i++, rxq++)
rxq->tail = QRX_TAIL(vsi->rx_qmap[i]);
return (0);
err_release_tx_queues:
ice_resmgr_release_map(&sc->tx_qmgr, sc->pf_vsi.tx_qmap,
sc->pf_vsi.num_tx_queues);
return (err);
}
#define CTX_ACTIVE(ctx) ((if_getdrvflags(iflib_get_ifp(ctx)) & IFF_DRV_RUNNING))
static void
ice_rebuild_recovery_mode(struct ice_softc *sc)
{
device_t dev = sc->dev;
pci_enable_busmaster(dev);
ice_configure_misc_interrupts(sc);
ice_enable_intr(&sc->hw, sc->irqvs[0].me);
ice_clear_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET);
log(LOG_INFO, "%s: device rebuild successful\n", if_name(sc->ifp));
ice_request_stack_reinit(sc);
return;
}
static void
ice_rebuild(struct ice_softc *sc)
{
struct ice_hw *hw = &sc->hw;
device_t dev = sc->dev;
enum ice_ddp_state pkg_state;
int status;
int err;
sc->rebuild_ticks = ticks;
ice_clear_state(&sc->state, ICE_STATE_RESET_FAILED);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE)) {
ice_rebuild_recovery_mode(sc);
return;
}
pci_enable_busmaster(dev);
status = ice_init_all_ctrlq(hw);
if (status) {
device_printf(dev, "failed to re-init controlqs, err %s\n",
ice_status_str(status));
goto err_shutdown_ctrlq;
}
status = ice_sched_query_res_alloc(hw);
if (status) {
device_printf(dev,
"Failed to query scheduler resources, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
goto err_shutdown_ctrlq;
}
status = ICE_SUCCESS;
if (hw->pf_id == 0)
status = ice_fwlog_set(hw, &hw->fwlog_cfg);
if (!status) {
if (hw->fwlog_cfg.options & ICE_FWLOG_OPTION_IS_REGISTERED) {
status = ice_fwlog_register(hw);
if (status)
device_printf(dev, "failed to re-register fw logging, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
}
} else
device_printf(dev, "failed to rebuild fw logging configuration, err %s aq_err %s\n",
ice_status_str(status),
ice_aq_str(hw->adminq.sq_last_status));
err = ice_send_version(sc);
if (err)
goto err_shutdown_ctrlq;
err = ice_init_link_events(sc);
if (err) {
device_printf(dev, "ice_init_link_events failed: %s\n",
ice_err_str(err));
goto err_shutdown_ctrlq;
}
status = ice_clear_pf_cfg(hw);
if (status) {
device_printf(dev, "failed to clear PF configuration, err %s\n",
ice_status_str(status));
goto err_shutdown_ctrlq;
}
ice_clean_all_vsi_rss_cfg(sc);
ice_clear_pxe_mode(hw);
status = ice_get_caps(hw);
if (status) {
device_printf(dev, "failed to get capabilities, err %s\n",
ice_status_str(status));
goto err_shutdown_ctrlq;
}
status = ice_sched_init_port(hw->port_info);
if (status) {
device_printf(dev, "failed to initialize port, err %s\n",
ice_status_str(status));
goto err_sched_cleanup;
}
if (!ice_is_bit_set(sc->feat_en, ICE_FEATURE_SAFE_MODE)) {
pkg_state = ice_init_pkg(hw, hw->pkg_copy, hw->pkg_size);
if (!ice_is_init_pkg_successful(pkg_state)) {
ice_log_pkg_init(sc, pkg_state);
ice_transition_safe_mode(sc);
}
}
ice_reset_pf_stats(sc);
err = ice_rebuild_pf_vsi_qmap(sc);
if (err) {
device_printf(sc->dev, "Unable to re-assign main VSI queues, err %s\n",
ice_err_str(err));
goto err_sched_cleanup;
}
err = ice_initialize_vsi(&sc->pf_vsi);
if (err) {
device_printf(sc->dev, "Unable to re-initialize Main VSI, err %s\n",
ice_err_str(err));
goto err_release_queue_allocations;
}
err = ice_replay_all_vsi_cfg(sc);
if (err)
goto err_deinit_pf_vsi;
ice_init_health_events(sc);
err = ice_config_rss(&sc->pf_vsi);
if (err) {
device_printf(sc->dev,
"Unable to reconfigure RSS for the main VSI, err %s\n",
ice_err_str(err));
goto err_deinit_pf_vsi;
}
if (hw->port_info->qos_cfg.is_sw_lldp)
ice_add_rx_lldp_filter(sc);
ice_clear_state(&sc->state, ICE_STATE_LINK_STATUS_REPORTED);
ice_init_link(sc);
ice_configure_misc_interrupts(sc);
ice_enable_intr(&sc->hw, sc->irqvs[0].me);
ice_clear_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET);
if (sc->mirr_if) {
err = ice_subif_rebuild(sc);
if (err)
goto err_deinit_pf_vsi;
}
log(LOG_INFO, "%s: device rebuild successful\n", if_name(sc->ifp));
if (hw->port_info->qos_cfg.is_sw_lldp)
ice_request_stack_reinit(sc);
else
ice_do_dcb_reconfig(sc, false);
return;
err_deinit_pf_vsi:
ice_deinit_vsi(&sc->pf_vsi);
err_release_queue_allocations:
ice_resmgr_release_map(&sc->tx_qmgr, sc->pf_vsi.tx_qmap,
sc->pf_vsi.num_tx_queues);
ice_resmgr_release_map(&sc->rx_qmgr, sc->pf_vsi.rx_qmap,
sc->pf_vsi.num_rx_queues);
err_sched_cleanup:
ice_sched_cleanup_all(hw);
err_shutdown_ctrlq:
ice_shutdown_all_ctrlq(hw, false);
ice_clear_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET);
ice_set_state(&sc->state, ICE_STATE_RESET_FAILED);
device_printf(dev, "Driver rebuild failed, please reload the device driver\n");
}
static void
ice_handle_reset_event(struct ice_softc *sc)
{
struct ice_hw *hw = &sc->hw;
int status;
device_t dev = sc->dev;
if (!ice_testandclear_state(&sc->state, ICE_STATE_RESET_OICR_RECV))
return;
ice_prepare_for_reset(sc);
IFLIB_CTX_UNLOCK(sc);
#define ICE_EMPR_ADDL_WAIT_MSEC_SLOW 20000
if ((ice_is_e830(hw) || ice_is_e825c(hw)) &&
(((rd32(hw, GLGEN_RSTAT) & GLGEN_RSTAT_RESET_TYPE_M) >>
GLGEN_RSTAT_RESET_TYPE_S) == ICE_RESET_EMPR))
ice_msec_pause(ICE_EMPR_ADDL_WAIT_MSEC_SLOW);
status = ice_check_reset(hw);
IFLIB_CTX_LOCK(sc);
if (status) {
device_printf(dev, "Device never came out of reset, err %s\n",
ice_status_str(status));
ice_set_state(&sc->state, ICE_STATE_RESET_FAILED);
ice_clear_state(&sc->state, ICE_STATE_RESET_PFR_REQ);
ice_clear_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET);
device_printf(dev, "Reset failed; please reload the device driver\n");
return;
}
sc->hw.reset_ongoing = false;
ice_rebuild(sc);
if (ice_testandclear_state(&sc->state, ICE_STATE_RESET_PFR_REQ))
device_printf(dev, "Ignoring PFR request that occurred while a reset was ongoing\n");
}
static void
ice_handle_pf_reset_request(struct ice_softc *sc)
{
struct ice_hw *hw = &sc->hw;
int status;
if (!ice_testandclear_state(&sc->state, ICE_STATE_RESET_PFR_REQ))
return;
ice_prepare_for_reset(sc);
IFLIB_CTX_UNLOCK(sc);
status = ice_reset(hw, ICE_RESET_PFR);
IFLIB_CTX_LOCK(sc);
if (status) {
device_printf(sc->dev, "device PF reset failed, err %s\n",
ice_status_str(status));
ice_set_state(&sc->state, ICE_STATE_RESET_FAILED);
return;
}
sc->soft_stats.pfr_count++;
ice_rebuild(sc);
}
static void
ice_init_device_features(struct ice_softc *sc)
{
struct ice_hw *hw = &sc->hw;
ice_set_bit(ICE_FEATURE_SRIOV, sc->feat_cap);
ice_set_bit(ICE_FEATURE_RSS, sc->feat_cap);
ice_set_bit(ICE_FEATURE_RDMA, sc->feat_cap);
ice_set_bit(ICE_FEATURE_LENIENT_LINK_MODE, sc->feat_cap);
ice_set_bit(ICE_FEATURE_LINK_MGMT_VER_1, sc->feat_cap);
ice_set_bit(ICE_FEATURE_LINK_MGMT_VER_2, sc->feat_cap);
ice_set_bit(ICE_FEATURE_HEALTH_STATUS, sc->feat_cap);
ice_set_bit(ICE_FEATURE_FW_LOGGING, sc->feat_cap);
ice_set_bit(ICE_FEATURE_HAS_PBA, sc->feat_cap);
ice_set_bit(ICE_FEATURE_DCB, sc->feat_cap);
ice_set_bit(ICE_FEATURE_TX_BALANCE, sc->feat_cap);
ice_set_bit(ICE_FEATURE_PHY_STATISTICS, sc->feat_cap);
if (ice_is_e810(hw))
ice_set_bit(ICE_FEATURE_PHY_STATISTICS, sc->feat_en);
if (ice_is_e825c(hw))
ice_set_bit(ICE_FEATURE_DUAL_NAC, sc->feat_cap);
if (!hw->func_caps.common_cap.rss_table_size)
ice_clear_bit(ICE_FEATURE_RSS, sc->feat_cap);
if (!hw->func_caps.common_cap.iwarp || !ice_enable_irdma)
ice_clear_bit(ICE_FEATURE_RDMA, sc->feat_cap);
if (!hw->func_caps.common_cap.dcb)
ice_clear_bit(ICE_FEATURE_DCB, sc->feat_cap);
if (!ice_is_fw_health_report_supported(hw))
ice_clear_bit(ICE_FEATURE_HEALTH_STATUS, sc->feat_cap);
if (!ice_fwlog_supported(hw))
ice_clear_bit(ICE_FEATURE_FW_LOGGING, sc->feat_cap);
if (hw->fwlog_cfg.options & ICE_FWLOG_OPTION_IS_REGISTERED) {
if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_FW_LOGGING))
ice_set_bit(ICE_FEATURE_FW_LOGGING, sc->feat_en);
else
ice_fwlog_unregister(hw);
}
ice_disable_unsupported_features(sc->feat_cap);
if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_RSS))
ice_set_bit(ICE_FEATURE_RSS, sc->feat_en);
if (!ice_tx_balance_en)
ice_clear_bit(ICE_FEATURE_TX_BALANCE, sc->feat_cap);
if (hw->dev_caps.supported_sensors & ICE_SENSOR_SUPPORT_E810_INT_TEMP) {
ice_set_bit(ICE_FEATURE_TEMP_SENSOR, sc->feat_cap);
ice_set_bit(ICE_FEATURE_TEMP_SENSOR, sc->feat_en);
}
if (hw->func_caps.common_cap.next_cluster_id_support ||
hw->dev_caps.common_cap.next_cluster_id_support) {
ice_set_bit(ICE_FEATURE_NEXT_CLUSTER_ID, sc->feat_cap);
ice_set_bit(ICE_FEATURE_NEXT_CLUSTER_ID, sc->feat_en);
}
}
static void
ice_if_multi_set(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
int err;
ASSERT_CTX_LOCKED(sc);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
err = ice_sync_multicast_filters(sc);
if (err) {
device_printf(sc->dev,
"Failed to synchronize multicast filter list: %s\n",
ice_err_str(err));
return;
}
}
static void
ice_if_vlan_register(if_ctx_t ctx, u16 vtag)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
int status;
ASSERT_CTX_LOCKED(sc);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
status = ice_add_vlan_hw_filter(&sc->pf_vsi, vtag);
if (status) {
device_printf(sc->dev,
"Failure adding VLAN %d to main VSI, err %s aq_err %s\n",
vtag, ice_status_str(status),
ice_aq_str(sc->hw.adminq.sq_last_status));
}
}
static void
ice_if_vlan_unregister(if_ctx_t ctx, u16 vtag)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
int status;
ASSERT_CTX_LOCKED(sc);
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
status = ice_remove_vlan_hw_filter(&sc->pf_vsi, vtag);
if (status) {
device_printf(sc->dev,
"Failure removing VLAN %d from main VSI, err %s aq_err %s\n",
vtag, ice_status_str(status),
ice_aq_str(sc->hw.adminq.sq_last_status));
}
}
static void
ice_if_stop(if_ctx_t ctx)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
ASSERT_CTX_LOCKED(sc);
if (!ice_testandclear_state(&sc->state, ICE_STATE_DRIVER_INITIALIZED))
return;
if (ice_test_state(&sc->state, ICE_STATE_RESET_FAILED)) {
device_printf(sc->dev, "request to stop interface cannot be completed as the device failed to reset\n");
return;
}
if (ice_test_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET)) {
device_printf(sc->dev, "request to stop interface while device is prepared for impending reset\n");
return;
}
ice_rdma_pf_stop(sc);
ice_rm_pf_default_mac_filters(sc);
ice_flush_txq_interrupts(&sc->pf_vsi);
ice_flush_rxq_interrupts(&sc->pf_vsi);
ice_vsi_disable_tx(&sc->pf_vsi);
ice_control_all_rx_queues(&sc->pf_vsi, false);
if (!ice_test_state(&sc->state, ICE_STATE_LINK_ACTIVE_ON_DOWN) &&
!(if_getflags(sc->ifp) & IFF_UP) && sc->link_up)
ice_set_link(sc, false);
if (sc->mirr_if && ice_test_state(&mif->state, ICE_STATE_SUBIF_NEEDS_REINIT)) {
ice_subif_if_stop(sc->mirr_if->subctx);
device_printf(sc->dev, "The subinterface also comes down and up after reset\n");
}
}
static uint64_t
ice_if_get_counter(if_ctx_t ctx, ift_counter counter)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
return ice_get_ifnet_counter(&sc->pf_vsi, counter);
}
void
ice_request_stack_reinit(struct ice_softc *sc)
{
if (CTX_ACTIVE(sc->ctx)) {
iflib_request_reset(sc->ctx);
iflib_admin_intr_deferred(sc->ctx);
}
}
bool
ice_driver_is_detaching(struct ice_softc *sc)
{
return (ice_test_state(&sc->state, ICE_STATE_DETACHING) ||
iflib_in_detach(sc->ctx));
}
static int
ice_if_priv_ioctl(if_ctx_t ctx, u_long command, caddr_t data)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
struct ifdrv *ifd;
device_t dev = sc->dev;
if (data == NULL)
return (EINVAL);
ASSERT_CTX_LOCKED(sc);
switch (command) {
case SIOCSDRVSPEC:
case SIOCGDRVSPEC:
break;
case SIOCGPRIVATE_0:
return (ENOTSUP);
default:
device_printf(dev, "%s: unexpected ioctl command %08lx\n",
__func__, command);
return (EINVAL);
}
ifd = (struct ifdrv *)data;
switch (ifd->ifd_cmd) {
case ICE_NVM_ACCESS:
return ice_handle_nvm_access_ioctl(sc, ifd);
case ICE_DEBUG_DUMP:
return ice_handle_debug_dump_ioctl(sc, ifd);
default:
return EINVAL;
}
}
static int
ice_if_i2c_req(if_ctx_t ctx, struct ifi2creq *req)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
return ice_handle_i2c_req(sc, req);
}
static int
ice_if_suspend(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
ice_clear_state(&sc->state, ICE_STATE_RESET_PFR_REQ);
ice_prepare_for_reset(sc);
return (0);
}
static int
ice_if_resume(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
ice_rebuild(sc);
return (0);
}
static bool
ice_if_needs_restart(if_ctx_t ctx, enum iflib_restart_event event)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
switch (event) {
case IFLIB_RESTART_VLAN_CONFIG:
if (!ice_test_state(&sc->state, ICE_STATE_LINK_ACTIVE_ON_DOWN) &&
!(if_getflags(sc->ifp) & IFF_UP))
return false;
default:
return true;
}
}
static void
ice_init_link(struct ice_softc *sc)
{
struct ice_hw *hw = &sc->hw;
device_t dev = sc->dev;
if (ice_is_e830(hw) &&
(rd32(hw, GL_MNG_FWSM) & GL_MNG_FWSM_FW_LOADING_M)) {
ice_set_state(&sc->state, ICE_STATE_PHY_FW_INIT_PENDING);
device_printf(dev,
"Link initialization is blocked by PHY FW initialization.\n");
device_printf(dev,
"Link initialization will continue after PHY FW initialization completes.\n");
} else {
ice_clear_state(&sc->state, ICE_STATE_PHY_FW_INIT_PENDING);
ice_init_link_configuration(sc);
ice_update_link_status(sc, true);
}
}
#ifdef PCI_IOV
static int
ice_if_iov_init(if_ctx_t ctx, uint16_t num_vfs, const nvlist_t *params)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
return ice_iov_init(sc, num_vfs, params);
}
static void
ice_if_iov_uninit(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
ice_iov_uninit(sc);
}
static int
ice_if_iov_vf_add(if_ctx_t ctx, uint16_t vfnum, const nvlist_t *params)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
return ice_iov_add_vf(sc, vfnum, params);
}
static void
ice_if_vflr_handle(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
ice_iov_handle_vflr(sc);
}
#endif
extern struct if_txrx ice_subif_txrx;
static device_method_t ice_subif_methods[] = {
DEVMETHOD(device_register, ice_subif_register),
DEVMETHOD_END
};
static driver_t ice_subif_driver = {
.name = "ice_subif",
.methods = ice_subif_methods,
.size = sizeof(struct ice_mirr_if),
};
static device_method_t ice_iflib_subif_methods[] = {
DEVMETHOD(ifdi_attach_pre, ice_subif_if_attach_pre),
DEVMETHOD(ifdi_attach_post, ice_subif_if_attach_post),
DEVMETHOD(ifdi_tx_queues_alloc, ice_subif_if_tx_queues_alloc),
DEVMETHOD(ifdi_rx_queues_alloc, ice_subif_if_rx_queues_alloc),
DEVMETHOD(ifdi_msix_intr_assign, ice_subif_if_msix_intr_assign),
DEVMETHOD(ifdi_intr_enable, ice_subif_if_intr_enable),
DEVMETHOD(ifdi_rx_queue_intr_enable, ice_subif_if_rx_queue_intr_enable),
DEVMETHOD(ifdi_tx_queue_intr_enable, ice_subif_if_tx_queue_intr_enable),
DEVMETHOD(ifdi_init, ice_subif_if_init),
DEVMETHOD(ifdi_stop, ice_subif_if_stop),
DEVMETHOD(ifdi_queues_free, ice_subif_if_queues_free),
DEVMETHOD(ifdi_media_status, ice_subif_if_media_status),
DEVMETHOD(ifdi_promisc_set, ice_subif_if_promisc_set),
};
static driver_t ice_iflib_subif_driver = {
.name = "ice_subif",
.methods = ice_iflib_subif_methods,
.size = sizeof(struct ice_mirr_if),
};
static struct if_shared_ctx ice_subif_sctx = {
.isc_magic = IFLIB_MAGIC,
.isc_q_align = PAGE_SIZE,
.isc_tx_maxsize = ICE_MAX_FRAME_SIZE,
.isc_tx_maxsegsize = ICE_MAX_FRAME_SIZE,
.isc_tso_maxsize = ICE_TSO_SIZE + sizeof(struct ether_vlan_header),
.isc_tso_maxsegsize = ICE_MAX_DMA_SEG_SIZE,
.isc_rx_maxsize = ICE_MAX_FRAME_SIZE,
.isc_rx_nsegments = ICE_MAX_RX_SEGS,
.isc_rx_maxsegsize = ICE_MAX_FRAME_SIZE,
.isc_nfl = 1,
.isc_ntxqs = 1,
.isc_nrxqs = 1,
.isc_admin_intrcnt = 0,
.isc_vendor_info = ice_vendor_info_array,
.isc_driver_version = __DECONST(char *, ice_driver_version),
.isc_driver = &ice_iflib_subif_driver,
.isc_flags = IFLIB_NEED_SCRATCH | IFLIB_TSO_INIT_IP |
IFLIB_ADMIN_ALWAYS_RUN | IFLIB_SKIP_MSIX |
IFLIB_IS_VF,
.isc_nrxd_min = {ICE_MIN_DESC_COUNT},
.isc_ntxd_min = {ICE_MIN_DESC_COUNT},
.isc_nrxd_max = {ICE_IFLIB_MAX_DESC_COUNT},
.isc_ntxd_max = {ICE_IFLIB_MAX_DESC_COUNT},
.isc_nrxd_default = {ICE_DEFAULT_DESC_COUNT},
.isc_ntxd_default = {ICE_DEFAULT_DESC_COUNT},
};
static void *
ice_subif_register(device_t dev __unused)
{
return (&ice_subif_sctx);
}
static void
ice_subif_setup_scctx(struct ice_mirr_if *mif)
{
if_softc_ctx_t scctx = mif->subscctx;
scctx->isc_txrx = &ice_subif_txrx;
scctx->isc_capenable = ICE_FULL_CAPS;
scctx->isc_tx_csum_flags = ICE_CSUM_OFFLOAD;
scctx->isc_ntxqsets = 4;
scctx->isc_nrxqsets = 4;
scctx->isc_vectors = scctx->isc_nrxqsets;
scctx->isc_ntxqsets_max = 256;
scctx->isc_nrxqsets_max = 256;
scctx->isc_txqsizes[0] = roundup2(scctx->isc_ntxd[0]
* sizeof(struct ice_tx_desc), DBA_ALIGN);
scctx->isc_rxqsizes[0] = roundup2(scctx->isc_nrxd[0]
* sizeof(union ice_32b_rx_flex_desc), DBA_ALIGN);
scctx->isc_tx_nsegments = ICE_MAX_TX_SEGS;
scctx->isc_tx_tso_segments_max = ICE_MAX_TSO_SEGS;
scctx->isc_tx_tso_size_max = ICE_TSO_SIZE;
scctx->isc_tx_tso_segsize_max = ICE_MAX_DMA_SEG_SIZE;
}
static int
ice_subif_if_attach_pre(if_ctx_t ctx)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
device_t dev = iflib_get_dev(ctx);
mif->subctx = ctx;
mif->subdev = dev;
mif->subscctx = iflib_get_softc_ctx(ctx);
ice_subif_setup_scctx(mif);
return (0);
}
static int
ice_subif_if_attach_post(if_ctx_t ctx __unused)
{
return (0);
}
void
ice_destroy_mirror_interface(struct ice_softc *sc)
{
struct ice_mirr_if *mif = sc->mirr_if;
struct ice_vsi *vsi = mif->vsi;
bool is_locked = false;
int ret;
is_locked = sx_xlocked(sc->iflib_ctx_lock);
if (is_locked)
IFLIB_CTX_UNLOCK(sc);
if (mif->ifp) {
ret = iflib_device_deregister(mif->subctx);
if (ret) {
device_printf(sc->dev,
"iflib_device_deregister for mirror interface failed: %d\n",
ret);
}
}
bus_topo_lock();
ret = device_delete_child(sc->dev, mif->subdev);
bus_topo_unlock();
if (ret) {
device_printf(sc->dev,
"device_delete_child for mirror interface failed: %d\n",
ret);
}
if (is_locked)
IFLIB_CTX_LOCK(sc);
if (mif->if_imap) {
free(mif->if_imap, M_ICE);
mif->if_imap = NULL;
}
if (mif->os_imap) {
free(mif->os_imap, M_ICE);
mif->os_imap = NULL;
}
ice_release_vsi(vsi);
free(mif, M_ICE);
sc->mirr_if = NULL;
}
static int
ice_setup_mirror_vsi(struct ice_mirr_if *mif)
{
struct ice_softc *sc = mif->back;
device_t dev = sc->dev;
struct ice_vsi *vsi;
int ret = 0;
vsi = ice_alloc_vsi(sc, ICE_VSI_VMDQ2);
if (!vsi) {
return (ENOMEM);
}
mif->vsi = vsi;
ice_alloc_vsi_qmap(vsi, ICE_DEFAULT_VF_QUEUES, ICE_DEFAULT_VF_QUEUES);
vsi->num_tx_queues = vsi->num_rx_queues = ICE_DEFAULT_VF_QUEUES;
ret = ice_resmgr_assign_scattered(&sc->tx_qmgr, vsi->tx_qmap,
vsi->num_tx_queues);
if (ret) {
device_printf(dev, "Unable to assign mirror VSI Tx queues: %s\n",
ice_err_str(ret));
goto release_vsi;
}
ret = ice_resmgr_assign_scattered(&sc->rx_qmgr, vsi->rx_qmap,
vsi->num_rx_queues);
if (ret) {
device_printf(dev, "Unable to assign mirror VSI Rx queues: %s\n",
ice_err_str(ret));
goto release_vsi;
}
vsi->qmap_type = ICE_RESMGR_ALLOC_SCATTERED;
vsi->max_frame_size = ICE_MAX_FRAME_SIZE;
ret = ice_initialize_vsi(vsi);
if (ret) {
device_printf(dev, "%s: Error in ice_initialize_vsi for mirror VSI: %s\n",
__func__, ice_err_str(ret));
goto release_vsi;
}
ret = ice_config_rss(vsi);
if (ret) {
device_printf(dev,
"Unable to configure RSS for mirror VSI: %s\n",
ice_err_str(ret));
goto release_vsi;
}
vsi->mirror_src_vsi = sc->pf_vsi.idx;
ice_debug(&sc->hw, ICE_DBG_INIT,
"Configuring mirroring from VSI %d to %d\n",
vsi->mirror_src_vsi, vsi->idx);
ice_debug(&sc->hw, ICE_DBG_INIT, "(HW num: VSI %d to %d)\n",
ice_get_hw_vsi_num(&sc->hw, vsi->mirror_src_vsi),
ice_get_hw_vsi_num(&sc->hw, vsi->idx));
ret = ice_setup_vsi_mirroring(vsi);
if (ret) {
device_printf(dev,
"Unable to configure mirroring for VSI: %s\n",
ice_err_str(ret));
goto release_vsi;
}
return (0);
release_vsi:
ice_release_vsi(vsi);
mif->vsi = NULL;
return (ret);
}
int
ice_create_mirror_interface(struct ice_softc *sc)
{
device_t dev = sc->dev;
struct ice_mirr_if *mif;
struct ifmedia *media;
struct sbuf *sb;
int ret = 0;
mif = (struct ice_mirr_if *)malloc(sizeof(*mif), M_ICE, M_ZERO | M_NOWAIT);
if (!mif) {
device_printf(dev, "malloc() error allocating mirror interface\n");
return (ENOMEM);
}
sc->mirr_if = mif;
mif->back = sc;
ret = ice_setup_mirror_vsi(mif);
if (ret)
goto out;
sb = sbuf_new_auto();
MPASS(sb != NULL);
sbuf_printf(sb, "%sm", device_get_nameunit(dev));
sbuf_finish(sb);
bus_topo_lock();
mif->subdev = device_add_child(dev, sbuf_data(sb), 0);
bus_topo_unlock();
if (!mif->subdev) {
device_printf(dev, "device_add_child failed for %s0\n", sbuf_data(sb));
sbuf_delete(sb);
free(mif, M_ICE);
sc->mirr_if = NULL;
return (ENOMEM);
}
sbuf_delete(sb);
device_set_driver(mif->subdev, &ice_subif_driver);
ret = iflib_device_register(mif->subdev, mif, &ice_subif_sctx, &mif->subctx);
if (ret)
goto out;
mif->ifp = iflib_get_ifp(mif->subctx);
if_setflagbits(mif->ifp, IFF_MONITOR, 0);
media = iflib_get_media(mif->subctx);
ifmedia_add(media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(media, IFM_ETHER | IFM_AUTO);
device_printf(dev, "Created dev %s and ifnet %s for mirroring\n",
device_get_nameunit(mif->subdev), if_name(mif->ifp));
ice_add_vsi_sysctls(mif->vsi);
ret = ice_wire_mirror_intrs(mif);
if (ret)
goto out;
mif->if_attached = true;
return (0);
out:
ice_destroy_mirror_interface(sc);
return (ret);
}
static int
ice_wire_mirror_intrs(struct ice_mirr_if *mif)
{
struct ice_softc *sc = mif->back;
struct ice_hw *hw = &sc->hw;
struct ice_vsi *vsi = mif->vsi;
device_t dev = mif->subdev;
int err, i, rid;
if_ctx_t ctx = mif->subctx;
ice_debug(hw, ICE_DBG_INIT, "%s: Last rid: %d\n", __func__, sc->last_rid);
rid = sc->last_rid + 1;
for (i = 0; i < vsi->num_rx_queues; i++, rid++) {
struct ice_rx_queue *rxq = &vsi->rx_queues[i];
struct ice_tx_queue *txq = &vsi->tx_queues[i];
char irq_name[16];
snprintf(irq_name, sizeof(irq_name), "m0rxq%d", i);
err = iflib_irq_alloc_generic_subctx(sc->ctx, ctx,
&mif->rx_irqvs[i].irq, rid, IFLIB_INTR_RXTX, ice_msix_que,
rxq, rxq->me, irq_name);
if (err) {
device_printf(dev,
"Failed to allocate q int %d err: %s\n",
i, ice_err_str(err));
i--;
goto fail;
}
MPASS(rid - 1 > 0);
mif->rx_irqvs[i].me = rid - 1;
rxq->irqv = &mif->rx_irqvs[i];
bzero(irq_name, sizeof(irq_name));
snprintf(irq_name, sizeof(irq_name), "m0txq%d", i);
iflib_softirq_alloc_generic(ctx, &mif->rx_irqvs[i].irq,
IFLIB_INTR_TX, txq, txq->me, irq_name);
txq->irqv = &mif->rx_irqvs[i];
}
sc->last_rid = rid - 1;
ice_debug(hw, ICE_DBG_INIT, "%s: New last rid: %d\n", __func__,
sc->last_rid);
return (0);
fail:
for (; i >= 0; i--)
iflib_irq_free(ctx, &mif->rx_irqvs[i].irq);
return (err);
}
static int
ice_subif_rebuild(struct ice_softc *sc)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(sc->ctx);
struct ice_vsi *vsi = sc->mirr_if->vsi;
int err;
err = ice_subif_rebuild_vsi_qmap(sc);
if (err) {
device_printf(sc->dev, "Unable to re-assign mirror VSI queues, err %s\n",
ice_err_str(err));
return (err);
}
err = ice_initialize_vsi(vsi);
if (err) {
device_printf(sc->dev, "Unable to re-initialize mirror VSI, err %s\n",
ice_err_str(err));
goto err_release_queue_allocations_subif;
}
err = ice_config_rss(vsi);
if (err) {
device_printf(sc->dev,
"Unable to reconfigure RSS for the mirror VSI, err %s\n",
ice_err_str(err));
goto err_deinit_subif_vsi;
}
vsi->mirror_src_vsi = sc->pf_vsi.idx;
err = ice_setup_vsi_mirroring(vsi);
if (err) {
device_printf(sc->dev,
"Unable to configure mirroring for VSI: %s\n",
ice_err_str(err));
goto err_deinit_subif_vsi;
}
ice_set_state(&mif->state, ICE_STATE_SUBIF_NEEDS_REINIT);
return (0);
err_deinit_subif_vsi:
ice_deinit_vsi(vsi);
err_release_queue_allocations_subif:
ice_resmgr_release_map(&sc->tx_qmgr, vsi->tx_qmap,
sc->mirr_if->num_irq_vectors);
ice_resmgr_release_map(&sc->rx_qmgr, vsi->rx_qmap,
sc->mirr_if->num_irq_vectors);
return (err);
}
static int
ice_subif_rebuild_vsi_qmap(struct ice_softc *sc)
{
struct ice_vsi *vsi = sc->mirr_if->vsi;
struct ice_tx_queue *txq;
struct ice_rx_queue *rxq;
int err, i;
err = ice_resmgr_assign_scattered(&sc->tx_qmgr, vsi->tx_qmap, sc->mirr_if->num_irq_vectors);
if (err) {
device_printf(sc->dev, "Unable to assign mirror VSI Tx queues: %s\n",
ice_err_str(err));
return (err);
}
err = ice_resmgr_assign_scattered(&sc->rx_qmgr, vsi->rx_qmap, sc->mirr_if->num_irq_vectors);
if (err) {
device_printf(sc->dev, "Unable to assign mirror VSI Rx queues: %s\n",
ice_err_str(err));
goto err_release_tx_queues;
}
vsi->qmap_type = ICE_RESMGR_ALLOC_SCATTERED;
for (i = 0, txq = vsi->tx_queues; i < vsi->num_tx_queues; i++, txq++)
txq->tail = QTX_COMM_DBELL(vsi->tx_qmap[i]);
for (i = 0, rxq = vsi->rx_queues; i < vsi->num_rx_queues; i++, rxq++)
rxq->tail = QRX_TAIL(vsi->rx_qmap[i]);
return (0);
err_release_tx_queues:
ice_resmgr_release_map(&sc->tx_qmgr, vsi->tx_qmap, vsi->num_tx_queues);
return (err);
}
static int
ice_subif_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs,
int __invariant_only ntxqs, int ntxqsets)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_tx_queue *txq;
device_t dev = mif->subdev;
struct ice_vsi *vsi;
int err, i, j;
MPASS(mif != NULL);
MPASS(ntxqs == 1);
MPASS(mif->subscctx->isc_ntxd[0] <= ICE_MAX_DESC_COUNT);
vsi = mif->vsi;
MPASS(vsi->num_tx_queues == ntxqsets);
if (!(vsi->tx_queues =
(struct ice_tx_queue *)malloc(sizeof(struct ice_tx_queue) * ntxqsets, M_ICE, M_NOWAIT | M_ZERO))) {
device_printf(dev, "%s: Unable to allocate Tx queue memory for subfunction\n",
__func__);
return (ENOMEM);
}
for (i = 0, txq = vsi->tx_queues; i < ntxqsets; i++, txq++) {
if (!(txq->tx_rsq =
(uint16_t *)malloc(sizeof(uint16_t) * mif->subscctx->isc_ntxd[0], M_ICE, M_NOWAIT))) {
device_printf(dev,
"%s: Unable to allocate tx_rsq memory for subfunction\n", __func__);
err = ENOMEM;
goto free_tx_queues;
}
for (j = 0; j < mif->subscctx->isc_ntxd[0]; j++)
txq->tx_rsq[j] = QIDX_INVALID;
}
ice_vsi_add_txqs_ctx(vsi);
for (i = 0, txq = vsi->tx_queues; i < ntxqsets; i++, txq++) {
txq->me = txq->q_handle = i;
txq->vsi = vsi;
txq->desc_count = mif->subscctx->isc_ntxd[0];
txq->tail = QTX_COMM_DBELL(vsi->tx_qmap[i]);
txq->tx_base = (struct ice_tx_desc *)vaddrs[i];
txq->tx_paddr = paddrs[i];
ice_add_txq_sysctls(txq);
}
return (0);
free_tx_queues:
for (i = 0, txq = vsi->tx_queues; i < ntxqsets; i++, txq++) {
if (txq->tx_rsq != NULL) {
free(txq->tx_rsq, M_ICE);
txq->tx_rsq = NULL;
}
}
free(vsi->tx_queues, M_ICE);
vsi->tx_queues = NULL;
return (err);
}
static int
ice_subif_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs,
int __invariant_only nrxqs, int nrxqsets)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_rx_queue *rxq;
device_t dev = mif->subdev;
struct ice_vsi *vsi;
int i;
MPASS(mif != NULL);
MPASS(nrxqs == 1);
MPASS(mif->subscctx->isc_nrxd[0] <= ICE_MAX_DESC_COUNT);
vsi = mif->vsi;
MPASS(vsi->num_rx_queues == nrxqsets);
if (!(vsi->rx_queues =
(struct ice_rx_queue *) malloc(sizeof(struct ice_rx_queue) * nrxqsets, M_ICE, M_NOWAIT | M_ZERO))) {
device_printf(dev, "%s: Unable to allocate Rx queue memory for subfunction\n",
__func__);
return (ENOMEM);
}
ice_vsi_add_rxqs_ctx(vsi);
for (i = 0, rxq = vsi->rx_queues; i < nrxqsets; i++, rxq++) {
rxq->me = i;
rxq->vsi = vsi;
rxq->desc_count = mif->subscctx->isc_nrxd[0];
rxq->tail = QRX_TAIL(vsi->rx_qmap[i]);
rxq->rx_base = (union ice_32b_rx_flex_desc *)vaddrs[i];
rxq->rx_paddr = paddrs[i];
ice_add_rxq_sysctls(rxq);
}
return (0);
}
static int
ice_subif_if_msix_intr_assign(if_ctx_t ctx, int msix)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = mif->back;
struct ice_vsi *vsi = mif->vsi;
device_t dev = mif->subdev;
int ret;
if (vsi->num_rx_queues != vsi->num_tx_queues) {
device_printf(dev,
"iflib requested %d Tx queues, and %d Rx queues, but the driver isn't able to support a differing number of Tx and Rx queues\n",
vsi->num_tx_queues, vsi->num_rx_queues);
return (EOPNOTSUPP);
}
if (msix > sc->extra_vectors) {
device_printf(dev,
"%s: Not enough spare (%d) msix vectors for new sub-interface requested (%d)\n",
__func__, sc->extra_vectors, msix);
return (ENOSPC);
}
device_printf(dev, "%s: Using %d vectors for sub-interface\n", __func__,
msix);
mif->num_irq_vectors = vsi->num_rx_queues;
mif->rx_irqvs = (struct ice_irq_vector *)
malloc(sizeof(struct ice_irq_vector) * (mif->num_irq_vectors),
M_ICE, M_NOWAIT);
if (!mif->rx_irqvs) {
device_printf(dev,
"Unable to allocate RX irqv memory for mirror's %d vectors\n",
mif->num_irq_vectors);
return (ENOMEM);
}
if (!(mif->if_imap =
(u16 *)malloc(sizeof(u16) * mif->num_irq_vectors,
M_ICE, M_NOWAIT))) {
device_printf(dev, "Unable to allocate mirror intfc if_imap memory\n");
ret = ENOMEM;
goto free_irqvs;
}
ret = ice_resmgr_assign_contiguous(&sc->dev_imgr, mif->if_imap, mif->num_irq_vectors);
if (ret) {
device_printf(dev, "Unable to assign mirror intfc PF device interrupt mapping: %s\n",
ice_err_str(ret));
goto free_if_imap;
}
if (!(mif->os_imap =
(u16 *)malloc(sizeof(u16) * mif->num_irq_vectors,
M_ICE, M_NOWAIT))) {
device_printf(dev, "Unable to allocate mirror intfc os_imap memory\n");
ret = ENOMEM;
goto free_if_imap;
}
ret = ice_resmgr_assign_contiguous(&sc->os_imgr, mif->os_imap, mif->num_irq_vectors);
if (ret) {
device_printf(dev, "Unable to assign mirror intfc OS interrupt mapping: %s\n",
ice_err_str(ret));
goto free_if_imap;
}
return (0);
free_if_imap:
free(mif->if_imap, M_ICE);
mif->if_imap = NULL;
free_irqvs:
free(mif->rx_irqvs, M_ICE);
mif->rx_irqvs = NULL;
return (ret);
}
static void
ice_subif_if_intr_enable(if_ctx_t ctx)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = mif->back;
struct ice_vsi *vsi = mif->vsi;
struct ice_hw *hw = &sc->hw;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
for (int i = 0; i < vsi->num_rx_queues; i++)
ice_enable_intr(hw, vsi->rx_queues[i].irqv->me);
}
static int
ice_subif_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = mif->back;
struct ice_vsi *vsi = mif->vsi;
struct ice_hw *hw = &sc->hw;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (ENOSYS);
ice_enable_intr(hw, vsi->rx_queues[rxqid].irqv->me);
return (0);
}
static int
ice_subif_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = mif->back;
struct ice_vsi *vsi = mif->vsi;
struct ice_hw *hw = &sc->hw;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return (ENOSYS);
ice_enable_intr(hw, vsi->tx_queues[txqid].irqv->me);
return (0);
}
static void
ice_subif_if_init(if_ctx_t ctx)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = mif->back;
struct ice_vsi *vsi = mif->vsi;
device_t dev = mif->subdev;
int err;
if (ice_driver_is_detaching(sc))
return;
if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE))
return;
if (ice_test_state(&sc->state, ICE_STATE_RESET_FAILED)) {
device_printf(dev,
"request to start interface cannot be completed as the parent device %s failed to reset\n",
device_get_nameunit(sc->dev));
return;
}
if (ice_test_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET)) {
device_printf(dev,
"request to start interface cannot be completed while parent device %s is prepared for impending reset\n",
device_get_nameunit(sc->dev));
return;
}
vsi->mbuf_sz = iflib_get_rx_mbuf_sz(ctx);
ice_init_tx_tracking(vsi);
err = ice_cfg_vsi_for_tx(vsi);
if (err) {
device_printf(dev,
"Unable to configure subif VSI for Tx: %s\n",
ice_err_str(err));
return;
}
err = ice_cfg_vsi_for_rx(vsi);
if (err) {
device_printf(dev,
"Unable to configure subif VSI for Rx: %s\n",
ice_err_str(err));
goto err_cleanup_tx;
}
err = ice_control_all_rx_queues(vsi, true);
if (err) {
device_printf(dev,
"Unable to enable subif Rx rings for receive: %s\n",
ice_err_str(err));
goto err_cleanup_tx;
}
ice_configure_all_rxq_interrupts(vsi);
ice_configure_rx_itr(vsi);
ice_set_state(&mif->state, ICE_STATE_DRIVER_INITIALIZED);
return;
err_cleanup_tx:
ice_vsi_disable_tx(vsi);
}
static void
ice_subif_if_stop(if_ctx_t ctx)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_softc *sc = mif->back;
struct ice_vsi *vsi = mif->vsi;
device_t dev = mif->subdev;
if (!ice_testandclear_state(&mif->state, ICE_STATE_DRIVER_INITIALIZED))
return;
if (ice_test_state(&sc->state, ICE_STATE_RESET_FAILED)) {
device_printf(dev,
"request to stop interface cannot be completed as the parent device %s failed to reset\n",
device_get_nameunit(sc->dev));
return;
}
if (ice_test_state(&sc->state, ICE_STATE_PREPARED_FOR_RESET)) {
device_printf(dev,
"request to stop interface cannot be completed while parent device %s is prepared for impending reset\n",
device_get_nameunit(sc->dev));
return;
}
ice_flush_txq_interrupts(vsi);
ice_flush_rxq_interrupts(vsi);
ice_vsi_disable_tx(vsi);
ice_control_all_rx_queues(vsi, false);
}
static void
ice_free_irqvs_subif(struct ice_mirr_if *mif)
{
struct ice_softc *sc = mif->back;
struct ice_vsi *vsi = mif->vsi;
if_ctx_t ctx = sc->ctx;
int i;
if (mif->rx_irqvs == NULL)
return;
for (i = 0; i < vsi->num_rx_queues; i++)
iflib_irq_free(ctx, &mif->rx_irqvs[i].irq);
ice_resmgr_release_map(&sc->os_imgr, mif->os_imap,
mif->num_irq_vectors);
ice_resmgr_release_map(&sc->dev_imgr, mif->if_imap,
mif->num_irq_vectors);
sc->last_rid -= vsi->num_rx_queues;
for (i = 0; i < vsi->num_rx_queues; i++)
vsi->rx_queues[i].irqv = NULL;
for (i = 0; i < vsi->num_tx_queues; i++)
vsi->tx_queues[i].irqv = NULL;
free(mif->rx_irqvs, M_ICE);
mif->rx_irqvs = NULL;
}
static void
ice_subif_if_queues_free(if_ctx_t ctx)
{
struct ice_mirr_if *mif = (struct ice_mirr_if *)iflib_get_softc(ctx);
struct ice_vsi *vsi = mif->vsi;
struct ice_tx_queue *txq;
int i;
ice_vsi_del_txqs_ctx(vsi);
ice_vsi_del_rxqs_ctx(vsi);
ice_free_irqvs_subif(mif);
if (vsi->tx_queues != NULL) {
for (i = 0, txq = vsi->tx_queues; i < vsi->num_tx_queues; i++, txq++) {
if (txq->tx_rsq != NULL) {
free(txq->tx_rsq, M_ICE);
txq->tx_rsq = NULL;
}
}
free(vsi->tx_queues, M_ICE);
vsi->tx_queues = NULL;
}
if (vsi->rx_queues != NULL) {
free(vsi->rx_queues, M_ICE);
vsi->rx_queues = NULL;
}
}
static void
ice_subif_if_media_status(if_ctx_t ctx __unused, struct ifmediareq *ifmr)
{
ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
ifmr->ifm_active = IFM_ETHER | IFM_AUTO;
}
static int
ice_subif_if_promisc_set(if_ctx_t ctx __unused, int flags __unused)
{
return (0);
}