diff options
Diffstat (limited to 'sys/dev/pci/if_ice.c')
-rw-r--r-- | sys/dev/pci/if_ice.c | 310 |
1 files changed, 307 insertions, 3 deletions
diff --git a/sys/dev/pci/if_ice.c b/sys/dev/pci/if_ice.c index aab1769d504..15855060b4b 100644 --- a/sys/dev/pci/if_ice.c +++ b/sys/dev/pci/if_ice.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ice.c,v 1.10 2024/11/19 09:39:57 stsp Exp $ */ +/* $OpenBSD: if_ice.c,v 1.11 2024/11/19 09:41:32 stsp Exp $ */ /* Copyright (c) 2024, Intel Corporation * All rights reserved. @@ -180,6 +180,9 @@ uint32_t ice_debug = 0xffffffff & ~(ICE_DBG_AQ); #define ICE_READ(hw, reg) \ bus_space_read_4((hw)->hw_sc->sc_st, (hw)->hw_sc->sc_sh, (reg)) +#define ICE_READ_8(hw, reg) \ + bus_space_read_8((hw)->hw_sc->sc_st, (hw)->hw_sc->sc_sh, (reg)) + #define ICE_WRITE(hw, reg, val) \ bus_space_write_4((hw)->hw_sc->sc_st, (hw)->hw_sc->sc_sh, (reg), (val)) @@ -315,6 +318,10 @@ struct ice_softc { struct ice_resmgr tx_qmgr; struct ice_resmgr rx_qmgr; + /* device statistics */ + struct ice_pf_hw_stats stats; + struct ice_pf_sw_stats soft_stats; + struct ice_vsi **all_vsi; /* Array of VSI pointers */ uint16_t num_available_vsi; /* Size of VSI array */ @@ -17772,11 +17779,9 @@ ice_clean_all_vsi_rss_cfg(struct ice_softc *sc) void ice_reset_pf_stats(struct ice_softc *sc) { -#if 0 memset(&sc->stats.prev, 0, sizeof(sc->stats.prev)); memset(&sc->stats.cur, 0, sizeof(sc->stats.cur)); sc->stats.offsets_loaded = false; -#endif } /** @@ -22790,6 +22795,302 @@ ice_poll_for_media_avail(struct ice_softc *sc) } /** + * ice_stat_update40 - read 40 bit stat from the chip and update stat values + * @hw: ptr to the hardware info + * @reg: offset of 64 bit HW register to read from + * @prev_stat_loaded: bool to specify if previous stats are loaded + * @prev_stat: ptr to previous loaded stat value + * @cur_stat: ptr to current stat value + */ +void +ice_stat_update40(struct ice_hw *hw, uint32_t reg, bool prev_stat_loaded, + uint64_t *prev_stat, uint64_t *cur_stat) +{ + uint64_t new_data = ICE_READ_8(hw, reg) & (BIT_ULL(40) - 1); + + /* + * Device stats are not reset at PFR, they likely will not be zeroed + * when the driver starts. Thus, save the value from the first read + * without adding to the statistic value so that we report stats which + * count up from zero. + */ + if (!prev_stat_loaded) { + *prev_stat = new_data; + return; + } + + /* + * Calculate the difference between the new and old values, and then + * add it to the software stat value. + */ + if (new_data >= *prev_stat) + *cur_stat += new_data - *prev_stat; + else + /* to manage the potential roll-over */ + *cur_stat += (new_data + BIT_ULL(40)) - *prev_stat; + + /* Update the previously stored value to prepare for next read */ + *prev_stat = new_data; +} + +/** + * ice_stat_update32 - read 32 bit stat from the chip and update stat values + * @hw: ptr to the hardware info + * @reg: offset of HW register to read from + * @prev_stat_loaded: bool to specify if previous stats are loaded + * @prev_stat: ptr to previous loaded stat value + * @cur_stat: ptr to current stat value + */ +void +ice_stat_update32(struct ice_hw *hw, uint32_t reg, bool prev_stat_loaded, + uint64_t *prev_stat, uint64_t *cur_stat) +{ + uint32_t new_data; + + new_data = ICE_READ(hw, reg); + + /* + * Device stats are not reset at PFR, they likely will not be zeroed + * when the driver starts. Thus, save the value from the first read + * without adding to the statistic value so that we report stats which + * count up from zero. + */ + if (!prev_stat_loaded) { + *prev_stat = new_data; + return; + } + + /* + * Calculate the difference between the new and old values, and then + * add it to the software stat value. + */ + if (new_data >= *prev_stat) + *cur_stat += new_data - *prev_stat; + else + /* to manage the potential roll-over */ + *cur_stat += (new_data + BIT_ULL(32)) - *prev_stat; + + /* Update the previously stored value to prepare for next read */ + *prev_stat = new_data; +} + +/** + * ice_stat_update_repc - read GLV_REPC stats from chip and update stat values + * @hw: ptr to the hardware info + * @vsi_handle: VSI handle + * @prev_stat_loaded: bool to specify if the previous stat values are loaded + * @cur_stats: ptr to current stats structure + * + * The GLV_REPC statistic register actually tracks two 16bit statistics, and + * thus cannot be read using the normal ice_stat_update32 function. + * + * Read the GLV_REPC register associated with the given VSI, and update the + * rx_no_desc and rx_error values in the ice_eth_stats structure. + * + * Because the statistics in GLV_REPC stick at 0xFFFF, the register must be + * cleared each time it's read. + * + * Note that the GLV_RDPC register also counts the causes that would trigger + * GLV_REPC. However, it does not give the finer grained detail about why the + * packets are being dropped. The GLV_REPC values can be used to distinguish + * whether Rx packets are dropped due to errors or due to no available + * descriptors. + */ +void +ice_stat_update_repc(struct ice_hw *hw, uint16_t vsi_handle, + bool prev_stat_loaded, struct ice_eth_stats *cur_stats) +{ + uint16_t vsi_num, no_desc, error_cnt; + uint32_t repc; + + if (!ice_is_vsi_valid(hw, vsi_handle)) + return; + + vsi_num = hw->vsi_ctx[vsi_handle]->vsi_num; + + /* If we haven't loaded stats yet, just clear the current value */ + if (!prev_stat_loaded) { + ICE_WRITE(hw, GLV_REPC(vsi_num), 0); + return; + } + + repc = ICE_READ(hw, GLV_REPC(vsi_num)); + no_desc = (repc & GLV_REPC_NO_DESC_CNT_M) >> GLV_REPC_NO_DESC_CNT_S; + error_cnt = (repc & GLV_REPC_ERROR_CNT_M) >> GLV_REPC_ERROR_CNT_S; + + /* Clear the count by writing to the stats register */ + ICE_WRITE(hw, GLV_REPC(vsi_num), 0); + + cur_stats->rx_no_desc += no_desc; + cur_stats->rx_errors += error_cnt; +} + +/** + * ice_update_pf_stats - Update port stats counters + * @sc: device private softc structure + * + * Reads hardware statistics registers and updates the software tracking + * structure with new values. + */ +void +ice_update_pf_stats(struct ice_softc *sc) +{ + struct ice_hw_port_stats *prev_ps, *cur_ps; + struct ice_hw *hw = &sc->hw; + uint8_t lport; + + KASSERT(hw->port_info); + + prev_ps = &sc->stats.prev; + cur_ps = &sc->stats.cur; + lport = hw->port_info->lport; + +#define ICE_PF_STAT_PFC(name, location, index) \ + ice_stat_update40(hw, name(lport, index), \ + sc->stats.offsets_loaded, \ + &prev_ps->location[index], &cur_ps->location[index]) + +#define ICE_PF_STAT40(name, location) \ + ice_stat_update40(hw, name ## L(lport), \ + sc->stats.offsets_loaded, \ + &prev_ps->location, &cur_ps->location) + +#define ICE_PF_STAT32(name, location) \ + ice_stat_update32(hw, name(lport), \ + sc->stats.offsets_loaded, \ + &prev_ps->location, &cur_ps->location) + + ICE_PF_STAT40(GLPRT_GORC, eth.rx_bytes); + ICE_PF_STAT40(GLPRT_UPRC, eth.rx_unicast); + ICE_PF_STAT40(GLPRT_MPRC, eth.rx_multicast); + ICE_PF_STAT40(GLPRT_BPRC, eth.rx_broadcast); + ICE_PF_STAT40(GLPRT_GOTC, eth.tx_bytes); + ICE_PF_STAT40(GLPRT_UPTC, eth.tx_unicast); + ICE_PF_STAT40(GLPRT_MPTC, eth.tx_multicast); + ICE_PF_STAT40(GLPRT_BPTC, eth.tx_broadcast); + /* This stat register doesn't have an lport */ + ice_stat_update32(hw, PRTRPB_RDPC, + sc->stats.offsets_loaded, + &prev_ps->eth.rx_discards, &cur_ps->eth.rx_discards); + + ICE_PF_STAT32(GLPRT_TDOLD, tx_dropped_link_down); + ICE_PF_STAT40(GLPRT_PRC64, rx_size_64); + ICE_PF_STAT40(GLPRT_PRC127, rx_size_127); + ICE_PF_STAT40(GLPRT_PRC255, rx_size_255); + ICE_PF_STAT40(GLPRT_PRC511, rx_size_511); + ICE_PF_STAT40(GLPRT_PRC1023, rx_size_1023); + ICE_PF_STAT40(GLPRT_PRC1522, rx_size_1522); + ICE_PF_STAT40(GLPRT_PRC9522, rx_size_big); + ICE_PF_STAT40(GLPRT_PTC64, tx_size_64); + ICE_PF_STAT40(GLPRT_PTC127, tx_size_127); + ICE_PF_STAT40(GLPRT_PTC255, tx_size_255); + ICE_PF_STAT40(GLPRT_PTC511, tx_size_511); + ICE_PF_STAT40(GLPRT_PTC1023, tx_size_1023); + ICE_PF_STAT40(GLPRT_PTC1522, tx_size_1522); + ICE_PF_STAT40(GLPRT_PTC9522, tx_size_big); + + /* Update Priority Flow Control Stats */ + for (int i = 0; i <= GLPRT_PXOFFRXC_MAX_INDEX; i++) { + ICE_PF_STAT_PFC(GLPRT_PXONRXC, priority_xon_rx, i); + ICE_PF_STAT_PFC(GLPRT_PXOFFRXC, priority_xoff_rx, i); + ICE_PF_STAT_PFC(GLPRT_PXONTXC, priority_xon_tx, i); + ICE_PF_STAT_PFC(GLPRT_PXOFFTXC, priority_xoff_tx, i); + ICE_PF_STAT_PFC(GLPRT_RXON2OFFCNT, priority_xon_2_xoff, i); + } + + ICE_PF_STAT32(GLPRT_LXONRXC, link_xon_rx); + ICE_PF_STAT32(GLPRT_LXOFFRXC, link_xoff_rx); + ICE_PF_STAT32(GLPRT_LXONTXC, link_xon_tx); + ICE_PF_STAT32(GLPRT_LXOFFTXC, link_xoff_tx); + ICE_PF_STAT32(GLPRT_CRCERRS, crc_errors); + ICE_PF_STAT32(GLPRT_ILLERRC, illegal_bytes); + ICE_PF_STAT32(GLPRT_MLFC, mac_local_faults); + ICE_PF_STAT32(GLPRT_MRFC, mac_remote_faults); + ICE_PF_STAT32(GLPRT_RLEC, rx_len_errors); + ICE_PF_STAT32(GLPRT_RUC, rx_undersize); + ICE_PF_STAT32(GLPRT_RFC, rx_fragments); + ICE_PF_STAT32(GLPRT_ROC, rx_oversize); + ICE_PF_STAT32(GLPRT_RJC, rx_jabber); + +#undef ICE_PF_STAT40 +#undef ICE_PF_STAT32 +#undef ICE_PF_STAT_PFC + + sc->stats.offsets_loaded = true; +} + +/** + * ice_update_vsi_hw_stats - Update VSI-specific ethernet statistics counters + * @vsi: the VSI to be updated + * + * Reads hardware stats and updates the ice_vsi_hw_stats tracking structure with + * the updated values. + */ +void +ice_update_vsi_hw_stats(struct ice_vsi *vsi) +{ + struct ice_eth_stats *prev_es, *cur_es; + struct ice_hw *hw = &vsi->sc->hw; + uint16_t vsi_num; + + if (!ice_is_vsi_valid(hw, vsi->idx)) + return; + + /* HW absolute index of a VSI */ + vsi_num = hw->vsi_ctx[vsi->idx]->vsi_num; + prev_es = &vsi->hw_stats.prev; + cur_es = &vsi->hw_stats.cur; + +#define ICE_VSI_STAT40(name, location) \ + ice_stat_update40(hw, name ## L(vsi_num), \ + vsi->hw_stats.offsets_loaded, \ + &prev_es->location, &cur_es->location) + +#define ICE_VSI_STAT32(name, location) \ + ice_stat_update32(hw, name(vsi_num), \ + vsi->hw_stats.offsets_loaded, \ + &prev_es->location, &cur_es->location) + + ICE_VSI_STAT40(GLV_GORC, rx_bytes); + ICE_VSI_STAT40(GLV_UPRC, rx_unicast); + ICE_VSI_STAT40(GLV_MPRC, rx_multicast); + ICE_VSI_STAT40(GLV_BPRC, rx_broadcast); + ICE_VSI_STAT32(GLV_RDPC, rx_discards); + ICE_VSI_STAT40(GLV_GOTC, tx_bytes); + ICE_VSI_STAT40(GLV_UPTC, tx_unicast); + ICE_VSI_STAT40(GLV_MPTC, tx_multicast); + ICE_VSI_STAT40(GLV_BPTC, tx_broadcast); + ICE_VSI_STAT32(GLV_TEPC, tx_errors); + + ice_stat_update_repc(hw, vsi->idx, vsi->hw_stats.offsets_loaded, + cur_es); + +#undef ICE_VSI_STAT40 +#undef ICE_VSI_STAT32 + + vsi->hw_stats.offsets_loaded = true; +} + +void +ice_update_stats(struct ice_softc *sc) +{ + /* Do not attempt to update stats when in recovery mode */ + if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE)) + return; + + /* Update device statistics */ + ice_update_pf_stats(sc); + + /* Update the primary VSI stats */ + ice_update_vsi_hw_stats(&sc->pf_vsi); +#if 0 + /* Update mirror VSI stats */ + if (sc->mirr_if && sc->mirr_if->if_attached) + ice_update_vsi_hw_stats(sc->mirr_if->vsi); +#endif +} + +/** * ice_if_update_admin_status - update admin status * @ctx: iflib ctx structure * @@ -22872,6 +23173,9 @@ ice_if_update_admin_status(void *arg) /* Check and update link status */ ice_update_link_status(sc, false); + /* Update statistics. */ + ice_update_stats(sc); + /* * If there are still messages to process, we need to reschedule * ourselves. Otherwise, we can just re-enable the interrupt. We'll be |