[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>
This commit is contained in:
brking@us.ibm.com 2005-11-01 17:01:27 -06:00 коммит произвёл James Bottomley
Родитель 0bc42e35c7
Коммит 12baa4202d
2 изменённых файлов: 57 добавлений и 50 удалений

Просмотреть файл

@ -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,
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); }
}
else { /**
dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n"); * 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; return -EIO;
} }
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; 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; 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,
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); result = ipr_update_ioa_ucode(ioa_cfg, sglist);
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;
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)
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 {
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];
}; };