diff options
author | Dan Williams <dan.j.williams@intel.com> | 2012-03-22 05:09:07 +0100 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-04-23 13:11:47 +0200 |
commit | b2024459252a9d2d312ee562f86f332a1498f412 (patch) | |
tree | 751987779eee44a601a68c766ba2ff258b77d25a | |
parent | [SCSI] libsas: fix ata_eh clobbering ex_phys via smp_ata_check_ready (diff) | |
download | linux-b2024459252a9d2d312ee562f86f332a1498f412.tar.xz linux-b2024459252a9d2d312ee562f86f332a1498f412.zip |
[SCSI] libsas, libata: fix start of life for a sas ata_port
This changes the ordering of initialization and probing events from:
1/ allocate rphy in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN
2/ allocate ata_port and schedule port probe in DISCE_PROBE
...to:
1/ allocate ata_port in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN
2/ allocate rphy in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN
3/ schedule port probe in DISCE_PROBE
This ordering prevents PHYE_SIGNAL_LOSS_EVENTS from sneaking in to
destrory ata devices before they have been fully initialized:
BUG: unable to handle kernel paging request at 0000000000003b10
IP: [<ffffffffa0053d7e>] sas_ata_end_eh+0x12/0x5e [libsas]
...
[<ffffffffa004d1af>] sas_unregister_common_dev+0x78/0xc9 [libsas]
[<ffffffffa004d4d4>] sas_unregister_dev+0x4f/0xad [libsas]
[<ffffffffa004d5b1>] sas_unregister_domain_devices+0x7f/0xbf [libsas]
[<ffffffffa004c487>] sas_deform_port+0x61/0x1b8 [libsas]
[<ffffffffa004bed0>] sas_phye_loss_of_signal+0x29/0x2b [libsas]
...and kills the awkward "sata domain_device briefly existing in the
domain without an ata_port" state.
Reported-by: Michal Kosciowski <michal.kosciowski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r-- | drivers/ata/libata-scsi.c | 35 | ||||
-rw-r--r-- | drivers/scsi/ipr.c | 6 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 33 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 13 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 8 | ||||
-rw-r--r-- | include/linux/libata.h | 3 | ||||
-rw-r--r-- | include/scsi/sas_ata.h | 4 |
7 files changed, 54 insertions, 48 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7832b1ad2327..22226350cd0c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3839,18 +3839,25 @@ void ata_sas_port_stop(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_port_stop); -int ata_sas_async_port_init(struct ata_port *ap) +/** + * ata_sas_async_probe - simply schedule probing and return + * @ap: Port to probe + * + * For batch scheduling of probe for sas attached ata devices, assumes + * the port has already been through ata_sas_port_init() + */ +void ata_sas_async_probe(struct ata_port *ap) { - int rc = ap->ops->port_start(ap); - - if (!rc) { - ap->print_id = atomic_inc_return(&ata_print_id); - __ata_port_probe(ap); - } + __ata_port_probe(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_async_probe); - return rc; +int ata_sas_sync_probe(struct ata_port *ap) +{ + return ata_port_probe(ap); } -EXPORT_SYMBOL_GPL(ata_sas_async_port_init); +EXPORT_SYMBOL_GPL(ata_sas_sync_probe); + /** * ata_sas_port_init - Initialize a SATA device @@ -3867,12 +3874,10 @@ int ata_sas_port_init(struct ata_port *ap) { int rc = ap->ops->port_start(ap); - if (!rc) { - ap->print_id = atomic_inc_return(&ata_print_id); - rc = ata_port_probe(ap); - } - - return rc; + if (rc) + return rc; + ap->print_id = atomic_inc_return(&ata_print_id); + return 0; } EXPORT_SYMBOL_GPL(ata_sas_port_init); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e002cd466e9a..467dc38246f9 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4549,8 +4549,12 @@ static int ipr_ata_slave_alloc(struct scsi_device *sdev) ENTER; if (sdev->sdev_target) sata_port = sdev->sdev_target->hostdata; - if (sata_port) + if (sata_port) { rc = ata_sas_port_init(sata_port->ap); + if (rc == 0) + rc = ata_sas_sync_probe(sata_port->ap); + } + if (rc) ipr_slave_destroy(sdev); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index bc0cecc6ad62..441d88ad99a7 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -546,11 +546,12 @@ static struct ata_port_info sata_port_info = { .port_ops = &sas_sata_ops }; -int sas_ata_init_host_and_port(struct domain_device *found_dev) +int sas_ata_init(struct domain_device *found_dev) { struct sas_ha_struct *ha = found_dev->port->ha; struct Scsi_Host *shost = ha->core.shost; struct ata_port *ap; + int rc; ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, @@ -567,8 +568,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev) ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; - /* publish initialized ata port */ - smp_wmb(); + rc = ata_sas_port_init(ap); + if (rc) { + ata_sas_port_destroy(ap); + return rc; + } found_dev->sata_dev.ap = ap; return 0; @@ -648,18 +652,13 @@ static void sas_get_ata_command_set(struct domain_device *dev) void sas_probe_sata(struct asd_sas_port *port) { struct domain_device *dev, *n; - int err; mutex_lock(&port->ha->disco_mutex); - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + list_for_each_entry(dev, &port->disco_list, disco_list_node) { if (!dev_is_sata(dev)) continue; - err = sas_ata_init_host_and_port(dev); - if (err) - sas_fail_probe(dev, __func__, err); - else - ata_sas_async_port_init(dev->sata_dev.ap); + ata_sas_async_probe(dev->sata_dev.ap); } mutex_unlock(&port->ha->disco_mutex); @@ -718,18 +717,6 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) sas_put_device(dev); } -static bool sas_ata_dev_eh_valid(struct domain_device *dev) -{ - struct ata_port *ap; - - if (!dev_is_sata(dev)) - return false; - ap = dev->sata_dev.ap; - /* consume fully initialized ata ports */ - smp_rmb(); - return !!ap; -} - void sas_ata_strategy_handler(struct Scsi_Host *shost) { struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); @@ -753,7 +740,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) spin_lock(&port->dev_list_lock); list_for_each_entry(dev, &port->dev_list, dev_list_node) { - if (!sas_ata_dev_eh_valid(dev)) + if (!dev_is_sata(dev)) continue; async_schedule_domain(async_sas_ata_eh, dev, &async); } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 13b5891f9961..629a0865b130 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -72,6 +72,7 @@ static int sas_get_port_device(struct asd_sas_port *port) struct asd_sas_phy *phy; struct sas_rphy *rphy; struct domain_device *dev; + int rc = -ENODEV; dev = sas_alloc_device(); if (!dev) @@ -110,9 +111,16 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_init_dev(dev); + dev->port = port; switch (dev->dev_type) { - case SAS_END_DEV: case SATA_DEV: + rc = sas_ata_init(dev); + if (rc) { + rphy = NULL; + break; + } + /* fall through */ + case SAS_END_DEV: rphy = sas_end_device_alloc(port->port); break; case EDGE_DEV: @@ -131,7 +139,7 @@ static int sas_get_port_device(struct asd_sas_port *port) if (!rphy) { sas_put_device(dev); - return -ENODEV; + return rc; } rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; @@ -139,7 +147,6 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_fill_in_rphy(dev, rphy); sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); port->port_dev = dev; - dev->port = port; dev->linkrate = port->linkrate; dev->min_linkrate = port->linkrate; dev->max_linkrate = port->linkrate; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index c1f91b1c27c3..75247a176c6b 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -790,12 +790,14 @@ static struct domain_device *sas_ex_discover_end_dev( if (res) goto out_free; + sas_init_dev(child); + res = sas_ata_init(child); + if (res) + goto out_free; rphy = sas_end_device_alloc(phy->port); - if (unlikely(!rphy)) + if (!rphy) goto out_free; - sas_init_dev(child); - child->rphy = rphy; get_device(&rphy->dev); diff --git a/include/linux/libata.h b/include/linux/libata.h index 42378d637ffb..e926df7b54c9 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -996,7 +996,8 @@ extern int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *dev, extern void ata_sas_port_destroy(struct ata_port *); extern struct ata_port *ata_sas_port_alloc(struct ata_host *, struct ata_port_info *, struct Scsi_Host *); -extern int ata_sas_async_port_init(struct ata_port *); +extern void ata_sas_async_probe(struct ata_port *ap); +extern int ata_sas_sync_probe(struct ata_port *ap); extern int ata_sas_port_init(struct ata_port *); extern int ata_sas_port_start(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index cdccd2eb7b6c..77670e823ed8 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -37,7 +37,7 @@ static inline int dev_is_sata(struct domain_device *dev) } int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy); -int sas_ata_init_host_and_port(struct domain_device *found_dev); +int sas_ata_init(struct domain_device *dev); void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, @@ -52,7 +52,7 @@ static inline int dev_is_sata(struct domain_device *dev) { return 0; } -static inline int sas_ata_init_host_and_port(struct domain_device *found_dev) +static inline int sas_ata_init(struct domain_device *dev) { return 0; } |