linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] libata: add host_set->next for legacy two host sets case
@ 2006-06-12  5:17 Tejun Heo
  2006-06-12  6:55 ` Jeff Garzik
  0 siblings, 1 reply; 13+ messages in thread
From: Tejun Heo @ 2006-06-12  5:17 UTC (permalink / raw)
  To: Jeff Garzik, linux-ide, Albert Lee

For a legacy ATA controller, libata registers two separate host sets.
There was no connection between the two hosts making it impossible to
traverse all ports related to the controller.  This patch adds
host_set->next which points to the second host_set and implements
ata_host_set_for_all_ports() which traverses all ports associated with
the controller.  This fixes the following bugs.

* On device removal, all ports hanging off the device are properly
  detached.  Prior to this patch, ports on the first host_set weren't
  detached casuing oops on driver unloading.

* On device removal, both host sets are freed

This will also be used by new power management code to suspend and
resume all ports of a controller.  host_set/port representation will
be improved to handle legacy controllers better and this host_set
linking hack will go away with it.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-bmdma.c |   15 +++++++++++++--
 drivers/scsi/libata-core.c  |   39 +++++++++++++++++++++++++++++++++------
 include/linux/libata.h      |    3 ++-
 3 files changed, 48 insertions(+), 9 deletions(-)

b1d1062e1b5b1de896b687118ed9a1926fd6f3b0
diff --git a/drivers/scsi/libata-bmdma.c b/drivers/scsi/libata-bmdma.c
index 4bc0537..13fab97 100644
--- a/drivers/scsi/libata-bmdma.c
+++ b/drivers/scsi/libata-bmdma.c
@@ -1076,10 +1076,21 @@ int ata_pci_init_one (struct pci_dev *pd
 
 	/* FIXME: check ata_device_add return */
 	if (legacy_mode) {
-		if (legacy_mode & (1 << 0))
+		struct device *dev = &pdev->dev;
+		struct ata_host_set *host_set = NULL;
+
+		if (legacy_mode & (1 << 0)) {
 			ata_device_add(probe_ent);
-		if (legacy_mode & (1 << 1))
+			host_set = dev_get_drvdata(dev);
+		}
+
+		if (legacy_mode & (1 << 1)) {
 			ata_device_add(probe_ent2);
+			if (host_set) {
+				host_set->next = dev_get_drvdata(dev);
+				dev_set_drvdata(dev, host_set);
+			}
+		}
 	} else
 		ata_device_add(probe_ent);
 
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 014855e..357f253 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -94,6 +94,32 @@ MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 
 
+static struct ata_port *__ata_host_set_next_port(struct ata_port *ap,
+						 int *p_idx,
+						 struct ata_host_set *host_set)
+{
+	int idx = ++(*p_idx);
+
+	if (idx >= host_set->n_ports && host_set->next) {
+		idx -= host_set->n_ports;
+		host_set = host_set->next;
+	}
+
+	if (idx < host_set->n_ports)
+		return host_set->ports[idx];
+
+	return NULL;
+}
+
+/* No, it's not for_each_port.  The following macro traverses not only
+ * host_set->ports but also host_set->next->ports.  Don't use it
+ * unless you know what you're doing.  It will go away once legacy
+ * host handling is improved.
+ */
+#define ata_host_set_for_all_ports(ap, idx, host_set) \
+	for (ap = host_set->ports[0], idx = 0; ap; \
+	     ap = __ata_host_set_next_port(ap, &idx, host_set))
+
 /**
  *	ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure
  *	@tf: Taskfile to convert
@@ -5510,16 +5536,15 @@ void ata_port_detach(struct ata_port *ap
 
 void ata_host_set_remove(struct ata_host_set *host_set)
 {
-	unsigned int i;
+	struct ata_port *ap;
+	int i;
 
-	for (i = 0; i < host_set->n_ports; i++)
-		ata_port_detach(host_set->ports[i]);
+	ata_host_set_for_all_ports(ap, i, host_set)
+		ata_port_detach(ap);
 
 	free_irq(host_set->irq, host_set);
 
-	for (i = 0; i < host_set->n_ports; i++) {
-		struct ata_port *ap = host_set->ports[i];
-
+	ata_host_set_for_all_ports(ap, i, host_set) {
 		ata_scsi_release(ap->host);
 
 		if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) {
@@ -5537,6 +5562,8 @@ void ata_host_set_remove(struct ata_host
 	if (host_set->ops->host_stop)
 		host_set->ops->host_stop(host_set);
 
+	if (host_set->next)
+		kfree(host_set->next);
 	kfree(host_set);
 }
 
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 61eea57..f03b866 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -356,7 +356,8 @@ struct ata_host_set {
 	unsigned long		flags;
 	int			simplex_claimed;	/* Keep seperate in case we
 							   ever need to do this locked */
-	struct ata_port *	ports[0];
+	struct ata_host_set	*next;		/* for legacy mode */
+	struct ata_port		*ports[0];
 };
 
 struct ata_queued_cmd {
-- 
1.3.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2006-06-28 16:12 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-06-12  5:17 [PATCH] libata: add host_set->next for legacy two host sets case Tejun Heo
2006-06-12  6:55 ` Jeff Garzik
2006-06-12  7:06   ` Tejun Heo
2006-06-12 10:14   ` [PATCH] libata: add host_set->next for legacy two host_sets case, take #2 Tejun Heo
2006-06-12 13:35     ` Jeff Garzik
2006-06-12 13:44       ` Tejun Heo
2006-06-12 14:05       ` [PATCH] libata: add host_set->next for legacy two host_sets case, take #3 Tejun Heo
2006-06-12 14:24         ` Jeff Garzik
2006-06-28 10:27         ` Albert Lee
2006-06-28 10:42           ` Tejun Heo
2006-06-28 10:53             ` Albert Lee
2006-06-28 16:25             ` Alan Cox
2006-06-28 11:38           ` Alan Cox

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).