mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-06 06:35:12 +00:00
scsi: ipr: Error path locking fixes
This patch closes up some potential race conditions observed in the error handling paths in ipr while debugging an issue resulting in a hang with SATA error handling. These patches ensure we are holding the correct lock when adding and removing commands from the free and pending queues in some error scenarios. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Reviewed-by: Wendy Xiong <wenxiong@linux.vnet.ibm.com> Tested-by: Wendy Xiong <wenxiong@linux.vnet.ibm.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
439ae285b9
commit
f646f325a8
1 changed files with 102 additions and 22 deletions
|
@ -819,6 +819,29 @@ static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __ipr_sata_eh_done - done function for aborted SATA commands
|
||||||
|
* @ipr_cmd: ipr command struct
|
||||||
|
*
|
||||||
|
* This function is invoked for ops generated to SATA
|
||||||
|
* devices which are being aborted.
|
||||||
|
*
|
||||||
|
* Return value:
|
||||||
|
* none
|
||||||
|
**/
|
||||||
|
static void __ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
|
||||||
|
{
|
||||||
|
struct ata_queued_cmd *qc = ipr_cmd->qc;
|
||||||
|
struct ipr_sata_port *sata_port = qc->ap->private_data;
|
||||||
|
|
||||||
|
qc->err_mask |= AC_ERR_OTHER;
|
||||||
|
sata_port->ioasa.status |= ATA_BUSY;
|
||||||
|
ata_qc_complete(qc);
|
||||||
|
if (ipr_cmd->eh_comp)
|
||||||
|
complete(ipr_cmd->eh_comp);
|
||||||
|
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ipr_sata_eh_done - done function for aborted SATA commands
|
* ipr_sata_eh_done - done function for aborted SATA commands
|
||||||
* @ipr_cmd: ipr command struct
|
* @ipr_cmd: ipr command struct
|
||||||
|
@ -831,12 +854,32 @@ static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
|
||||||
**/
|
**/
|
||||||
static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
|
static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
|
||||||
{
|
{
|
||||||
struct ata_queued_cmd *qc = ipr_cmd->qc;
|
struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
|
||||||
struct ipr_sata_port *sata_port = qc->ap->private_data;
|
unsigned long hrrq_flags;
|
||||||
|
|
||||||
qc->err_mask |= AC_ERR_OTHER;
|
spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
|
||||||
sata_port->ioasa.status |= ATA_BUSY;
|
__ipr_sata_eh_done(ipr_cmd);
|
||||||
ata_qc_complete(qc);
|
spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __ipr_scsi_eh_done - mid-layer done function for aborted ops
|
||||||
|
* @ipr_cmd: ipr command struct
|
||||||
|
*
|
||||||
|
* This function is invoked by the interrupt handler for
|
||||||
|
* ops generated by the SCSI mid-layer which are being aborted.
|
||||||
|
*
|
||||||
|
* Return value:
|
||||||
|
* none
|
||||||
|
**/
|
||||||
|
static void __ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
|
||||||
|
{
|
||||||
|
struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
|
||||||
|
|
||||||
|
scsi_cmd->result |= (DID_ERROR << 16);
|
||||||
|
|
||||||
|
scsi_dma_unmap(ipr_cmd->scsi_cmd);
|
||||||
|
scsi_cmd->scsi_done(scsi_cmd);
|
||||||
if (ipr_cmd->eh_comp)
|
if (ipr_cmd->eh_comp)
|
||||||
complete(ipr_cmd->eh_comp);
|
complete(ipr_cmd->eh_comp);
|
||||||
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
|
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
|
||||||
|
@ -854,15 +897,12 @@ static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
|
||||||
**/
|
**/
|
||||||
static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
|
static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
|
||||||
{
|
{
|
||||||
struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
|
unsigned long hrrq_flags;
|
||||||
|
struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
|
||||||
|
|
||||||
scsi_cmd->result |= (DID_ERROR << 16);
|
spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
|
||||||
|
__ipr_scsi_eh_done(ipr_cmd);
|
||||||
scsi_dma_unmap(ipr_cmd->scsi_cmd);
|
spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
|
||||||
scsi_cmd->scsi_done(scsi_cmd);
|
|
||||||
if (ipr_cmd->eh_comp)
|
|
||||||
complete(ipr_cmd->eh_comp);
|
|
||||||
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -892,9 +932,9 @@ static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg)
|
||||||
cpu_to_be32(IPR_DRIVER_ILID);
|
cpu_to_be32(IPR_DRIVER_ILID);
|
||||||
|
|
||||||
if (ipr_cmd->scsi_cmd)
|
if (ipr_cmd->scsi_cmd)
|
||||||
ipr_cmd->done = ipr_scsi_eh_done;
|
ipr_cmd->done = __ipr_scsi_eh_done;
|
||||||
else if (ipr_cmd->qc)
|
else if (ipr_cmd->qc)
|
||||||
ipr_cmd->done = ipr_sata_eh_done;
|
ipr_cmd->done = __ipr_sata_eh_done;
|
||||||
|
|
||||||
ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH,
|
ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH,
|
||||||
IPR_IOASC_IOA_WAS_RESET);
|
IPR_IOASC_IOA_WAS_RESET);
|
||||||
|
@ -5948,7 +5988,7 @@ static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ipr_erp_done - Process completion of ERP for a device
|
* __ipr_erp_done - Process completion of ERP for a device
|
||||||
* @ipr_cmd: ipr command struct
|
* @ipr_cmd: ipr command struct
|
||||||
*
|
*
|
||||||
* This function copies the sense buffer into the scsi_cmd
|
* This function copies the sense buffer into the scsi_cmd
|
||||||
|
@ -5957,7 +5997,7 @@ static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg,
|
||||||
* Return value:
|
* Return value:
|
||||||
* nothing
|
* nothing
|
||||||
**/
|
**/
|
||||||
static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
|
static void __ipr_erp_done(struct ipr_cmnd *ipr_cmd)
|
||||||
{
|
{
|
||||||
struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
|
struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
|
||||||
struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
|
struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
|
||||||
|
@ -5984,6 +6024,26 @@ static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
|
||||||
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
|
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ipr_erp_done - Process completion of ERP for a device
|
||||||
|
* @ipr_cmd: ipr command struct
|
||||||
|
*
|
||||||
|
* This function copies the sense buffer into the scsi_cmd
|
||||||
|
* struct and pushes the scsi_done function.
|
||||||
|
*
|
||||||
|
* Return value:
|
||||||
|
* nothing
|
||||||
|
**/
|
||||||
|
static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
|
||||||
|
{
|
||||||
|
struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
|
||||||
|
unsigned long hrrq_flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
|
||||||
|
__ipr_erp_done(ipr_cmd);
|
||||||
|
spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ipr_reinit_ipr_cmnd_for_erp - Re-initialize a cmnd block to be used for ERP
|
* ipr_reinit_ipr_cmnd_for_erp - Re-initialize a cmnd block to be used for ERP
|
||||||
* @ipr_cmd: ipr command struct
|
* @ipr_cmd: ipr command struct
|
||||||
|
@ -6016,7 +6076,7 @@ static void ipr_reinit_ipr_cmnd_for_erp(struct ipr_cmnd *ipr_cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ipr_erp_request_sense - Send request sense to a device
|
* __ipr_erp_request_sense - Send request sense to a device
|
||||||
* @ipr_cmd: ipr command struct
|
* @ipr_cmd: ipr command struct
|
||||||
*
|
*
|
||||||
* This function sends a request sense to a device as a result
|
* This function sends a request sense to a device as a result
|
||||||
|
@ -6025,13 +6085,13 @@ static void ipr_reinit_ipr_cmnd_for_erp(struct ipr_cmnd *ipr_cmd)
|
||||||
* Return value:
|
* Return value:
|
||||||
* nothing
|
* nothing
|
||||||
**/
|
**/
|
||||||
static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
|
static void __ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
|
||||||
{
|
{
|
||||||
struct ipr_cmd_pkt *cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
|
struct ipr_cmd_pkt *cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
|
||||||
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
|
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
|
||||||
|
|
||||||
if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
|
if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
|
||||||
ipr_erp_done(ipr_cmd);
|
__ipr_erp_done(ipr_cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6051,6 +6111,26 @@ static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
|
||||||
IPR_REQUEST_SENSE_TIMEOUT * 2);
|
IPR_REQUEST_SENSE_TIMEOUT * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ipr_erp_request_sense - Send request sense to a device
|
||||||
|
* @ipr_cmd: ipr command struct
|
||||||
|
*
|
||||||
|
* This function sends a request sense to a device as a result
|
||||||
|
* of a check condition.
|
||||||
|
*
|
||||||
|
* Return value:
|
||||||
|
* nothing
|
||||||
|
**/
|
||||||
|
static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
|
||||||
|
{
|
||||||
|
struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
|
||||||
|
unsigned long hrrq_flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
|
||||||
|
__ipr_erp_request_sense(ipr_cmd);
|
||||||
|
spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ipr_erp_cancel_all - Send cancel all to a device
|
* ipr_erp_cancel_all - Send cancel all to a device
|
||||||
* @ipr_cmd: ipr command struct
|
* @ipr_cmd: ipr command struct
|
||||||
|
@ -6074,7 +6154,7 @@ static void ipr_erp_cancel_all(struct ipr_cmnd *ipr_cmd)
|
||||||
ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
|
ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
|
||||||
|
|
||||||
if (!scsi_cmd->device->simple_tags) {
|
if (!scsi_cmd->device->simple_tags) {
|
||||||
ipr_erp_request_sense(ipr_cmd);
|
__ipr_erp_request_sense(ipr_cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6294,7 +6374,7 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
|
||||||
u32 masked_ioasc = ioasc & IPR_IOASC_IOASC_MASK;
|
u32 masked_ioasc = ioasc & IPR_IOASC_IOASC_MASK;
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
ipr_scsi_eh_done(ipr_cmd);
|
__ipr_scsi_eh_done(ipr_cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue