[PATCH 4/20] libata: separate out ata_host_alloc() and ata_host_attach()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Separate out ata_host_alloc(), ata_host_add_ports() and
ata_host_attach() from ata_device_add().  These functions will be used
by new LLD init model where LLD allocates host directly, then
intializes and attaches it instead of going through probe_ent.

ata_host_alloc() can be called with NULL @sht which indicates that
Scsi_Host association is handled by the caller.  This will be used by
SAS.

This patch does not introduce behavior changes.

Signed-off-by: Tejun Heo <htejun@xxxxxxxxx>
---
 drivers/ata/libata-core.c |  451 ++++++++++++++++++++++++++++++---------------
 include/linux/libata.h    |    7 +
 2 files changed, 312 insertions(+), 146 deletions(-)

diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index d7bf4a1..eeb9dc6 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5243,11 +5243,15 @@ void ata_dev_init(struct ata_device *dev
  *	ata_port_init - Initialize an ata_port structure
  *	@ap: Structure to initialize
  *	@host: Collection of hosts to which @ap belongs
- *	@ent: Probe information provided by low-level driver
- *	@port_no: Port number associated with this ata_port
+ *	@ent: Probe information provided by low-level driver (optional)
+ *	@port_no: Port number associated with this ata_port (optional)
  *
  *	Initialize a new ata_port structure.
  *
+ *	@ent and @port_no are to support sas interface.  These will be
+ *	removed once sas is converted to use new alloc/init/attach
+ *	interface.
+ *
  *	LOCKING:
  *	Inherited from caller.
  */
@@ -5261,13 +5265,17 @@ void ata_port_init(struct ata_port *ap, 
 	ap->id = ata_unique_id++;
 	ap->ctl = ATA_DEVCTL_OBS;
 	ap->host = host;
-	ap->dev = ent->dev;
-	ap->port_no = port_no;
-	ap->pio_mask = ent->pio_mask;
-	ap->mwdma_mask = ent->mwdma_mask;
-	ap->udma_mask = ent->udma_mask;
-	ap->flags |= ent->port_flags;
-	ap->ops = ent->port_ops;
+	ap->dev = host->dev;
+
+	if (ent) {
+		ap->port_no = port_no;
+		ap->pio_mask = ent->pio_mask;
+		ap->mwdma_mask = ent->mwdma_mask;
+		ap->udma_mask = ent->udma_mask;
+		ap->flags |= ent->port_flags;
+		ap->ops = ent->port_ops;
+	}
+
 	ap->hw_sata_spd_limit = UINT_MAX;
 	ap->active_tag = ATA_TAG_POISON;
 	ap->last_ctl = 0xFF;
@@ -5287,10 +5295,7 @@ #endif
 	INIT_LIST_HEAD(&ap->eh_done_q);
 	init_waitqueue_head(&ap->eh_wait_q);
 
-	/* set cable type */
 	ap->cbl = ATA_CBL_NONE;
-	if (ap->flags & ATA_FLAG_SATA)
-		ap->cbl = ATA_CBL_SATA;
 
 	for (i = 0; i < ATA_MAX_DEVICES; i++) {
 		struct ata_device *dev = &ap->device[i];
@@ -5304,7 +5309,12 @@ #ifdef ATA_IRQ_TRAP
 	ap->stats.idle_irq = 1;
 #endif
 
-	memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports));
+	if (ent) {
+		if (ap->flags & ATA_FLAG_SATA)
+			ap->cbl = ATA_CBL_SATA;
+		memcpy(&ap->ioaddr, &ent->port[port_no],
+		       sizeof(struct ata_ioports));
+	}
 }
 
 /**
@@ -5329,47 +5339,140 @@ static void ata_port_init_shost(struct a
 }
 
 /**
- *	ata_port_add - Attach low-level ATA driver to system
- *	@ent: Information provided by low-level driver
- *	@host: Collections of ports to which we add
- *	@port_no: Port number associated with this host
+ *	ata_port_alloc - allocate and initialize basic ATA port resources
+ *	@host: ATA host this allocated port belongs to
+ *	@sht: template for SCSI host (can be NULL)
  *
- *	Attach low-level ATA driver to system.
+ *	Allocate and initialize basic ATA port resources.
  *
- *	LOCKING:
- *	PCI/etc. bus probe sem.
+ *	If @sht is NULL, SCSI host is not allocated nor freed later
+ *	with the port.  The caller is responsible for associating SCSI
+ *	host.  This is for SAS.
  *
  *	RETURNS:
- *	New ata_port on success, for NULL on error.
+ *	Allocate ATA port on success, NULL on failure.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
  */
