mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08: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
@ -4913,6 +4913,7 @@ EXPORT_SYMBOL_GPL(ata_ratelimit);
|
|||||||
EXPORT_SYMBOL_GPL(ata_busy_sleep);
|
EXPORT_SYMBOL_GPL(ata_busy_sleep);
|
||||||
EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
|
EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
|
||||||
EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
|
EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
|
||||||
|
EXPORT_SYMBOL_GPL(ata_scsi_timed_out);
|
||||||
EXPORT_SYMBOL_GPL(ata_scsi_error);
|
EXPORT_SYMBOL_GPL(ata_scsi_error);
|
||||||
EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
|
EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
|
||||||
EXPORT_SYMBOL_GPL(ata_scsi_release);
|
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 */
|
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
|
* ata_scsi_error - SCSI layer error handler callback
|
||||||
* @host: SCSI host on which error occurred
|
* @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_detect(struct scsi_host_template *sht);
|
||||||
extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
|
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 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 int ata_scsi_error(struct Scsi_Host *host);
|
||||||
extern void ata_eh_qc_complete(struct ata_queued_cmd *qc);
|
extern void ata_eh_qc_complete(struct ata_queued_cmd *qc);
|
||||||
extern void ata_eh_qc_retry(struct ata_queued_cmd *qc);
|
extern void ata_eh_qc_retry(struct ata_queued_cmd *qc);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user