diff options
author | Greg Rose <gregory.v.rose@intel.com> | 2011-09-07 01:59:35 -0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-10-13 01:45:24 -0400 |
commit | 83c61fa97a7d4ef16506a760f9e52b3144978346 (patch) | |
tree | 470c146ce7a19da9836a16b9bc454941b2b9a428 /drivers/net/ethernet/intel | |
parent | 9687c637388f63b87fcc18eee6e65bcfca4f49ca (diff) |
ixgbe: Add protection from VF invalid target DMA
It is possible for a VF to set an invalid target DMA address in its
Tx/Rx descriptor buffer pointers. The workarounds in this patch
will guard against such an event and issue a VFLR to the VF in response.
The VFLR will shut down the VF until an administrator can take action
to investigate the event and correct the problem.
Signed-off-by: Greg Rose <gregory.v.rose@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel')
-rw-r--r-- | drivers/net/ethernet/intel/ixgbe/ixgbe.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 172 |
2 files changed, 175 insertions, 1 deletions
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 38940d72991d..c1f76aaf8774 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h | |||
@@ -116,6 +116,8 @@ | |||
116 | #define MAX_EMULATION_MAC_ADDRS 16 | 116 | #define MAX_EMULATION_MAC_ADDRS 16 |
117 | #define IXGBE_MAX_PF_MACVLANS 15 | 117 | #define IXGBE_MAX_PF_MACVLANS 15 |
118 | #define VMDQ_P(p) ((p) + adapter->num_vfs) | 118 | #define VMDQ_P(p) ((p) + adapter->num_vfs) |
119 | #define IXGBE_82599_VF_DEVICE_ID 0x10ED | ||
120 | #define IXGBE_X540_VF_DEVICE_ID 0x1515 | ||
119 | 121 | ||
120 | struct vf_data_storage { | 122 | struct vf_data_storage { |
121 | unsigned char vf_mac_addresses[ETH_ALEN]; | 123 | unsigned char vf_mac_addresses[ETH_ALEN]; |
@@ -512,6 +514,8 @@ struct ixgbe_adapter { | |||
512 | struct hlist_head fdir_filter_list; | 514 | struct hlist_head fdir_filter_list; |
513 | union ixgbe_atr_input fdir_mask; | 515 | union ixgbe_atr_input fdir_mask; |
514 | int fdir_filter_count; | 516 | int fdir_filter_count; |
517 | u32 timer_event_accumulator; | ||
518 | u32 vferr_refcount; | ||
515 | }; | 519 | }; |
516 | 520 | ||
517 | struct ixgbe_fdir_filter { | 521 | struct ixgbe_fdir_filter { |
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 1519a23421af..b95c6e979832 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | |||
@@ -6112,6 +6112,51 @@ static void ixgbe_sfp_link_config_subtask(struct ixgbe_adapter *adapter) | |||
6112 | clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state); | 6112 | clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state); |
6113 | } | 6113 | } |
6114 | 6114 | ||
6115 | #ifdef CONFIG_PCI_IOV | ||
6116 | static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter) | ||
6117 | { | ||
6118 | int vf; | ||
6119 | struct ixgbe_hw *hw = &adapter->hw; | ||
6120 | struct net_device *netdev = adapter->netdev; | ||
6121 | u32 gpc; | ||
6122 | u32 ciaa, ciad; | ||
6123 | |||
6124 | gpc = IXGBE_READ_REG(hw, IXGBE_TXDGPC); | ||
6125 | if (gpc) /* If incrementing then no need for the check below */ | ||
6126 | return; | ||
6127 | /* | ||
6128 | * Check to see if a bad DMA write target from an errant or | ||
6129 | * malicious VF has caused a PCIe error. If so then we can | ||
6130 | * issue a VFLR to the offending VF(s) and then resume without | ||
6131 | * requesting a full slot reset. | ||
6132 | */ | ||
6133 | |||
6134 | for (vf = 0; vf < adapter->num_vfs; vf++) { | ||
6135 | ciaa = (vf << 16) | 0x80000000; | ||
6136 | /* 32 bit read so align, we really want status at offset 6 */ | ||
6137 | ciaa |= PCI_COMMAND; | ||
6138 | IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa); | ||
6139 | ciad = IXGBE_READ_REG(hw, IXGBE_CIAD_82599); | ||
6140 | ciaa &= 0x7FFFFFFF; | ||
6141 | /* disable debug mode asap after reading data */ | ||
6142 | IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa); | ||
6143 | /* Get the upper 16 bits which will be the PCI status reg */ | ||
6144 | ciad >>= 16; | ||
6145 | if (ciad & PCI_STATUS_REC_MASTER_ABORT) { | ||
6146 | netdev_err(netdev, "VF %d Hung DMA\n", vf); | ||
6147 | /* Issue VFLR */ | ||
6148 | ciaa = (vf << 16) | 0x80000000; | ||
6149 | ciaa |= 0xA8; | ||
6150 | IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa); | ||
6151 | ciad = 0x00008000; /* VFLR */ | ||
6152 | IXGBE_WRITE_REG(hw, IXGBE_CIAD_82599, ciad); | ||
6153 | ciaa &= 0x7FFFFFFF; | ||
6154 | IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa); | ||
6155 | } | ||
6156 | } | ||
6157 | } | ||
6158 | |||
6159 | #endif | ||
6115 | /** | 6160 | /** |
6116 | * ixgbe_service_timer - Timer Call-back | 6161 | * ixgbe_service_timer - Timer Call-back |
6117 | * @data: pointer to adapter cast into an unsigned long | 6162 | * @data: pointer to adapter cast into an unsigned long |
@@ -6120,17 +6165,49 @@ static void ixgbe_service_timer(unsigned long data) | |||
6120 | { | 6165 | { |
6121 | struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data; | 6166 | struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data; |
6122 | unsigned long next_event_offset; | 6167 | unsigned long next_event_offset; |
6168 | bool ready = true; | ||
6123 | 6169 | ||
6170 | #ifdef CONFIG_PCI_IOV | ||
6171 | ready = false; | ||
6172 | |||
6173 | /* | ||
6174 | * don't bother with SR-IOV VF DMA hang check if there are | ||
6175 | * no VFs or the link is down | ||
6176 | */ | ||
6177 | if (!adapter->num_vfs || | ||
6178 | (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE)) { | ||
6179 | ready = true; | ||
6180 | goto normal_timer_service; | ||
6181 | } | ||
6182 | |||
6183 | /* If we have VFs allocated then we must check for DMA hangs */ | ||
6184 | ixgbe_check_for_bad_vf(adapter); | ||
6185 | next_event_offset = HZ / 50; | ||
6186 | adapter->timer_event_accumulator++; | ||
6187 | |||
6188 | if (adapter->timer_event_accumulator >= 100) { | ||
6189 | ready = true; | ||
6190 | adapter->timer_event_accumulator = 0; | ||
6191 | } | ||
6192 | |||
6193 | goto schedule_event; | ||
6194 | |||
6195 | normal_timer_service: | ||
6196 | #endif | ||
6124 | /* poll faster when waiting for link */ | 6197 | /* poll faster when waiting for link */ |
6125 | if (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE) | 6198 | if (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE) |
6126 | next_event_offset = HZ / 10; | 6199 | next_event_offset = HZ / 10; |
6127 | else | 6200 | else |
6128 | next_event_offset = HZ * 2; | 6201 | next_event_offset = HZ * 2; |
6129 | 6202 | ||
6203 | #ifdef CONFIG_PCI_IOV | ||
6204 | schedule_event: | ||
6205 | #endif | ||
6130 | /* Reset the timer */ | 6206 | /* Reset the timer */ |
6131 | mod_timer(&adapter->service_timer, next_event_offset + jiffies); | 6207 | mod_timer(&adapter->service_timer, next_event_offset + jiffies); |
6132 | 6208 | ||
6133 | ixgbe_service_event_schedule(adapter); | 6209 | if (ready) |
6210 | ixgbe_service_event_schedule(adapter); | ||
6134 | } | 6211 | } |
6135 | 6212 | ||
6136 | static void ixgbe_reset_subtask(struct ixgbe_adapter *adapter) | 6213 | static void ixgbe_reset_subtask(struct ixgbe_adapter *adapter) |
@@ -7717,6 +7794,91 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev, | |||
7717 | struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); | 7794 | struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); |
7718 | struct net_device *netdev = adapter->netdev; | 7795 | struct net_device *netdev = adapter->netdev; |
7719 | 7796 | ||
7797 | #ifdef CONFIG_PCI_IOV | ||
7798 | struct pci_dev *bdev, *vfdev; | ||
7799 | u32 dw0, dw1, dw2, dw3; | ||
7800 | int vf, pos; | ||
7801 | u16 req_id, pf_func; | ||
7802 | |||
7803 | if (adapter->hw.mac.type == ixgbe_mac_82598EB || | ||
7804 | adapter->num_vfs == 0) | ||
7805 | goto skip_bad_vf_detection; | ||
7806 | |||
7807 | bdev = pdev->bus->self; | ||
7808 | while (bdev && (bdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT)) | ||
7809 | bdev = bdev->bus->self; | ||
7810 | |||
7811 | if (!bdev) | ||
7812 | goto skip_bad_vf_detection; | ||
7813 | |||
7814 | pos = pci_find_ext_capability(bdev, PCI_EXT_CAP_ID_ERR); | ||
7815 | if (!pos) | ||
7816 | goto skip_bad_vf_detection; | ||
7817 | |||
7818 | pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG, &dw0); | ||
7819 | pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 4, &dw1); | ||
7820 | pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 8, &dw2); | ||
7821 | pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 12, &dw3); | ||
7822 | |||
7823 | req_id = dw1 >> 16; | ||
7824 | /* On the 82599 if bit 7 of the requestor ID is set then it's a VF */ | ||
7825 | if (!(req_id & 0x0080)) | ||
7826 | goto skip_bad_vf_detection; | ||
7827 | |||
7828 | pf_func = req_id & 0x01; | ||
7829 | if ((pf_func & 1) == (pdev->devfn & 1)) { | ||
7830 | unsigned int device_id; | ||
7831 | |||
7832 | vf = (req_id & 0x7F) >> 1; | ||
7833 | e_dev_err("VF %d has caused a PCIe error\n", vf); | ||
7834 | e_dev_err("TLP: dw0: %8.8x\tdw1: %8.8x\tdw2: " | ||
7835 | "%8.8x\tdw3: %8.8x\n", | ||
7836 | dw0, dw1, dw2, dw3); | ||
7837 | switch (adapter->hw.mac.type) { | ||
7838 | case ixgbe_mac_82599EB: | ||
7839 | device_id = IXGBE_82599_VF_DEVICE_ID; | ||
7840 | break; | ||
7841 | case ixgbe_mac_X540: | ||
7842 | device_id = IXGBE_X540_VF_DEVICE_ID; | ||
7843 | break; | ||
7844 | default: | ||
7845 | device_id = 0; | ||
7846 | break; | ||
7847 | } | ||
7848 | |||
7849 | /* Find the pci device of the offending VF */ | ||
7850 | vfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, device_id, NULL); | ||
7851 | while (vfdev) { | ||
7852 | if (vfdev->devfn == (req_id & 0xFF)) | ||
7853 | break; | ||
7854 | vfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, | ||
7855 | device_id, vfdev); | ||
7856 | } | ||
7857 | /* | ||
7858 | * There's a slim chance the VF could have been hot plugged, | ||
7859 | * so if it is no longer present we don't need to issue the | ||
7860 | * VFLR. Just clean up the AER in that case. | ||
7861 | */ | ||
7862 | if (vfdev) { | ||
7863 | e_dev_err("Issuing VFLR to VF %d\n", vf); | ||
7864 | pci_write_config_dword(vfdev, 0xA8, 0x00008000); | ||
7865 | } | ||
7866 | |||
7867 | pci_cleanup_aer_uncorrect_error_status(pdev); | ||
7868 | } | ||
7869 | |||
7870 | /* | ||
7871 | * Even though the error may have occurred on the other port | ||
7872 | * we still need to increment the vf error reference count for | ||
7873 | * both ports because the I/O resume function will be called | ||
7874 | * for both of them. | ||
7875 | */ | ||
7876 | adapter->vferr_refcount++; | ||
7877 | |||
7878 | return PCI_ERS_RESULT_RECOVERED; | ||
7879 | |||
7880 | skip_bad_vf_detection: | ||
7881 | #endif /* CONFIG_PCI_IOV */ | ||
7720 | netif_device_detach(netdev); | 7882 | netif_device_detach(netdev); |
7721 | 7883 | ||
7722 | if (state == pci_channel_io_perm_failure) | 7884 | if (state == pci_channel_io_perm_failure) |
@@ -7779,6 +7941,14 @@ static void ixgbe_io_resume(struct pci_dev *pdev) | |||
7779 | struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); | 7941 | struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); |
7780 | struct net_device *netdev = adapter->netdev; | 7942 | struct net_device *netdev = adapter->netdev; |
7781 | 7943 | ||
7944 | #ifdef CONFIG_PCI_IOV | ||
7945 | if (adapter->vferr_refcount) { | ||
7946 | e_info(drv, "Resuming after VF err\n"); | ||
7947 | adapter->vferr_refcount--; | ||
7948 | return; | ||
7949 | } | ||
7950 | |||
7951 | #endif | ||
7782 | if (netif_running(netdev)) | 7952 | if (netif_running(netdev)) |
7783 | ixgbe_up(adapter); | 7953 | ixgbe_up(adapter); |
7784 | 7954 | ||