aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-11-07 23:09:00 -0500
committerJeff Garzik <jeff@garzik.org>2007-11-08 13:08:42 -0500
commit32ebbc0c0d5d18c0135b55d1eb0029f48c54aff0 (patch)
tree7da2103a3fd6998da95c3b38b5db69c24122df1d /drivers
parent1974e20161a2c097c481d2ff711de7db56cb2cd6 (diff)
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 <htejun@gmail.com> Cc: Mark Lord <liml@rtr.ca> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/libata-core.c52
1 files changed, 39 insertions, 13 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index fd332612d64a..81898036dbca 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)
6827 if (!ap) 6827 if (!ap)
6828 continue; 6828 continue;
6829 6829
6830 if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop)
6831 ap->ops->port_stop(ap);
6832 }
6833
6834 if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop)
6835 host->ops->host_stop(host);
6836
6837 for (i = 0; i < host->n_ports; i++) {
6838 struct ata_port *ap = host->ports[i];
6839
6840 if (!ap)
6841 continue;
6842
6843 if (ap->scsi_host) 6830 if (ap->scsi_host)
6844 scsi_host_put(ap->scsi_host); 6831 scsi_host_put(ap->scsi_host);
6845 6832
@@ -6966,6 +6953,24 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev,
6966 return host; 6953 return host;
6967} 6954}
6968 6955
6956static void ata_host_stop(struct device *gendev, void *res)
6957{
6958 struct ata_host *host = dev_get_drvdata(gendev);
6959 int i;
6960
6961 WARN_ON(!(host->flags & ATA_HOST_STARTED));
6962
6963 for (i = 0; i < host->n_ports; i++) {
6964 struct ata_port *ap = host->ports[i];
6965
6966 if (ap->ops->port_stop)
6967 ap->ops->port_stop(ap);
6968 }
6969
6970 if (host->ops->host_stop)
6971 host->ops->host_stop(host);
6972}
6973
6969/** 6974/**
6970 * ata_host_start - start and freeze ports of an ATA host 6975 * ata_host_start - start and freeze ports of an ATA host
6971 * @host: ATA host to start ports for 6976 * @host: ATA host to start ports for
@@ -6984,6 +6989,8 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev,
6984 */ 6989 */
6985int ata_host_start(struct ata_host *host) 6990int ata_host_start(struct ata_host *host)
6986{ 6991{
6992 int have_stop = 0;
6993 void *start_dr = NULL;
6987 int i, rc; 6994 int i, rc;
6988 6995
6989 if (host->flags & ATA_HOST_STARTED) 6996 if (host->flags & ATA_HOST_STARTED)
@@ -6995,6 +7002,22 @@ int ata_host_start(struct ata_host *host)
6995 if (!host->ops && !ata_port_is_dummy(ap)) 7002 if (!host->ops && !ata_port_is_dummy(ap))
6996 host->ops = ap->ops; 7003 host->ops = ap->ops;
6997 7004
7005 if (ap->ops->port_stop)
7006 have_stop = 1;
7007 }
7008
7009 if (host->ops->host_stop)
7010 have_stop = 1;
7011
7012 if (have_stop) {
7013 start_dr = devres_alloc(ata_host_stop, 0, GFP_KERNEL);
7014 if (!start_dr)
7015 return -ENOMEM;
7016 }
7017
7018 for (i = 0; i < host->n_ports; i++) {
7019 struct ata_port *ap = host->ports[i];
7020
6998 if (ap->ops->port_start) { 7021 if (ap->ops->port_start) {
6999 rc = ap->ops->port_start(ap); 7022 rc = ap->ops->port_start(ap);
7000 if (rc) { 7023 if (rc) {
@@ -7007,6 +7030,8 @@ int ata_host_start(struct ata_host *host)
7007 ata_eh_freeze_port(ap); 7030 ata_eh_freeze_port(ap);
7008 } 7031 }
7009 7032
7033 if (start_dr)
7034 devres_add(host->dev, start_dr);
7010 host->flags |= ATA_HOST_STARTED; 7035 host->flags |= ATA_HOST_STARTED;
7011 return 0; 7036 return 0;
7012 7037
@@ -7017,6 +7042,7 @@ int ata_host_start(struct ata_host *host)
7017 if (ap->ops->port_stop) 7042 if (ap->ops->port_stop)
7018 ap->ops->port_stop(ap); 7043 ap->ops->port_stop(ap);
7019 } 7044 }
7045 devres_free(start_dr);
7020 return rc; 7046 return rc;
7021} 7047}
7022 7048