scsi: lpfc: Correct nvmet buffer free race condition
authorJames Smart <jsmart2021@gmail.com>
Wed, 22 May 2019 00:48:53 +0000 (17:48 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 18 Jun 2019 23:46:20 +0000 (19:46 -0400)
A race condition resulted in receive buffers being placed in the free list
twice.

Change the locking and handling to check whether the "other" path will be
freeing the entry in a later thread and skip it if it is.

Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc_nvmet.c

index c901157..3a11861 100644 (file)
@@ -343,16 +343,23 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
        }
 
        if (ctxp->rqb_buffer) {
-               nvmebuf = ctxp->rqb_buffer;
                spin_lock_irqsave(&ctxp->ctxlock, iflag);
-               ctxp->rqb_buffer = NULL;
-               if (ctxp->flag & LPFC_NVMET_CTX_REUSE_WQ) {
-                       ctxp->flag &= ~LPFC_NVMET_CTX_REUSE_WQ;
-                       spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
-                       nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
+               nvmebuf = ctxp->rqb_buffer;
+               /* check if freed in another path whilst acquiring lock */
+               if (nvmebuf) {
+                       ctxp->rqb_buffer = NULL;
+                       if (ctxp->flag & LPFC_NVMET_CTX_REUSE_WQ) {
+                               ctxp->flag &= ~LPFC_NVMET_CTX_REUSE_WQ;
+                               spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+                               nvmebuf->hrq->rqbp->rqb_free_buffer(phba,
+                                                                   nvmebuf);
+                       } else {
+                               spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+                               /* repost */
+                               lpfc_rq_buf_free(phba, &nvmebuf->hbuf);
+                       }
                } else {
                        spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
-                       lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */
                }
        }
        ctxp->state = LPFC_NVMET_STE_FREE;