aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/libata-core.c1
-rw-r--r--drivers/scsi/libata-eh.c6
-rw-r--r--drivers/scsi/libata-scsi.c116
-rw-r--r--drivers/scsi/libata.h1
-rw-r--r--include/linux/libata.h2
5 files changed, 124 insertions, 2 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 */
2800static 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 */
2861void 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
77extern void ata_scsi_scan_host(struct ata_port *ap); 77extern void ata_scsi_scan_host(struct ata_port *ap);
78extern int ata_scsi_offline_dev(struct ata_device *dev); 78extern int ata_scsi_offline_dev(struct ata_device *dev);
79extern void ata_scsi_hotplug(void *data);
79extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, 80extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
80 unsigned int buflen); 81 unsigned int buflen);
81 82
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 56971943d261..407115624d9f 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -521,7 +521,7 @@ struct ata_port {
521 struct ata_host_set *host_set; 521 struct ata_host_set *host_set;
522 struct device *dev; 522 struct device *dev;
523 523
524 struct work_struct port_task; 524 struct work_struct port_task, hotplug_task;
525 525
526 unsigned int hsm_task_state; 526 unsigned int hsm_task_state;
527 527