diff options
author | Bhanu Prakash Gollapudi <bprakash@broadcom.com> | 2011-08-04 20:38:46 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2011-08-27 10:35:42 -0400 |
commit | b338c785c5c945383046ff39092e3021ea5b1d95 (patch) | |
tree | 548bd66957226efdd21e32eefefc222fdc593a69 /drivers | |
parent | 81214013130cd24142f6465f7f5a256fed530c17 (diff) |
[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: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_hwi.c | 10 | ||||
-rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_tgt.c | 19 |
2 files changed, 18 insertions, 11 deletions
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 72cfb14acd3a..b241f3d33362 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c | |||
@@ -1009,6 +1009,7 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt) | |||
1009 | u32 cq_cons; | 1009 | u32 cq_cons; |
1010 | struct fcoe_cqe *cqe; | 1010 | struct fcoe_cqe *cqe; |
1011 | u32 num_free_sqes = 0; | 1011 | u32 num_free_sqes = 0; |
1012 | u32 num_cqes = 0; | ||
1012 | u16 wqe; | 1013 | u16 wqe; |
1013 | 1014 | ||
1014 | /* | 1015 | /* |
@@ -1058,10 +1059,11 @@ unlock: | |||
1058 | wake_up_process(fps->iothread); | 1059 | wake_up_process(fps->iothread); |
1059 | else | 1060 | else |
1060 | bnx2fc_process_cq_compl(tgt, wqe); | 1061 | bnx2fc_process_cq_compl(tgt, wqe); |
1062 | num_free_sqes++; | ||
1061 | } | 1063 | } |
1062 | cqe++; | 1064 | cqe++; |
1063 | tgt->cq_cons_idx++; | 1065 | tgt->cq_cons_idx++; |
1064 | num_free_sqes++; | 1066 | num_cqes++; |
1065 | 1067 | ||
1066 | if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) { | 1068 | if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) { |
1067 | tgt->cq_cons_idx = 0; | 1069 | tgt->cq_cons_idx = 0; |
@@ -1070,8 +1072,10 @@ unlock: | |||
1070 | 1 - tgt->cq_curr_toggle_bit; | 1072 | 1 - tgt->cq_curr_toggle_bit; |
1071 | } | 1073 | } |
1072 | } | 1074 | } |
1073 | if (num_free_sqes) { | 1075 | if (num_cqes) { |
1074 | bnx2fc_arm_cq(tgt); | 1076 | /* Arm CQ only if doorbell is mapped */ |
1077 | if (tgt->ctx_base) | ||
1078 | bnx2fc_arm_cq(tgt); | ||
1075 | atomic_add(num_free_sqes, &tgt->free_sqes); | 1079 | atomic_add(num_free_sqes, &tgt->free_sqes); |
1076 | } | 1080 | } |
1077 | spin_unlock_bh(&tgt->cq_lock); | 1081 | spin_unlock_bh(&tgt->cq_lock); |
diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c index 3d28fbe1d99e..2f7a7da5b27b 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c +++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c | |||
@@ -133,9 +133,9 @@ retry_ofld: | |||
133 | printk(KERN_ERR PFX "map doorbell failed - no mem\n"); | 133 | printk(KERN_ERR PFX "map doorbell failed - no mem\n"); |
134 | /* upload will take care of cleaning up sess resc */ | 134 | /* upload will take care of cleaning up sess resc */ |
135 | lport->tt.rport_logoff(rdata); | 135 | lport->tt.rport_logoff(rdata); |
136 | } | 136 | } else |
137 | /* Arm CQ */ | 137 | /* Arm CQ */ |
138 | bnx2fc_arm_cq(tgt); | 138 | bnx2fc_arm_cq(tgt); |
139 | return; | 139 | return; |
140 | 140 | ||
141 | ofld_err: | 141 | ofld_err: |
@@ -806,14 +806,14 @@ mem_alloc_failure: | |||
806 | static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba, | 806 | static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba, |
807 | struct bnx2fc_rport *tgt) | 807 | struct bnx2fc_rport *tgt) |
808 | { | 808 | { |
809 | BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n"); | 809 | void __iomem *ctx_base_ptr; |
810 | 810 | ||
811 | if (tgt->ctx_base) { | 811 | BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n"); |
812 | iounmap(tgt->ctx_base); | ||
813 | tgt->ctx_base = NULL; | ||
814 | } | ||
815 | 812 | ||
816 | spin_lock_bh(&tgt->cq_lock); | 813 | spin_lock_bh(&tgt->cq_lock); |
814 | ctx_base_ptr = tgt->ctx_base; | ||
815 | tgt->ctx_base = NULL; | ||
816 | |||
817 | /* Free LCQ */ | 817 | /* Free LCQ */ |
818 | if (tgt->lcq) { | 818 | if (tgt->lcq) { |
819 | dma_free_coherent(&hba->pcidev->dev, tgt->lcq_mem_size, | 819 | 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 | tgt->sq = NULL; | 867 | tgt->sq = NULL; |
868 | } | 868 | } |
869 | spin_unlock_bh(&tgt->cq_lock); | 869 | spin_unlock_bh(&tgt->cq_lock); |
870 | |||
871 | if (ctx_base_ptr) | ||
872 | iounmap(ctx_base_ptr); | ||
870 | } | 873 | } |