Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=32ebbc0c0d5d18c0135b55d1eb0029f48c54aff0
Commit:     32ebbc0c0d5d18c0135b55d1eb0029f48c54aff0
Parent:     1974e20161a2c097c481d2ff711de7db56cb2cd6
Author:     Tejun Heo <[EMAIL PROTECTED]>
AuthorDate: Thu Nov 8 13:09:00 2007 +0900
Committer:  Jeff Garzik <[EMAIL PROTECTED]>
CommitDate: Thu Nov 8 13:08:42 2007 -0500

    libata: port and host should be stopped before hardware resources are 
released
    
    Port / host stop calls used to be made from ata_host_release() which
    is called after all hardware resources acquired after host allocation
    are released.  This is wrong as port and host stop routines often
    access the hardware.
    
    Add separate devres for port / host stop which is invoked right after
    IRQ is released but with all other hardware resources intact.  The
    devres is added iff ->host_stop and/or ->port_stop exist.
    
    This problem has been spotted by Mark Lord.
    
    Signed-off-by: Tejun Heo <[EMAIL PROTECTED]>
    Cc: Mark Lord <[EMAIL PROTECTED]>
    Signed-off-by: Jeff Garzik <[EMAIL PROTECTED]>
---
 drivers/ata/libata-core.c |   52 +++++++++++++++++++++++++++++++++-----------
 1 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index fd33261..8189803 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6827,19 +6827,6 @@ static void ata_host_release(struct device *gendev, void 
*res)
                if (!ap)
                        continue;
 
-               if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop)
-                       ap->ops->port_stop(ap);
-       }
-
-       if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop)
-               host->ops->host_stop(host);
-
-       for (i = 0; i < host->n_ports; i++) {
-               struct ata_port *ap = host->ports[i];
-
-               if (!ap)
-                       continue;
-
                if (ap->scsi_host)
                        scsi_host_put(ap->scsi_host);
 
@@ -6966,6 +6953,24 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev,
        return host;
 }
 
+static void ata_host_stop(struct device *gendev, void *res)
+{
+       struct ata_host *host = dev_get_drvdata(gendev);
+       int i;
+
+       WARN_ON(!(host->flags & ATA_HOST_STARTED));
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+
+               if (ap->ops->port_stop)
+                       ap->ops->port_stop(ap);
+       }
+
+       if (host->ops->host_stop)
+               host->ops->host_stop(host);
+}
+
 /**
  *     ata_host_start - start and freeze ports of an ATA host
  *     @host: ATA host to start ports for
@@ -6984,6 +6989,8 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev,
  */
 int ata_host_start(struct ata_host *host)
 {
+       int have_stop = 0;
+       void *start_dr = NULL;
        int i, rc;
 
        if (host->flags & ATA_HOST_STARTED)
@@ -6995,6 +7002,22 @@ int ata_host_start(struct ata_host *host)
                if (!host->ops && !ata_port_is_dummy(ap))
                        host->ops = ap->ops;
 
+               if (ap->ops->port_stop)
+                       have_stop = 1;
+       }
+
+       if (host->ops->host_stop)
+               have_stop = 1;
+
+       if (have_stop) {
+               start_dr = devres_alloc(ata_host_stop, 0, GFP_KERNEL);
+               if (!start_dr)
+                       return -ENOMEM;
+       }
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+
                if (ap->ops->port_start) {
                        rc = ap->ops->port_start(ap);
                        if (rc) {
@@ -7007,6 +7030,8 @@ int ata_host_start(struct ata_host *host)
                ata_eh_freeze_port(ap);
        }
 
+       if (start_dr)
+               devres_add(host->dev, start_dr);
        host->flags |= ATA_HOST_STARTED;
        return 0;
 
@@ -7017,6 +7042,7 @@ int ata_host_start(struct ata_host *host)
                if (ap->ops->port_stop)
                        ap->ops->port_stop(ap);
        }
+       devres_free(start_dr);
        return rc;
 }
 
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to