mirror of
https://github.com/adulau/aha.git
synced 2024-12-30 12:46:17 +00:00
[PATCH] libata: implement ata_scsi_timed_out()
Implement ata_scsi_timed_out(), to be used as scsi_host_template->eh_timed_out callback for all libata drivers. Without this function, the following race exists. If a qc completes after SCSI timer expires but before libata EH kicks in, the qc gets completed but the scsicmd still gets passed to libata EH resulting in ->eng_timeout invocation with NULL qc, which none is handling properly. This patch makes sure that scmd and qc share the same lifetime. Original idea from Jeff Garzik <jgarzik@pobox.com>. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
This commit is contained in:
parent
341963b909
commit
f29841e08f
3 changed files with 43 additions and 0 deletions
|
@ -4913,6 +4913,7 @@ EXPORT_SYMBOL_GPL(ata_ratelimit);
|
|||
EXPORT_SYMBOL_GPL(ata_busy_sleep);
|
||||
EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
|
||||
EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
|
||||
EXPORT_SYMBOL_GPL(ata_scsi_timed_out);
|
||||
EXPORT_SYMBOL_GPL(ata_scsi_error);
|
||||
EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
|
||||
EXPORT_SYMBOL_GPL(ata_scsi_release);
|
||||
|
|
|
@ -716,6 +716,47 @@ int ata_scsi_slave_config(struct scsi_device *sdev)
|
|||
return 0; /* scsi layer doesn't check return value, sigh */
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_scsi_timed_out - SCSI layer time out callback
|
||||
* @cmd: timed out SCSI command
|
||||
*
|
||||
* Handles SCSI layer timeout. We race with normal completion of
|
||||
* the qc for @cmd. If the qc is already gone, we lose and let
|
||||
* the scsi command finish (EH_HANDLED). Otherwise, the qc has
|
||||
* timed out and EH should be invoked. Prevent ata_qc_complete()
|
||||
* from finishing it by setting EH_SCHEDULED and return
|
||||
* EH_NOT_HANDLED.
|
||||
*
|
||||
* LOCKING:
|
||||
* Called from timer context
|
||||
*
|
||||
* RETURNS:
|
||||
* EH_HANDLED or EH_NOT_HANDLED
|
||||
*/
|
||||
enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
|
||||
{
|
||||
struct Scsi_Host *host = cmd->device->host;
|
||||
struct ata_port *ap = (struct ata_port *) &host->hostdata[0];
|
||||
unsigned long flags;
|
||||
struct ata_queued_cmd *qc;
|
||||
enum scsi_eh_timer_return ret = EH_HANDLED;
|
||||
|
||||
DPRINTK("ENTER\n");
|
||||
|
||||
spin_lock_irqsave(&ap->host_set->lock, flags);
|
||||
qc = ata_qc_from_tag(ap, ap->active_tag);
|
||||
if (qc) {
|
||||
assert(qc->scsicmd == cmd);
|
||||
qc->flags |= ATA_QCFLAG_EH_SCHEDULED;
|
||||
qc->err_mask |= AC_ERR_TIMEOUT;
|
||||
ret = EH_NOT_HANDLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&ap->host_set->lock, flags);
|
||||
|
||||
DPRINTK("EXIT, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_scsi_error - SCSI layer error handler callback
|
||||
* @host: SCSI host on which error occurred
|
||||
|
|
|
@ -509,6 +509,7 @@ extern void ata_host_set_remove(struct ata_host_set *host_set);
|
|||
extern int ata_scsi_detect(struct scsi_host_template *sht);
|
||||
extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
|
||||
extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *));
|
||||
extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
|
||||
extern int ata_scsi_error(struct Scsi_Host *host);
|
||||
extern void ata_eh_qc_complete(struct ata_queued_cmd *qc);
|
||||
extern void ata_eh_qc_retry(struct ata_queued_cmd *qc);
|
||||
|
|
Loading…
Reference in a new issue