Commit fb52cab5 authored by David S. Miller's avatar David S. Miller

Merge branch 'intel-next'

Aaron Brown says:

====================
Intel Wired LAN Driver Updates, ixgbe: Add LER support

The following patches add Live Error Recovery (LER) support to the
ixgbe driver. This support also improves behavior in Thunderbolt
environments. This involves checking all register reads for a
value of all ones and when that is seen, to read the status
register, which should never properly return all ones, to
confirm whether the received value was correct. When this detects
a removal, the hw_addr field is cleared to indicate the removal.
This then blocks subsequent access to the device registers.

All register access macros have been changed to static inline
functions and all register accesses now use them.· Macro versions
are temporarily provided.

The __IXGBE_DOWN bit is no longer overloaded to also mean that
device removal has been initiated. Now the bit can be used to
protect ixgbe_down from multiple entry via test_and_set_bit. A
needed smp_mb__before_clear_bit was also added.

V2 Changes:
- Use ACCESS_ONCE where needed, thanks to Ben Hutchings
- Fix crash on module removal
- Use boolean values for boolean returns instead of 0 and 1
- Reword Kconfig help text

V3 Changes:
- Drop config option, per David Miller
- Drop tail register write checks, per Alexander Duyck
- Change writeq implementation to a static inline, thanks to Joe Perches

