diff options
author | Tejun Heo <htejun@gmail.com> | 2006-02-10 01:10:48 -0500 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2006-02-10 06:50:46 -0500 |
commit | f29841e08fa20a7f2c8bc1b70306975299c66ee7 (patch) | |
tree | c2f6c87cf79fbd36ec49ea3c2247a382da5d75cc /drivers/scsi/libata-scsi.c | |
parent | 341963b909a01d2f38d86f5db8dd1f8c80bd6dbf (diff) |
[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>
Diffstat (limited to 'drivers/scsi/libata-scsi.c')
-rw-r--r-- | drivers/scsi/libata-scsi.c | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 1df468eb2bf..d67cc2fb569 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c | |||
@@ -717,6 +717,47 @@ int ata_scsi_slave_config(struct scsi_device *sdev) | |||
717 | } | 717 | } |
718 | 718 | ||
719 | /** | 719 | /** |
720 | * ata_scsi_timed_out - SCSI layer time out callback | ||
721 | * @cmd: timed out SCSI command | ||
722 | * | ||
723 | * Handles SCSI layer timeout. We race with normal completion of | ||
724 | * the qc for @cmd. If the qc is already gone, we lose and let | ||
725 | * the scsi command finish (EH_HANDLED). Otherwise, the qc has | ||
726 | * timed out and EH should be invoked. Prevent ata_qc_complete() | ||
727 | * from finishing it by setting EH_SCHEDULED and return | ||
728 | * EH_NOT_HANDLED. | ||
729 | * | ||
730 | * LOCKING: | ||
731 | * Called from timer context | ||
732 | * | ||
733 | * RETURNS: | ||
734 | * EH_HANDLED or EH_NOT_HANDLED | ||
735 | */ | ||
736 | enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) | ||
737 | { | ||
738 | struct Scsi_Host *host = cmd->device->host; | ||
739 | struct ata_port *ap = (struct ata_port *) &host->hostdata[0]; | ||
740 | unsigned long flags; | ||
741 | struct ata_queued_cmd *qc; | ||
742 | enum scsi_eh_timer_return ret = EH_HANDLED; | ||
743 | |||
744 | DPRINTK("ENTER\n"); | ||
745 | |||
746 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
747 | qc = ata_qc_from_tag(ap, ap->active_tag); | ||
748 | if (qc) { | ||
749 | assert(qc->scsicmd == cmd); | ||
750 | qc->flags |= ATA_QCFLAG_EH_SCHEDULED; | ||
751 | qc->err_mask |= AC_ERR_TIMEOUT; | ||
752 | ret = EH_NOT_HANDLED; | ||
753 | } | ||
754 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
755 | |||
756 | DPRINTK("EXIT, ret=%d\n", ret); | ||
757 | return ret; | ||
758 | } | ||
759 | |||
760 | /** | ||
720 | * ata_scsi_error - SCSI layer error handler callback | 761 | * ata_scsi_error - SCSI layer error handler callback |
721 | * @host: SCSI host on which error occurred | 762 | * @host: SCSI host on which error occurred |
722 | * | 763 | * |