From 83c61fa97a7d4ef16506a760f9e52b3144978346 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Wed, 7 Sep 2011 05:59:35 +0000 Subject: [PATCH 01/11] 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 Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 4 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 172 +++++++++++++++++- 2 files changed, 175 insertions(+), 1 deletion(-) 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 @@ #define MAX_EMULATION_MAC_ADDRS 16 #define IXGBE_MAX_PF_MACVLANS 15 #define VMDQ_P(p) ((p) + adapter->num_vfs) +#define IXGBE_82599_VF_DEVICE_ID 0x10ED +#define IXGBE_X540_VF_DEVICE_ID 0x1515 struct vf_data_storage { unsigned char vf_mac_addresses[ETH_ALEN]; @@ -512,6 +514,8 @@ struct ixgbe_adapter { struct hlist_head fdir_filter_list; union ixgbe_atr_input fdir_mask; int fdir_filter_count; + u32 timer_event_accumulator; + u32 vferr_refcount; }; 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) 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_82599, ciaa); + ciad = IXGBE_READ_REG(hw, IXGBE_CIAD_82599); + ciaa &= 0x7FFFFFFF; + /* disable debug mode asap after reading data */ + IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, 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_82599, ciaa); + ciad = 0x00008000; /* VFLR */ + IXGBE_WRITE_REG(hw, IXGBE_CIAD_82599, ciad); + ciaa &= 0x7FFFFFFF; + IXGBE_WRITE_REG(hw, IXGBE_CIAA_82599, ciaa); + } + } +} + +#endif /** * ixgbe_service_timer - Timer Call-back * @data: pointer to adapter cast into an unsigned long @@ -6120,17 +6165,49 @@ static void ixgbe_service_timer(unsigned long data) { struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data; unsigned long next_event_offset; + bool ready = true; +#ifdef CONFIG_PCI_IOV + ready = false; + + /* + * 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)) { + ready = true; + 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) { + ready = true; + adapter->timer_event_accumulator = 0; + } + + goto schedule_event; + +normal_timer_service: +#endif /* poll faster when waiting for link */ if (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE) next_event_offset = HZ / 10; else next_event_offset = HZ * 2; +#ifdef CONFIG_PCI_IOV +schedule_event: +#endif /* Reset the timer */ mod_timer(&adapter->service_timer, next_event_offset + jiffies); - ixgbe_service_event_schedule(adapter); + if (ready) + ixgbe_service_event_schedule(adapter); } 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, struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev = adapter->netdev; +#ifdef CONFIG_PCI_IOV + struct pci_dev *bdev, *vfdev; + u32 dw0, dw1, dw2, dw3; + int vf, pos; + u16 req_id, pf_func; + + if (adapter->hw.mac.type == ixgbe_mac_82598EB || + adapter->num_vfs == 0) + goto skip_bad_vf_detection; + + bdev = pdev->bus->self; + while (bdev && (bdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT)) + bdev = bdev->bus->self; + + if (!bdev) + goto skip_bad_vf_detection; + + pos = pci_find_ext_capability(bdev, PCI_EXT_CAP_ID_ERR); + if (!pos) + goto skip_bad_vf_detection; + + pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG, &dw0); + pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 4, &dw1); + pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 8, &dw2); + pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 12, &dw3); + + req_id = dw1 >> 16; + /* On the 82599 if bit 7 of the requestor ID is set then it's a VF */ + if (!(req_id & 0x0080)) + goto skip_bad_vf_detection; + + pf_func = req_id & 0x01; + if ((pf_func & 1) == (pdev->devfn & 1)) { + unsigned int device_id; + + vf = (req_id & 0x7F) >> 1; + e_dev_err("VF %d has caused a PCIe error\n", vf); + e_dev_err("TLP: dw0: %8.8x\tdw1: %8.8x\tdw2: " + "%8.8x\tdw3: %8.8x\n", + dw0, dw1, dw2, dw3); + switch (adapter->hw.mac.type) { + case ixgbe_mac_82599EB: + device_id = IXGBE_82599_VF_DEVICE_ID; + break; + case ixgbe_mac_X540: + device_id = IXGBE_X540_VF_DEVICE_ID; + break; + default: + device_id = 0; + break; + } + + /* Find the pci device of the offending VF */ + vfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, device_id, NULL); + while (vfdev) { + if (vfdev->devfn == (req_id & 0xFF)) + break; + vfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, + device_id, vfdev); + } + /* + * There's a slim chance the VF could have been hot plugged, + * so if it is no longer present we don't need to issue the + * 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); + } + + pci_cleanup_aer_uncorrect_error_status(pdev); + } + + /* + * Even though the error may have occurred on the other port + * we still need to increment the vf error reference count for + * both ports because the I/O resume function will be called + * for both of them. + */ + adapter->vferr_refcount++; + + return PCI_ERS_RESULT_RECOVERED; + +skip_bad_vf_detection: +#endif /* CONFIG_PCI_IOV */ netif_device_detach(netdev); if (state == pci_channel_io_perm_failure) @@ -7779,6 +7941,14 @@ static void ixgbe_io_resume(struct pci_dev *pdev) struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev = adapter->netdev; +#ifdef CONFIG_PCI_IOV + if (adapter->vferr_refcount) { + e_info(drv, "Resuming after VF err\n"); + adapter->vferr_refcount--; + return; + } + +#endif if (netif_running(netdev)) ixgbe_up(adapter); From 7b859ebc0a69a7d142f705bd4a8e5720b810f718 Mon Sep 17 00:00:00 2001 From: Amir Hanania Date: Wed, 31 Aug 2011 02:07:55 +0000 Subject: [PATCH 02/11] ixgbe: Add FCoE DDP allocation failure counters to ethtool stats. Add 2 new counters to ethtool: 1. Count DDP allocation failure since we max the number of buffers allowed in one DDP context. 2. Count DDP allocation failure since we max the number of buffers allowed in one DDP context when we alloc an extra buffer. Signed-off-by: Amir Hanania Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 2 + drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c | 44 ++++++++++++++----- drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h | 2 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 17 +++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 2 + 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 18520cef3e94..e102ff6fb08d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -113,6 +113,8 @@ static struct ixgbe_stats ixgbe_gstrings_stats[] = { {"rx_fcoe_dropped", IXGBE_STAT(stats.fcoerpdc)}, {"rx_fcoe_packets", IXGBE_STAT(stats.fcoeprc)}, {"rx_fcoe_dwords", IXGBE_STAT(stats.fcoedwrc)}, + {"fcoe_noddp", IXGBE_STAT(stats.fcoe_noddp)}, + {"fcoe_noddp_ext_buff", IXGBE_STAT(stats.fcoe_noddp_ext_buff)}, {"tx_fcoe_packets", IXGBE_STAT(stats.fcoeptc)}, {"tx_fcoe_dwords", IXGBE_STAT(stats.fcoedwtc)}, #endif /* IXGBE_FCOE */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index 323f4529992d..df3b1be69d83 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -145,6 +145,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid, u32 fcbuff, fcdmarw, fcfltrw, fcrxctl; dma_addr_t addr = 0; struct pci_pool *pool; + unsigned int cpu; if (!netdev || !sgl) return 0; @@ -182,7 +183,8 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid, } /* alloc the udl from per cpu ddp pool */ - pool = *per_cpu_ptr(fcoe->pool, get_cpu()); + cpu = get_cpu(); + pool = *per_cpu_ptr(fcoe->pool, cpu); ddp->udl = pci_pool_alloc(pool, GFP_ATOMIC, &ddp->udp); if (!ddp->udl) { e_err(drv, "failed allocated ddp context\n"); @@ -199,9 +201,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid, while (len) { /* max number of buffers allowed in one DDP context */ if (j >= IXGBE_BUFFCNT_MAX) { - e_err(drv, "xid=%x:%d,%d,%d:addr=%llx " - "not enough descriptors\n", - xid, i, j, dmacount, (u64)addr); + *per_cpu_ptr(fcoe->pcpu_noddp, cpu) += 1; goto out_noddp_free; } @@ -241,12 +241,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid, */ if (lastsize == bufflen) { if (j >= IXGBE_BUFFCNT_MAX) { - printk_once("Will NOT use DDP since there are not " - "enough user buffers. We need an extra " - "buffer because lastsize is bufflen. " - "xid=%x:%d,%d,%d:addr=%llx\n", - xid, i, j, dmacount, (u64)addr); - + *per_cpu_ptr(fcoe->pcpu_noddp_ext_buff, cpu) += 1; goto out_noddp_free; } @@ -600,6 +595,7 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_fcoe *fcoe = &adapter->fcoe; struct ixgbe_ring_feature *f = &adapter->ring_feature[RING_F_FCOE]; + unsigned int cpu; if (!fcoe->pool) { spin_lock_init(&fcoe->lock); @@ -627,6 +623,24 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter) e_err(drv, "failed to map extra DDP buffer\n"); goto out_extra_ddp_buffer; } + + /* Alloc per cpu mem to count the ddp alloc failure number */ + fcoe->pcpu_noddp = alloc_percpu(u64); + if (!fcoe->pcpu_noddp) { + e_err(drv, "failed to alloc noddp counter\n"); + goto out_pcpu_noddp_alloc_fail; + } + + fcoe->pcpu_noddp_ext_buff = alloc_percpu(u64); + if (!fcoe->pcpu_noddp_ext_buff) { + e_err(drv, "failed to alloc noddp extra buff cnt\n"); + goto out_pcpu_noddp_extra_buff_alloc_fail; + } + + for_each_possible_cpu(cpu) { + *per_cpu_ptr(fcoe->pcpu_noddp, cpu) = 0; + *per_cpu_ptr(fcoe->pcpu_noddp_ext_buff, cpu) = 0; + } } /* Enable L2 eth type filter for FCoE */ @@ -664,7 +678,13 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_FCRXCTRL, IXGBE_FCRXCTRL_FCCRCBO | (FC_FCOE_VER << IXGBE_FCRXCTRL_FCOEVER_SHIFT)); return; - +out_pcpu_noddp_extra_buff_alloc_fail: + free_percpu(fcoe->pcpu_noddp); +out_pcpu_noddp_alloc_fail: + dma_unmap_single(&adapter->pdev->dev, + fcoe->extra_ddp_buffer_dma, + IXGBE_FCBUFF_MIN, + DMA_FROM_DEVICE); out_extra_ddp_buffer: kfree(fcoe->extra_ddp_buffer); out_ddp_pools: @@ -693,6 +713,8 @@ void ixgbe_cleanup_fcoe(struct ixgbe_adapter *adapter) fcoe->extra_ddp_buffer_dma, IXGBE_FCBUFF_MIN, DMA_FROM_DEVICE); + free_percpu(fcoe->pcpu_noddp); + free_percpu(fcoe->pcpu_noddp_ext_buff); kfree(fcoe->extra_ddp_buffer); ixgbe_fcoe_ddp_pools_free(fcoe); } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h index 99de145e290d..261fd62dda18 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h @@ -73,6 +73,8 @@ struct ixgbe_fcoe { unsigned char *extra_ddp_buffer; dma_addr_t extra_ddp_buffer_dma; unsigned long mode; + u64 __percpu *pcpu_noddp; + u64 __percpu *pcpu_noddp_ext_buff; #ifdef CONFIG_IXGBE_DCB u8 up; #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index b95c6e979832..f6fea6798851 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5552,6 +5552,11 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) u64 non_eop_descs = 0, restart_queue = 0, tx_busy = 0; u64 alloc_rx_page_failed = 0, alloc_rx_buff_failed = 0; u64 bytes = 0, packets = 0; +#ifdef IXGBE_FCOE + struct ixgbe_fcoe *fcoe = &adapter->fcoe; + unsigned int cpu; + u64 fcoe_noddp_counts_sum = 0, fcoe_noddp_ext_buff_counts_sum = 0; +#endif /* IXGBE_FCOE */ if (test_bit(__IXGBE_DOWN, &adapter->state) || test_bit(__IXGBE_RESETTING, &adapter->state)) @@ -5679,6 +5684,18 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) hwstats->fcoeptc += IXGBE_READ_REG(hw, IXGBE_FCOEPTC); hwstats->fcoedwrc += IXGBE_READ_REG(hw, IXGBE_FCOEDWRC); hwstats->fcoedwtc += IXGBE_READ_REG(hw, IXGBE_FCOEDWTC); + /* Add up per cpu counters for total ddp aloc fail */ + if (fcoe->pcpu_noddp && fcoe->pcpu_noddp_ext_buff) { + for_each_possible_cpu(cpu) { + fcoe_noddp_counts_sum += + *per_cpu_ptr(fcoe->pcpu_noddp, cpu); + fcoe_noddp_ext_buff_counts_sum += + *per_cpu_ptr(fcoe-> + pcpu_noddp_ext_buff, cpu); + } + } + hwstats->fcoe_noddp = fcoe_noddp_counts_sum; + hwstats->fcoe_noddp_ext_buff = fcoe_noddp_ext_buff_counts_sum; #endif /* IXGBE_FCOE */ break; default: diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index d1d689471523..6c5cca808bd7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -2682,6 +2682,8 @@ struct ixgbe_hw_stats { u64 fcoeptc; u64 fcoedwrc; u64 fcoedwtc; + u64 fcoe_noddp; + u64 fcoe_noddp_ext_buff; u64 b2ospc; u64 b2ogprc; u64 o2bgptc; From 15d447ecaff457e6f89b459e70c0770b35b35533 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Tue, 20 Sep 2011 03:00:22 +0000 Subject: [PATCH 03/11] ixgbe: Correct check for change in FCoE priority Correct a check for change in FCoE priority when IEEE mode DCB is in use. In IEEE mode a different function has to be used to get the FCoE priority mask. Also, the check for the mask assumed that only one priority was set. In case there should be more than one, check just the bit. These changes help avoid link flapping issues that can come up when IEEE DCB is in use. Signed-off-by: Mark Rustad Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index be66bb679d5a..3631d639d86a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -318,7 +318,15 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) .selector = DCB_APP_IDTYPE_ETHTYPE, .protocol = ETH_P_FCOE, }; - u8 up = dcb_getapp(netdev, &app); + u8 up; + + /* In IEEE mode, use the IEEE Ethertype selector value */ + if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) { + app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; + up = dcb_ieee_getapp_mask(netdev, &app); + } else { + up = dcb_getapp(netdev, &app); + } #endif /* Fail command if not in CEE mode */ @@ -331,7 +339,7 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev) return DCB_NO_HW_CHG; #ifdef IXGBE_FCOE - if (up && (up != (1 << adapter->fcoe.up))) + if (up && !(up & (1 << adapter->fcoe.up))) adapter->dcb_set_bitmap |= BIT_APP_UPCHG; /* From 0d1ae7f46f1b51623bed2904576d15f6ecd5dc10 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 26 Aug 2011 07:46:34 +0000 Subject: [PATCH 04/11] igb: avoid unnecessarily creating a local copy of the q_vector This is mostly a drop of unnecessary pointer defines for q_vector when we don't have issues with line width and don't have multiple references to the pointer. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 46 +++++++++-------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 10670f944115..3905a499a591 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1317,11 +1317,9 @@ static void igb_free_irq(struct igb_adapter *adapter) free_irq(adapter->msix_entries[vector++].vector, adapter); - for (i = 0; i < adapter->num_q_vectors; i++) { - struct igb_q_vector *q_vector = adapter->q_vector[i]; + for (i = 0; i < adapter->num_q_vectors; i++) free_irq(adapter->msix_entries[vector++].vector, - q_vector); - } + adapter->q_vector[i]); } else { free_irq(adapter->pdev->irq, adapter); } @@ -1523,10 +1521,9 @@ int igb_up(struct igb_adapter *adapter) clear_bit(__IGB_DOWN, &adapter->state); - for (i = 0; i < adapter->num_q_vectors; i++) { - struct igb_q_vector *q_vector = adapter->q_vector[i]; - napi_enable(&q_vector->napi); - } + for (i = 0; i < adapter->num_q_vectors; i++) + napi_enable(&(adapter->q_vector[i]->napi)); + if (adapter->msix_entries) igb_configure_msix(adapter); else @@ -1578,10 +1575,8 @@ void igb_down(struct igb_adapter *adapter) wrfl(); msleep(10); - for (i = 0; i < adapter->num_q_vectors; i++) { - struct igb_q_vector *q_vector = adapter->q_vector[i]; - napi_disable(&q_vector->napi); - } + for (i = 0; i < adapter->num_q_vectors; i++) + napi_disable(&(adapter->q_vector[i]->napi)); igb_irq_disable(adapter); @@ -2546,10 +2541,8 @@ static int igb_open(struct net_device *netdev) /* From here on the code is the same as igb_up() */ clear_bit(__IGB_DOWN, &adapter->state); - for (i = 0; i < adapter->num_q_vectors; i++) { - struct igb_q_vector *q_vector = adapter->q_vector[i]; - napi_enable(&q_vector->napi); - } + for (i = 0; i < adapter->num_q_vectors; i++) + napi_enable(&(adapter->q_vector[i]->napi)); /* Clear any pending interrupts. */ rd32(E1000_ICR); @@ -3769,10 +3762,8 @@ static void igb_watchdog_task(struct work_struct *work) /* Cause software interrupt to ensure rx ring is cleaned */ if (adapter->msix_entries) { u32 eics = 0; - for (i = 0; i < adapter->num_q_vectors; i++) { - struct igb_q_vector *q_vector = adapter->q_vector[i]; - eics |= q_vector->eims_value; - } + for (i = 0; i < adapter->num_q_vectors; i++) + eics |= adapter->q_vector[i]->eims_value; wr32(E1000_EICS, eics); } else { wr32(E1000_ICS, E1000_ICS_RXDMT0); @@ -6671,18 +6662,15 @@ static void igb_netpoll(struct net_device *netdev) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + struct igb_q_vector *q_vector; int i; - if (!adapter->msix_entries) { - struct igb_q_vector *q_vector = adapter->q_vector[0]; - igb_irq_disable(adapter); - napi_schedule(&q_vector->napi); - return; - } - for (i = 0; i < adapter->num_q_vectors; i++) { - struct igb_q_vector *q_vector = adapter->q_vector[i]; - wr32(E1000_EIMC, q_vector->eims_value); + q_vector = adapter->q_vector[i]; + if (adapter->msix_entries) + wr32(E1000_EIMC, q_vector->eims_value); + else + igb_irq_disable(adapter); napi_schedule(&q_vector->napi); } } From c74d588e2addd9a13cca49a4d9172e0e2948448f Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 26 Aug 2011 07:46:45 +0000 Subject: [PATCH 05/11] igb: Make certain one vector is always assigned in igb_request_irq This change makes certain that one interrupt is always initialized in igb_request_irq. In addition we drop the use of adapter->pdev and instead just call pdev since we made a local copy of the pointer earlier in the function. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 3905a499a591..efc367bddc47 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1262,7 +1262,7 @@ static int igb_request_irq(struct igb_adapter *adapter) goto request_done; /* fall back to MSI */ igb_clear_interrupt_scheme(adapter); - if (!pci_enable_msi(adapter->pdev)) + if (!pci_enable_msi(pdev)) adapter->flags |= IGB_FLAG_HAS_MSI; igb_free_all_tx_resources(adapter); igb_free_all_rx_resources(adapter); @@ -1284,12 +1284,12 @@ static int igb_request_irq(struct igb_adapter *adapter) } igb_setup_all_tx_resources(adapter); igb_setup_all_rx_resources(adapter); - } else { - igb_assign_vector(adapter->q_vector[0], 0); } + igb_assign_vector(adapter->q_vector[0], 0); + if (adapter->flags & IGB_FLAG_HAS_MSI) { - err = request_irq(adapter->pdev->irq, igb_intr_msi, 0, + err = request_irq(pdev->irq, igb_intr_msi, 0, netdev->name, adapter); if (!err) goto request_done; @@ -1299,11 +1299,11 @@ static int igb_request_irq(struct igb_adapter *adapter) adapter->flags &= ~IGB_FLAG_HAS_MSI; } - err = request_irq(adapter->pdev->irq, igb_intr, IRQF_SHARED, + err = request_irq(pdev->irq, igb_intr, IRQF_SHARED, netdev->name, adapter); if (err) - dev_err(&adapter->pdev->dev, "Error %d getting interrupt\n", + dev_err(&pdev->dev, "Error %d getting interrupt\n", err); request_done: From 06218a8dbf046c0e9ba51dcbe1ce980a10a0be42 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 26 Aug 2011 07:46:55 +0000 Subject: [PATCH 06/11] igb: Fix features that are currently 82580 only and should also be i350 This change allows support for per packet timesync and global device reset on the i350 adapter. These features were supported on both 82580 and i350 however it looks like several checks where not updated and as such the i350 support was not enabled. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index efc367bddc47..fee771b4d332 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -563,7 +563,7 @@ static cycle_t igb_read_clock(const struct cyclecounter *tc) * the lowest register is SYSTIMR instead of SYSTIML. However we never * adjusted TIMINCA so SYSTIMR will just read as all 0s so ignore it. */ - if (hw->mac.type == e1000_82580) { + if (hw->mac.type >= e1000_82580) { stamp = rd32(E1000_SYSTIMR) >> 8; shift = IGB_82580_TSYNC_SHIFT; } @@ -1367,7 +1367,7 @@ static void igb_irq_enable(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; if (adapter->msix_entries) { - u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC; + u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC | E1000_IMS_DRSTA; u32 regval = rd32(E1000_EIAC); wr32(E1000_EIAC, regval | adapter->eims_enable_mask); regval = rd32(E1000_EIAM); @@ -1377,9 +1377,6 @@ static void igb_irq_enable(struct igb_adapter *adapter) wr32(E1000_MBVFIMR, 0xFF); ims |= E1000_IMS_VMMB; } - if (adapter->hw.mac.type == e1000_82580) - ims |= E1000_IMS_DRSTA; - wr32(E1000_IMS, ims); } else { wr32(E1000_IMS, IMS_ENABLE_MASK | @@ -3112,7 +3109,7 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, srrctl |= (PAGE_SIZE / 2) >> E1000_SRRCTL_BSIZEPKT_SHIFT; #endif srrctl |= E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS; - if (hw->mac.type == e1000_82580) + if (hw->mac.type >= e1000_82580) srrctl |= E1000_SRRCTL_TIMESTAMP; /* Only set Drop Enable if we are supporting multiple queues */ if (adapter->vfs_allocated_count || adapter->num_rx_queues > 1) @@ -4463,7 +4460,7 @@ static void igb_tx_timeout(struct net_device *netdev) /* Do the reset outside of interrupt context */ adapter->tx_timeout_count++; - if (hw->mac.type == e1000_82580) + if (hw->mac.type >= e1000_82580) hw->dev_spec._82575.global_device_reset = true; schedule_work(&adapter->reset_task); @@ -5581,7 +5578,7 @@ static void igb_systim_to_hwtstamp(struct igb_adapter *adapter, * The 82580 starts with 1ns at bit 0 in RX/TXSTMPL, shift this up to * 24 to match clock shift we setup earlier. */ - if (adapter->hw.mac.type == e1000_82580) + if (adapter->hw.mac.type >= e1000_82580) regval <<= IGB_82580_TSYNC_SHIFT; ns = timecounter_cyc2time(&adapter->clock, regval); @@ -6276,7 +6273,7 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev, * timestamped, so enable timestamping in all packets as * long as one rx filter was configured. */ - if ((hw->mac.type == e1000_82580) && tsync_rx_ctl) { + if ((hw->mac.type >= e1000_82580) && tsync_rx_ctl) { tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED; tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL; } From 9ab64ba3c74540cfb8716232834df486bcc6120d Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 26 Aug 2011 07:47:01 +0000 Subject: [PATCH 07/11] igb: Drop unnecessary write of E1000_IMS from igb_msix_other Since we mask interrupts in EIMS not in IMS there is no need to re-enable mask bits in that register. As such we can remove the write to IMS from the end of igb_msix_other. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index fee771b4d332..9e7930686cd0 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -4766,12 +4766,6 @@ static irqreturn_t igb_msix_other(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (adapter->vfs_allocated_count) - wr32(E1000_IMS, E1000_IMS_LSC | - E1000_IMS_VMMB | - E1000_IMS_DOUTSYNC); - else - wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC); wr32(E1000_EIMS, adapter->eims_other); return IRQ_HANDLED; From 8be10e9130a75c49ddcffdfca74b19b1c3169230 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 26 Aug 2011 07:47:11 +0000 Subject: [PATCH 08/11] igb: Add workaround for byte swapped VLAN on i350 local traffic On i350 when traffic is looped back from a VF to the PF the value is byte swapped from the normal format. In order to address this we need to add a flag indicating that the ring will need to byte swap the loopback packets prior to processing them. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- .../net/ethernet/intel/igb/e1000_defines.h | 1 + drivers/net/ethernet/intel/igb/igb.h | 1 + drivers/net/ethernet/intel/igb/igb_main.c | 29 +++++++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index 68558be6f9e7..f5fc5725ea94 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -85,6 +85,7 @@ #define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ #define E1000_RXD_STAT_TS 0x10000 /* Pkt was time stamped */ +#define E1000_RXDEXT_STATERR_LB 0x00040000 #define E1000_RXDEXT_STATERR_CE 0x01000000 #define E1000_RXDEXT_STATERR_SE 0x02000000 #define E1000_RXDEXT_STATERR_SEQ 0x04000000 diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 4e665a9b4763..4c500a76972e 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -245,6 +245,7 @@ struct igb_ring { enum e1000_ring_flags_t { IGB_RING_FLAG_RX_SCTP_CSUM, + IGB_RING_FLAG_RX_LB_VLAN_BSWAP, IGB_RING_FLAG_TX_CTX_IDX, IGB_RING_FLAG_TX_DETECT_HANG }; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 9e7930686cd0..582432f24166 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -735,6 +735,11 @@ static int igb_alloc_queues(struct igb_adapter *adapter) /* set flag indicating ring supports SCTP checksum offload */ if (adapter->hw.mac.type >= e1000_82576) set_bit(IGB_RING_FLAG_RX_SCTP_CSUM, &ring->flags); + + /* On i350, loopback VLAN packets have the tag byte-swapped. */ + if (adapter->hw.mac.type == e1000_i350) + set_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &ring->flags); + adapter->rx_ring[i] = ring; } /* Restore the adapter's original node */ @@ -5864,6 +5869,23 @@ static void igb_rx_hwtstamp(struct igb_q_vector *q_vector, igb_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval); } + +static void igb_rx_vlan(struct igb_ring *ring, + union e1000_adv_rx_desc *rx_desc, + struct sk_buff *skb) +{ + if (igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) { + u16 vid; + if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) && + test_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &ring->flags)) + vid = be16_to_cpu(rx_desc->wb.upper.vlan); + else + vid = le16_to_cpu(rx_desc->wb.upper.vlan); + + __vlan_hwaccel_put_tag(skb, vid); + } +} + static inline u16 igb_get_hlen(union e1000_adv_rx_desc *rx_desc) { /* HW will not DMA in data larger than the given buffer, even if it @@ -5960,12 +5982,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) igb_rx_hwtstamp(q_vector, rx_desc, skb); igb_rx_hash(rx_ring, rx_desc, skb); igb_rx_checksum(rx_ring, rx_desc, skb); - - if (igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) { - u16 vid = le16_to_cpu(rx_desc->wb.upper.vlan); - - __vlan_hwaccel_put_tag(skb, vid); - } + igb_rx_vlan(rx_ring, rx_desc, skb); total_bytes += skb->len; total_packets++; From bed45a6ed51d00007f5eb6d75560218ddcecfe51 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Tue, 30 Aug 2011 06:35:04 +0000 Subject: [PATCH 09/11] igb: fix static function warnings reported by sparse igb_update/validate_nvm_checksum_with_offset() should be static. Also removes unneeded prototypes for the above functions. Signed-off-by: Emil Tantilov Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index c0857bdfb03a..3771bd20f437 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -66,10 +66,6 @@ static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw); static s32 igb_reset_mdicnfg_82580(struct e1000_hw *hw); static s32 igb_validate_nvm_checksum_82580(struct e1000_hw *hw); static s32 igb_update_nvm_checksum_82580(struct e1000_hw *hw); -static s32 igb_update_nvm_checksum_with_offset(struct e1000_hw *hw, - u16 offset); -static s32 igb_validate_nvm_checksum_with_offset(struct e1000_hw *hw, - u16 offset); static s32 igb_validate_nvm_checksum_i350(struct e1000_hw *hw); static s32 igb_update_nvm_checksum_i350(struct e1000_hw *hw); static const u16 e1000_82580_rxpbs_table[] = @@ -1820,7 +1816,8 @@ u16 igb_rxpbs_adjust_82580(u32 data) * Calculates the EEPROM checksum by reading/adding each word of the EEPROM * and then verifies that the sum of the EEPROM is equal to 0xBABA. **/ -s32 igb_validate_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) +static s32 igb_validate_nvm_checksum_with_offset(struct e1000_hw *hw, + u16 offset) { s32 ret_val = 0; u16 checksum = 0; @@ -1855,7 +1852,7 @@ s32 igb_validate_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) * up to the checksum. Then calculates the EEPROM checksum and writes the * value to the EEPROM. **/ -s32 igb_update_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) +static s32 igb_update_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) { s32 ret_val; u16 checksum = 0; From ca2e3e7ec98937e12df4bbdcc9a367b8768290ce Mon Sep 17 00:00:00 2001 From: "Akeem G. Abodunrin" Date: Thu, 8 Sep 2011 20:39:48 +0000 Subject: [PATCH 10/11] igb: Loopback functionality supports for i350 devices This patch adds VMDq loopback pf support for i350 devices. The patch is necessary since the register that enabled loopback was moved and renamed from DTXSWC to TXSWC. Signed-off-by: "Akeem G. Abodunrin" Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 29 ++++++++++++++++---- drivers/net/ethernet/intel/igb/e1000_regs.h | 1 + 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 3771bd20f437..6580cea796c5 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -1580,14 +1580,31 @@ void igb_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf) **/ void igb_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable) { - u32 dtxswc = rd32(E1000_DTXSWC); + u32 dtxswc; + + switch (hw->mac.type) { + case e1000_82576: + dtxswc = rd32(E1000_DTXSWC); + if (enable) + dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; + else + dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN; + wr32(E1000_DTXSWC, dtxswc); + break; + case e1000_i350: + dtxswc = rd32(E1000_TXSWC); + if (enable) + dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; + else + dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN; + wr32(E1000_TXSWC, dtxswc); + break; + default: + /* Currently no other hardware supports loopback */ + break; + } - if (enable) - dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; - else - dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN; - wr32(E1000_DTXSWC, dtxswc); } /** diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h index 0990f6d860c7..0a860bc1198e 100644 --- a/drivers/net/ethernet/intel/igb/e1000_regs.h +++ b/drivers/net/ethernet/intel/igb/e1000_regs.h @@ -318,6 +318,7 @@ #define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */ #define E1000_UTA 0x0A000 /* Unicast Table Array - RW */ #define E1000_IOVTCL 0x05BBC /* IOV Control Register */ +#define E1000_TXSWC 0x05ACC /* Tx Switch Control */ /* These act per VF so an array friendly macro is used */ #define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n))) #define E1000_VMBMEM(_n) (0x00800 + (64 * (_n))) From a28dc43f1d8dfc4fe61c9b9505c1b902285c96b8 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Fri, 7 Oct 2011 07:00:27 +0000 Subject: [PATCH 11/11] igb: Version bump. This change updates the driver version to 3.2.10. Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 582432f24166..8227824e2027 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -57,8 +57,8 @@ #include "igb.h" #define MAJ 3 -#define MIN 0 -#define BUILD 6 +#define MIN 2 +#define BUILD 10 #define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \ __stringify(BUILD) "-k" char igb_driver_name[] = "igb";