-static struct ata_port * ata_port_add(const struct ata_probe_ent *ent,
-				      struct ata_host *host,
-				      unsigned int port_no)
+static struct ata_port *ata_port_alloc(struct ata_host *host,
+				       struct scsi_host_template *sht)
 {
-	struct Scsi_Host *shost;
 	struct ata_port *ap;
 
 	DPRINTK("ENTER\n");
 
-	if (!ent->port_ops->error_handler &&
-	    !(ent->port_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) {
-		printk(KERN_ERR "ata%u: no reset mechanism available\n",
-		       port_no);
-		return NULL;
+	if (sht) {
+		struct Scsi_Host *shost;
+
+		shost = scsi_host_alloc(sht, sizeof(struct ata_port));
+		if (!shost)
+			return NULL;
+
+		shost->transportt = &ata_scsi_transport_template;
+
+		ap = ata_shost_to_port(shost);
+		ata_port_init_shost(ap, shost);
+
+		ap->pflags |= ATA_PFLAG_SHOST_OWNER;
+	} else {
+		ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+		if (!ap)
+			return NULL;
 	}
 
-	shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port));
-	if (!shost)
+	ata_port_init(ap, host, NULL, 0);
+
+	return ap;
+}
+
+/**
+ *	ata_host_alloc - allocate and init basic ATA host resources
+ *	@dev: generic device this host is associated with
+ *	@sht: template for SCSI host
+ *	@n_ports: number of ATA ports associated with this host (can be 0)
+ *
+ *	Allocate and initialize basic ATA host resources.  LLD calls
+ *	this function to allocate a host, initializes it fully and
+ *	attaches it using ata_host_attach().
+ *
+ *	RETURNS:
+ *	Allocate ATA host on success, NULL on failure.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ */
+struct ata_host *ata_host_alloc(struct device *dev,
+				struct scsi_host_template *sht, int n_ports)
+{
+	struct ata_host *host;
+	size_t sz;
+
+	DPRINTK("ENTER\n");
+
+	/* alloc a container for our list of ATA ports (buses) */
+	sz = sizeof(struct ata_host) + n_ports * sizeof(void *);
+	if (n_ports == 0)
+		sz += ATA_MAX_PORTS * sizeof(void *);
+
+	host = kzalloc(sz, GFP_KERNEL);
+	if (!host)
 		return NULL;
 
-	shost->transportt = &ata_scsi_transport_template;
+	spin_lock_init(&host->lock);
+	host->dev = dev;
 
-	ap = ata_shost_to_port(shost);
+	if (ata_host_add_ports(host, sht, n_ports))
+		goto err_free;
 
-	ata_port_init(ap, host, ent, port_no);
-	ata_port_init_shost(ap, shost);
+	return host;
 
-	return ap;
+ err_free:
+	ata_host_free(host);
+	return NULL;
+}
+
+/**
+ *	ata_host_add_ports - allocate ports to zero-port host
+ *	@host: target ATA host
+ *	@sht: template for SCSI host
+ *	@n_ports: number of ATA ports associated with this host
+ *
+ *	Allocate @n_ports ports and associate them with @host.
+ *	@n_ports must be less than or equal to ATA_MAX_PORTS and @host
+ *	must have been allocated with zero port.  This function is
+ *	used by LLDs which can't determine number of ports at host
+ *	allocation time.
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ */
+int ata_host_add_ports(struct ata_host *host,
+		       struct scsi_host_template *sht, int n_ports)
+{
+	int i;
+
+	if (host->n_ports || n_ports > ATA_MAX_PORTS)
+		return -EINVAL;
+
+	/* allocate ports bound to this host */
+	for (i = 0; i < n_ports; i++) {
+		struct ata_port *ap;
+
+		ap = ata_port_alloc(host, sht);
+		if (!ap)
+			return -ENOMEM;
+
+		ap->port_no = i;
+		host->ports[i] = ap;
+	}
+
+	host->n_ports = n_ports;
+
+	return 0;
 }
 
 /**
@@ -5401,6 +5504,9 @@ void ata_host_init(struct ata_host *host
  *	ap->pflags, so this function can be called multiple times.
  *	Ports are guaranteed to get started only once.
  *
+ *	This function also initializes ap->cbl according to
+ *	ATA_FLAG_SATA if cable type isn't set yet.
+ *
  *	LOCKING:
  *	Inherited from calling layer (may sleep).
  *
@@ -5414,6 +5520,11 @@ int ata_host_start(struct ata_host *host
 	for (i = 0; i < host->n_ports; i++) {
 		struct ata_port *ap = host->ports[i];
 
+		/* set cable type */
+		if (ap->cbl == ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA))
+			ap->cbl = ATA_CBL_SATA;
+
+		/* start ports */
 		if (ap->pflags & ATA_PFLAG_STARTED)
 			continue;
 
