ixgbe: remove CIAA/D register reads from bad VF check

Accessing the CIAA/D register can block access to the PCI config space.

This patch removes the read/write operations to the CIAA/D registers
and makes use of standard kernel functions for accessing the PCI config
space.

In addition it moves ixgbevf_check_for_bad_vf() into the watchdog subtask
which reduces the frequency of the checks.

CC: Alex Williamson <alex.williamson@redhat.com>
Reported-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
Emil Tantilov 2014-11-19 03:18:19 +00:00 committed by Jeff Kirsher
parent c762dff24c
commit 9079e41631
1 changed files with 74 additions and 71 deletions

View File

@ -6318,6 +6318,66 @@ static void ixgbe_watchdog_flush_tx(struct ixgbe_adapter *adapter)
}
}
#ifdef CONFIG_PCI_IOV
static inline void ixgbe_issue_vf_flr(struct ixgbe_adapter *adapter,
struct pci_dev *vfdev)
{
if (!pci_wait_for_pending_transaction(vfdev))
e_dev_warn("Issuing VFLR with pending transactions\n");
e_dev_err("Issuing VFLR for VF %s\n", pci_name(vfdev));
pcie_capability_set_word(vfdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
msleep(100);
}
static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
struct pci_dev *vfdev;
u32 gpc;
int pos;
unsigned short vf_id;
if (!(netif_carrier_ok(adapter->netdev)))
return;
gpc = IXGBE_READ_REG(hw, IXGBE_TXDGPC);
if (gpc) /* If incrementing then no need for the check below */
return;
/* Check to see if a bad DMA write target from an errant or
* malicious VF has caused a PCIe error. If so then we can
* issue a VFLR to the offending VF(s) and then resume without
* requesting a full slot reset.
*/
if (!pdev)
return;
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
if (!pos)
return;
/* get the device ID for the VF */
pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id);
/* check status reg for all VFs owned by this PF */
vfdev = pci_get_device(pdev->vendor, vf_id, NULL);
while (vfdev) {
if (vfdev->is_virtfn && (vfdev->physfn == pdev)) {
u16 status_reg;
pci_read_config_word(vfdev, PCI_STATUS, &status_reg);
if (status_reg & PCI_STATUS_REC_MASTER_ABORT)
/* issue VFLR */
ixgbe_issue_vf_flr(adapter, vfdev);
}
vfdev = pci_get_device(pdev->vendor, vf_id, vfdev);
}
}
static void ixgbe_spoof_check(struct ixgbe_adapter *adapter)
{
u32 ssvpc;
@ -6338,6 +6398,17 @@ static void ixgbe_spoof_check(struct ixgbe_adapter *adapter)
e_warn(drv, "%u Spoofed packets detected\n", ssvpc);
}
#else
static void ixgbe_spoof_check(struct ixgbe_adapter __always_unused *adapter)
{
}
static void
ixgbe_check_for_bad_vf(struct ixgbe_adapter __always_unused *adapter)
{
}
#endif /* CONFIG_PCI_IOV */
/**
* ixgbe_watchdog_subtask - check and bring link up
@ -6358,6 +6429,7 @@ static void ixgbe_watchdog_subtask(struct ixgbe_adapter *adapter)
else
ixgbe_watchdog_link_is_down(adapter);
ixgbe_check_for_bad_vf(adapter);
ixgbe_spoof_check(adapter);
ixgbe_update_stats(adapter);
@ -6469,51 +6541,6 @@ static void ixgbe_sfp_link_config_subtask(struct ixgbe_adapter *adapter)
clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state);
}
#ifdef CONFIG_PCI_IOV
static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter)
{
int vf;
struct ixgbe_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
u32 gpc;
u32 ciaa, ciad;
gpc = IXGBE_READ_REG(hw, IXGBE_TXDGPC);
if (gpc) /* If incrementing then no need for the check below */
return;
/*
* Check to see if a bad DMA write target from an errant or
* malicious VF has caused a PCIe error. If so then we can
* issue a VFLR to the offending VF(s) and then resume without
* requesting a full slot reset.
*/
for (vf = 0; vf < adapter->num_vfs; vf++) {
ciaa = (vf << 16) | 0x80000000;
/* 32 bit read so align, we really want status at offset 6 */
ciaa |= PCI_COMMAND;
IXGBE_WRITE_REG(hw, IXGBE_CIAA_BY_MAC(hw), ciaa);
ciad = IXGBE_READ_REG(hw, IXGBE_CIAD_BY_MAC(hw));
ciaa &= 0x7FFFFFFF;
/* disable debug mode asap after reading data */
IXGBE_WRITE_REG(hw, IXGBE_CIAA_BY_MAC(hw), ciaa);
/* Get the upper 16 bits which will be the PCI status reg */
ciad >>= 16;
if (ciad & PCI_STATUS_REC_MASTER_ABORT) {
netdev_err(netdev, "VF %d Hung DMA\n", vf);
/* Issue VFLR */
ciaa = (vf << 16) | 0x80000000;
ciaa |= 0xA8;
IXGBE_WRITE_REG(hw, IXGBE_CIAA_BY_MAC(hw), ciaa);
ciad = 0x00008000; /* VFLR */
IXGBE_WRITE_REG(hw, IXGBE_CIAD_BY_MAC(hw), ciad);
ciaa &= 0x7FFFFFFF;
IXGBE_WRITE_REG(hw, IXGBE_CIAA_BY_MAC(hw), ciaa);
}
}
}
#endif
/**
* ixgbe_service_timer - Timer Call-back
* @data: pointer to adapter cast into an unsigned long
@ -6522,7 +6549,6 @@ static void ixgbe_service_timer(unsigned long data)
{
struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
unsigned long next_event_offset;
bool ready = true;
/* poll faster when waiting for link */
if (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE)
@ -6530,32 +6556,10 @@ static void ixgbe_service_timer(unsigned long data)
else
next_event_offset = HZ * 2;
#ifdef CONFIG_PCI_IOV
/*
* don't bother with SR-IOV VF DMA hang check if there are
* no VFs or the link is down
*/
if (!adapter->num_vfs ||
(adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE))
goto normal_timer_service;
/* If we have VFs allocated then we must check for DMA hangs */
ixgbe_check_for_bad_vf(adapter);
next_event_offset = HZ / 50;
adapter->timer_event_accumulator++;
if (adapter->timer_event_accumulator >= 100)
adapter->timer_event_accumulator = 0;
else
ready = false;
normal_timer_service:
#endif
/* Reset the timer */
mod_timer(&adapter->service_timer, next_event_offset + jiffies);
if (ready)
ixgbe_service_event_schedule(adapter);
ixgbe_service_event_schedule(adapter);
}
static void ixgbe_reset_subtask(struct ixgbe_adapter *adapter)
@ -8643,8 +8647,7 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
* VFLR. Just clean up the AER in that case.
*/
if (vfdev) {
e_dev_err("Issuing VFLR to VF %d\n", vf);
pci_write_config_dword(vfdev, 0xA8, 0x00008000);
ixgbe_issue_vf_flr(adapter, vfdev);
/* Free device reference count */
pci_dev_put(vfdev);
}