diff options
author | Dan Williams <dan.j.williams@intel.com> | 2012-06-22 02:25:27 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-07-20 03:58:45 -0400 |
commit | e4a9c3732cea3e3c8c704aad86636090ffe6b25f (patch) | |
tree | faea8e91b3d917dcdb4c89adbb021ac737a6cb35 /drivers/scsi | |
parent | 3b661a92e869ebe2358de8f4b3230ad84f7fce51 (diff) |
[SCSI] libata, libsas: introduce sched_eh and end_eh port ops
When managing shost->host_eh_scheduled libata assumes that there is a
1:1 shost-to-ata_port relationship. libsas creates a 1:N relationship
so it needs to manage host_eh_scheduled cumulatively at the host level.
The sched_eh and end_eh port port ops allow libsas to track when domain
devices enter/leave the "eh-pending" state under ha->lock (previously
named ha->state_lock, but it is no longer just a lock for ha->state
changes).
Since host_eh_scheduled indicates eh without backing commands pinning
the device it can be deallocated at any time. Move the taking of the
domain_device reference under the port_lock to guarantee that the
ata_port stays around for the duration of eh.
Reviewed-by: Jacek Danecki <jacek.danecki@intel.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')
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 38 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 6 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_event.c | 12 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 14 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 27 |
5 files changed, 72 insertions, 25 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index d109cc3a17b6..b035acf18730 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c | |||
@@ -523,6 +523,31 @@ static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) | |||
523 | i->dft->lldd_ata_set_dmamode(dev); | 523 | i->dft->lldd_ata_set_dmamode(dev); |
524 | } | 524 | } |
525 | 525 | ||
526 | static void sas_ata_sched_eh(struct ata_port *ap) | ||
527 | { | ||
528 | struct domain_device *dev = ap->private_data; | ||
529 | struct sas_ha_struct *ha = dev->port->ha; | ||
530 | unsigned long flags; | ||
531 | |||
532 | spin_lock_irqsave(&ha->lock, flags); | ||
533 | if (!test_and_set_bit(SAS_DEV_EH_PENDING, &dev->state)) | ||
534 | ha->eh_active++; | ||
535 | ata_std_sched_eh(ap); | ||
536 | spin_unlock_irqrestore(&ha->lock, flags); | ||
537 | } | ||
538 | |||
539 | void sas_ata_end_eh(struct ata_port *ap) | ||
540 | { | ||
541 | struct domain_device *dev = ap->private_data; | ||
542 | struct sas_ha_struct *ha = dev->port->ha; | ||
543 | unsigned long flags; | ||
544 | |||
545 | spin_lock_irqsave(&ha->lock, flags); | ||
546 | if (test_and_clear_bit(SAS_DEV_EH_PENDING, &dev->state)) | ||
547 | ha->eh_active--; | ||
548 | spin_unlock_irqrestore(&ha->lock, flags); | ||
549 | } | ||
550 | |||
526 | static struct ata_port_operations sas_sata_ops = { | 551 | static struct ata_port_operations sas_sata_ops = { |
527 | .prereset = ata_std_prereset, | 552 | .prereset = ata_std_prereset, |
528 | .hardreset = sas_ata_hard_reset, | 553 | .hardreset = sas_ata_hard_reset, |
@@ -536,6 +561,8 @@ static struct ata_port_operations sas_sata_ops = { | |||
536 | .port_start = ata_sas_port_start, | 561 | .port_start = ata_sas_port_start, |
537 | .port_stop = ata_sas_port_stop, | 562 | .port_stop = ata_sas_port_stop, |
538 | .set_dmamode = sas_ata_set_dmamode, | 563 | .set_dmamode = sas_ata_set_dmamode, |
564 | .sched_eh = sas_ata_sched_eh, | ||
565 | .end_eh = sas_ata_end_eh, | ||
539 | }; | 566 | }; |
540 | 567 | ||
541 | static struct ata_port_info sata_port_info = { | 568 | static struct ata_port_info sata_port_info = { |
@@ -708,10 +735,6 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) | |||
708 | struct ata_port *ap = dev->sata_dev.ap; | 735 | struct ata_port *ap = dev->sata_dev.ap; |
709 | struct sas_ha_struct *ha = dev->port->ha; | 736 | struct sas_ha_struct *ha = dev->port->ha; |
710 | 737 | ||
711 | /* hold a reference over eh since we may be racing with final | ||
712 | * remove once all commands are completed | ||
713 | */ | ||
714 | kref_get(&dev->kref); | ||
715 | sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); | 738 | sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); |
716 | ata_scsi_port_error_handler(ha->core.shost, ap); | 739 | ata_scsi_port_error_handler(ha->core.shost, ap); |
717 | sas_put_device(dev); | 740 | sas_put_device(dev); |
@@ -742,6 +765,13 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) | |||
742 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { | 765 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { |
743 | if (!dev_is_sata(dev)) | 766 | if (!dev_is_sata(dev)) |
744 | continue; | 767 | continue; |
768 | |||
769 | /* hold a reference over eh since we may be | ||
770 | * racing with final remove once all commands | ||
771 | * are completed | ||
772 | */ | ||
773 | kref_get(&dev->kref); | ||
774 | |||
745 | async_schedule_domain(async_sas_ata_eh, dev, &async); | 775 | async_schedule_domain(async_sas_ata_eh, dev, &async); |
746 | } | 776 | } |
747 | spin_unlock(&port->dev_list_lock); | 777 | spin_unlock(&port->dev_list_lock); |
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 629a0865b130..ff497ac76cb4 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c | |||
@@ -294,6 +294,8 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d | |||
294 | 294 | ||
295 | spin_lock_irq(&port->dev_list_lock); | 295 | spin_lock_irq(&port->dev_list_lock); |
296 | list_del_init(&dev->dev_list_node); | 296 | list_del_init(&dev->dev_list_node); |
297 | if (dev_is_sata(dev)) | ||
298 | sas_ata_end_eh(dev->sata_dev.ap); | ||
297 | spin_unlock_irq(&port->dev_list_lock); | 299 | spin_unlock_irq(&port->dev_list_lock); |
298 | 300 | ||
299 | sas_put_device(dev); | 301 | sas_put_device(dev); |
@@ -488,9 +490,9 @@ static void sas_chain_event(int event, unsigned long *pending, | |||
488 | if (!test_and_set_bit(event, pending)) { | 490 | if (!test_and_set_bit(event, pending)) { |
489 | unsigned long flags; | 491 | unsigned long flags; |
490 | 492 | ||
491 | spin_lock_irqsave(&ha->state_lock, flags); | 493 | spin_lock_irqsave(&ha->lock, flags); |
492 | sas_chain_work(ha, sw); | 494 | sas_chain_work(ha, sw); |
493 | spin_unlock_irqrestore(&ha->state_lock, flags); | 495 | spin_unlock_irqrestore(&ha->lock, flags); |
494 | } | 496 | } |
495 | } | 497 | } |
496 | 498 | ||
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 4e4292d210c1..789c4d8bb7a7 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c | |||
@@ -47,9 +47,9 @@ static void sas_queue_event(int event, unsigned long *pending, | |||
47 | if (!test_and_set_bit(event, pending)) { | 47 | if (!test_and_set_bit(event, pending)) { |
48 | unsigned long flags; | 48 | unsigned long flags; |
49 | 49 | ||
50 | spin_lock_irqsave(&ha->state_lock, flags); | 50 | spin_lock_irqsave(&ha->lock, flags); |
51 | sas_queue_work(ha, work); | 51 | sas_queue_work(ha, work); |
52 | spin_unlock_irqrestore(&ha->state_lock, flags); | 52 | spin_unlock_irqrestore(&ha->lock, flags); |
53 | } | 53 | } |
54 | } | 54 | } |
55 | 55 | ||
@@ -61,18 +61,18 @@ void __sas_drain_work(struct sas_ha_struct *ha) | |||
61 | 61 | ||
62 | set_bit(SAS_HA_DRAINING, &ha->state); | 62 | set_bit(SAS_HA_DRAINING, &ha->state); |
63 | /* flush submitters */ | 63 | /* flush submitters */ |
64 | spin_lock_irq(&ha->state_lock); | 64 | spin_lock_irq(&ha->lock); |
65 | spin_unlock_irq(&ha->state_lock); | 65 | spin_unlock_irq(&ha->lock); |
66 | 66 | ||
67 | drain_workqueue(wq); | 67 | drain_workqueue(wq); |
68 | 68 | ||
69 | spin_lock_irq(&ha->state_lock); | 69 | spin_lock_irq(&ha->lock); |
70 | clear_bit(SAS_HA_DRAINING, &ha->state); | 70 | clear_bit(SAS_HA_DRAINING, &ha->state); |
71 | list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { | 71 | list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { |
72 | list_del_init(&sw->drain_node); | 72 | list_del_init(&sw->drain_node); |
73 | sas_queue_work(ha, sw); | 73 | sas_queue_work(ha, sw); |
74 | } | 74 | } |
75 | spin_unlock_irq(&ha->state_lock); | 75 | spin_unlock_irq(&ha->lock); |
76 | } | 76 | } |
77 | 77 | ||
78 | int sas_drain_work(struct sas_ha_struct *ha) | 78 | int sas_drain_work(struct sas_ha_struct *ha) |
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 10cb5ae30977..6909fefa32c5 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c | |||
@@ -114,7 +114,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) | |||
114 | sas_ha->lldd_queue_size = 128; /* Sanity */ | 114 | sas_ha->lldd_queue_size = 128; /* Sanity */ |
115 | 115 | ||
116 | set_bit(SAS_HA_REGISTERED, &sas_ha->state); | 116 | set_bit(SAS_HA_REGISTERED, &sas_ha->state); |
117 | spin_lock_init(&sas_ha->state_lock); | 117 | spin_lock_init(&sas_ha->lock); |
118 | mutex_init(&sas_ha->drain_mutex); | 118 | mutex_init(&sas_ha->drain_mutex); |
119 | INIT_LIST_HEAD(&sas_ha->defer_q); | 119 | INIT_LIST_HEAD(&sas_ha->defer_q); |
120 | 120 | ||
@@ -163,9 +163,9 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) | |||
163 | * events to be queued, and flush any in-progress drainers | 163 | * events to be queued, and flush any in-progress drainers |
164 | */ | 164 | */ |
165 | mutex_lock(&sas_ha->drain_mutex); | 165 | mutex_lock(&sas_ha->drain_mutex); |
166 | spin_lock_irq(&sas_ha->state_lock); | 166 | spin_lock_irq(&sas_ha->lock); |
167 | clear_bit(SAS_HA_REGISTERED, &sas_ha->state); | 167 | clear_bit(SAS_HA_REGISTERED, &sas_ha->state); |
168 | spin_unlock_irq(&sas_ha->state_lock); | 168 | spin_unlock_irq(&sas_ha->lock); |
169 | __sas_drain_work(sas_ha); | 169 | __sas_drain_work(sas_ha); |
170 | mutex_unlock(&sas_ha->drain_mutex); | 170 | mutex_unlock(&sas_ha->drain_mutex); |
171 | 171 | ||
@@ -411,9 +411,9 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) | |||
411 | d->reset_result = 0; | 411 | d->reset_result = 0; |
412 | d->hard_reset = hard_reset; | 412 | d->hard_reset = hard_reset; |
413 | 413 | ||
414 | spin_lock_irq(&ha->state_lock); | 414 | spin_lock_irq(&ha->lock); |
415 | sas_queue_work(ha, &d->reset_work); | 415 | sas_queue_work(ha, &d->reset_work); |
416 | spin_unlock_irq(&ha->state_lock); | 416 | spin_unlock_irq(&ha->lock); |
417 | 417 | ||
418 | rc = sas_drain_work(ha); | 418 | rc = sas_drain_work(ha); |
419 | if (rc == 0) | 419 | if (rc == 0) |
@@ -438,9 +438,9 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) | |||
438 | d->enable_result = 0; | 438 | d->enable_result = 0; |
439 | d->enable = enable; | 439 | d->enable = enable; |
440 | 440 | ||
441 | spin_lock_irq(&ha->state_lock); | 441 | spin_lock_irq(&ha->lock); |
442 | sas_queue_work(ha, &d->enable_work); | 442 | sas_queue_work(ha, &d->enable_work); |
443 | spin_unlock_irq(&ha->state_lock); | 443 | spin_unlock_irq(&ha->lock); |
444 | 444 | ||
445 | rc = sas_drain_work(ha); | 445 | rc = sas_drain_work(ha); |
446 | if (rc == 0) | 446 | if (rc == 0) |
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index f0b9b7bf1882..a09da44e282b 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c | |||
@@ -667,16 +667,20 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * | |||
667 | goto out; | 667 | goto out; |
668 | } | 668 | } |
669 | 669 | ||
670 | |||
670 | void sas_scsi_recover_host(struct Scsi_Host *shost) | 671 | void sas_scsi_recover_host(struct Scsi_Host *shost) |
671 | { | 672 | { |
672 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | 673 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); |
673 | unsigned long flags; | ||
674 | LIST_HEAD(eh_work_q); | 674 | LIST_HEAD(eh_work_q); |
675 | int tries = 0; | ||
676 | bool retry; | ||
675 | 677 | ||
676 | spin_lock_irqsave(shost->host_lock, flags); | 678 | retry: |
679 | tries++; | ||
680 | retry = true; | ||
681 | spin_lock_irq(shost->host_lock); | ||
677 | list_splice_init(&shost->eh_cmd_q, &eh_work_q); | 682 | list_splice_init(&shost->eh_cmd_q, &eh_work_q); |
678 | shost->host_eh_scheduled = 0; | 683 | spin_unlock_irq(shost->host_lock); |
679 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
680 | 684 | ||
681 | SAS_DPRINTK("Enter %s busy: %d failed: %d\n", | 685 | SAS_DPRINTK("Enter %s busy: %d failed: %d\n", |
682 | __func__, shost->host_busy, shost->host_failed); | 686 | __func__, shost->host_busy, shost->host_failed); |
@@ -710,8 +714,19 @@ out: | |||
710 | 714 | ||
711 | scsi_eh_flush_done_q(&ha->eh_done_q); | 715 | scsi_eh_flush_done_q(&ha->eh_done_q); |
712 | 716 | ||
713 | SAS_DPRINTK("--- Exit %s: busy: %d failed: %d\n", | 717 | /* check if any new eh work was scheduled during the last run */ |
714 | __func__, shost->host_busy, shost->host_failed); | 718 | spin_lock_irq(&ha->lock); |
719 | if (ha->eh_active == 0) { | ||
720 | shost->host_eh_scheduled = 0; | ||
721 | retry = false; | ||
722 | } | ||
723 | spin_unlock_irq(&ha->lock); | ||
724 | |||
725 | if (retry) | ||
726 | goto retry; | ||
727 | |||
728 | SAS_DPRINTK("--- Exit %s: busy: %d failed: %d tries: %d\n", | ||
729 | __func__, shost->host_busy, shost->host_failed, tries); | ||
715 | } | 730 | } |
716 | 731 | ||
717 | enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) | 732 | enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) |