diff options
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/libata-core.c | 1 | ||||
-rw-r--r-- | drivers/scsi/libata-eh.c | 6 | ||||
-rw-r--r-- | drivers/scsi/libata-scsi.c | 116 | ||||
-rw-r--r-- | drivers/scsi/libata.h | 1 |
4 files changed, 123 insertions, 1 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index c965eea3b3d4..8df8ecc51a78 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c | |||
@@ -5370,6 +5370,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, | |||
5370 | ap->msg_enable = ATA_MSG_DRV; | 5370 | ap->msg_enable = ATA_MSG_DRV; |
5371 | 5371 | ||
5372 | INIT_WORK(&ap->port_task, NULL, NULL); | 5372 | INIT_WORK(&ap->port_task, NULL, NULL); |
5373 | INIT_WORK(&ap->hotplug_task, ata_scsi_hotplug, ap); | ||
5373 | INIT_LIST_HEAD(&ap->eh_done_q); | 5374 | INIT_LIST_HEAD(&ap->eh_done_q); |
5374 | init_waitqueue_head(&ap->eh_wait_q); | 5375 | init_waitqueue_head(&ap->eh_wait_q); |
5375 | 5376 | ||
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index b53e2e7db498..733dfa532977 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c | |||
@@ -287,9 +287,13 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
287 | /* clean up */ | 287 | /* clean up */ |
288 | spin_lock_irqsave(hs_lock, flags); | 288 | spin_lock_irqsave(hs_lock, flags); |
289 | 289 | ||
290 | if (ap->flags & ATA_FLAG_SCSI_HOTPLUG) | ||
291 | queue_work(ata_aux_wq, &ap->hotplug_task); | ||
292 | |||
290 | if (ap->flags & ATA_FLAG_RECOVERED) | 293 | if (ap->flags & ATA_FLAG_RECOVERED) |
291 | ata_port_printk(ap, KERN_INFO, "EH complete\n"); | 294 | ata_port_printk(ap, KERN_INFO, "EH complete\n"); |
292 | ap->flags &= ~ATA_FLAG_RECOVERED; | 295 | |
296 | ap->flags &= ~(ATA_FLAG_SCSI_HOTPLUG | ATA_FLAG_RECOVERED); | ||
293 | 297 | ||
294 | /* tell wait_eh that we're done */ | 298 | /* tell wait_eh that we're done */ |
295 | ap->flags &= ~ATA_FLAG_EH_IN_PROGRESS; | 299 | ap->flags &= ~ATA_FLAG_EH_IN_PROGRESS; |
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 3dc6188af0e8..12563998d97c 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c | |||
@@ -2786,3 +2786,119 @@ int ata_scsi_offline_dev(struct ata_device *dev) | |||
2786 | } | 2786 | } |
2787 | return 0; | 2787 | return 0; |
2788 | } | 2788 | } |
2789 | |||
2790 | /** | ||
2791 | * ata_scsi_remove_dev - remove attached SCSI device | ||
2792 | * @dev: ATA device to remove attached SCSI device for | ||
2793 | * | ||
2794 | * This function is called from ata_eh_scsi_hotplug() and | ||
2795 | * responsible for removing the SCSI device attached to @dev. | ||
2796 | * | ||
2797 | * LOCKING: | ||
2798 | * Kernel thread context (may sleep). | ||
2799 | */ | ||
2800 | static void ata_scsi_remove_dev(struct ata_device *dev) | ||
2801 | { | ||
2802 | struct ata_port *ap = dev->ap; | ||
2803 | struct scsi_device *sdev; | ||
2804 | unsigned long flags; | ||
2805 | |||
2806 | /* Alas, we need to grab scan_mutex to ensure SCSI device | ||
2807 | * state doesn't change underneath us and thus | ||
2808 | * scsi_device_get() always succeeds. The mutex locking can | ||
2809 | * be removed if there is __scsi_device_get() interface which | ||
2810 | * increments reference counts regardless of device state. | ||
2811 | */ | ||
2812 | mutex_lock(&ap->host->scan_mutex); | ||
2813 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
2814 | |||
2815 | /* clearing dev->sdev is protected by host_set lock */ | ||
2816 | sdev = dev->sdev; | ||
2817 | dev->sdev = NULL; | ||
2818 | |||
2819 | if (sdev) { | ||
2820 | /* If user initiated unplug races with us, sdev can go | ||
2821 | * away underneath us after the host_set lock and | ||
2822 | * scan_mutex are released. Hold onto it. | ||
2823 | */ | ||
2824 | if (scsi_device_get(sdev) == 0) { | ||
2825 | /* The following ensures the attached sdev is | ||
2826 | * offline on return from ata_scsi_offline_dev() | ||
2827 | * regardless it wins or loses the race | ||
2828 | * against this function. | ||
2829 | */ | ||
2830 | scsi_device_set_state(sdev, SDEV_OFFLINE); | ||
2831 | } else { | ||
2832 | WARN_ON(1); | ||
2833 | sdev = NULL; | ||
2834 | } | ||
2835 | } | ||
2836 | |||
2837 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
2838 | mutex_unlock(&ap->host->scan_mutex); | ||
2839 | |||
2840 | if (sdev) { | ||
2841 | ata_dev_printk(dev, KERN_INFO, "detaching (SCSI %s)\n", | ||
2842 | sdev->sdev_gendev.bus_id); | ||
2843 | |||
2844 | scsi_remove_device(sdev); | ||
2845 | scsi_device_put(sdev); | ||
2846 | } | ||
2847 | } | ||
2848 | |||
2849 | /** | ||
2850 | * ata_scsi_hotplug - SCSI part of hotplug | ||
2851 | * @data: Pointer to ATA port to perform SCSI hotplug on | ||
2852 | * | ||
2853 | * Perform SCSI part of hotplug. It's executed from a separate | ||
2854 | * workqueue after EH completes. This is necessary because SCSI | ||
2855 | * hot plugging requires working EH and hot unplugging is | ||
2856 | * synchronized with hot plugging with a mutex. | ||
2857 | * | ||
2858 | * LOCKING: | ||
2859 | * Kernel thread context (may sleep). | ||
2860 | */ | ||
2861 | void ata_scsi_hotplug(void *data) | ||
2862 | { | ||
2863 | struct ata_port *ap = data; | ||
2864 | int i; | ||
2865 | |||
2866 | if (ap->flags & ATA_FLAG_UNLOADING) { | ||
2867 | DPRINTK("ENTER/EXIT - unloading\n"); | ||
2868 | return; | ||
2869 | } | ||
2870 | |||
2871 | DPRINTK("ENTER\n"); | ||
2872 | |||
2873 | /* unplug detached devices */ | ||
2874 | for (i = 0; i < ATA_MAX_DEVICES; i++) { | ||
2875 | struct ata_device *dev = &ap->device[i]; | ||
2876 | unsigned long flags; | ||
2877 | |||
2878 | if (!(dev->flags & ATA_DFLAG_DETACHED)) | ||
2879 | continue; | ||
2880 | |||
2881 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
2882 | dev->flags &= ~ATA_DFLAG_DETACHED; | ||
2883 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
2884 | |||
2885 | ata_scsi_remove_dev(dev); | ||
2886 | } | ||
2887 | |||
2888 | /* scan for new ones */ | ||
2889 | ata_scsi_scan_host(ap); | ||
2890 | |||
2891 | /* If we scanned while EH was in progress, scan would have | ||
2892 | * failed silently. Requeue if there are enabled but | ||
2893 | * unattached devices. | ||
2894 | */ | ||
2895 | for (i = 0; i < ATA_MAX_DEVICES; i++) { | ||
2896 | struct ata_device *dev = &ap->device[i]; | ||
2897 | if (ata_dev_enabled(dev) && !dev->sdev) { | ||
2898 | queue_delayed_work(ata_aux_wq, &ap->hotplug_task, HZ); | ||
2899 | break; | ||
2900 | } | ||
2901 | } | ||
2902 | |||
2903 | DPRINTK("EXIT\n"); | ||
2904 | } | ||
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h index e38759fdc183..0586b0cd73fd 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h | |||
@@ -76,6 +76,7 @@ extern struct scsi_transport_template ata_scsi_transport_template; | |||
76 | 76 | ||
77 | extern void ata_scsi_scan_host(struct ata_port *ap); | 77 | extern void ata_scsi_scan_host(struct ata_port *ap); |
78 | extern int ata_scsi_offline_dev(struct ata_device *dev); | 78 | extern int ata_scsi_offline_dev(struct ata_device *dev); |
79 | extern void ata_scsi_hotplug(void *data); | ||
79 | extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, | 80 | extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, |
80 | unsigned int buflen); | 81 | unsigned int buflen); |
81 | 82 | ||