[SCSI] libata, libsas: introduce sched_eh and end_eh port ops
When managing shost->host_eh_scheduled libata assumes that there is a
1:1 shost-to-ata_port relationship. libsas creates a 1:N relationship
so it needs to manage host_eh_scheduled cumulatively at the host level.
The sched_eh and end_eh port port ops allow libsas to track when domain
devices enter/leave the "eh-pending" state under ha->lock (previously
named ha->state_lock, but it is no longer just a lock for ha->state
changes).
Since host_eh_scheduled indicates eh without backing commands pinning
the device it can be deallocated at any time. Move the taking of the
domain_device reference under the port_lock to guarantee that the
ata_port stays around for the duration of eh.
Reviewed-by: Jacek Danecki <jacek.danecki@intel.com>
Acked-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 6d53cf9..77fc806 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -793,12 +793,12 @@
ata_for_each_link(link, ap, HOST_FIRST)
memset(&link->eh_info, 0, sizeof(link->eh_info));
- /* Clear host_eh_scheduled while holding ap->lock such
- * that if exception occurs after this point but
- * before EH completion, SCSI midlayer will
+ /* end eh (clear host_eh_scheduled) while holding
+ * ap->lock such that if exception occurs after this
+ * point but before EH completion, SCSI midlayer will
* re-initiate EH.
*/
- host->host_eh_scheduled = 0;
+ ap->ops->end_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
ata_eh_release(ap);
@@ -986,6 +986,48 @@
}
/**
+ * ata_std_sched_eh - non-libsas ata_ports issue eh with this common routine
+ * @ap: ATA port to schedule EH for
+ *
+ * LOCKING: inherited from ata_port_schedule_eh
+ * spin_lock_irqsave(host lock)
+ */
+void ata_std_sched_eh(struct ata_port *ap)
+{
+ WARN_ON(!ap->ops->error_handler);
+
+ if (ap->pflags & ATA_PFLAG_INITIALIZING)
+ return;
+
+ ata_eh_set_pending(ap, 1);
+ scsi_schedule_eh(ap->scsi_host);
+
+ DPRINTK("port EH scheduled\n");
+}
+EXPORT_SYMBOL_GPL(ata_std_sched_eh);
+
+/**
+ * ata_std_end_eh - non-libsas ata_ports complete eh with this common routine
+ * @ap: ATA port to end EH for
+ *
+ * In the libata object model there is a 1:1 mapping of ata_port to
+ * shost, so host fields can be directly manipulated under ap->lock, in
+ * the libsas case we need to hold a lock at the ha->level to coordinate
+ * these events.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+void ata_std_end_eh(struct ata_port *ap)
+{
+ struct Scsi_Host *host = ap->scsi_host;
+
+ host->host_eh_scheduled = 0;
+}
+EXPORT_SYMBOL(ata_std_end_eh);
+
+
+/**
* ata_port_schedule_eh - schedule error handling without a qc
* @ap: ATA port to schedule EH for
*
@@ -997,15 +1039,8 @@
*/
void ata_port_schedule_eh(struct ata_port *ap)
{
- WARN_ON(!ap->ops->error_handler);
-
- if (ap->pflags & ATA_PFLAG_INITIALIZING)
- return;
-
- ata_eh_set_pending(ap, 1);
- scsi_schedule_eh(ap->scsi_host);
-
- DPRINTK("port EH scheduled\n");
+ /* see: ata_std_sched_eh, unless you know better */
+ ap->ops->sched_eh(ap);
}
static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link)