diff options
Diffstat (limited to 'sys/dev/igc/if_igc.c')
| -rw-r--r-- | sys/dev/igc/if_igc.c | 561 |
1 files changed, 375 insertions, 186 deletions
diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c index a4e5de2ae82a..a06caaaceeab 100644 --- a/sys/dev/igc/if_igc.c +++ b/sys/dev/igc/if_igc.c @@ -108,12 +108,14 @@ static void igc_if_media_status(if_ctx_t, struct ifmediareq *); static int igc_if_media_change(if_ctx_t); static int igc_if_mtu_set(if_ctx_t, uint32_t); static void igc_if_timer(if_ctx_t, uint16_t); -static void igc_if_watchdog_reset(if_ctx_t); +static void igc_if_vlan_register(if_ctx_t, u16); +static void igc_if_vlan_unregister(if_ctx_t, u16); static bool igc_if_needs_restart(if_ctx_t, enum iflib_restart_event); static void igc_identify_hardware(if_ctx_t); static int igc_allocate_pci_resources(if_ctx_t); static void igc_free_pci_resources(if_ctx_t); +static void igc_disable_broken_l1_2(if_ctx_t); static void igc_reset(if_ctx_t); static int igc_setup_interface(if_ctx_t); static int igc_setup_msix(if_ctx_t); @@ -127,10 +129,15 @@ static int igc_if_rx_queue_intr_enable(if_ctx_t, uint16_t); static int igc_if_tx_queue_intr_enable(if_ctx_t, uint16_t); static void igc_if_multi_set(if_ctx_t); static void igc_if_update_admin_status(if_ctx_t); +static void igc_apply_i225_ipg_workaround(struct igc_softc *); static void igc_if_debug(if_ctx_t); static void igc_update_stats_counters(struct igc_softc *); static void igc_add_hw_stats(struct igc_softc *); static int igc_if_set_promisc(if_ctx_t, int); +static bool igc_if_vlan_filter_capable(if_ctx_t); +static bool igc_if_vlan_filter_used(if_ctx_t); +static void igc_if_vlan_filter_enable(struct igc_softc *); +static void igc_if_vlan_filter_disable(struct igc_softc *); static void igc_setup_vlan_hw_support(if_ctx_t); static void igc_fw_version(struct igc_softc *); static void igc_sbuf_fw_version(struct igc_fw_version *, struct sbuf *); @@ -143,7 +150,7 @@ static int igc_get_rs(SYSCTL_HANDLER_ARGS); static void igc_print_debug_info(struct igc_softc *); static int igc_is_valid_ether_addr(u8 *); static void igc_neweitr(struct igc_softc *, struct igc_rx_queue *, - struct tx_ring *, struct rx_ring *); + struct rx_ring *); static int igc_sysctl_tso_tcp_flags_mask(SYSCTL_HANDLER_ARGS); /* Management and WOL Support */ static void igc_get_hw_control(struct igc_softc *); @@ -165,6 +172,7 @@ static int igc_sysctl_eee(SYSCTL_HANDLER_ARGS); static int igc_get_regs(SYSCTL_HANDLER_ARGS); static void igc_configure_queues(struct igc_softc *); +static void igc_initialize_interrupt_rate(struct igc_softc *); /********************************************************************* @@ -216,7 +224,8 @@ static device_method_t igc_if_methods[] = { DEVMETHOD(ifdi_mtu_set, igc_if_mtu_set), DEVMETHOD(ifdi_promisc_set, igc_if_set_promisc), DEVMETHOD(ifdi_timer, igc_if_timer), - DEVMETHOD(ifdi_watchdog_reset, igc_if_watchdog_reset), + DEVMETHOD(ifdi_vlan_register, igc_if_vlan_register), + DEVMETHOD(ifdi_vlan_unregister, igc_if_vlan_unregister), DEVMETHOD(ifdi_get_counter, igc_if_get_counter), DEVMETHOD(ifdi_rx_queue_intr_enable, igc_if_rx_queue_intr_enable), DEVMETHOD(ifdi_tx_queue_intr_enable, igc_if_tx_queue_intr_enable), @@ -444,8 +453,9 @@ igc_set_num_queues(if_ctx_t ctx) #define IGC_CAPS \ IFCAP_HWCSUM | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | \ - IFCAP_VLAN_HWCSUM | IFCAP_WOL | IFCAP_TSO4 | IFCAP_LRO | \ - IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | IFCAP_HWCSUM_IPV6 | IFCAP_TSO6 + IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWFILTER | IFCAP_WOL | IFCAP_TSO4 | \ + IFCAP_LRO | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | \ + IFCAP_HWCSUM_IPV6 | IFCAP_TSO6 /********************************************************************* * Device initialization routine @@ -469,6 +479,13 @@ igc_if_attach_pre(if_ctx_t ctx) dev = iflib_get_dev(ctx); sc = iflib_get_softc(ctx); + if (igc_max_interrupt_rate <= 0) { + device_printf(dev, + "Invalid max_interrupt_rate %d; using default %d\n", + igc_max_interrupt_rate, IGC_INTS_DEFAULT); + igc_max_interrupt_rate = IGC_INTS_DEFAULT; + } + sc->ctx = sc->osdep.ctx = ctx; sc->dev = sc->osdep.dev = dev; scctx = sc->shared = iflib_get_softc_ctx(ctx); @@ -546,6 +563,9 @@ igc_if_attach_pre(if_ctx_t ctx) /* Determine hardware and mac info */ igc_identify_hardware(ctx); + /* Apply device-specific PCIe L1.2 errata workarounds. */ + igc_disable_broken_l1_2(ctx); + scctx->isc_tx_nsegments = IGC_MAX_SCATTER; scctx->isc_nrxqsets_max = scctx->isc_ntxqsets_max = igc_set_num_queues(ctx); @@ -788,7 +808,11 @@ igc_if_suspend(if_ctx_t ctx) static int igc_if_resume(if_ctx_t ctx) { - igc_if_init(ctx); + /* + * PCIe config space, and with it L1.2, may have been reset + * across the suspend/resume cycle. + */ + igc_disable_broken_l1_2(ctx); return(0); } @@ -880,6 +904,7 @@ igc_if_init(if_ctx_t ctx) if (sc->intr_type == IFLIB_INTR_MSIX) /* Set up queue routing */ igc_configure_queues(sc); + igc_initialize_interrupt_rate(sc); /* this clears any pending interrupts */ IGC_READ_REG(&sc->hw, IGC_ICR); @@ -892,12 +917,101 @@ igc_if_init(if_ctx_t ctx) igc_set_eee_i225(&sc->hw, true, true, true); } -enum eitr_latency_target { - eitr_latency_disabled = 0, - eitr_latency_lowest = 1, - eitr_latency_low = 2, - eitr_latency_bulk = 3 -}; +/* + * RX publishes its byte and packet counters as one snapshot when iflib + * returns descriptors to hardware. This also covers watchdog-driven RX + * processing, which can run while the interrupt vector is unmasked. + */ +static __inline void +igc_aim_rx_delta(struct rx_ring *rxr, u32 *bytes, u32 *packets) +{ + uint64_t snapshot; + u32 now_bytes, now_packets; + + snapshot = atomic_load_acq_64(&rxr->rx_aim_snapshot); + now_bytes = snapshot >> 32; + now_packets = (u32)snapshot; + *bytes = now_bytes - rxr->rx_bytes_last; + *packets = now_packets - rxr->rx_packets_last; + rxr->rx_bytes_last = now_bytes; + rxr->rx_packets_last = now_packets; +} + +/* + * TX publishes its byte and packet counters as one snapshot at the doorbell, + * because encapsulation can overlap the interrupt filter. The two halves + * remain independent free running u32 counters, so their deltas are correct + * across wrap. + */ +static __inline void +igc_aim_tx_delta(struct tx_ring *txr, u32 *bytes, u32 *packets) +{ + uint64_t snapshot; + u32 now_bytes, now_packets; + + snapshot = atomic_load_acq_64(&txr->tx_aim_snapshot); + now_bytes = snapshot >> 32; + now_packets = (u32)snapshot; + *bytes = now_bytes - txr->tx_bytes_last; + *packets = now_packets - txr->tx_packets_last; + txr->tx_bytes_last = now_bytes; + txr->tx_packets_last = now_packets; +} + +/********************************************************************* + * + * Do Adaptive Interrupt Moderation: + * - Calculate based on average size over the last interval + * + * Returns interrupts per second rather than a register value, so that the + * caller's IGC_INTS_TO_EITR() conversion applies, or zero if the interval + * carried no packet to measure. + * + *********************************************************************/ +static u32 +igc_ring_itr(struct igc_softc *sc, u32 rxbytes, u32 rxpackets, u32 txbytes, + u32 txpackets) +{ + u32 newitr = 0; + + if (txbytes && txpackets) + newitr = txbytes / txpackets; + if (rxbytes && rxpackets) + newitr = max(newitr, rxbytes / rxpackets); + + /* + * No packet was observed, so there is no size to work from. Report no + * observation and let the caller keep the rate it already has. + */ + if (newitr == 0) + return (0); + + newitr += 24; /* account for hardware frame, crc */ + /* set an upper boundary */ + newitr = min(newitr, 3000); + /* Be nice to the mid range */ + if ((newitr > 300) && (newitr < 1200)) + newitr = (newitr / 3); + else + newitr = (newitr / 2); + + /* The value above was written straight to EITR; make it a rate */ + newitr = IGC_AIM_DIVIDEND / newitr; + + /* + * Cap the rate: enable_aim=1 is the normal setting, enable_aim=2 opts + * into the low latency end. The original was unbounded and would ask + * for ~95k ints/s on minimum sized frames. There is deliberately no + * floor, so jumbo traffic settles near 2.7k ints/s. + */ + if (sc->enable_aim == 1) + newitr = min(newitr, IGC_INTS_20K); + else + newitr = min(newitr, IGC_INTS_70K); + + return (newitr); +} + /********************************************************************* * * Helper to calculate next EITR value for AIM @@ -905,127 +1019,51 @@ enum eitr_latency_target { *********************************************************************/ static void igc_neweitr(struct igc_softc *sc, struct igc_rx_queue *que, - struct tx_ring *txr, struct rx_ring *rxr) + struct rx_ring *rxr) { struct igc_hw *hw = &sc->hw; - unsigned long bytes, bytes_per_packet, packets; - unsigned long rxbytes, rxpackets, txbytes, txpackets; + struct igc_tx_queue *tx_que; + u32 ringbytes, ringpackets, rxbytes, rxpackets, txbytes, txpackets; u32 neweitr; - u8 nextlatency; + int i; - rxbytes = atomic_load_long(&rxr->rx_bytes); - txbytes = atomic_load_long(&txr->tx_bytes); + igc_aim_rx_delta(rxr, &rxbytes, &rxpackets); + + /* + * A vector can service more than one TX ring when iflib is configured + * with unequal RX and TX queue counts. Sample every ring routed to + * this vector rather than treating the vector as a TX queue index. + */ + txbytes = txpackets = 0; + for (i = 0; i < sc->tx_num_queues; i++) { + tx_que = &sc->tx_queues[i]; + if (tx_que->msix != que->msix) + continue; + igc_aim_tx_delta(&tx_que->txr, &ringbytes, &ringpackets); + txbytes += ringbytes; + txpackets += ringpackets; + } /* Idle, do nothing */ if (txbytes == 0 && rxbytes == 0) return; - neweitr = 0; - - if (sc->enable_aim) { - nextlatency = rxr->rx_nextlatency; - + if (sc->enable_aim == 0) { + neweitr = igc_max_interrupt_rate; + } else if (sc->link_speed < SPEED_1000) { /* Use half default (4K) ITR if sub-gig */ - if (sc->link_speed < 1000) { - neweitr = IGC_INTS_4K; - goto igc_set_next_eitr; - } - /* Want at least enough packet buffer for two frames to AIM */ - if (sc->shared->isc_max_frame_size * 2 > (sc->pba << 10)) { - neweitr = igc_max_interrupt_rate; - sc->enable_aim = 0; - goto igc_set_next_eitr; - } - - bytes = bytes_per_packet = 0; - /* Get largest values from the associated tx and rx ring */ - txpackets = atomic_load_long(&txr->tx_packets); - if (txpackets != 0) { - bytes = txbytes; - bytes_per_packet = txbytes / txpackets; - packets = txpackets; - } - rxpackets = atomic_load_long(&rxr->rx_packets); - if (rxpackets != 0) { - bytes = lmax(bytes, rxbytes); - bytes_per_packet = - lmax(bytes_per_packet, rxbytes / rxpackets); - packets = lmax(packets, rxpackets); - } - - /* Latency state machine */ - switch (nextlatency) { - case eitr_latency_disabled: /* Bootstrapping */ - nextlatency = eitr_latency_low; - break; - case eitr_latency_lowest: /* 70k ints/s */ - /* TSO and jumbo frames */ - if (bytes_per_packet > 8000) - nextlatency = eitr_latency_bulk; - else if ((packets < 5) && (bytes > 512)) - nextlatency = eitr_latency_low; - break; - case eitr_latency_low: /* 20k ints/s */ - if (bytes > 10000) { - /* Handle TSO */ - if (bytes_per_packet > 8000) - nextlatency = eitr_latency_bulk; - else if ((packets < 10) || - (bytes_per_packet > 1200)) - nextlatency = eitr_latency_bulk; - else if (packets > 35) - nextlatency = eitr_latency_lowest; - } else if (bytes_per_packet > 2000) { - nextlatency = eitr_latency_bulk; - } else if (packets < 3 && bytes < 512) { - nextlatency = eitr_latency_lowest; - } - break; - case eitr_latency_bulk: /* 4k ints/s */ - if (bytes > 25000) { - if (packets > 35) - nextlatency = eitr_latency_low; - } else if (bytes < 1500) - nextlatency = eitr_latency_low; - break; - default: - nextlatency = eitr_latency_low; - device_printf(sc->dev, - "Unexpected neweitr transition %d\n", - nextlatency); - break; - } - - /* Trim itr_latency_lowest for default AIM setting */ - if (sc->enable_aim == 1 && nextlatency == eitr_latency_lowest) - nextlatency = eitr_latency_low; - - /* Request new latency */ - rxr->rx_nextlatency = nextlatency; - } else { - /* We may have toggled to AIM disabled */ - nextlatency = eitr_latency_disabled; - rxr->rx_nextlatency = nextlatency; - } - - /* ITR state machine */ - switch(nextlatency) { - case eitr_latency_lowest: - neweitr = IGC_INTS_70K; - break; - case eitr_latency_low: - neweitr = IGC_INTS_20K; - break; - case eitr_latency_bulk: neweitr = IGC_INTS_4K; - break; - case eitr_latency_disabled: - default: + } else if (sc->shared->isc_max_frame_size * 2 > (sc->pba << 10)) { + /* Want at least enough packet buffer for two frames to AIM */ neweitr = igc_max_interrupt_rate; - break; + } else { + neweitr = igc_ring_itr(sc, rxbytes, rxpackets, txbytes, + txpackets); + /* No usable observation; leave the rate where it is */ + if (neweitr == 0) + return; } -igc_set_next_eitr: neweitr = IGC_INTS_TO_EITR(neweitr); neweitr |= IGC_EITR_CNT_IGNR; @@ -1047,7 +1085,6 @@ igc_intr(void *arg) struct igc_softc *sc = arg; struct igc_hw *hw = &sc->hw; struct igc_rx_queue *que = &sc->rx_queues[0]; - struct tx_ring *txr = &sc->tx_queues[0].txr; struct rx_ring *rxr = &que->rxr; if_ctx_t ctx = sc->ctx; u32 reg_icr; @@ -1080,13 +1117,7 @@ igc_intr(void *arg) if (reg_icr & IGC_ICR_RXO) sc->rx_overruns++; - igc_neweitr(sc, que, txr, rxr); - - /* Reset state */ - txr->tx_bytes = 0; - txr->tx_packets = 0; - rxr->rx_bytes = 0; - rxr->rx_packets = 0; + igc_neweitr(sc, que, rxr); return (FILTER_SCHEDULE_THREAD); } @@ -1121,18 +1152,11 @@ igc_msix_que(void *arg) { struct igc_rx_queue *que = arg; struct igc_softc *sc = que->sc; - struct tx_ring *txr = &sc->tx_queues[que->msix].txr; struct rx_ring *rxr = &que->rxr; ++que->irqs; - igc_neweitr(sc, que, txr, rxr); - - /* Reset state */ - txr->tx_bytes = 0; - txr->tx_packets = 0; - rxr->rx_bytes = 0; - rxr->rx_packets = 0; + igc_neweitr(sc, que, rxr); return (FILTER_SCHEDULE_THREAD); } @@ -1269,8 +1293,6 @@ igc_if_media_change(if_ctx_t ctx) device_printf(sc->dev, "Unsupported media type\n"); } - igc_if_init(ctx); - return (0); } @@ -1291,20 +1313,24 @@ igc_if_set_promisc(if_ctx_t ctx, int flags) /* Don't disable if in MAX groups */ if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) - reg_rctl &= (~IGC_RCTL_MPE); - IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl); + reg_rctl &= ~IGC_RCTL_MPE; if (flags & IFF_PROMISC) { reg_rctl |= (IGC_RCTL_UPE | IGC_RCTL_MPE); /* Turn this on if you want to see bad packets */ if (igc_debug_sbp) reg_rctl |= IGC_RCTL_SBP; - IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl); } else if (flags & IFF_ALLMULTI) { reg_rctl |= IGC_RCTL_MPE; reg_rctl &= ~IGC_RCTL_UPE; - IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl); } + + if ((flags & IFF_PROMISC) || !igc_if_vlan_filter_used(ctx)) + reg_rctl &= ~IGC_RCTL_VFE; + else + reg_rctl |= IGC_RCTL_VFE; + IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl); + return (0); } @@ -1383,6 +1409,33 @@ igc_if_timer(if_ctx_t ctx, uint16_t qid) } static void +igc_apply_i225_ipg_workaround(struct igc_softc *sc) +{ + struct igc_hw *hw = &sc->hw; + u32 ipgt, tipg; + + /* + * I225 v1 cannot receive the minimum IPG required at 2.5 Gb/s. + * Intel's documented back-to-back workaround is for the transmitter + * to use a 15-byte IPG instead of 12 bytes. I225 v2 and later have + * the receive-side fix and should retain the standard IPG. + */ + if (!igc_is_device_id_i225(hw) || + hw->revision_id >= IGC_REVISION_2) + return; + + ipgt = sc->link_speed == SPEED_2500 ? IGC_I225_TIPG_IPGT_2P5 : + DEFAULT_82543_TIPG_IPGT_COPPER; + tipg = IGC_READ_REG(hw, IGC_TIPG); + if ((tipg & IGC_TIPG_IPGT_MASK) == ipgt) + return; + + tipg &= ~IGC_TIPG_IPGT_MASK; + tipg |= ipgt; + IGC_WRITE_REG(hw, IGC_TIPG, tipg); +} + +static void igc_if_update_admin_status(if_ctx_t ctx) { struct igc_softc *sc = iflib_get_softc(ctx); @@ -1427,21 +1480,10 @@ igc_if_update_admin_status(if_ctx_t ctx) sc->link_active = 0; iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); } + igc_apply_i225_ipg_workaround(sc); igc_update_stats_counters(sc); } -static void -igc_if_watchdog_reset(if_ctx_t ctx) -{ - struct igc_softc *sc = iflib_get_softc(ctx); - - /* - * Just count the event; iflib(4) will already trigger a - * sufficient reset of the controller. - */ - sc->watchdog_events++; -} - /********************************************************************* * * This routine disables all traffic on the adapter by issuing a @@ -1489,6 +1531,42 @@ igc_identify_hardware(if_ctx_t ctx) } } +/********************************************************************* + * + * Intel's I225/I226 Specification Update, erratum 2, states that I225 + * devices can incorrectly enter L1 substates while CLKREQ# is asserted, + * causing repeated L1-substate entry and exit. Disable both ASPM and + * PCI-PM L1.2, as the erratum can occur while idle or in D3. + * + * I226 devices have a separate erratum where ASPM L1.2 exit latency can + * exceed what the packet buffer can tolerate under load. Disabling ASPM + * L1.2 on the device itself works around the issue. + * + **********************************************************************/ +static void +igc_disable_broken_l1_2(if_ctx_t ctx) +{ + device_t dev = iflib_get_dev(ctx); + struct igc_softc *sc = iflib_get_softc(ctx); + int cap; + uint32_t ctl1, mask; + + if (igc_is_device_id_i225(&sc->hw)) + mask = PCIM_L1PM_CTL1_ASPM_L1_2 | + PCIM_L1PM_CTL1_PCIPM_L1_2; + else if (igc_is_device_id_i226(&sc->hw)) + mask = PCIM_L1PM_CTL1_ASPM_L1_2; + else + return; + + if (pci_find_extcap(dev, PCIZ_L1PM, &cap) != 0) + return; + + ctl1 = pci_read_config(dev, cap + PCIR_L1PM_CTL1, 4); + ctl1 &= ~mask; + pci_write_config(dev, cap + PCIR_L1PM_CTL1, ctl1, 4); +} + static int igc_allocate_pci_resources(if_ctx_t ctx) { @@ -1599,7 +1677,7 @@ igc_configure_queues(struct igc_softc *sc) struct igc_hw *hw = &sc->hw; struct igc_rx_queue *rx_que; struct igc_tx_queue *tx_que; - u32 ivar = 0, newitr = 0; + u32 ivar = 0; /* First turn on RSS capability */ IGC_WRITE_REG(hw, IGC_GPIE, @@ -1642,18 +1720,25 @@ igc_configure_queues(struct igc_softc *sc) sc->link_mask = 1 << sc->linkvec; IGC_WRITE_REG(hw, IGC_IVAR_MISC, ivar); - /* Set the starting interrupt rate */ - if (igc_max_interrupt_rate > 0) - newitr = IGC_INTS_TO_EITR(igc_max_interrupt_rate); + return; +} + +static void +igc_initialize_interrupt_rate(struct igc_softc *sc) +{ + struct igc_hw *hw = &sc->hw; + struct igc_rx_queue *rx_que; + u32 newitr; + newitr = IGC_INTS_TO_EITR(igc_max_interrupt_rate); newitr |= IGC_EITR_CNT_IGNR; for (int i = 0; i < sc->rx_num_queues; i++) { rx_que = &sc->rx_queues[i]; - IGC_WRITE_REG(hw, IGC_EITR(rx_que->msix), newitr); + rx_que->eitr_setting = newitr; + IGC_WRITE_REG(hw, IGC_EITR(rx_que->msix), + rx_que->eitr_setting); } - - return; } static void @@ -2022,6 +2107,9 @@ igc_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, /* Set up some basics */ struct tx_ring *txr = &que->txr; + KASSERT(__is_aligned(&txr->tx_aim_snapshot, sizeof(uint64_t)), + ("%s: misaligned TX AIM snapshot %p", __func__, + &txr->tx_aim_snapshot)); txr->sc = que->sc = sc; que->me = txr->me = i; @@ -2074,6 +2162,9 @@ igc_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, for (i = 0, que = sc->rx_queues; i < nrxqsets; i++, que++) { /* Set up some basics */ struct rx_ring *rxr = &que->rxr; + KASSERT(__is_aligned(&rxr->rx_aim_snapshot, sizeof(uint64_t)), + ("%s: misaligned RX AIM snapshot %p", __func__, + &rxr->rx_aim_snapshot)); rxr->sc = que->sc = sc; rxr->que = que; que->me = rxr->me = i; @@ -2168,13 +2259,9 @@ igc_initialize_transmit_unit(if_ctx_t ctx) IGC_READ_REG(&sc->hw, IGC_TDBAL(i)), IGC_READ_REG(&sc->hw, IGC_TDLEN(i))); - txdctl = 0; /* clear txdctl */ - txdctl |= 0x1f; /* PTHRESH */ - txdctl |= 1 << 8; /* HTHRESH */ - txdctl |= 1 << 16;/* WTHRESH */ - txdctl |= 1 << 22; /* Reserved bit 22 must always be 1 */ - txdctl |= IGC_TXDCTL_GRAN; - txdctl |= 1 << 25; /* LWTHRESH */ + /* WTHRESH must be zero when iflib uses sparse RS. */ + txdctl = IGC_TX_PTHRESH | (IGC_TX_HTHRESH << 8) | + IGC_TXDCTL_QUEUE_ENABLE; IGC_WRITE_REG(hw, IGC_TXDCTL(i), txdctl); } @@ -2302,11 +2389,10 @@ igc_initialize_receive_unit(if_ctx_t ctx) IGC_WRITE_REG(hw, IGC_RDT(i), 0); /* Enable this Queue */ rxdctl = IGC_READ_REG(hw, IGC_RXDCTL(i)); - rxdctl |= IGC_RXDCTL_QUEUE_ENABLE; - rxdctl &= 0xFFF00000; - rxdctl |= IGC_RX_PTHRESH; - rxdctl |= IGC_RX_HTHRESH << 8; - rxdctl |= IGC_RX_WTHRESH << 16; + rxdctl &= ~(IGC_RXDCTL_PTHRESH | IGC_RXDCTL_HTHRESH | + IGC_RXDCTL_WTHRESH); + rxdctl |= IGC_RX_PTHRESH | (IGC_RX_HTHRESH << 8) | + (IGC_RX_WTHRESH << 16) | IGC_RXDCTL_QUEUE_ENABLE; IGC_WRITE_REG(hw, IGC_RXDCTL(i), rxdctl); } @@ -2320,15 +2406,86 @@ igc_initialize_receive_unit(if_ctx_t ctx) } static void +igc_if_vlan_register(if_ctx_t ctx, u16 vtag) +{ + struct igc_softc *sc = iflib_get_softc(ctx); + u32 index, mask; + + index = (vtag >> 5) & 0x7f; + mask = 1U << (vtag & 0x1f); + if ((sc->shadow_vfta[index] & mask) != 0) + return; + sc->shadow_vfta[index] |= mask; + igc_write_vfta(&sc->hw, index, sc->shadow_vfta[index]); +} + +static void +igc_if_vlan_unregister(if_ctx_t ctx, u16 vtag) +{ + struct igc_softc *sc = iflib_get_softc(ctx); + u32 index, mask; + + index = (vtag >> 5) & 0x7f; + mask = 1U << (vtag & 0x1f); + if ((sc->shadow_vfta[index] & mask) == 0) + return; + sc->shadow_vfta[index] &= ~mask; + igc_write_vfta(&sc->hw, index, sc->shadow_vfta[index]); +} + +static bool +igc_if_vlan_filter_capable(if_ctx_t ctx) +{ + if_t ifp = iflib_get_ifp(ctx); + + return ((if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) != 0 && + !igc_disable_crc_stripping); +} + +static bool +igc_if_vlan_filter_used(if_ctx_t ctx) +{ + struct igc_softc *sc = iflib_get_softc(ctx); + + if (!igc_if_vlan_filter_capable(ctx)) + return (false); + + for (int i = 0; i < IGC_VFTA_SIZE; i++) + if (sc->shadow_vfta[i] != 0) + return (true); + + return (false); +} + +static void +igc_if_vlan_filter_enable(struct igc_softc *sc) +{ + u32 reg; + + reg = IGC_READ_REG(&sc->hw, IGC_RCTL); + reg &= ~IGC_RCTL_CFIEN; + reg |= IGC_RCTL_VFE; + IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg); +} + +static void +igc_if_vlan_filter_disable(struct igc_softc *sc) +{ + u32 reg; + + reg = IGC_READ_REG(&sc->hw, IGC_RCTL); + reg &= ~(IGC_RCTL_VFE | IGC_RCTL_CFIEN); + IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg); +} + +static void igc_setup_vlan_hw_support(if_ctx_t ctx) { struct igc_softc *sc = iflib_get_softc(ctx); struct igc_hw *hw = &sc->hw; - struct ifnet *ifp = iflib_get_ifp(ctx); + if_t ifp = iflib_get_ifp(ctx); u32 reg; - /* igc hardware doesn't seem to implement VFTA for HWFILTER */ - if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING && !igc_disable_crc_stripping) { reg = IGC_READ_REG(hw, IGC_CTRL); @@ -2339,6 +2496,20 @@ igc_setup_vlan_hw_support(if_ctx_t ctx) reg &= ~IGC_CTRL_VME; IGC_WRITE_REG(hw, IGC_CTRL, reg); } + + if (!igc_if_vlan_filter_capable(ctx)) { + igc_if_vlan_filter_disable(sc); + return; + } + + /* Always admit priority-tagged frames. */ + sc->shadow_vfta[0] |= 1U; + + /* A reset may clear the VFTA, so restore the complete desired table. */ + for (int i = 0; i < IGC_VFTA_SIZE; i++) + igc_write_vfta(hw, i, sc->shadow_vfta[i]); + + igc_if_vlan_filter_enable(sc); } static void @@ -2503,6 +2674,7 @@ igc_update_stats_counters(struct igc_softc *sc) u64 prev_xoffrxc = sc->stats.xoffrxc; sc->stats.crcerrs += IGC_READ_REG(&sc->hw, IGC_CRCERRS); + sc->stats.rxerrc += IGC_READ_REG(&sc->hw, IGC_RXERRC); sc->stats.mpc += IGC_READ_REG(&sc->hw, IGC_MPC); sc->stats.scc += IGC_READ_REG(&sc->hw, IGC_SCC); sc->stats.ecol += IGC_READ_REG(&sc->hw, IGC_ECOL); @@ -2510,7 +2682,7 @@ igc_update_stats_counters(struct igc_softc *sc) sc->stats.mcc += IGC_READ_REG(&sc->hw, IGC_MCC); sc->stats.latecol += IGC_READ_REG(&sc->hw, IGC_LATECOL); sc->stats.colc += IGC_READ_REG(&sc->hw, IGC_COLC); - sc->stats.colc += IGC_READ_REG(&sc->hw, IGC_RERC); + sc->stats.rerc += IGC_READ_REG(&sc->hw, IGC_RERC); sc->stats.dc += IGC_READ_REG(&sc->hw, IGC_DC); sc->stats.rlec += IGC_READ_REG(&sc->hw, IGC_RLEC); sc->stats.xonrxc += IGC_READ_REG(&sc->hw, IGC_XONRXC); @@ -2589,13 +2761,17 @@ igc_if_get_counter(if_ctx_t ctx, ift_counter cnt) case IFCOUNTER_COLLISIONS: return (sc->stats.colc); case IFCOUNTER_IERRORS: + /* + * RERC overlaps the counters below and, on I225, omits length + * errors. RFC covers bad-CRC runts that CRCERRS does not count. + */ return (sc->dropped_pkts + sc->stats.rxerrc + sc->stats.crcerrs + sc->stats.algnerrc + - sc->stats.ruc + sc->stats.roc + - sc->stats.mpc + sc->stats.htdpmc); + sc->stats.ruc + sc->stats.rfc + sc->stats.roc + + sc->stats.mpc); case IFCOUNTER_OERRORS: return (if_get_counter_default(ifp, cnt) + - sc->stats.ecol + sc->stats.latecol + sc->watchdog_events); + sc->stats.ecol + sc->stats.latecol); default: return (if_get_counter_default(ifp, cnt)); } @@ -2646,7 +2822,7 @@ igc_sysctl_interrupt_rate_handler(SYSCTL_HANDLER_ARGS) if (tx) { tque = oidp->oid_arg1; hw = &tque->sc->hw; - reg = IGC_READ_REG(hw, IGC_EITR(tque->me)); + reg = IGC_READ_REG(hw, IGC_EITR(tque->msix)); } else { rque = oidp->oid_arg1; hw = &rque->sc->hw; @@ -2655,7 +2831,7 @@ igc_sysctl_interrupt_rate_handler(SYSCTL_HANDLER_ARGS) usec = (reg & IGC_QVECTOR_MASK); if (usec > 0) - rate = IGC_INTS_TO_EITR(usec); + rate = IGC_EITR_TO_INTS(usec); else rate = 0; @@ -2696,9 +2872,6 @@ igc_add_hw_stats(struct igc_softc *sc) SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_overruns", CTLFLAG_RD, &sc->rx_overruns, "RX overruns"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_timeouts", - CTLFLAG_RD, &sc->watchdog_events, - "Watchdog timeouts"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "device_control", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, IGC_CTRL, igc_sysctl_reg_handler, "IU", @@ -2815,6 +2988,9 @@ igc_add_hw_stats(struct igc_softc *sc) SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_errs", CTLFLAG_RD, &sc->stats.rxerrc, "Receive Errors"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_error_count", + CTLFLAG_RD, &sc->stats.rerc, + "Receive Error Count (RERC)"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "crc_errs", CTLFLAG_RD, &sc->stats.crcerrs, "CRC errors"); @@ -2891,6 +3067,9 @@ igc_add_hw_stats(struct igc_softc *sc) SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_txd", CTLFLAG_RD, &sc->stats.gptc, "Good Packets Transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "host_tx_discarded", + CTLFLAG_RD, &sc->stats.htdpmc, + "Host Packets Discarded by Transmit MAC"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_txd", CTLFLAG_RD, &sc->stats.bptc, "Broadcast Packets Transmitted"); @@ -3152,6 +3331,16 @@ igc_set_flowcntl(SYSCTL_HANDLER_ARGS) return (error); } +static void +igc_sysctl_request_reinit(struct igc_softc *sc) +{ + if ((if_getflags(iflib_get_ifp(sc->ctx)) & IFF_UP) == 0) + return; + + iflib_request_reset(sc->ctx); + iflib_admin_intr_deferred(sc->ctx); +} + /* * Manage DMA Coalesce: * Control values: @@ -3197,7 +3386,7 @@ igc_sysctl_dmac(SYSCTL_HANDLER_ARGS) return (EINVAL); } /* Reinit the interface */ - igc_if_init(sc->ctx); + igc_sysctl_request_reinit(sc); return (error); } @@ -3218,7 +3407,7 @@ igc_sysctl_eee(SYSCTL_HANDLER_ARGS) return (error); sc->hw.dev_spec._i225.eee_disable = (value != 0); - igc_if_init(sc->ctx); + igc_sysctl_request_reinit(sc); return (0); } |
