aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2006-05-31 05:28:05 -0400
committerTejun Heo <htejun@gmail.com>2006-05-31 05:28:05 -0400
commit580b2102327ab8444af5bde4e70b50d268a1d558 (patch)
tree01389e898e09dbaddb06405b182116b9be0e6d59 /drivers
parent084fe639b81c4d418a2cf714acb0475e3713cb73 (diff)
[PATCH] libata-hp: implement SCSI part of hotplug
Implement SCSI part of hotplug. This must be done in a separate context as SCSI makes use of EH during probing. SCSI scan fails silently if EH is in progress. In such cases, libata pauses briefly and retries until every device is attached. Signed-off-by: Tejun Heo <htejun@gmail.com>
Diffstat (limited to 'drivers')
-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
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 */
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