@@ -5430,6 +5541,126 @@ int ata_host_start(struct ata_host *host
 }
 
 /**
+ *	ata_host_attach - attach initialized ATA host
+ *	@host: ATA host to attach
+ *
+ *	Attach initialized ATA host.  @host is allocated using
+ *	ata_host_alloc() and fully initialized by LLD.  This function
+ *	registers @host with ATA and SCSI layers and probe attached
+ *	devices.
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ */
+int ata_host_attach(struct ata_host *host)
+{
+	int i, rc;
+
+	/* make sure ports are started */
+	rc = ata_host_start(host);
+	if (rc)
+		return rc;
+
+	/* perform each probe synchronously */
+	DPRINTK("probe begin\n");
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+		int irq_line = host->irq;
+		unsigned long xfer_mode_mask;
+		u32 scontrol;
+		int rc;
+
+		/* report the secondary IRQ for second channel legacy */
+		if (i == 1 && host->irq2)
+			irq_line = host->irq2;
+
+		xfer_mode_mask = (ap->udma_mask << ATA_SHIFT_UDMA) |
+				 (ap->mwdma_mask << ATA_SHIFT_MWDMA) |
+				 (ap->pio_mask << ATA_SHIFT_PIO);
+
+		/* print per-port info to dmesg */
+		if (ap->ops != &ata_dummy_port_ops)
+			ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%lu "
+					"ctl 0x%lX bmdma 0x%lX irq %d\n",
+					ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
+					ata_mode_string(xfer_mode_mask),
+					ap->ioaddr.cmd_addr,
+					ap->ioaddr.ctl_addr,
+					ap->ioaddr.bmdma_addr, irq_line);
+		else
+			ata_port_printk(ap, KERN_INFO, "DUMMY port\n");
+
+		/* init sata_spd_limit to the current value */
+		if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) {
+			int spd = (scontrol >> 4) & 0xf;
+			ap->hw_sata_spd_limit &= (1 << spd) - 1;
+		}
+		ap->sata_spd_limit = ap->hw_sata_spd_limit;
+
+		rc = scsi_add_host(ap->scsi_host, host->dev);
+		if (rc) {
+			ata_port_printk(ap, KERN_ERR, "scsi_add_host failed\n");
+			/* FIXME: do something useful here */
+			/* FIXME: handle unconditional calls to
+			 * scsi_scan_host and ata_host_remove, below,
+			 * at the very least
+			 */
+		}
+
+		if (ap->ops->error_handler) {
+			struct ata_eh_info *ehi = &ap->eh_info;
+			unsigned long flags;
+
+			ata_port_probe(ap);
+
+			/* kick EH for boot probing */
+			spin_lock_irqsave(ap->lock, flags);
+
+			ehi->probe_mask = (1 << ATA_MAX_DEVICES) - 1;
+			ehi->action |= ATA_EH_SOFTRESET;
+			ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
+
+			ap->pflags |= ATA_PFLAG_LOADING;
+			ata_port_schedule_eh(ap);
+
+			spin_unlock_irqrestore(ap->lock, flags);
+
+			/* wait for EH to finish */
+			ata_port_wait_eh(ap);
+		} else {
+			DPRINTK("ata%u: bus probe begin\n", ap->id);
+			rc = ata_bus_probe(ap);
+			DPRINTK("ata%u: bus probe end\n", ap->id);
+
+			if (rc) {
+				/* FIXME: do something useful here?
+				 * Current libata behavior will
+				 * tear down everything when
+				 * the module is removed
+				 * or the h/w is unplugged.
+				 */
+			}
+		}
+	}
+
+	/* probes are done, now scan each port's disk(s) */
+	DPRINTK("host probe begin\n");
+	for (i = 0; i < host->n_ports; i++) {
+		struct ata_port *ap = host->ports[i];
+
+		ata_scsi_scan_host(ap);
+	}
+
+	dev_set_drvdata(host->dev, host);
+
+	return 0;
+}
+
+
+/**
  *	ata_device_add - Register hardware device with ATA and SCSI layers
  *	@ent: Probe information describing hardware device to be registered
  *
@@ -5455,61 +5686,48 @@ int ata_device_add(const struct ata_prob
 	int rc;
 
 	DPRINTK("ENTER\n");
-	/* alloc a container for our list of ATA ports (buses) */
-	host = kzalloc(sizeof(struct ata_host) +
-		       (ent->n_ports * sizeof(void *)), GFP_KERNEL);
+
+	if (!ent->port_ops->error_handler &&
+	    !(ent->port_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) {
+		dev_printk(KERN_ERR, dev, "no reset mechanism available\n");
+		return 0;
+	}
+
+	/* allocate host */
+	host = ata_host_alloc(dev, ent->sht, ent->n_ports);
 	if (!host)
 		return 0;
 
-	ata_host_init(host, dev, ent->_host_flags, ent->port_ops);
-	host->n_ports = ent->n_ports;
 	host->irq = ent->irq;
 	host->irq2 = ent->irq2;
 	host->mmio_base = ent->mmio_base;
 	host->private_data = ent->private_data;
+	host->ops = ent->port_ops;
+	host->flags = ent->_host_flags;
 
-	/* register each port bound to this device */
 	for (i = 0; i < host->n_ports; i++) {
-		struct ata_port *ap;
-		unsigned long xfer_mode_mask;
-		int irq_line = ent->irq;
-
-		ap = ata_port_add(ent, host, i);
-		if (!ap)
-			goto err_out;
-
-		host->ports[i] = ap;
+		struct ata_port *ap = host->ports[i];
 
 		/* dummy? */
 		if (ent->dummy_port_mask & (1 << i)) {
-			ata_port_printk(ap, KERN_INFO, "DUMMY\n");
 			ap->ops = &ata_dummy_port_ops;
 			continue;
 		}
 
-		/* Report the secondary IRQ for second channel legacy */
-		if (i == 1 && ent->irq2)
-			irq_line = ent->irq2;
-
-		xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
-				(ap->mwdma_mask << ATA_SHIFT_MWDMA) |
-				(ap->pio_mask << ATA_SHIFT_PIO);
+		ap->pio_mask = ent->pio_mask;
+		ap->mwdma_mask = ent->mwdma_mask;
+		ap->udma_mask = ent->udma_mask;
+		ap->flags |= ent->port_flags;
+		ap->ops = ent->port_ops;
 
-		/* print per-port info to dmesg */
-		ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%lX "
-				"ctl 0x%lX bmdma 0x%lX irq %d\n",
-				ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
-				ata_mode_string(xfer_mode_mask),
-				ap->ioaddr.cmd_addr,
-				ap->ioaddr.ctl_addr,
-				ap->ioaddr.bmdma_addr,
-				irq_line);
+		memcpy(&ap->ioaddr, &ent->port[ap->port_no],
+		       sizeof(struct ata_ioports));
 	}
 
 	/* start ports */
 	rc = ata_host_start(host);
 	if (rc)
