diff options
| author | Dan Williams <dan.j.williams@intel.com> | 2012-06-22 02:30:48 -0400 |
|---|---|---|
| committer | James Bottomley <JBottomley@Parallels.com> | 2012-07-20 03:58:50 -0400 |
| commit | 5db45bdc87ce4f503947adf7896586d60c63322c (patch) | |
| tree | f939bbcf1e6fc2664c54b46949eb8e5af8aa070b /drivers/scsi/libsas | |
| parent | b9d5c6b7ef570bea0d22746944d7b58fa7f17b13 (diff) | |
[SCSI] libsas: enforce eh strategy handlers only in eh context
The strategy handlers may be called in places that are problematic for
libsas (i.e. sata resets outside of domain revalidation filtering /
libata link recovery), or problematic for userspace (non-blocking ioctl
to sleeping reset functions). However, these routines are also called
for eh escalations and recovery of scsi_eh_prep_cmnd(), so permit them
as long as we are running in the host's error handler, otherwise arrange
for them to be triggered in eh_context.
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_discover.c | 11 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_init.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 121 |
3 files changed, 130 insertions, 4 deletions
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index ff497ac76cb4..b031d238eb7b 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c | |||
| @@ -39,6 +39,7 @@ void sas_init_dev(struct domain_device *dev) | |||
| 39 | { | 39 | { |
| 40 | switch (dev->dev_type) { | 40 | switch (dev->dev_type) { |
| 41 | case SAS_END_DEV: | 41 | case SAS_END_DEV: |
| 42 | INIT_LIST_HEAD(&dev->ssp_dev.eh_list_node); | ||
| 42 | break; | 43 | break; |
| 43 | case EDGE_DEV: | 44 | case EDGE_DEV: |
| 44 | case FANOUT_DEV: | 45 | case FANOUT_DEV: |
| @@ -286,6 +287,8 @@ void sas_free_device(struct kref *kref) | |||
| 286 | 287 | ||
| 287 | static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) | 288 | static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) |
| 288 | { | 289 | { |
| 290 | struct sas_ha_struct *ha = port->ha; | ||
| 291 | |||
| 289 | sas_notify_lldd_dev_gone(dev); | 292 | sas_notify_lldd_dev_gone(dev); |
| 290 | if (!dev->parent) | 293 | if (!dev->parent) |
| 291 | dev->port->port_dev = NULL; | 294 | dev->port->port_dev = NULL; |
| @@ -298,6 +301,14 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d | |||
| 298 | sas_ata_end_eh(dev->sata_dev.ap); | 301 | sas_ata_end_eh(dev->sata_dev.ap); |
| 299 | spin_unlock_irq(&port->dev_list_lock); | 302 | spin_unlock_irq(&port->dev_list_lock); |
| 300 | 303 | ||
| 304 | spin_lock_irq(&ha->lock); | ||
| 305 | if (dev->dev_type == SAS_END_DEV && | ||
| 306 | !list_empty(&dev->ssp_dev.eh_list_node)) { | ||
| 307 | list_del_init(&dev->ssp_dev.eh_list_node); | ||
| 308 | ha->eh_active--; | ||
| 309 | } | ||
| 310 | spin_unlock_irq(&ha->lock); | ||
| 311 | |||
| 301 | sas_put_device(dev); | 312 | sas_put_device(dev); |
| 302 | } | 313 | } |
| 303 | 314 | ||
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 6909fefa32c5..1bbab3d94a20 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c | |||
| @@ -116,7 +116,9 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) | |||
| 116 | set_bit(SAS_HA_REGISTERED, &sas_ha->state); | 116 | set_bit(SAS_HA_REGISTERED, &sas_ha->state); |
| 117 | spin_lock_init(&sas_ha->lock); | 117 | spin_lock_init(&sas_ha->lock); |
| 118 | mutex_init(&sas_ha->drain_mutex); | 118 | mutex_init(&sas_ha->drain_mutex); |
| 119 | init_waitqueue_head(&sas_ha->eh_wait_q); | ||
| 119 | INIT_LIST_HEAD(&sas_ha->defer_q); | 120 | INIT_LIST_HEAD(&sas_ha->defer_q); |
| 121 | INIT_LIST_HEAD(&sas_ha->eh_dev_q); | ||
| 120 | 122 | ||
| 121 | error = sas_register_phys(sas_ha); | 123 | error = sas_register_phys(sas_ha); |
| 122 | if (error) { | 124 | if (error) { |
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 52d5b0133db0..2e0e779fb3b2 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c | |||
| @@ -460,14 +460,88 @@ struct sas_phy *sas_get_local_phy(struct domain_device *dev) | |||
| 460 | } | 460 | } |
| 461 | EXPORT_SYMBOL_GPL(sas_get_local_phy); | 461 | EXPORT_SYMBOL_GPL(sas_get_local_phy); |
| 462 | 462 | ||
| 463 | static void sas_wait_eh(struct domain_device *dev) | ||
| 464 | { | ||
| 465 | struct sas_ha_struct *ha = dev->port->ha; | ||
| 466 | DEFINE_WAIT(wait); | ||
| 467 | |||
| 468 | if (dev_is_sata(dev)) { | ||
| 469 | ata_port_wait_eh(dev->sata_dev.ap); | ||
| 470 | return; | ||
| 471 | } | ||
| 472 | retry: | ||
| 473 | spin_lock_irq(&ha->lock); | ||
| 474 | |||
| 475 | while (test_bit(SAS_DEV_EH_PENDING, &dev->state)) { | ||
| 476 | prepare_to_wait(&ha->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); | ||
| 477 | spin_unlock_irq(&ha->lock); | ||
| 478 | schedule(); | ||
| 479 | spin_lock_irq(&ha->lock); | ||
| 480 | } | ||
| 481 | finish_wait(&ha->eh_wait_q, &wait); | ||
| 482 | |||
| 483 | spin_unlock_irq(&ha->lock); | ||
| 484 | |||
| 485 | /* make sure SCSI EH is complete */ | ||
| 486 | if (scsi_host_in_recovery(ha->core.shost)) { | ||
| 487 | msleep(10); | ||
| 488 | goto retry; | ||
| 489 | } | ||
| 490 | } | ||
| 491 | EXPORT_SYMBOL(sas_wait_eh); | ||
| 492 | |||
| 493 | static int sas_queue_reset(struct domain_device *dev, int reset_type, int lun, int wait) | ||
| 494 | { | ||
| 495 | struct sas_ha_struct *ha = dev->port->ha; | ||
| 496 | int scheduled = 0, tries = 100; | ||
| 497 | |||
| 498 | /* ata: promote lun reset to bus reset */ | ||
| 499 | if (dev_is_sata(dev)) { | ||
| 500 | sas_ata_schedule_reset(dev); | ||
| 501 | if (wait) | ||
| 502 | sas_ata_wait_eh(dev); | ||
| 503 | return SUCCESS; | ||
| 504 | } | ||
| 505 | |||
| 506 | while (!scheduled && tries--) { | ||
| 507 | spin_lock_irq(&ha->lock); | ||
| 508 | if (!test_bit(SAS_DEV_EH_PENDING, &dev->state) && | ||
| 509 | !test_bit(reset_type, &dev->state)) { | ||
| 510 | scheduled = 1; | ||
| 511 | ha->eh_active++; | ||
| 512 | list_add_tail(&dev->ssp_dev.eh_list_node, &ha->eh_dev_q); | ||
| 513 | set_bit(SAS_DEV_EH_PENDING, &dev->state); | ||
| 514 | set_bit(reset_type, &dev->state); | ||
| 515 | int_to_scsilun(lun, &dev->ssp_dev.reset_lun); | ||
| 516 | scsi_schedule_eh(ha->core.shost); | ||
| 517 | } | ||
| 518 | spin_unlock_irq(&ha->lock); | ||
| 519 | |||
| 520 | if (wait) | ||
| 521 | sas_wait_eh(dev); | ||
| 522 | |||
| 523 | if (scheduled) | ||
| 524 | return SUCCESS; | ||
| 525 | } | ||
| 526 | |||
| 527 | SAS_DPRINTK("%s reset of %s failed\n", | ||
| 528 | reset_type == SAS_DEV_LU_RESET ? "LUN" : "Bus", | ||
| 529 | dev_name(&dev->rphy->dev)); | ||
| 530 | |||
| 531 | return FAILED; | ||
| 532 | } | ||
| 533 | |||
| 463 | /* Attempt to send a LUN reset message to a device */ | 534 | /* Attempt to send a LUN reset message to a device */ |
| 464 | int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) | 535 | int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) |
| 465 | { | 536 | { |
| 466 | struct domain_device *dev = cmd_to_domain_dev(cmd); | ||
| 467 | struct sas_internal *i = | ||
| 468 | to_sas_internal(dev->port->ha->core.shost->transportt); | ||
| 469 | struct scsi_lun lun; | ||
| 470 | int res; | 537 | int res; |
| 538 | struct scsi_lun lun; | ||
| 539 | struct Scsi_Host *host = cmd->device->host; | ||
| 540 | struct domain_device *dev = cmd_to_domain_dev(cmd); | ||
| 541 | struct sas_internal *i = to_sas_internal(host->transportt); | ||
| 542 | |||
| 543 | if (current != host->ehandler) | ||
| 544 | return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun, 0); | ||
| 471 | 545 | ||
| 472 | int_to_scsilun(cmd->device->lun, &lun); | 546 | int_to_scsilun(cmd->device->lun, &lun); |
| 473 | 547 | ||
| @@ -486,8 +560,12 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) | |||
| 486 | { | 560 | { |
| 487 | struct domain_device *dev = cmd_to_domain_dev(cmd); | 561 | struct domain_device *dev = cmd_to_domain_dev(cmd); |
| 488 | struct sas_phy *phy = sas_get_local_phy(dev); | 562 | struct sas_phy *phy = sas_get_local_phy(dev); |
| 563 | struct Scsi_Host *host = cmd->device->host; | ||
| 489 | int res; | 564 | int res; |
| 490 | 565 | ||
| 566 | if (current != host->ehandler) | ||
| 567 | return sas_queue_reset(dev, SAS_DEV_RESET, 0, 0); | ||
| 568 | |||
| 491 | res = sas_phy_reset(phy, 1); | 569 | res = sas_phy_reset(phy, 1); |
| 492 | if (res) | 570 | if (res) |
| 493 | SAS_DPRINTK("Bus reset of %s failed 0x%x\n", | 571 | SAS_DPRINTK("Bus reset of %s failed 0x%x\n", |
| @@ -667,6 +745,39 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * | |||
| 667 | goto out; | 745 | goto out; |
| 668 | } | 746 | } |
| 669 | 747 | ||
| 748 | static void sas_eh_handle_resets(struct Scsi_Host *shost) | ||
| 749 | { | ||
| 750 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | ||
| 751 | struct sas_internal *i = to_sas_internal(shost->transportt); | ||
| 752 | |||
| 753 | /* handle directed resets to sas devices */ | ||
| 754 | spin_lock_irq(&ha->lock); | ||
| 755 | while (!list_empty(&ha->eh_dev_q)) { | ||
| 756 | struct domain_device *dev; | ||
| 757 | struct ssp_device *ssp; | ||
| 758 | |||
| 759 | ssp = list_entry(ha->eh_dev_q.next, typeof(*ssp), eh_list_node); | ||
| 760 | list_del_init(&ssp->eh_list_node); | ||
| 761 | dev = container_of(ssp, typeof(*dev), ssp_dev); | ||
| 762 | kref_get(&dev->kref); | ||
| 763 | WARN_ONCE(dev_is_sata(dev), "ssp reset to ata device?\n"); | ||
| 764 | |||
| 765 | spin_unlock_irq(&ha->lock); | ||
| 766 | |||
| 767 | if (test_and_clear_bit(SAS_DEV_LU_RESET, &dev->state)) | ||
| 768 | i->dft->lldd_lu_reset(dev, ssp->reset_lun.scsi_lun); | ||
| 769 | |||
| 770 | if (test_and_clear_bit(SAS_DEV_RESET, &dev->state)) | ||
| 771 | i->dft->lldd_I_T_nexus_reset(dev); | ||
| 772 | |||
| 773 | sas_put_device(dev); | ||
| 774 | spin_lock_irq(&ha->lock); | ||
| 775 | clear_bit(SAS_DEV_EH_PENDING, &dev->state); | ||
| 776 | ha->eh_active--; | ||
| 777 | } | ||
| 778 | spin_unlock_irq(&ha->lock); | ||
| 779 | } | ||
| 780 | |||
| 670 | 781 | ||
| 671 | void sas_scsi_recover_host(struct Scsi_Host *shost) | 782 | void sas_scsi_recover_host(struct Scsi_Host *shost) |
| 672 | { | 783 | { |
| @@ -709,6 +820,8 @@ out: | |||
| 709 | if (ha->lldd_max_execute_num > 1) | 820 | if (ha->lldd_max_execute_num > 1) |
| 710 | wake_up_process(ha->core.queue_thread); | 821 | wake_up_process(ha->core.queue_thread); |
| 711 | 822 | ||
| 823 | sas_eh_handle_resets(shost); | ||
| 824 | |||
| 712 | /* now link into libata eh --- if we have any ata devices */ | 825 | /* now link into libata eh --- if we have any ata devices */ |
| 713 | sas_ata_strategy_handler(shost); | 826 | sas_ata_strategy_handler(shost); |
| 714 | 827 | ||
