summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libata-core.c
diff options
context:
space:
mode:
authorTejun Heo2006-05-15 13:58:09 +0200
committerTejun Heo2006-05-15 13:58:09 +0200
commite318049949b07152d851dbfebbd93e560af45ebe (patch)
treecbc322c94717c9ac5d7e2c48a9f690a2e0145b81 /drivers/scsi/libata-core.c
parent[PATCH] libata-eh-fw: implement ata_port_schedule_eh() and ata_port_abort() (diff)
downloadkernel-qcow2-linux-e318049949b07152d851dbfebbd93e560af45ebe.tar.gz
kernel-qcow2-linux-e318049949b07152d851dbfebbd93e560af45ebe.tar.xz
kernel-qcow2-linux-e318049949b07152d851dbfebbd93e560af45ebe.zip
[PATCH] libata-eh-fw: implement freeze/thaw
Freezing is performed atomic w.r.t. host_set->lock and once frozen LLDD is not allowed to access the port or any qc on it. Also, libata makes sure that no new qc gets issued to a frozen port. A frozen port is thawed after a reset operation completes successfully, so reset methods must do its job while the port is frozen. During initialization all ports get frozen before requesting IRQ, so reset methods are always invoked on a frozen port. Optional ->freeze and ->thaw operations notify LLDD that the port is being frozen and thawed, respectively. LLDD can disable/enable hardware interrupt in these callbacks if the controller's IRQ mask can be changed dynamically. If the controller doesn't allow such operation, LLDD can check for frozen state in the interrupt handler and ack/clear interrupts unconditionally while frozen. Signed-off-by: Tejun Heo <htejun@gmail.com>
Diffstat (limited to 'drivers/scsi/libata-core.c')
-rw-r--r--drivers/scsi/libata-core.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 9c97783462d6..63857a90ac28 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -987,6 +987,12 @@ unsigned ata_exec_internal(struct ata_device *dev,
spin_lock_irqsave(&ap->host_set->lock, flags);
+ /* no internal command while frozen */
+ if (ap->flags & ATA_FLAG_FROZEN) {
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+ return AC_ERR_SYSTEM;
+ }
+
/* initialize internal qc */
/* XXX: Tag 0 is used for drivers with legacy EH as some
@@ -2565,8 +2571,11 @@ void ata_std_postreset(struct ata_port *ap, unsigned int *classes)
sata_scr_write(ap, SCR_ERROR, serror);
/* re-enable interrupts */
- if (ap->ioaddr.ctl_addr) /* FIXME: hack. create a hook instead */
- ata_irq_on(ap);
+ if (!ap->ops->error_handler) {
+ /* FIXME: hack. create a hook instead */
+ if (ap->ioaddr.ctl_addr)
+ ata_irq_on(ap);
+ }
/* is double-select really necessary? */
if (classes[0] != ATA_DEV_NONE)
@@ -2681,6 +2690,8 @@ int ata_drive_probe_reset(struct ata_port *ap, ata_probeinit_fn_t probeinit,
{
int rc = -EINVAL;
+ ata_eh_freeze_port(ap);
+
if (probeinit)
probeinit(ap);
@@ -2725,6 +2736,9 @@ int ata_drive_probe_reset(struct ata_port *ap, ata_probeinit_fn_t probeinit,
if (rc == 0) {
if (postreset)
postreset(ap, classes);
+
+ ata_eh_thaw_port(ap);
+
if (classes[0] == ATA_DEV_UNKNOWN)
rc = -ENODEV;
}
@@ -4039,6 +4053,10 @@ static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap)
struct ata_queued_cmd *qc = NULL;
unsigned int i;
+ /* no command while frozen */
+ if (unlikely(ap->flags & ATA_FLAG_FROZEN))
+ return NULL;
+
/* the last tag is reserved for internal command. */
for (i = 0; i < ATA_MAX_QUEUE - 1; i++)
if (!test_and_set_bit(i, &ap->qactive)) {
@@ -4953,6 +4971,7 @@ int ata_device_add(const struct ata_probe_ent *ent)
ata_chk_status(ap);
host_set->ops->irq_clear(ap);
+ ata_eh_freeze_port(ap); /* freeze port before requesting IRQ */
count++;
}
@@ -5385,5 +5404,8 @@ EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
EXPORT_SYMBOL_GPL(ata_eng_timeout);
EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
EXPORT_SYMBOL_GPL(ata_port_abort);
+EXPORT_SYMBOL_GPL(ata_port_freeze);
+EXPORT_SYMBOL_GPL(ata_eh_freeze_port);
+EXPORT_SYMBOL_GPL(ata_eh_thaw_port);
EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
EXPORT_SYMBOL_GPL(ata_eh_qc_retry);