diff options
Diffstat (limited to 'drivers/net/ethernet/intel/ixgbe/ixgbe_main.c')
-rw-r--r-- | drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 239 |
1 files changed, 210 insertions, 29 deletions
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index cba860f0e1f1..6122a0abb41f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9,6 +9,7 @@ #include <linux/string.h> #include <linux/in.h> #include <linux/interrupt.h> +#include <linux/iopoll.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/sctp.h> @@ -1040,6 +1041,48 @@ static u64 ixgbe_get_tx_pending(struct ixgbe_ring *ring) return ((head <= tail) ? tail : tail + ring->count) - head; } +/** + * ixgbe_get_vf_idx - provide VF index number based on queue index + * @adapter: pointer to the adapter struct + * @queue: Tx queue identifier + * @vf: output VF index + * + * Provide VF index number associated to the input queue. + * + * Returns: 0 if VF provided or error number. + */ +static int ixgbe_get_vf_idx(struct ixgbe_adapter *adapter, u16 queue, u16 *vf) +{ + struct ixgbe_hw *hw = &adapter->hw; + u8 queue_count; + u32 reg; + + if (queue >= adapter->num_tx_queues) + return -EINVAL; + + /* Determine number of queues by checking + * number of virtual functions + */ + reg = IXGBE_READ_REG(hw, IXGBE_GCR_EXT); + switch (reg & IXGBE_GCR_EXT_VT_MODE_MASK) { + case IXGBE_GCR_EXT_VT_MODE_64: + queue_count = IXGBE_64VFS_QUEUES; + break; + case IXGBE_GCR_EXT_VT_MODE_32: + queue_count = IXGBE_32VFS_QUEUES; + break; + case IXGBE_GCR_EXT_VT_MODE_16: + queue_count = IXGBE_16VFS_QUEUES; + break; + default: + return -EINVAL; + } + + *vf = queue / queue_count; + + return 0; +} + static bool ixgbe_check_tx_hang(struct ixgbe_ring *tx_ring) { u32 tx_done = ixgbe_get_tx_completed(tx_ring); @@ -1159,6 +1202,150 @@ void ixgbe_update_rx_ring_stats(struct ixgbe_ring *rx_ring, } /** + * ixgbe_pf_handle_tx_hang - handle Tx hang on PF + * @tx_ring: tx ring number + * @next: next ring + * + * Prints a message containing details about the tx hang. + */ +static void ixgbe_pf_handle_tx_hang(struct ixgbe_ring *tx_ring, + unsigned int next) +{ + struct ixgbe_adapter *adapter = netdev_priv(tx_ring->netdev); + struct ixgbe_hw *hw = &adapter->hw; + + e_err(drv, "Detected Tx Unit Hang%s\n" + " Tx Queue <%d>\n" + " TDH, TDT <%x>, <%x>\n" + " next_to_use <%x>\n" + " next_to_clean <%x>\n" + "tx_buffer_info[next_to_clean]\n" + " time_stamp <%lx>\n" + " jiffies <%lx>\n", + ring_is_xdp(tx_ring) ? " (XDP)" : "", + tx_ring->queue_index, + IXGBE_READ_REG(hw, IXGBE_TDH(tx_ring->reg_idx)), + IXGBE_READ_REG(hw, IXGBE_TDT(tx_ring->reg_idx)), + tx_ring->next_to_use, next, + tx_ring->tx_buffer_info[next].time_stamp, jiffies); + + if (!ring_is_xdp(tx_ring)) + netif_stop_subqueue(tx_ring->netdev, + tx_ring->queue_index); +} + +/** + * ixgbe_vf_handle_tx_hang - handle Tx hang on VF + * @adapter: structure containing ring specific data + * @vf: VF index + * + * Print a message containing details about malicious driver detection. + * Set malicious VF link down if the detection happened several times. + */ +static void ixgbe_vf_handle_tx_hang(struct ixgbe_adapter *adapter, u16 vf) +{ + struct ixgbe_hw *hw = &adapter->hw; + + if (adapter->hw.mac.type != ixgbe_mac_e610) + return; + + e_warn(drv, + "Malicious Driver Detection tx hang detected on PF %d VF %d MAC: %pM", + hw->bus.func, vf, adapter->vfinfo[vf].vf_mac_addresses); + + adapter->tx_hang_count[vf]++; + if (adapter->tx_hang_count[vf] == IXGBE_MAX_TX_VF_HANGS) { + ixgbe_set_vf_link_state(adapter, vf, + IFLA_VF_LINK_STATE_DISABLE); + adapter->tx_hang_count[vf] = 0; + } +} + +static u32 ixgbe_poll_tx_icache(struct ixgbe_hw *hw, u16 queue, u16 idx) +{ + IXGBE_WRITE_REG(hw, IXGBE_TXDESCIC, queue * idx); + return IXGBE_READ_REG(hw, IXGBE_TXDESCIC); +} + +/** + * ixgbe_check_illegal_queue - search for queue with illegal packet + * @adapter: structure containing ring specific data + * @queue: queue index + * + * Check if tx descriptor connected with input queue + * contains illegal packet. + * + * Returns: true if queue contain illegal packet. + */ +static bool ixgbe_check_illegal_queue(struct ixgbe_adapter *adapter, + u16 queue) +{ + u32 hdr_len_reg, mss_len_reg, type_reg; + struct ixgbe_hw *hw = &adapter->hw; + u32 mss_len, header_len, reg; + + for (u16 i = 0; i < IXGBE_MAX_TX_DESCRIPTORS; i++) { + /* HW will clear bit IXGBE_TXDESCIC_READY when address + * is written to address field. HW will set this bit + * when iCache read is done, and data is ready at TIC_DWx. + * Set descriptor address. + */ + read_poll_timeout(ixgbe_poll_tx_icache, reg, + !(reg & IXGBE_TXDESCIC_READY), 0, 0, false, + hw, queue, i); + + /* read tx descriptor access registers */ + hdr_len_reg = IXGBE_READ_REG(hw, IXGBE_TIC_DW2(IXGBE_VLAN_MACIP_LENS_REG)); + type_reg = IXGBE_READ_REG(hw, IXGBE_TIC_DW2(IXGBE_TYPE_TUCMD_MLHL)); + mss_len_reg = IXGBE_READ_REG(hw, IXGBE_TIC_DW2(IXGBE_MSS_L4LEN_IDX)); + + /* check if Advanced Context Descriptor */ + if (FIELD_GET(IXGBE_ADVTXD_DTYP_MASK, type_reg) != + IXGBE_ADVTXD_DTYP_CTXT) + continue; + + /* check for illegal MSS and Header length */ + mss_len = FIELD_GET(IXGBE_ADVTXD_MSS_MASK, mss_len_reg); + header_len = FIELD_GET(IXGBE_ADVTXD_HEADER_LEN_MASK, + hdr_len_reg); + if ((mss_len + header_len) > SZ_16K) { + e_warn(probe, "mss len + header len too long\n"); + return true; + } + } + + return false; +} + +/** + * ixgbe_handle_mdd_event - handle mdd event + * @adapter: structure containing ring specific data + * @tx_ring: tx descriptor ring to handle + * + * Reset VF driver if malicious vf detected or + * illegal packet in an any queue detected. + */ +static void ixgbe_handle_mdd_event(struct ixgbe_adapter *adapter, + struct ixgbe_ring *tx_ring) +{ + u16 vf, q; + + if (adapter->vfinfo && ixgbe_check_mdd_event(adapter)) { + /* vf mdd info and malicious vf detected */ + if (!ixgbe_get_vf_idx(adapter, tx_ring->queue_index, &vf)) + ixgbe_vf_handle_tx_hang(adapter, vf); + } else { + /* malicious vf not detected */ + for (q = 0; q < IXGBE_MAX_TX_QUEUES; q++) { + if (ixgbe_check_illegal_queue(adapter, q) && + !ixgbe_get_vf_idx(adapter, q, &vf)) + /* illegal queue detected */ + ixgbe_vf_handle_tx_hang(adapter, vf); + } + } +} + +/** * ixgbe_clean_tx_irq - Reclaim resources after transmit completes * @q_vector: structure containing interrupt and ring information * @tx_ring: tx ring to clean @@ -1265,26 +1452,10 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, adapter->tx_ipsec += total_ipsec; if (check_for_tx_hang(tx_ring) && ixgbe_check_tx_hang(tx_ring)) { - /* schedule immediate reset if we believe we hung */ - struct ixgbe_hw *hw = &adapter->hw; - e_err(drv, "Detected Tx Unit Hang %s\n" - " Tx Queue <%d>\n" - " TDH, TDT <%x>, <%x>\n" - " next_to_use <%x>\n" - " next_to_clean <%x>\n" - "tx_buffer_info[next_to_clean]\n" - " time_stamp <%lx>\n" - " jiffies <%lx>\n", - ring_is_xdp(tx_ring) ? "(XDP)" : "", - tx_ring->queue_index, - IXGBE_READ_REG(hw, IXGBE_TDH(tx_ring->reg_idx)), - IXGBE_READ_REG(hw, IXGBE_TDT(tx_ring->reg_idx)), - tx_ring->next_to_use, i, - tx_ring->tx_buffer_info[i].time_stamp, jiffies); - - if (!ring_is_xdp(tx_ring)) - netif_stop_subqueue(tx_ring->netdev, - tx_ring->queue_index); + if (adapter->hw.mac.type == ixgbe_mac_e610) + ixgbe_handle_mdd_event(adapter, tx_ring); + + ixgbe_pf_handle_tx_hang(tx_ring, i); e_info(probe, "tx hang %d detected on queue %d, resetting adapter\n", @@ -2200,7 +2371,7 @@ static struct sk_buff *ixgbe_build_skb(struct ixgbe_ring *rx_ring, struct sk_buff *skb; /* Prefetch first cache line of first page. If xdp->data_meta - * is unused, this points extactly as xdp->data, otherwise we + * is unused, this points exactly as xdp->data, otherwise we * likely have a consumer accessing first few bytes of meta * data, and then actual data. */ @@ -2323,7 +2494,7 @@ static void ixgbe_rx_buffer_flip(struct ixgbe_ring *rx_ring, * This function provides a "bounce buffer" approach to Rx interrupt * processing. The advantage to this is that on systems that have * expensive overhead for IOMMU access this provides a means of avoiding - * it by maintaining the mapping of the page to the syste. + * it by maintaining the mapping of the page to the system. * * Returns amount of work completed **/ @@ -3933,8 +4104,12 @@ void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter) static void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter) #endif { - int i; bool pfc_en = adapter->dcb_cfg.pfc_mode_enable; + struct ixgbe_hw *hw = &adapter->hw; + int i; + + if (hw->mac.ops.disable_mdd) + hw->mac.ops.disable_mdd(hw); if (adapter->ixgbe_ieee_pfc) pfc_en |= !!(adapter->ixgbe_ieee_pfc->pfc_en); @@ -3956,6 +4131,9 @@ static void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter) for (i = 0; i < adapter->num_rx_queues; i++) ixgbe_disable_rx_drop(adapter, adapter->rx_ring[i]); } + + if (hw->mac.ops.enable_mdd) + hw->mac.ops.enable_mdd(hw); } #define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT 2 @@ -4885,7 +5063,7 @@ static void ixgbe_scrub_vfta(struct ixgbe_adapter *adapter, u32 vfta_offset) /* pull VLAN ID from VLVF */ vid = vlvf & VLAN_VID_MASK; - /* only concern outselves with a certain range */ + /* only concern ourselves with a certain range */ if (vid < vid_start || vid >= vid_end) continue; @@ -7964,6 +8142,9 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter) netif_carrier_on(netdev); ixgbe_check_vf_rate_limit(adapter); + if (adapter->num_vfs && hw->mac.ops.enable_mdd) + hw->mac.ops.enable_mdd(hw); + /* enable transmits */ netif_tx_wake_all_queues(adapter->netdev); @@ -7991,6 +8172,8 @@ static void ixgbe_watchdog_link_is_down(struct ixgbe_adapter *adapter) if (!netif_carrier_ok(netdev)) return; + adapter->link_down_events++; + /* poll for SFP+ cable when link is down */ if (ixgbe_is_sfp(hw) && hw->mac.type == ixgbe_mac_82598EB) adapter->flags2 |= IXGBE_FLAG2_SEARCH_FOR_SFP; @@ -9439,10 +9622,6 @@ static int ixgbe_ioctl(struct net_device *netdev, struct ifreq *req, int cmd) struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev); switch (cmd) { - case SIOCSHWTSTAMP: - return ixgbe_ptp_set_ts_config(adapter, req); - case SIOCGHWTSTAMP: - return ixgbe_ptp_get_ts_config(adapter, req); case SIOCGMIIPHY: if (!adapter->hw.phy.ops.read_reg) return -EOPNOTSUPP; @@ -10906,6 +11085,8 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_bpf = ixgbe_xdp, .ndo_xdp_xmit = ixgbe_xdp_xmit, .ndo_xsk_wakeup = ixgbe_xsk_wakeup, + .ndo_hwtstamp_get = ixgbe_ptp_hwtstamp_get, + .ndo_hwtstamp_set = ixgbe_ptp_hwtstamp_set, }; static void ixgbe_disable_txr_hw(struct ixgbe_adapter *adapter, @@ -11108,7 +11289,7 @@ void ixgbe_txrx_ring_enable(struct ixgbe_adapter *adapter, int ring) * ixgbe_enumerate_functions - Get the number of ports this device has * @adapter: adapter structure * - * This function enumerates the phsyical functions co-located on a single slot, + * This function enumerates the physical functions co-located on a single slot, * in order to determine how many ports a device has. This is most useful in * determining the required GT/s of PCIe bandwidth necessary for optimal * performance. |