[SCSI] advansys: fix narrow board error path
authorHerton Ronaldo Krzesinski <herton@mandriva.com.br>
Tue, 30 Mar 2010 16:35:38 +0000 (13:35 -0300)
committerJames Bottomley <James.Bottomley@suse.de>
Sat, 1 May 2010 14:54:22 +0000 (09:54 -0500)
Error handling on advansys_board_found is fixed, because it's buggy in
the case we have an ASC_NARROW_BOARD set and failure happens on
AscInitAsc1000Driver step: it was freeing items of wrong struct in the
dvc_var union of struct asc_board, which could lead to an oops in the
case we set some of the fields in struct of narrow board as code was
choosing to always freeing wide board fields, and not everything was
being freed/released properly.

Signed-off-by: Herton Ronaldo Krzesinski <herton@mandriva.com.br>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/advansys.c

index 9201afe65609bd23ee732818534b91d41252ea9b..7f87979da22d31b9702b2f8fc06fd489df7fe19e 100644 (file)
@@ -4724,6 +4724,10 @@ static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
        BUG_ON((unsigned long)asc_dvc->overrun_buf & 7);
        asc_dvc->overrun_dma = dma_map_single(board->dev, asc_dvc->overrun_buf,
                                        ASC_OVERRUN_BSIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(board->dev, asc_dvc->overrun_dma)) {
+               warn_code = -ENOMEM;
+               goto err_dma_map;
+       }
        phy_addr = cpu_to_le32(asc_dvc->overrun_dma);
        AscMemDWordCopyPtrToLram(iop_base, ASCV_OVERRUN_PADDR_D,
                                 (uchar *)&phy_addr, 1);
@@ -4739,14 +4743,23 @@ static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
        AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
        if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
                asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
-               return warn_code;
+               warn_code = UW_ERR;
+               goto err_mcode_start;
        }
        if (AscStartChip(iop_base) != 1) {
                asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
-               return warn_code;
+               warn_code = UW_ERR;
+               goto err_mcode_start;
        }
 
        return warn_code;
+
+err_mcode_start:
+       dma_unmap_single(board->dev, asc_dvc->overrun_dma,
+                        ASC_OVERRUN_BSIZE, DMA_FROM_DEVICE);
+err_dma_map:
+       asc_dvc->overrun_dma = 0;
+       return warn_code;
 }
 
 static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
@@ -4802,6 +4815,8 @@ static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
        }
        release_firmware(fw);
        warn_code |= AscInitMicroCodeVar(asc_dvc);
+       if (!asc_dvc->overrun_dma)
+               return warn_code;
        asc_dvc->init_state |= ASC_INIT_STATE_END_LOAD_MC;
        AscEnableInterrupt(iop_base);
        return warn_code;
@@ -7978,9 +7993,10 @@ static int advansys_reset(struct scsi_cmnd *scp)
                status = AscInitAsc1000Driver(asc_dvc);
 
                /* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */
-               if (asc_dvc->err_code) {
+               if (asc_dvc->err_code || !asc_dvc->overrun_dma) {
                        scmd_printk(KERN_INFO, scp, "SCSI bus reset error: "
-                                   "0x%x\n", asc_dvc->err_code);
+                                   "0x%x, status: 0x%x\n", asc_dvc->err_code,
+                                   status);
                        ret = FAILED;
                } else if (status) {
                        scmd_printk(KERN_INFO, scp, "SCSI bus reset warning: "
@@ -12311,7 +12327,7 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
                asc_dvc_varp->overrun_buf = kzalloc(ASC_OVERRUN_BSIZE, GFP_KERNEL);
                if (!asc_dvc_varp->overrun_buf) {
                        ret = -ENOMEM;
-                       goto err_free_wide_mem;
+                       goto err_free_irq;
                }
                warn_code = AscInitAsc1000Driver(asc_dvc_varp);
 
@@ -12320,30 +12336,36 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
                                        "warn 0x%x, error 0x%x\n",
                                        asc_dvc_varp->init_state, warn_code,
                                        asc_dvc_varp->err_code);
-                       if (asc_dvc_varp->err_code) {
+                       if (!asc_dvc_varp->overrun_dma) {
                                ret = -ENODEV;
-                               kfree(asc_dvc_varp->overrun_buf);
+                               goto err_free_mem;
                        }
                }
        } else {
-               if (advansys_wide_init_chip(shost))
+               if (advansys_wide_init_chip(shost)) {
                        ret = -ENODEV;
+                       goto err_free_mem;
+               }
        }
 
-       if (ret)
-               goto err_free_wide_mem;
-
        ASC_DBG_PRT_SCSI_HOST(2, shost);
 
        ret = scsi_add_host(shost, boardp->dev);
        if (ret)
-               goto err_free_wide_mem;
+               goto err_free_mem;
 
        scsi_scan_host(shost);
        return 0;
 
- err_free_wide_mem:
-       advansys_wide_free_mem(boardp);
+ err_free_mem:
+       if (ASC_NARROW_BOARD(boardp)) {
+               if (asc_dvc_varp->overrun_dma)
+                       dma_unmap_single(boardp->dev, asc_dvc_varp->overrun_dma,
+                                        ASC_OVERRUN_BSIZE, DMA_FROM_DEVICE);
+               kfree(asc_dvc_varp->overrun_buf);
+       } else
+               advansys_wide_free_mem(boardp);
+ err_free_irq:
        free_irq(boardp->irq, shost);
  err_free_dma:
 #ifdef CONFIG_ISA