Commit b338c785 authored by Bhanu Prakash Gollapudi's avatar Bhanu Prakash Gollapudi Committed by James Bottomley

[SCSI] bnx2fc: Fix NULL pointer deref during arm_cq.

There exists a race condition between CQ doorbell unmap and IO completion path
that arms the CQ which causes a NULL dereference. Protect the ctx_base with
cq_lock to avoid this. Also, wait for the CQ doorbell to be successfully mapped
before arming the CQ.

Also, do not count uncolicited CQ completions for free_sqes.
Signed-off-by: default avatarBhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 81214013
...@@ -1009,6 +1009,7 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt) ...@@ -1009,6 +1009,7 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt)
u32 cq_cons; u32 cq_cons;
struct fcoe_cqe *cqe; struct fcoe_cqe *cqe;
u32 num_free_sqes = 0; u32 num_free_sqes = 0;
u32 num_cqes = 0;
u16 wqe; u16 wqe;
/* /*
...@@ -1058,10 +1059,11 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt) ...@@ -1058,10 +1059,11 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt)
wake_up_process(fps->iothread); wake_up_process(fps->iothread);
else else
bnx2fc_process_cq_compl(tgt, wqe); bnx2fc_process_cq_compl(tgt, wqe);
num_free_sqes++;
} }
cqe++; cqe++;
tgt->cq_cons_idx++; tgt->cq_cons_idx++;
num_free_sqes++; num_cqes++;
if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) { if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) {
tgt->cq_cons_idx = 0; tgt->cq_cons_idx = 0;
...@@ -1070,7 +1072,9 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt) ...@@ -1070,7 +1072,9 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt)
1 - tgt->cq_curr_toggle_bit; 1 - tgt->cq_curr_toggle_bit;
} }
} }
if (num_free_sqes) { if (num_cqes) {
/* Arm CQ only if doorbell is mapped */
if (tgt->ctx_base)
bnx2fc_arm_cq(tgt); bnx2fc_arm_cq(tgt);
atomic_add(num_free_sqes, &tgt->free_sqes); atomic_add(num_free_sqes, &tgt->free_sqes);
} }
......
...@@ -133,7 +133,7 @@ static void bnx2fc_offload_session(struct fcoe_port *port, ...@@ -133,7 +133,7 @@ static void bnx2fc_offload_session(struct fcoe_port *port,
printk(KERN_ERR PFX "map doorbell failed - no mem\n"); printk(KERN_ERR PFX "map doorbell failed - no mem\n");
/* upload will take care of cleaning up sess resc */ /* upload will take care of cleaning up sess resc */
lport->tt.rport_logoff(rdata); lport->tt.rport_logoff(rdata);
} } else
/* Arm CQ */ /* Arm CQ */
bnx2fc_arm_cq(tgt); bnx2fc_arm_cq(tgt);
return; return;
...@@ -806,14 +806,14 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, ...@@ -806,14 +806,14 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba, static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt) struct bnx2fc_rport *tgt)
{ {
void __iomem *ctx_base_ptr;
BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n"); BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n");
if (tgt->ctx_base) { spin_lock_bh(&tgt->cq_lock);
iounmap(tgt->ctx_base); ctx_base_ptr = tgt->ctx_base;
tgt->ctx_base = NULL; tgt->ctx_base = NULL;
}
spin_lock_bh(&tgt->cq_lock);
/* Free LCQ */ /* Free LCQ */
if (tgt->lcq) { if (tgt->lcq) {
dma_free_coherent(&hba->pcidev->dev, tgt->lcq_mem_size, dma_free_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
...@@ -867,4 +867,7 @@ static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba, ...@@ -867,4 +867,7 @@ static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
tgt->sq = NULL; tgt->sq = NULL;
} }
spin_unlock_bh(&tgt->cq_lock); spin_unlock_bh(&tgt->cq_lock);
if (ctx_base_ptr)
iounmap(ctx_base_ptr);
} }
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