Commit 12baa420 authored by brking@us.ibm.com's avatar brking@us.ibm.com Committed by James Bottomley

[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: default avatarBrian King <brking@us.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 0bc42e35
...@@ -2351,31 +2351,24 @@ static int ipr_copy_ucode_buffer(struct ipr_sglist *sglist, ...@@ -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 * @ipr_cmd: ipr command struct
* @sglist: scatter/gather list * @sglist: scatter/gather list
* @len: total length of download buffer
* *
* Maps a microcode download scatter/gather list for DMA and * Builds a microcode download IOA data list (IOADL).
* builds the IOADL.
* *
* Return value:
* 0 on success / -EIO on failure
**/ **/
static int ipr_map_ucode_buffer(struct ipr_cmnd *ipr_cmd, static void ipr_build_ucode_ioadl(struct ipr_cmnd *ipr_cmd,
struct ipr_sglist *sglist, int len) struct ipr_sglist *sglist)
{ {
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
struct scatterlist *scatterlist = sglist->scatterlist; struct scatterlist *scatterlist = sglist->scatterlist;
int i; int i;
ipr_cmd->dma_use_sg = pci_map_sg(ioa_cfg->pdev, scatterlist, ipr_cmd->dma_use_sg = sglist->num_dma_sg;
sglist->num_sg, DMA_TO_DEVICE);
ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; 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 = ioarcb->write_ioadl_len =
cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); 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, ...@@ -2386,15 +2379,52 @@ static int ipr_map_ucode_buffer(struct ipr_cmnd *ipr_cmd,
cpu_to_be32(sg_dma_address(&scatterlist[i])); cpu_to_be32(sg_dma_address(&scatterlist[i]));
} }
if (likely(ipr_cmd->dma_use_sg)) { ioadl[i-1].flags_and_data_len |=
ioadl[i-1].flags_and_data_len |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
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; 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; return 0;
} }
...@@ -2417,7 +2447,6 @@ static ssize_t ipr_store_update_fw(struct class_device *class_dev, ...@@ -2417,7 +2447,6 @@ static ssize_t ipr_store_update_fw(struct class_device *class_dev,
struct ipr_ucode_image_header *image_hdr; struct ipr_ucode_image_header *image_hdr;
const struct firmware *fw_entry; const struct firmware *fw_entry;
struct ipr_sglist *sglist; struct ipr_sglist *sglist;
unsigned long lock_flags;
char fname[100]; char fname[100];
char *src; char *src;
int len, result, dnld_size; int len, result, dnld_size;
...@@ -2458,35 +2487,17 @@ static ssize_t ipr_store_update_fw(struct class_device *class_dev, ...@@ -2458,35 +2487,17 @@ static ssize_t ipr_store_update_fw(struct class_device *class_dev,
if (result) { if (result) {
dev_err(&ioa_cfg->pdev->dev, dev_err(&ioa_cfg->pdev->dev,
"Microcode buffer copy to DMA buffer failed\n"); "Microcode buffer copy to DMA buffer failed\n");
ipr_free_ucode_buffer(sglist); goto out;
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;
} }
ioa_cfg->ucode_sglist = sglist; result = ipr_update_ioa_ucode(ioa_cfg, 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);
if (!result)
result = count;
out:
ipr_free_ucode_buffer(sglist); ipr_free_ucode_buffer(sglist);
release_firmware(fw_entry); release_firmware(fw_entry);
return result;
return count;
} }
static struct class_device_attribute ipr_update_fw_attr = { static struct class_device_attribute ipr_update_fw_attr = {
...@@ -5291,12 +5302,7 @@ static int ipr_reset_ucode_download(struct ipr_cmnd *ipr_cmd) ...@@ -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[7] = (sglist->buffer_len & 0x00ff00) >> 8;
ipr_cmd->ioarcb.cmd_pkt.cdb[8] = sglist->buffer_len & 0x0000ff; ipr_cmd->ioarcb.cmd_pkt.cdb[8] = sglist->buffer_len & 0x0000ff;
if (ipr_map_ucode_buffer(ipr_cmd, sglist, sglist->buffer_len)) { ipr_build_ucode_ioadl(ipr_cmd, sglist);
dev_err(&ioa_cfg->pdev->dev,
"Failed to map microcode download buffer\n");
return IPR_RC_JOB_CONTINUE;
}
ipr_cmd->job_step = ipr_reset_ucode_download_done; ipr_cmd->job_step = ipr_reset_ucode_download_done;
ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
......
...@@ -811,6 +811,7 @@ struct ipr_trace_entry { ...@@ -811,6 +811,7 @@ struct ipr_trace_entry {
struct ipr_sglist { struct ipr_sglist {
u32 order; u32 order;
u32 num_sg; u32 num_sg;
u32 num_dma_sg;
u32 buffer_len; u32 buffer_len;
struct scatterlist scatterlist[1]; struct scatterlist scatterlist[1];
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment