diff options
Diffstat (limited to 'drivers/scsi/hpsa.c')
-rw-r--r-- | drivers/scsi/hpsa.c | 117 |
1 files changed, 84 insertions, 33 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 4f338061b5c3..7f4f790a3d71 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -165,7 +165,7 @@ static void cmd_free(struct ctlr_info *h, struct CommandList *c); static void cmd_special_free(struct ctlr_info *h, struct CommandList *c); static struct CommandList *cmd_alloc(struct ctlr_info *h); static struct CommandList *cmd_special_alloc(struct ctlr_info *h); -static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, +static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, void *buff, size_t size, u8 page_code, unsigned char *scsi3addr, int cmd_type); @@ -1131,7 +1131,7 @@ clean: return -ENOMEM; } -static void hpsa_map_sg_chain_block(struct ctlr_info *h, +static int hpsa_map_sg_chain_block(struct ctlr_info *h, struct CommandList *c) { struct SGDescriptor *chain_sg, *chain_block; @@ -1144,8 +1144,15 @@ static void hpsa_map_sg_chain_block(struct ctlr_info *h, (c->Header.SGTotal - h->max_cmd_sg_entries); temp64 = pci_map_single(h->pdev, chain_block, chain_sg->Len, PCI_DMA_TODEVICE); + if (dma_mapping_error(&h->pdev->dev, temp64)) { + /* prevent subsequent unmapping */ + chain_sg->Addr.lower = 0; + chain_sg->Addr.upper = 0; + return -1; + } chain_sg->Addr.lower = (u32) (temp64 & 0x0FFFFFFFFULL); chain_sg->Addr.upper = (u32) ((temp64 >> 32) & 0x0FFFFFFFFULL); + return 0; } static void hpsa_unmap_sg_chain_block(struct ctlr_info *h, @@ -1390,7 +1397,7 @@ static void hpsa_pci_unmap(struct pci_dev *pdev, } } -static void hpsa_map_one(struct pci_dev *pdev, +static int hpsa_map_one(struct pci_dev *pdev, struct CommandList *cp, unsigned char *buf, size_t buflen, @@ -1401,10 +1408,16 @@ static void hpsa_map_one(struct pci_dev *pdev, if (buflen == 0 || data_direction == PCI_DMA_NONE) { cp->Header.SGList = 0; cp->Header.SGTotal = 0; - return; + return 0; } addr64 = (u64) pci_map_single(pdev, buf, buflen, data_direction); + if (dma_mapping_error(&pdev->dev, addr64)) { + /* Prevent subsequent unmap of something never mapped */ + cp->Header.SGList = 0; + cp->Header.SGTotal = 0; + return -1; + } cp->SG[0].Addr.lower = (u32) (addr64 & (u64) 0x00000000FFFFFFFF); cp->SG[0].Addr.upper = @@ -1412,6 +1425,7 @@ static void hpsa_map_one(struct pci_dev *pdev, cp->SG[0].Len = buflen; cp->Header.SGList = (u8) 1; /* no. SGs contig in this cmd */ cp->Header.SGTotal = (u16) 1; /* total sgs in this cmd list */ + return 0; } static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h, @@ -1540,13 +1554,18 @@ static int hpsa_scsi_do_inquiry(struct ctlr_info *h, unsigned char *scsi3addr, return -ENOMEM; } - fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize, page, scsi3addr, TYPE_CMD); + if (fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize, + page, scsi3addr, TYPE_CMD)) { + rc = -1; + goto out; + } hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE); ei = c->err_info; if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { hpsa_scsi_interpret_error(c); rc = -1; } +out: cmd_special_free(h, c); return rc; } @@ -1564,7 +1583,9 @@ static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr) return -ENOMEM; } - fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0, scsi3addr, TYPE_MSG); + /* fill_cmd can't fail here, no data buffer to map. */ + (void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, + NULL, 0, 0, scsi3addr, TYPE_MSG); hpsa_scsi_do_simple_cmd_core(h, c); /* no unmap needed here because no data xfer. */ @@ -1631,8 +1652,11 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical, } /* address the controller */ memset(scsi3addr, 0, sizeof(scsi3addr)); - fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h, - buf, bufsize, 0, scsi3addr, TYPE_CMD); + if (fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h, + buf, bufsize, 0, scsi3addr, TYPE_CMD)) { + rc = -1; + goto out; + } if (extended_response) c->Request.CDB[1] = extended_response; hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE); @@ -1642,6 +1666,7 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical, hpsa_scsi_interpret_error(c); rc = -1; } +out: cmd_special_free(h, c); return rc; } @@ -2105,7 +2130,10 @@ static int hpsa_scatter_gather(struct ctlr_info *h, if (chained) { cp->Header.SGList = h->max_cmd_sg_entries; cp->Header.SGTotal = (u16) (use_sg + 1); - hpsa_map_sg_chain_block(h, cp); + if (hpsa_map_sg_chain_block(h, cp)) { + scsi_dma_unmap(cmd); + return -1; + } return 0; } @@ -2353,8 +2381,9 @@ static int wait_for_device_to_become_ready(struct ctlr_info *h, if (waittime < HPSA_MAX_WAIT_INTERVAL_SECS) waittime = waittime * 2; - /* Send the Test Unit Ready */ - fill_cmd(c, TEST_UNIT_READY, h, NULL, 0, 0, lunaddr, TYPE_CMD); + /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */ + (void) fill_cmd(c, TEST_UNIT_READY, h, + NULL, 0, 0, lunaddr, TYPE_CMD); hpsa_scsi_do_simple_cmd_core(h, c); /* no unmap needed here because no data xfer. */ @@ -2439,7 +2468,9 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, return -ENOMEM; } - fill_cmd(c, HPSA_ABORT_MSG, h, abort, 0, 0, scsi3addr, TYPE_MSG); + /* fill_cmd can't fail here, no buffer to map */ + (void) fill_cmd(c, HPSA_ABORT_MSG, h, abort, + 0, 0, scsi3addr, TYPE_MSG); if (swizzle) swizzle_abort_tag(&c->Request.CDB[4]); hpsa_scsi_do_simple_cmd_core(h, c); @@ -2928,6 +2959,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) struct CommandList *c; char *buff = NULL; union u64bit temp64; + int rc = 0; if (!argp) return -EINVAL; @@ -2947,8 +2979,8 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) /* Copy the data into the buffer we created */ if (copy_from_user(buff, iocommand.buf, iocommand.buf_size)) { - kfree(buff); - return -EFAULT; + rc = -EFAULT; + goto out_kfree; } } else { memset(buff, 0, iocommand.buf_size); @@ -2956,8 +2988,8 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) } c = cmd_special_alloc(h); if (c == NULL) { - kfree(buff); - return -ENOMEM; + rc = -ENOMEM; + goto out_kfree; } /* Fill in the command type */ c->cmd_type = CMD_IOCTL_PEND; @@ -2982,6 +3014,13 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) if (iocommand.buf_size > 0) { temp64.val = pci_map_single(h->pdev, buff, iocommand.buf_size, PCI_DMA_BIDIRECTIONAL); + if (dma_mapping_error(&h->pdev->dev, temp64.val)) { + c->SG[0].Addr.lower = 0; + c->SG[0].Addr.upper = 0; + c->SG[0].Len = 0; + rc = -ENOMEM; + goto out; + } c->SG[0].Addr.lower = temp64.val32.lower; c->SG[0].Addr.upper = temp64.val32.upper; c->SG[0].Len = iocommand.buf_size; @@ -2996,22 +3035,22 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) memcpy(&iocommand.error_info, c->err_info, sizeof(iocommand.error_info)); if (copy_to_user(argp, &iocommand, sizeof(iocommand))) { - kfree(buff); - cmd_special_free(h, c); - return -EFAULT; + rc = -EFAULT; + goto out; } if (iocommand.Request.Type.Direction == XFER_READ && iocommand.buf_size > 0) { /* Copy the data out of the buffer we created */ if (copy_to_user(iocommand.buf, buff, iocommand.buf_size)) { - kfree(buff); - cmd_special_free(h, c); - return -EFAULT; + rc = -EFAULT; + goto out; } } - kfree(buff); +out: cmd_special_free(h, c); - return 0; +out_kfree: + kfree(buff); + return rc; } static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) @@ -3103,6 +3142,15 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) for (i = 0; i < sg_used; i++) { temp64.val = pci_map_single(h->pdev, buff[i], buff_size[i], PCI_DMA_BIDIRECTIONAL); + if (dma_mapping_error(&h->pdev->dev, temp64.val)) { + c->SG[i].Addr.lower = 0; + c->SG[i].Addr.upper = 0; + c->SG[i].Len = 0; + hpsa_pci_unmap(h->pdev, c, i, + PCI_DMA_BIDIRECTIONAL); + status = -ENOMEM; + goto cleanup1; + } c->SG[i].Addr.lower = temp64.val32.lower; c->SG[i].Addr.upper = temp64.val32.upper; c->SG[i].Len = buff_size[i]; @@ -3190,7 +3238,8 @@ static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr, c = cmd_alloc(h); if (!c) return -ENOMEM; - fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0, + /* fill_cmd can't fail here, no data buffer to map */ + (void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0, RAID_CTLR_LUNID, TYPE_MSG); c->Request.CDB[1] = reset_type; /* fill_cmd defaults to target reset */ c->waiting = NULL; @@ -3202,7 +3251,7 @@ static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr, return 0; } -static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, +static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, void *buff, size_t size, u8 page_code, unsigned char *scsi3addr, int cmd_type) { @@ -3271,7 +3320,7 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, default: dev_warn(&h->pdev->dev, "unknown command 0x%c\n", cmd); BUG(); - return; + return -1; } } else if (cmd_type == TYPE_MSG) { switch (cmd) { @@ -3343,10 +3392,9 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, default: pci_dir = PCI_DMA_BIDIRECTIONAL; } - - hpsa_map_one(h->pdev, c, buff, size, pci_dir); - - return; + if (hpsa_map_one(h->pdev, c, buff, size, pci_dir)) + return -1; + return 0; } /* @@ -4882,10 +4930,13 @@ static void hpsa_flush_cache(struct ctlr_info *h) dev_warn(&h->pdev->dev, "cmd_special_alloc returned NULL!\n"); goto out_of_memory; } - fill_cmd(c, HPSA_CACHE_FLUSH, h, flush_buf, 4, 0, - RAID_CTLR_LUNID, TYPE_CMD); + if (fill_cmd(c, HPSA_CACHE_FLUSH, h, flush_buf, 4, 0, + RAID_CTLR_LUNID, TYPE_CMD)) { + goto out; + } hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_TODEVICE); if (c->err_info->CommandStatus != 0) +out: dev_warn(&h->pdev->dev, "error flushing cache on controller\n"); cmd_special_free(h, c); |