V4 Changes:
- Change __IXGBE_REMOVE to __IXGBE_REMOVING, per Scott Feldman's comment
- Add #define writeq writeq, per Alexander Duyck
- Change static inline functions to lower case, per David Miller
- Use new lower case names in added and modified register accesses
- Provide temporary upper case macros for register access functions
- Change IXGBE_REMOVED from macro to static inline and change references
- Correct IXGBE_WRITE_FLUSH to properly enclose parameter expansion
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e1f7dedc b0483c8f
......@@ -585,6 +585,11 @@ static inline u16 ixgbe_desc_unused(struct ixgbe_ring *ring)
return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1;
}
static inline void ixgbe_write_tail(struct ixgbe_ring *ring, u32 value)
{
writel(value, ring->tail);
}
#define IXGBE_RX_DESC(R, i) \
(&(((union ixgbe_adv_rx_desc *)((R)->desc))[i]))
#define IXGBE_TX_DESC(R, i) \
......@@ -742,6 +747,7 @@ struct ixgbe_adapter {
#ifdef IXGBE_FCOE
struct ixgbe_fcoe fcoe;
#endif /* IXGBE_FCOE */
u8 __iomem *io_addr; /* Mainly for iounmap use */
u32 wol;
u16 bd_number;
......@@ -798,6 +804,7 @@ enum ixgbe_state_t {
__IXGBE_TESTING,
__IXGBE_RESETTING,
__IXGBE_DOWN,
__IXGBE_REMOVING,
__IXGBE_SERVICE_SCHED,
__IXGBE_IN_SFP_INIT,
__IXGBE_PTP_RUNNING,
......
......@@ -124,24 +124,65 @@ s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw);
s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw);
s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw);
#define IXGBE_WRITE_REG(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
#define IXGBE_FAILED_READ_REG 0xffffffffU
#ifndef writeq
#define writeq(val, addr) writel((u32) (val), addr); \
writel((u32) (val >> 32), (addr + 4));
#endif
static inline bool ixgbe_removed(void __iomem *addr)
{
return unlikely(!addr);
}
#define IXGBE_WRITE_REG64(a, reg, value) writeq((value), ((a)->hw_addr + (reg)))
void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg);
#define IXGBE_READ_REG(a, reg) readl((a)->hw_addr + (reg))
static inline void ixgbe_write_reg(struct ixgbe_hw *hw, u32 reg, u32 value)
{
u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
#define IXGBE_WRITE_REG_ARRAY(a, reg, offset, value) (\
writel((value), ((a)->hw_addr + (reg) + ((offset) << 2))))
if (ixgbe_removed(reg_addr))
return;
writel(value, reg_addr + reg);
}
#define IXGBE_WRITE_REG(a, reg, value) ixgbe_write_reg((a), (reg), (value))
#define IXGBE_READ_REG_ARRAY(a, reg, offset) (\
readl((a)->hw_addr + (reg) + ((offset) << 2)))
#ifndef writeq
#define writeq writeq
static inline void writeq(u64 val, void __iomem *addr)
{
writel((u32)val, addr);
writel((u32)(val >> 32), addr + 4);
}
#endif
#define IXGBE_WRITE_FLUSH(a) IXGBE_READ_REG(a, IXGBE_STATUS)
static inline void ixgbe_write_reg64(struct ixgbe_hw *hw, u32 reg, u64 value)
{
u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
if (ixgbe_removed(reg_addr))
return;
writeq(value, reg_addr + reg);
}
#define IXGBE_WRITE_REG64(a, reg, value) ixgbe_write_reg64((a), (reg), (value))
static inline u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg)
{
u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
u32 value;
if (ixgbe_removed(reg_addr))
return IXGBE_FAILED_READ_REG;
value = readl(reg_addr + reg);
if (unlikely(value == IXGBE_FAILED_READ_REG))
ixgbe_check_remove(hw, reg);
return value;
}
#define IXGBE_READ_REG(a, reg) ixgbe_read_reg((a), (reg))
#define IXGBE_WRITE_REG_ARRAY(a, reg, offset, value) \
ixgbe_write_reg((a), (reg) + ((offset) << 2), (value))
#define IXGBE_READ_REG_ARRAY(a, reg, offset) \
ixgbe_read_reg((a), (reg) + ((offset) << 2))
#define IXGBE_WRITE_FLUSH(a) ixgbe_read_reg((a), IXGBE_STATUS)
#define ixgbe_hw_to_netdev(hw) (((struct ixgbe_adapter *)(hw)->back)->netdev)
......
......@@ -1342,61 +1342,61 @@ static bool reg_pattern_test(struct ixgbe_adapter *adapter, u64 *data, int reg,
static const u32 test_pattern[] = {
0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
if (ixgbe_removed(adapter->hw.hw_addr)) {
*data = 1;
return 1;
}
for (pat = 0; pat < ARRAY_SIZE(test_pattern); pat++) {
before = readl(adapter->hw.hw_addr + reg);
writel((test_pattern[pat] & write),
(adapter->hw.hw_addr + reg));
val = readl(adapter->hw.hw_addr + reg);
before = ixgbe_read_reg(&adapter->hw, reg);
ixgbe_write_reg(&adapter->hw, reg, test_pattern[pat] & write);
val = ixgbe_read_reg(&adapter->hw, reg);
if (val != (test_pattern[pat] & write & mask)) {
e_err(drv, "pattern test reg %04X failed: got "
"0x%08X expected 0x%08X\n",
reg, val, (test_pattern[pat] & write & mask));
*data = reg;
writel(before, adapter->hw.hw_addr + reg);
return 1;
ixgbe_write_reg(&adapter->hw, reg, before);
return true;
}
writel(before, adapter->hw.hw_addr + reg);
ixgbe_write_reg(&adapter->hw, reg, before);
}
return 0;
return false;
}
static bool reg_set_and_check(struct ixgbe_adapter *adapter, u64 *data, int reg,
u32 mask, u32 write)
{
u32 val, before;
before = readl(adapter->hw.hw_addr + reg);
writel((write & mask), (adapter->hw.hw_addr + reg));
val = readl(adapter->hw.hw_addr + reg);
if (ixgbe_removed(adapter->hw.hw_addr)) {
*data = 1;
return 1;
}
before = ixgbe_read_reg(&adapter->hw, reg);
ixgbe_write_reg(&adapter->hw, reg, write & mask);
val = ixgbe_read_reg(&adapter->hw, reg);
if ((write & mask) != (val & mask)) {
e_err(drv, "set/check reg %04X test failed: got 0x%08X "
"expected 0x%08X\n", reg, (val & mask), (write & mask));
*data = reg;
writel(before, (adapter->hw.hw_addr + reg));
return 1;
ixgbe_write_reg(&adapter->hw, reg, before);
return true;
}
writel(before, (adapter->hw.hw_addr + reg));
return 0;
ixgbe_write_reg(&adapter->hw, reg, before);
return false;
}
#define REG_PATTERN_TEST(reg, mask, write) \
do { \
if (reg_pattern_test(adapter, data, reg, mask, write)) \
return 1; \
} while (0) \
#define REG_SET_AND_CHECK(reg, mask, write) \
do { \
if (reg_set_and_check(adapter, data, reg, mask, write)) \
return 1; \
} while (0) \
static int ixgbe_reg_test(struct ixgbe_adapter *adapter, u64 *data)
{
const struct ixgbe_reg_test *test;
u32 value, before, after;
u32 i, toggle;
if (ixgbe_removed(adapter->hw.hw_addr)) {
e_err(drv, "Adapter removed - register test blocked\n");
*data = 1;
return 1;
}
switch (adapter->hw.mac.type) {
case ixgbe_mac_82598EB:
toggle = 0x7FFFF3FF;
......@@ -1419,10 +1419,10 @@ static int ixgbe_reg_test(struct ixgbe_adapter *adapter, u64 *data)
* tests. Some bits are read-only, some toggle, and some
* are writeable on newer MACs.
*/
before = IXGBE_READ_REG(&adapter->hw, IXGBE_STATUS);
value = (IXGBE_READ_REG(&adapter->hw, IXGBE_STATUS) & toggle);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_STATUS, toggle);
after = IXGBE_READ_REG(&adapter->hw, IXGBE_STATUS) & toggle;
before = ixgbe_read_reg(&adapter->hw, IXGBE_STATUS);
value = (ixgbe_read_reg(&adapter->hw, IXGBE_STATUS) & toggle);
ixgbe_write_reg(&adapter->hw, IXGBE_STATUS, toggle);
after = ixgbe_read_reg(&adapter->hw, IXGBE_STATUS) & toggle;
if (value != after) {
e_err(drv, "failed STATUS register test got: 0x%08X "
"expected: 0x%08X\n", after, value);
......@@ -1430,7 +1430,7 @@ static int ixgbe_reg_test(struct ixgbe_adapter *adapter, u64 *data)
return 1;
}
/* restore previous status */
IXGBE_WRITE_REG(&adapter->hw, IXGBE_STATUS, before);
ixgbe_write_reg(&adapter->hw, IXGBE_STATUS, before);
/*
* Perform the remainder of the register test, looping through
......@@ -1438,38 +1438,47 @@ static int ixgbe_reg_test(struct ixgbe_adapter *adapter, u64 *data)
*/
while (test->reg) {
for (i = 0; i < test->array_len; i++) {
bool b = false;
switch (test->test_type) {
case PATTERN_TEST:
REG_PATTERN_TEST(test->reg + (i * 0x40),
test->mask,
test->write);
b = reg_pattern_test(adapter, data,
test->reg + (i * 0x40),
test->mask,
test->write);
break;
case SET_READ_TEST:
REG_SET_AND_CHECK(test->reg + (i * 0x40),
test->mask,
test->write);
b = reg_set_and_check(adapter, data,
test->reg + (i * 0x40),
test->mask,
test->write);
break;
case WRITE_NO_TEST:
writel(test->write,
(adapter->hw.hw_addr + test->reg)
+ (i * 0x40));
ixgbe_write_reg(&adapter->hw,
test->reg + (i * 0x40),
test->write);
break;
case TABLE32_TEST:
REG_PATTERN_TEST(test->reg + (i * 4),
test->mask,
test->write);
b = reg_pattern_test(adapter, data,
test->reg + (i * 4),
test->mask,
test->write);
break;
case TABLE64_TEST_LO:
REG_PATTERN_TEST(test->reg + (i * 8),
test->mask,
test->write);
b = reg_pattern_test(adapter, data,
test->reg + (i * 8),
test->mask,
test->write);
break;
case TABLE64_TEST_HI:
REG_PATTERN_TEST((test->reg + 4) + (i * 8),
test->mask,
test->write);
b = reg_pattern_test(adapter, data,
(test->reg + 4) + (i * 8),
test->mask,
test->write);
break;
}
if (b)
return 1;
}
test++;
}
......@@ -1954,6 +1963,15 @@ static void ixgbe_diag_test(struct net_device *netdev,
struct ixgbe_adapter *adapter = netdev_priv(netdev);
bool if_running = netif_running(netdev);
if (ixgbe_removed(adapter->hw.hw_addr)) {
e_err(hw, "Adapter removed - test blocked\n");
data[0] = 1;
data[1] = 1;
data[2] = 1;
data[3] = 1;
eth_test->flags |= ETH_TEST_FL_FAILED;
return;
}
set_bit(__IXGBE_TESTING, &adapter->state);
if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
struct ixgbe_hw *hw = &adapter->hw;
......
......@@ -278,10 +278,41 @@ static void ixgbe_check_minimum_link(struct ixgbe_adapter *adapter,
static void ixgbe_service_event_schedule(struct ixgbe_adapter *adapter)
{
if (!test_bit(__IXGBE_DOWN, &adapter->state) &&
!test_bit(__IXGBE_REMOVING, &adapter->state) &&
!test_and_set_bit(__IXGBE_SERVICE_SCHED, &adapter->state))
schedule_work(&adapter->service_task);
}
static void ixgbe_remove_adapter(struct ixgbe_hw *hw)
{
struct ixgbe_adapter *adapter = hw->back;
if (!hw->hw_addr)
return;
hw->hw_addr = NULL;
e_dev_err("Adapter removed\n");
ixgbe_service_event_schedule(adapter);
}
void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
{
u32 value;
/* The following check not only optimizes a bit by not
* performing a read on the status register when the
* register just read was a status register read that
* returned IXGBE_FAILED_READ_REG. It also blocks any
* potential recursion.
*/
if (reg == IXGBE_STATUS) {
ixgbe_remove_adapter(hw);
return;
}
value = ixgbe_read_reg(hw, IXGBE_STATUS);
if (value == IXGBE_FAILED_READ_REG)
ixgbe_remove_adapter(hw);
}
static void ixgbe_service_event_complete(struct ixgbe_adapter *adapter)
{
BUG_ON(!test_bit(__IXGBE_SERVICE_SCHED, &adapter->state));
......@@ -1314,7 +1345,7 @@ static inline void ixgbe_release_rx_desc(struct ixgbe_ring *rx_ring, u32 val)
* such as IA-64).
*/
wmb();
writel(val, rx_ring->tail);
ixgbe_write_tail(rx_ring, val);
}
static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring,
......@@ -2969,7 +3000,7 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter,
ring->count * sizeof(union ixgbe_adv_tx_desc));
IXGBE_WRITE_REG(hw, IXGBE_TDH(reg_idx), 0);
IXGBE_WRITE_REG(hw, IXGBE_TDT(reg_idx), 0);
ring->tail = hw->hw_addr + IXGBE_TDT(reg_idx);
ring->tail = adapter->io_addr + IXGBE_TDT(reg_idx);
/*
* set WTHRESH to encourage burst writeback, it should not be set
......@@ -3308,6 +3339,8 @@ static void ixgbe_rx_desc_queue_enable(struct ixgbe_adapter *adapter,
u32 rxdctl;
u8 reg_idx = ring->reg_idx;
if (ixgbe_removed(hw->hw_addr))
return;
/* RXDCTL.EN will return 0 on 82598 if link is down, so skip it */
if (hw->mac.type == ixgbe_mac_82598EB &&
!(IXGBE_READ_REG(hw, IXGBE_LINKS) & IXGBE_LINKS_UP))
......@@ -3332,6 +3365,8 @@ void ixgbe_disable_rx_queue(struct ixgbe_adapter *adapter,
u32 rxdctl;
u8 reg_idx = ring->reg_idx;
if (ixgbe_removed(hw->hw_addr))
return;
rxdctl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(reg_idx));
rxdctl &= ~IXGBE_RXDCTL_ENABLE;
......@@ -3372,7 +3407,7 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter,
ring->count * sizeof(union ixgbe_adv_rx_desc));
IXGBE_WRITE_REG(hw, IXGBE_RDH(reg_idx), 0);
IXGBE_WRITE_REG(hw, IXGBE_RDT(reg_idx), 0);
ring->tail = hw->hw_addr + IXGBE_RDT(reg_idx);
ring->tail = adapter->io_addr + IXGBE_RDT(reg_idx);
ixgbe_configure_srrctl(adapter, ring);
ixgbe_configure_rscctl(adapter, ring);
......@@ -4572,6 +4607,7 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter)
if (hw->mac.ops.enable_tx_laser)
hw->mac.ops.enable_tx_laser(hw);
smp_mb__before_clear_bit();
clear_bit(__IXGBE_DOWN, &adapter->state);
ixgbe_napi_enable_all(adapter);
......@@ -4656,6 +4692,8 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
int err;
if (ixgbe_removed(hw->hw_addr))
return;
/* lock SFP init bit to prevent race conditions with the watchdog */
while (test_and_set_bit(__IXGBE_IN_SFP_INIT, &adapter->state))
usleep_range(1000, 2000);
......@@ -4783,7 +4821,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
int i;
/* signal that we are down to the interrupt handler */
set_bit(__IXGBE_DOWN, &adapter->state);
if (test_and_set_bit(__IXGBE_DOWN, &adapter->state))
return; /* do nothing if already down */
/* disable receives */
rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
......@@ -5874,8 +5913,9 @@ static void ixgbe_check_hang_subtask(struct ixgbe_adapter *adapter)
u64 eics = 0;
int i;
/* If we're down or resetting, just bail */
/* If we're down, removing or resetting, just bail */
if (test_bit(__IXGBE_DOWN, &adapter->state) ||
test_bit(__IXGBE_REMOVING, &adapter->state) ||
test_bit(__IXGBE_RESETTING, &adapter->state))
return;
......@@ -6122,8 +6162,9 @@ static void ixgbe_spoof_check(struct ixgbe_adapter *adapter)
**/
static void ixgbe_watchdog_subtask(struct ixgbe_adapter *adapter)
{
/* if interface is down do nothing */
/* if interface is down, removing or resetting, do nothing */
if (test_bit(__IXGBE_DOWN, &adapter->state) ||
test_bit(__IXGBE_REMOVING, &adapter->state) ||
test_bit(__IXGBE_RESETTING, &adapter->state))
return;
......@@ -6341,8 +6382,9 @@ static void ixgbe_reset_subtask(struct ixgbe_adapter *adapter)
adapter->flags2 &= ~IXGBE_FLAG2_RESET_REQUESTED;
/* If we're already down or resetting, just bail */
/* If we're already down, removing or resetting, just bail */
if (test_bit(__IXGBE_DOWN, &adapter->state) ||
test_bit(__IXGBE_REMOVING, &adapter->state) ||
test_bit(__IXGBE_RESETTING, &adapter->state))
return;
......@@ -6362,6 +6404,15 @@ static void ixgbe_service_task(struct work_struct *work)
struct ixgbe_adapter *adapter = container_of(work,
struct ixgbe_adapter,
service_task);
if (ixgbe_removed(adapter->hw.hw_addr)) {
if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
rtnl_lock();
ixgbe_down(adapter);
rtnl_unlock();
}
ixgbe_service_event_complete(adapter);
return;
}
ixgbe_reset_subtask(adapter);
ixgbe_sfp_detection_subtask(adapter);
ixgbe_sfp_link_config_subtask(adapter);
......@@ -6693,7 +6744,7 @@ static void ixgbe_tx_map(struct ixgbe_ring *tx_ring,
tx_ring->next_to_use = i;
/* notify HW of packet */
writel(i, tx_ring->tail);
ixgbe_write_tail(tx_ring, i);
return;
dma_error:
......@@ -7874,6 +7925,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->hw_addr = ioremap(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
adapter->io_addr = hw->hw_addr;
if (!hw->hw_addr) {
err = -EIO;
goto err_ioremap;
......@@ -8182,7 +8234,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err_sw_init:
ixgbe_disable_sriov(adapter);
adapter->flags2 &= ~IXGBE_FLAG2_SEARCH_FOR_SFP;
iounmap(hw->hw_addr);
iounmap(adapter->io_addr);
err_ioremap:
free_netdev(netdev);
err_alloc_etherdev:
......@@ -8210,7 +8262,7 @@ static void ixgbe_remove(struct pci_dev *pdev)
ixgbe_dbg_adapter_exit(adapter);
set_bit(__IXGBE_DOWN, &adapter->state);
set_bit(__IXGBE_REMOVING, &adapter->state);
cancel_work_sync(&adapter->service_task);
......@@ -8249,7 +8301,7 @@ static void ixgbe_remove(struct pci_dev *pdev)
kfree(adapter->ixgbe_ieee_ets);
#endif
iounmap(adapter->hw.hw_addr);
iounmap(adapter->io_addr);
pci_release_selected_regions(pdev, pci_select_bars(pdev,
IORESOURCE_MEM));
......
......@@ -27,8 +27,7 @@
#include <linux/pci.h>
#include <linux/delay.h>
#include "ixgbe_type.h"
#include "ixgbe_common.h"
#include "ixgbe.h"
#include "ixgbe_mbx.h"
/**
......
......@@ -29,7 +29,7 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include "ixgbe_common.h"
#include "ixgbe.h"
#include "ixgbe_phy.h"
static void ixgbe_i2c_start(struct ixgbe_hw *hw);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment