diff options
author | brking@us.ibm.com <brking@us.ibm.com> | 2005-11-01 17:01:27 -0600 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.(none)> | 2005-11-06 13:04:18 -0600 |
commit | 12baa4202d74d799f4f8a4bd0455b485e4f8e876 (patch) | |
tree | 71601a7098f67c43c2642f32948a4ff01c492e6b /drivers/scsi/ipr.c | |
parent | 0bc42e35c74c0baab414cf623d6fe1e94cee4ca3 (diff) | |
download | lwn-12baa4202d74d799f4f8a4bd0455b485e4f8e876.tar.gz lwn-12baa4202d74d799f4f8a4bd0455b485e4f8e876.zip |
[SCSI] ipr: Fix adapter microcode update DMA mapping leak
If the write buffer command that is issued to the ipr adapter
to update its microcode fails for some reason, the DMA buffer
will never get unmapped. Move the pci_map/unmap out of the
IOA reset job so that the buffer is always clearly mapped
and unmapped.
Signed-off-by: Brian King <brking@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/ipr.c')
-rw-r--r-- | drivers/scsi/ipr.c | 104 |
1 files changed, 55 insertions, 49 deletions
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index ad13ae11e2e6..72b588d65562 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -2351,31 +2351,24 @@ static int ipr_copy_ucode_buffer(struct ipr_sglist *sglist, } /** - * ipr_map_ucode_buffer - Map a microcode download buffer + * ipr_build_ucode_ioadl - Build a microcode download IOADL * @ipr_cmd: ipr command struct * @sglist: scatter/gather list - * @len: total length of download buffer * - * Maps a microcode download scatter/gather list for DMA and - * builds the IOADL. + * Builds a microcode download IOA data list (IOADL). * - * Return value: - * 0 on success / -EIO on failure **/ -static int ipr_map_ucode_buffer(struct ipr_cmnd *ipr_cmd, - struct ipr_sglist *sglist, int len) +static void ipr_build_ucode_ioadl(struct ipr_cmnd *ipr_cmd, + struct ipr_sglist *sglist) { - struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; struct scatterlist *scatterlist = sglist->scatterlist; int i; - ipr_cmd->dma_use_sg = pci_map_sg(ioa_cfg->pdev, scatterlist, - sglist->num_sg, DMA_TO_DEVICE); - + ipr_cmd->dma_use_sg = sglist->num_dma_sg; ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; - ioarcb->write_data_transfer_length = cpu_to_be32(len); + ioarcb->write_data_transfer_length = cpu_to_be32(sglist->buffer_len); ioarcb->write_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); @@ -2386,15 +2379,52 @@ static int ipr_map_ucode_buffer(struct ipr_cmnd *ipr_cmd, cpu_to_be32(sg_dma_address(&scatterlist[i])); } - if (likely(ipr_cmd->dma_use_sg)) { - ioadl[i-1].flags_and_data_len |= - cpu_to_be32(IPR_IOADL_FLAGS_LAST); + ioadl[i-1].flags_and_data_len |= + cpu_to_be32(IPR_IOADL_FLAGS_LAST); +} + +/** + * ipr_update_ioa_ucode - Update IOA's microcode + * @ioa_cfg: ioa config struct + * @sglist: scatter/gather list + * + * Initiate an adapter reset to update the IOA's microcode + * + * Return value: + * 0 on success / -EIO on failure + **/ +static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_sglist *sglist) +{ + unsigned long lock_flags; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + if (ioa_cfg->ucode_sglist) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + dev_err(&ioa_cfg->pdev->dev, + "Microcode download already in progress\n"); + return -EIO; } - else { - dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n"); + + sglist->num_dma_sg = pci_map_sg(ioa_cfg->pdev, sglist->scatterlist, + sglist->num_sg, DMA_TO_DEVICE); + + if (!sglist->num_dma_sg) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + dev_err(&ioa_cfg->pdev->dev, + "Failed to map microcode download buffer!\n"); return -EIO; } + ioa_cfg->ucode_sglist = sglist; + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ioa_cfg->ucode_sglist = NULL; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return 0; } @@ -2417,7 +2447,6 @@ static ssize_t ipr_store_update_fw(struct class_device *class_dev, struct ipr_ucode_image_header *image_hdr; const struct firmware *fw_entry; struct ipr_sglist *sglist; - unsigned long lock_flags; char fname[100]; char *src; int len, result, dnld_size; @@ -2458,35 +2487,17 @@ static ssize_t ipr_store_update_fw(struct class_device *class_dev, if (result) { dev_err(&ioa_cfg->pdev->dev, "Microcode buffer copy to DMA buffer failed\n"); - ipr_free_ucode_buffer(sglist); - release_firmware(fw_entry); - return result; - } - - spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - - if (ioa_cfg->ucode_sglist) { - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - dev_err(&ioa_cfg->pdev->dev, - "Microcode download already in progress\n"); - ipr_free_ucode_buffer(sglist); - release_firmware(fw_entry); - return -EIO; + goto out; } - ioa_cfg->ucode_sglist = sglist; - ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL); - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); - - spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - ioa_cfg->ucode_sglist = NULL; - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + result = ipr_update_ioa_ucode(ioa_cfg, sglist); + if (!result) + result = count; +out: ipr_free_ucode_buffer(sglist); release_firmware(fw_entry); - - return count; + return result; } static struct class_device_attribute ipr_update_fw_attr = { @@ -5291,12 +5302,7 @@ static int ipr_reset_ucode_download(struct ipr_cmnd *ipr_cmd) ipr_cmd->ioarcb.cmd_pkt.cdb[7] = (sglist->buffer_len & 0x00ff00) >> 8; ipr_cmd->ioarcb.cmd_pkt.cdb[8] = sglist->buffer_len & 0x0000ff; - if (ipr_map_ucode_buffer(ipr_cmd, sglist, sglist->buffer_len)) { - dev_err(&ioa_cfg->pdev->dev, - "Failed to map microcode download buffer\n"); - return IPR_RC_JOB_CONTINUE; - } - + ipr_build_ucode_ioadl(ipr_cmd, sglist); ipr_cmd->job_step = ipr_reset_ucode_download_done; ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, |