-		goto err_out;
+		goto err_free_host;
 
 	/* freeze */
 	for (i = 0; i < host->n_ports; i++) {
@@ -5526,7 +5744,7 @@ int ata_device_add(const struct ata_prob
 	if (rc) {
 		dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n",
 			   ent->irq, rc);
-		goto err_out;
+		goto err_free_host;
 	}
 
 	/* do we have a second IRQ for the other channel, eg legacy mode */
@@ -5540,86 +5758,22 @@ int ata_device_add(const struct ata_prob
 		if (rc) {
 			dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n",
 				   ent->irq2, rc);
-			goto err_out_free_irq;
+			goto err_free_irq;
 		}
 	}
 
-	/* perform each probe synchronously */
-	DPRINTK("probe begin\n");
-	for (i = 0; i < host->n_ports; i++) {
-		struct ata_port *ap = host->ports[i];
-		u32 scontrol;
-		int rc;
-
-		/* init sata_spd_limit to the current value */
-		if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) {
-			int spd = (scontrol >> 4) & 0xf;
-			ap->hw_sata_spd_limit &= (1 << spd) - 1;
-		}
-		ap->sata_spd_limit = ap->hw_sata_spd_limit;
-
-		rc = scsi_add_host(ap->scsi_host, dev);
-		if (rc) {
-			ata_port_printk(ap, KERN_ERR, "scsi_add_host failed\n");
-			/* FIXME: do something useful here */
-			/* FIXME: handle unconditional calls to
-			 * scsi_scan_host and ata_host_remove, below,
-			 * at the very least
-			 */
-		}
-
-		if (ap->ops->error_handler) {
-			struct ata_eh_info *ehi = &ap->eh_info;
-			unsigned long flags;
-
-			ata_port_probe(ap);
-
-			/* kick EH for boot probing */
-			spin_lock_irqsave(ap->lock, flags);
-
-			ehi->probe_mask = (1 << ATA_MAX_DEVICES) - 1;
-			ehi->action |= ATA_EH_SOFTRESET;
-			ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
-
-			ap->pflags |= ATA_PFLAG_LOADING;
-			ata_port_schedule_eh(ap);
-
-			spin_unlock_irqrestore(ap->lock, flags);
-
-			/* wait for EH to finish */
-			ata_port_wait_eh(ap);
-		} else {
-			DPRINTK("ata%u: bus probe begin\n", ap->id);
-			rc = ata_bus_probe(ap);
-			DPRINTK("ata%u: bus probe end\n", ap->id);
-
-			if (rc) {
-				/* FIXME: do something useful here?
-				 * Current libata behavior will
-				 * tear down everything when
-				 * the module is removed
-				 * or the h/w is unplugged.
-				 */
-			}
-		}
-	}
-
-	/* probes are done, now scan each port's disk(s) */
-	DPRINTK("host probe begin\n");
-	for (i = 0; i < host->n_ports; i++) {
-		struct ata_port *ap = host->ports[i];
-
-		ata_scsi_scan_host(ap);
-	}
-
-	dev_set_drvdata(dev, host);
+	rc = ata_host_attach(host);
+	if (rc)
+		goto err_free_irq2;
 
