From dafadcde8d4dc5ea8c742faa7ff4403336b542b8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 15 May 2006 20:58:11 +0900 Subject: [PATCH] [PATCH] libata-eh-fw: implement new EH scheduling from PIO PIO executes without holding host_set lock, so it cannot be synchronized using the same mechanism as interrupt driven execution. port_task framework makes sure that EH is not entered until PIO task is flushed, so PIO task can be sure the qc in progress won't go away underneath it. One thing it cannot be sure of is whether the qc has already been scheduled for EH by another exception condition while host_set lock was released. This patch makes ata_poll_qc-complete() handle such conditions properly and make it freeze the port if HSM violation is detected during PIO execution. Signed-off-by: Tejun Heo --- drivers/scsi/libata-core.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 63857a90ac28..35ae5b41f662 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -3430,16 +3430,31 @@ static int ata_sg_setup(struct ata_queued_cmd *qc) * LOCKING: * None. (grabs host lock) */ - void ata_poll_qc_complete(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; unsigned long flags; spin_lock_irqsave(&ap->host_set->lock, flags); - ap->flags &= ~ATA_FLAG_NOINTR; - ata_irq_on(ap); - ata_qc_complete(qc); + + if (ap->ops->error_handler) { + /* EH might have kicked in while host_set lock is released */ + qc = ata_qc_from_tag(ap, qc->tag); + if (qc) { + if (!(qc->err_mask & AC_ERR_HSM)) { + ap->flags &= ~ATA_FLAG_NOINTR; + ata_irq_on(ap); + ata_qc_complete(qc); + } else + ata_port_freeze(ap); + } + } else { + /* old EH */ + ap->flags &= ~ATA_FLAG_NOINTR; + ata_irq_on(ap); + ata_qc_complete(qc); + } + spin_unlock_irqrestore(&ap->host_set->lock, flags); }