Skip to content

Commit

Permalink
bnx2: Add workaround to handle missed MSI.
Browse files Browse the repository at this point in the history
The bnx2 chips do not support per MSI vector masking.  On 5706/5708, new MSI
address/data are stored only when the MSI enable bit is toggled.  As a result,
SMP affinity no longer works in the latest kernel.  A more serious problem is
that the driver will no longer receive interrupts when the MSI receiving CPU
goes offline.

The workaround in this patch only addresses the problem of CPU going offline.
When that happens, the driver's timer function will detect that it is making
no forward progress on pending interrupt events and will recover from it.

Eric Dumazet reported the problem.

We also found that if an interrupt is internally asserted while MSI and INTA
are disabled, the chip will end up in the same state after MSI is re-enabled.
The same workaround is needed for this problem. 

Signed-off-by: Michael Chan <mchan@broadcom.com>
Tested-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Michael Chan authored and David S. Miller committed Dec 3, 2008
1 parent d5654ef commit efba018
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 3 deletions.
35 changes: 32 additions & 3 deletions drivers/net/bnx2.c
Original file line number Diff line number Diff line change
Expand Up @@ -3144,6 +3144,28 @@ bnx2_has_work(struct bnx2_napi *bnapi)
return 0;
}

static void
bnx2_chk_missed_msi(struct bnx2 *bp)
{
struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
u32 msi_ctrl;

if (bnx2_has_work(bnapi)) {
msi_ctrl = REG_RD(bp, BNX2_PCICFG_MSI_CONTROL);
if (!(msi_ctrl & BNX2_PCICFG_MSI_CONTROL_ENABLE))
return;

if (bnapi->last_status_idx == bp->idle_chk_status_idx) {
REG_WR(bp, BNX2_PCICFG_MSI_CONTROL, msi_ctrl &
~BNX2_PCICFG_MSI_CONTROL_ENABLE);
REG_WR(bp, BNX2_PCICFG_MSI_CONTROL, msi_ctrl);
bnx2_msi(bp->irq_tbl[0].vector, bnapi);
}
}

bp->idle_chk_status_idx = bnapi->last_status_idx;
}

static void bnx2_poll_link(struct bnx2 *bp, struct bnx2_napi *bnapi)
{
struct status_block *sblk = bnapi->status_blk.msi;
Expand Down Expand Up @@ -3218,14 +3240,15 @@ static int bnx2_poll(struct napi_struct *napi, int budget)

work_done = bnx2_poll_work(bp, bnapi, work_done, budget);

if (unlikely(work_done >= budget))
break;

/* bnapi->last_status_idx is used below to tell the hw how
* much work has been processed, so we must read it before
* checking for more work.
*/
bnapi->last_status_idx = sblk->status_idx;

if (unlikely(work_done >= budget))
break;

rmb();
if (likely(!bnx2_has_work(bnapi))) {
netif_rx_complete(bp->dev, napi);
Expand Down Expand Up @@ -4570,6 +4593,8 @@ bnx2_init_chip(struct bnx2 *bp)
for (i = 0; i < BNX2_MAX_MSIX_VEC; i++)
bp->bnx2_napi[i].last_status_idx = 0;

bp->idle_chk_status_idx = 0xffff;

bp->rx_mode = BNX2_EMAC_RX_MODE_SORT_MODE;

/* Set up how to generate a link change interrupt. */
Expand Down Expand Up @@ -5718,6 +5743,10 @@ bnx2_timer(unsigned long data)
if (atomic_read(&bp->intr_sem) != 0)
goto bnx2_restart_timer;

if ((bp->flags & (BNX2_FLAG_USING_MSI | BNX2_FLAG_ONE_SHOT_MSI)) ==
BNX2_FLAG_USING_MSI)
bnx2_chk_missed_msi(bp);

bnx2_send_heart_beat(bp);

bp->stats_blk->stat_FwRxDrop =
Expand Down
6 changes: 6 additions & 0 deletions drivers/net/bnx2.h
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ struct l2_fhdr {
* pci_config_l definition
* offset: 0000
*/
#define BNX2_PCICFG_MSI_CONTROL 0x00000058
#define BNX2_PCICFG_MSI_CONTROL_ENABLE (1L<<16)

#define BNX2_PCICFG_MISC_CONFIG 0x00000068
#define BNX2_PCICFG_MISC_CONFIG_TARGET_BYTE_SWAP (1L<<2)
#define BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP (1L<<3)
Expand Down Expand Up @@ -6863,6 +6866,9 @@ struct bnx2 {

u8 num_tx_rings;
u8 num_rx_rings;

u32 idle_chk_status_idx;

};

#define REG_RD(bp, offset) \
Expand Down

0 comments on commit efba018

Please sign in to comment.