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 | |
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>
-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 | ||||
-rw-r--r-- | include/scsi/libsas.h | 10 |
4 files changed, 140 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 | ||
diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 814d8cb592ad..df9cefdf2a8e 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h | |||
@@ -176,10 +176,17 @@ struct sata_device { | |||
176 | u8 fis[ATA_RESP_FIS_SIZE]; | 176 | u8 fis[ATA_RESP_FIS_SIZE]; |
177 | }; | 177 | }; |
178 | 178 | ||
179 | struct ssp_device { | ||
180 | struct list_head eh_list_node; /* pending a user requested eh action */ | ||
181 | struct scsi_lun reset_lun; | ||
182 | }; | ||
183 | |||
179 | enum { | 184 | enum { |
180 | SAS_DEV_GONE, | 185 | SAS_DEV_GONE, |
181 | SAS_DEV_DESTROY, | 186 | SAS_DEV_DESTROY, |
182 | SAS_DEV_EH_PENDING, | 187 | SAS_DEV_EH_PENDING, |
188 | SAS_DEV_LU_RESET, | ||
189 | SAS_DEV_RESET, | ||
183 | }; | 190 | }; |
184 | 191 | ||
185 | struct domain_device { | 192 | struct domain_device { |
@@ -213,6 +220,7 @@ struct domain_device { | |||
213 | union { | 220 | union { |
214 | struct expander_device ex_dev; | 221 | struct expander_device ex_dev; |
215 | struct sata_device sata_dev; /* STP & directly attached */ | 222 | struct sata_device sata_dev; /* STP & directly attached */ |
223 | struct ssp_device ssp_dev; | ||
216 | }; | 224 | }; |
217 | 225 | ||
218 | void *lldd_dev; | 226 | void *lldd_dev; |
@@ -389,6 +397,8 @@ struct sas_ha_struct { | |||
389 | unsigned long state; | 397 | unsigned long state; |
390 | spinlock_t lock; | 398 | spinlock_t lock; |
391 | int eh_active; | 399 | int eh_active; |
400 | wait_queue_head_t eh_wait_q; | ||
401 | struct list_head eh_dev_q; | ||
392 | 402 | ||
393 | struct mutex disco_mutex; | 403 | struct mutex disco_mutex; |
394 | 404 | ||