diff options
author | Michael Chan <mchan@broadcom.com> | 2011-03-02 14:00:50 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-03 00:57:51 +0100 |
commit | 0197b087ed6384760656f1e4a620a3e92d8dc0b0 (patch) | |
tree | 352d590c2f23cc541c6b796da6ac0e690812186f /drivers/net/cnic.c | |
parent | cnic: Prevent status block race conditions with hardware (diff) | |
download | linux-0197b087ed6384760656f1e4a620a3e92d8dc0b0.tar.xz linux-0197b087ed6384760656f1e4a620a3e92d8dc0b0.zip |
cnic: Fix lost interrupt on bnx2x
We service 2 queues (kcq1 and kcq2) in cnic_service_bnx2x_bh(). If
the status block index has changed when servicing the kcq2, we must
go back and check kcq1. The latest status block index will be used
to acknowledge the interrupt, and without looping back to check kcq1,
we may miss events on kcq1.
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/cnic.c')
-rw-r--r-- | drivers/net/cnic.c | 25 |
1 files changed, 17 insertions, 8 deletions
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index b0d9e4ab6467..302be4aa69d6 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -2914,26 +2914,35 @@ static void cnic_service_bnx2x_bh(unsigned long data) { struct cnic_dev *dev = (struct cnic_dev *) data; struct cnic_local *cp = dev->cnic_priv; - u32 status_idx; + u32 status_idx, new_status_idx; if (unlikely(!test_bit(CNIC_F_CNIC_UP, &dev->flags))) return; - status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq1); + while (1) { + status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq1); - CNIC_WR16(dev, cp->kcq1.io_addr, cp->kcq1.sw_prod_idx + MAX_KCQ_IDX); + CNIC_WR16(dev, cp->kcq1.io_addr, + cp->kcq1.sw_prod_idx + MAX_KCQ_IDX); - if (BNX2X_CHIP_IS_E2(cp->chip_id)) { - status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq2); + if (!BNX2X_CHIP_IS_E2(cp->chip_id)) { + cnic_ack_bnx2x_int(dev, cp->bnx2x_igu_sb_id, USTORM_ID, + status_idx, IGU_INT_ENABLE, 1); + break; + } + + new_status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq2); + + if (new_status_idx != status_idx) + continue; CNIC_WR16(dev, cp->kcq2.io_addr, cp->kcq2.sw_prod_idx + MAX_KCQ_IDX); cnic_ack_igu_sb(dev, cp->bnx2x_igu_sb_id, IGU_SEG_ACCESS_DEF, status_idx, IGU_INT_ENABLE, 1); - } else { - cnic_ack_bnx2x_int(dev, cp->bnx2x_igu_sb_id, USTORM_ID, - status_idx, IGU_INT_ENABLE, 1); + + break; } } |