diff options
| author | Dan Williams <dan.j.williams@intel.com> | 2012-01-18 23:47:01 -0500 |
|---|---|---|
| committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-29 16:35:41 -0500 |
| commit | 9508a66f898d46e726a318469312b45e0b1d078b (patch) | |
| tree | e6b61e6c2a7dc8b40fdc0fe34901ff3db4af47a9 /drivers/scsi/libsas | |
| parent | 92625f9bff3853951cc75f5bc084ee67c1317d2f (diff) | |
[SCSI] libsas: async ata scanning
libsas ata error handling is already async but this does not help the
scan case. Move initial link recovery out from under host->scan_mutex,
and delay synchronization with eh until after all port probe/recovery
work has been queued.
Device ordering is maintained with scan order by still calling
sas_rphy_add() in order of domain discovery.
Since we now scan the domain list when invoking libata-eh we need to be
careful to check for fully initialized ata ports.
Acked-by: Jack Wang <jack_wang@usish.com>
Acked-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/libsas')
| -rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 74 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 22 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 9 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 18 |
4 files changed, 83 insertions, 40 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index a9ec1643ee9..eb8b77c8616 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c | |||
| @@ -585,11 +585,10 @@ static struct ata_port_info sata_port_info = { | |||
| 585 | .port_ops = &sas_sata_ops | 585 | .port_ops = &sas_sata_ops |
| 586 | }; | 586 | }; |
| 587 | 587 | ||
| 588 | int sas_ata_init_host_and_port(struct domain_device *found_dev, | 588 | int sas_ata_init_host_and_port(struct domain_device *found_dev) |
| 589 | struct scsi_target *starget) | ||
| 590 | { | 589 | { |
| 591 | struct Scsi_Host *shost = dev_to_shost(&starget->dev); | 590 | struct sas_ha_struct *ha = found_dev->port->ha; |
| 592 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | 591 | struct Scsi_Host *shost = ha->core.shost; |
| 593 | struct ata_port *ap; | 592 | struct ata_port *ap; |
| 594 | 593 | ||
| 595 | ata_host_init(&found_dev->sata_dev.ata_host, | 594 | ata_host_init(&found_dev->sata_dev.ata_host, |
| @@ -607,6 +606,8 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, | |||
| 607 | ap->private_data = found_dev; | 606 | ap->private_data = found_dev; |
| 608 | ap->cbl = ATA_CBL_SATA; | 607 | ap->cbl = ATA_CBL_SATA; |
| 609 | ap->scsi_host = shost; | 608 | ap->scsi_host = shost; |
| 609 | /* publish initialized ata port */ | ||
| 610 | smp_wmb(); | ||
| 610 | found_dev->sata_dev.ap = ap; | 611 | found_dev->sata_dev.ap = ap; |
| 611 | 612 | ||
| 612 | return 0; | 613 | return 0; |
| @@ -683,6 +684,38 @@ static void sas_get_ata_command_set(struct domain_device *dev) | |||
| 683 | dev->sata_dev.command_set = ATAPI_COMMAND_SET; | 684 | dev->sata_dev.command_set = ATAPI_COMMAND_SET; |
| 684 | } | 685 | } |
| 685 | 686 | ||
| 687 | void sas_probe_sata(struct asd_sas_port *port) | ||
| 688 | { | ||
| 689 | struct domain_device *dev, *n; | ||
| 690 | int err; | ||
| 691 | |||
| 692 | mutex_lock(&port->ha->disco_mutex); | ||
| 693 | list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { | ||
| 694 | if (!dev_is_sata(dev)) | ||
| 695 | continue; | ||
| 696 | |||
| 697 | err = sas_ata_init_host_and_port(dev); | ||
| 698 | if (err) | ||
| 699 | sas_fail_probe(dev, __func__, err); | ||
| 700 | else | ||
| 701 | ata_sas_async_port_init(dev->sata_dev.ap); | ||
| 702 | } | ||
| 703 | mutex_unlock(&port->ha->disco_mutex); | ||
| 704 | |||
| 705 | list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { | ||
| 706 | if (!dev_is_sata(dev)) | ||
| 707 | continue; | ||
| 708 | |||
| 709 | sas_ata_wait_eh(dev); | ||
| 710 | |||
| 711 | /* if libata could not bring the link up, don't surface | ||
| 712 | * the device | ||
| 713 | */ | ||
| 714 | if (ata_dev_disabled(sas_to_ata_dev(dev))) | ||
| 715 | sas_fail_probe(dev, __func__, -ENODEV); | ||
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 686 | /** | 719 | /** |
| 687 | * sas_discover_sata -- discover an STP/SATA domain device | 720 | * sas_discover_sata -- discover an STP/SATA domain device |
| 688 | * @dev: pointer to struct domain_device of interest | 721 | * @dev: pointer to struct domain_device of interest |
| @@ -724,11 +757,23 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) | |||
| 724 | sas_put_device(dev); | 757 | sas_put_device(dev); |
| 725 | } | 758 | } |
| 726 | 759 | ||
| 760 | static bool sas_ata_dev_eh_valid(struct domain_device *dev) | ||
| 761 | { | ||
| 762 | struct ata_port *ap; | ||
| 763 | |||
| 764 | if (!dev_is_sata(dev)) | ||
| 765 | return false; | ||
| 766 | ap = dev->sata_dev.ap; | ||
| 767 | /* consume fully initialized ata ports */ | ||
| 768 | smp_rmb(); | ||
| 769 | return !!ap; | ||
| 770 | } | ||
| 771 | |||
| 727 | void sas_ata_strategy_handler(struct Scsi_Host *shost) | 772 | void sas_ata_strategy_handler(struct Scsi_Host *shost) |
| 728 | { | 773 | { |
| 729 | struct scsi_device *sdev; | ||
| 730 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); | 774 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); |
| 731 | LIST_HEAD(async); | 775 | LIST_HEAD(async); |
| 776 | int i; | ||
| 732 | 777 | ||
| 733 | /* it's ok to defer revalidation events during ata eh, these | 778 | /* it's ok to defer revalidation events during ata eh, these |
| 734 | * disks are in one of three states: | 779 | * disks are in one of three states: |
| @@ -740,14 +785,21 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) | |||
| 740 | */ | 785 | */ |
| 741 | sas_disable_revalidation(sas_ha); | 786 | sas_disable_revalidation(sas_ha); |
| 742 | 787 | ||
| 743 | shost_for_each_device(sdev, shost) { | 788 | spin_lock_irq(&sas_ha->phy_port_lock); |
| 744 | struct domain_device *ddev = sdev_to_domain_dev(sdev); | 789 | for (i = 0; i < sas_ha->num_phys; i++) { |
| 745 | 790 | struct asd_sas_port *port = sas_ha->sas_port[i]; | |
| 746 | if (!dev_is_sata(ddev)) | 791 | struct domain_device *dev; |
| 747 | continue; | ||
| 748 | 792 | ||
| 749 | async_schedule_domain(async_sas_ata_eh, ddev, &async); | 793 | spin_lock(&port->dev_list_lock); |
| 794 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { | ||
| 795 | if (!sas_ata_dev_eh_valid(dev)) | ||
| 796 | continue; | ||
| 797 | async_schedule_domain(async_sas_ata_eh, dev, &async); | ||
| 798 | } | ||
| 799 | spin_unlock(&port->dev_list_lock); | ||
| 750 | } | 800 | } |
| 801 | spin_unlock_irq(&sas_ha->phy_port_lock); | ||
| 802 | |||
| 751 | async_synchronize_full_domain(&async); | 803 | async_synchronize_full_domain(&async); |
| 752 | 804 | ||
| 753 | sas_enable_revalidation(sas_ha); | 805 | sas_enable_revalidation(sas_ha); |
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 18fa364aa00..0d58a8beaa3 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c | |||
| @@ -207,22 +207,22 @@ static void sas_probe_devices(struct work_struct *work) | |||
| 207 | 207 | ||
| 208 | clear_bit(DISCE_PROBE, &port->disc.pending); | 208 | clear_bit(DISCE_PROBE, &port->disc.pending); |
| 209 | 209 | ||
| 210 | list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { | 210 | /* devices must be domain members before link recovery and probe */ |
| 211 | int err; | 211 | list_for_each_entry(dev, &port->disco_list, disco_list_node) { |
| 212 | |||
| 213 | spin_lock_irq(&port->dev_list_lock); | 212 | spin_lock_irq(&port->dev_list_lock); |
| 214 | list_add_tail(&dev->dev_list_node, &port->dev_list); | 213 | list_add_tail(&dev->dev_list_node, &port->dev_list); |
| 215 | spin_unlock_irq(&port->dev_list_lock); | 214 | spin_unlock_irq(&port->dev_list_lock); |
| 215 | } | ||
| 216 | 216 | ||
| 217 | err = sas_rphy_add(dev->rphy); | 217 | sas_probe_sata(port); |
| 218 | 218 | ||
| 219 | if (err) { | 219 | list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { |
| 220 | SAS_DPRINTK("%s: for %s device %16llx returned %d\n", | 220 | int err; |
| 221 | __func__, dev->parent ? "exp-attached" : | 221 | |
| 222 | "direct-attached", | 222 | err = sas_rphy_add(dev->rphy); |
| 223 | SAS_ADDR(dev->sas_addr), err); | 223 | if (err) |
| 224 | sas_unregister_dev(port, dev); | 224 | sas_fail_probe(dev, __func__, err); |
| 225 | } else | 225 | else |
| 226 | list_del_init(&dev->disco_list_node); | 226 | list_del_init(&dev->disco_list_node); |
| 227 | } | 227 | } |
| 228 | } | 228 | } |
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index e028d7a4420..d0d9bf10f79 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h | |||
| @@ -113,6 +113,15 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, | |||
| 113 | } | 113 | } |
| 114 | #endif | 114 | #endif |
| 115 | 115 | ||
| 116 | static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err) | ||
| 117 | { | ||
| 118 | SAS_DPRINTK("%s: for %s device %16llx returned %d\n", | ||
| 119 | func, dev->parent ? "exp-attached" : | ||
| 120 | "direct-attached", | ||
| 121 | SAS_ADDR(dev->sas_addr), err); | ||
| 122 | sas_unregister_dev(dev->port, dev); | ||
| 123 | } | ||
| 124 | |||
| 116 | static inline void sas_fill_in_rphy(struct domain_device *dev, | 125 | static inline void sas_fill_in_rphy(struct domain_device *dev, |
| 117 | struct sas_rphy *rphy) | 126 | struct sas_rphy *rphy) |
| 118 | { | 127 | { |
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index e58ca50517d..3701ff7e726 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c | |||
| @@ -762,17 +762,10 @@ int sas_target_alloc(struct scsi_target *starget) | |||
| 762 | { | 762 | { |
| 763 | struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); | 763 | struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); |
| 764 | struct domain_device *found_dev = sas_find_dev_by_rphy(rphy); | 764 | struct domain_device *found_dev = sas_find_dev_by_rphy(rphy); |
| 765 | int res; | ||
| 766 | 765 | ||
| 767 | if (!found_dev) | 766 | if (!found_dev) |
| 768 | return -ENODEV; | 767 | return -ENODEV; |
| 769 | 768 | ||
| 770 | if (dev_is_sata(found_dev)) { | ||
| 771 | res = sas_ata_init_host_and_port(found_dev, starget); | ||
| 772 | if (res) | ||
| 773 | return res; | ||
| 774 | } | ||
| 775 | |||
| 776 | kref_get(&found_dev->kref); | 769 | kref_get(&found_dev->kref); |
| 777 | starget->hostdata = found_dev; | 770 | starget->hostdata = found_dev; |
| 778 | return 0; | 771 | return 0; |
| @@ -1012,16 +1005,6 @@ void sas_task_abort(struct sas_task *task) | |||
| 1012 | } | 1005 | } |
| 1013 | } | 1006 | } |
| 1014 | 1007 | ||
| 1015 | int sas_slave_alloc(struct scsi_device *scsi_dev) | ||
| 1016 | { | ||
| 1017 | struct domain_device *dev = sdev_to_domain_dev(scsi_dev); | ||
| 1018 | |||
| 1019 | if (dev_is_sata(dev)) | ||
| 1020 | return ata_sas_port_init(dev->sata_dev.ap); | ||
| 1021 | |||
| 1022 | return 0; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | void sas_target_destroy(struct scsi_target *starget) | 1008 | void sas_target_destroy(struct scsi_target *starget) |
| 1026 | { | 1009 | { |
| 1027 | struct domain_device *found_dev = starget->hostdata; | 1010 | struct domain_device *found_dev = starget->hostdata; |
| @@ -1082,6 +1065,5 @@ EXPORT_SYMBOL_GPL(sas_task_abort); | |||
| 1082 | EXPORT_SYMBOL_GPL(sas_phy_reset); | 1065 | EXPORT_SYMBOL_GPL(sas_phy_reset); |
| 1083 | EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); | 1066 | EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); |
| 1084 | EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); | 1067 | EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); |
| 1085 | EXPORT_SYMBOL_GPL(sas_slave_alloc); | ||
| 1086 | EXPORT_SYMBOL_GPL(sas_target_destroy); | 1068 | EXPORT_SYMBOL_GPL(sas_target_destroy); |
| 1087 | EXPORT_SYMBOL_GPL(sas_ioctl); | 1069 | EXPORT_SYMBOL_GPL(sas_ioctl); |