-	VPRINTK("EXIT, returning %u\n", ent->n_ports);
-	return ent->n_ports; /* success */
+	return host->n_ports; /* success */
 
-err_out_free_irq:
+ err_free_irq2:
+	if (ent->irq2)
+		free_irq(ent->irq2, host);
+ err_free_irq:
 	free_irq(ent->irq, host);
-err_out:
+ err_free_host:
 	ata_host_stop(host);
 	ata_host_free(host);
 	VPRINTK("EXIT, returning 0\n");
@@ -5755,8 +5909,10 @@ void ata_host_free(struct ata_host *host
 	/* free */
 	for (i = 0; i < host->n_ports; i++) {
 		struct ata_port *ap = host->ports[i];
-		if (ap)
+		if (ap->pflags & ATA_PFLAG_SHOST_OWNER)
 			scsi_host_put(ap->scsi_host);
+		else
+			kfree(ap);
 	}
 
 	kfree(host);
@@ -6108,7 +6264,10 @@ EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
 EXPORT_SYMBOL_GPL(ata_std_bios_param);
 EXPORT_SYMBOL_GPL(ata_std_ports);
 EXPORT_SYMBOL_GPL(ata_host_init);
+EXPORT_SYMBOL_GPL(ata_host_alloc);
+EXPORT_SYMBOL_GPL(ata_host_add_ports);
 EXPORT_SYMBOL_GPL(ata_host_start);
+EXPORT_SYMBOL_GPL(ata_host_attach);
 EXPORT_SYMBOL_GPL(ata_device_add);
 EXPORT_SYMBOL_GPL(ata_host_detach);
 EXPORT_SYMBOL_GPL(ata_host_stop);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 5263e6b..5e8160a 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -185,6 +185,7 @@ enum {
 	ATA_PFLAG_PM_PENDING	= (1 << 18), /* PM operation pending */
 
 	ATA_PFLAG_STARTED	= (1 << 24), /* port has been started */
+	ATA_PFLAG_SHOST_OWNER	= (1 << 25), /* owner of ap->scsi_host */
 
 	/* struct ata_queued_cmd flags */
 	ATA_QCFLAG_ACTIVE	= (1 << 0), /* cmd not yet ack'd to scsi lyer */
@@ -690,7 +691,13 @@ extern int ata_pci_device_suspend(struct
 extern int ata_pci_device_resume(struct pci_dev *pdev);
 extern int ata_pci_clear_simplex(struct pci_dev *pdev);
 #endif /* CONFIG_PCI */
+extern struct ata_host *ata_host_alloc(struct device *dev,
+				       struct scsi_host_template *sht,
+				       int n_ports);
+extern int ata_host_add_ports(struct ata_host *host,
+			      struct scsi_host_template *sht, int n_ports);
 extern int ata_host_start(struct ata_host *host);
+extern int ata_host_attach(struct ata_host *host);
 extern int ata_device_add(const struct ata_probe_ent *ent);
 extern void ata_host_detach(struct ata_host *host);
 extern void ata_host_stop(struct ata_host *host);
-- 
1.4.1.1


-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux