aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/hosts.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r--drivers/scsi/hosts.c113
1 files changed, 84 insertions, 29 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 5feb886c3392..85503fad789a 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -24,6 +24,7 @@
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/blkdev.h> 25#include <linux/blkdev.h>
26#include <linux/kernel.h> 26#include <linux/kernel.h>
27#include <linux/kthread.h>
27#include <linux/string.h> 28#include <linux/string.h>
28#include <linux/mm.h> 29#include <linux/mm.h>
29#include <linux/init.h> 30#include <linux/init.h>
@@ -52,21 +53,80 @@ static struct class shost_class = {
52}; 53};
53 54
54/** 55/**
55 * scsi_host_cancel - cancel outstanding IO to this host 56 * scsi_host_set_state - Take the given host through the host
56 * @shost: pointer to struct Scsi_Host 57 * state model.
57 * recovery: recovery requested to run. 58 * @shost: scsi host to change the state of.
59 * @state: state to change to.
60 *
61 * Returns zero if unsuccessful or an error if the requested
62 * transition is illegal.
58 **/ 63 **/
59static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) 64int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
60{ 65{
61 struct scsi_device *sdev; 66 enum scsi_host_state oldstate = shost->shost_state;
67
68 if (state == oldstate)
69 return 0;
70
71 switch (state) {
72 case SHOST_CREATED:
73 /* There are no legal states that come back to
74 * created. This is the manually initialised start
75 * state */
76 goto illegal;
77
78 case SHOST_RUNNING:
79 switch (oldstate) {
80 case SHOST_CREATED:
81 case SHOST_RECOVERY:
82 break;
83 default:
84 goto illegal;
85 }
86 break;
87
88 case SHOST_RECOVERY:
89 switch (oldstate) {
90 case SHOST_RUNNING:
91 break;
92 default:
93 goto illegal;
94 }
95 break;
96
97 case SHOST_CANCEL:
98 switch (oldstate) {
99 case SHOST_CREATED:
100 case SHOST_RUNNING:
101 break;
102 default:
103 goto illegal;
104 }
105 break;
106
107 case SHOST_DEL:
108 switch (oldstate) {
109 case SHOST_CANCEL:
110 break;
111 default:
112 goto illegal;
113 }
114 break;
62 115
63 set_bit(SHOST_CANCEL, &shost->shost_state);
64 shost_for_each_device(sdev, shost) {
65 scsi_device_cancel(sdev, recovery);
66 } 116 }
67 wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY, 117 shost->shost_state = state;
68 &shost->shost_state))); 118 return 0;
119
120 illegal:
121 SCSI_LOG_ERROR_RECOVERY(1,
122 dev_printk(KERN_ERR, &shost->shost_gendev,
123 "Illegal host state transition"
124 "%s->%s\n",
125 scsi_host_state_name(oldstate),
126 scsi_host_state_name(state)));
127 return -EINVAL;
69} 128}
129EXPORT_SYMBOL(scsi_host_set_state);
70 130
71/** 131/**
72 * scsi_remove_host - remove a scsi host 132 * scsi_remove_host - remove a scsi host
@@ -74,11 +134,13 @@ static void scsi_host_cancel(struct Scsi_Host *shost, int recovery)
74 **/ 134 **/
75void scsi_remove_host(struct Scsi_Host *shost) 135void scsi_remove_host(struct Scsi_Host *shost)
76{ 136{
137 down(&shost->scan_mutex);
138 scsi_host_set_state(shost, SHOST_CANCEL);
139 up(&shost->scan_mutex);
77 scsi_forget_host(shost); 140 scsi_forget_host(shost);
78 scsi_host_cancel(shost, 0);
79 scsi_proc_host_rm(shost); 141 scsi_proc_host_rm(shost);
80 142
81 set_bit(SHOST_DEL, &shost->shost_state); 143 scsi_host_set_state(shost, SHOST_DEL);
82 144
83 transport_unregister_device(&shost->shost_gendev); 145 transport_unregister_device(&shost->shost_gendev);
84 class_device_unregister(&shost->shost_classdev); 146 class_device_unregister(&shost->shost_classdev);
@@ -115,7 +177,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
115 if (error) 177 if (error)
116 goto out; 178 goto out;
117 179
118 set_bit(SHOST_ADD, &shost->shost_state); 180 scsi_host_set_state(shost, SHOST_RUNNING);
119 get_device(shost->shost_gendev.parent); 181 get_device(shost->shost_gendev.parent);
120 182
121 error = class_device_add(&shost->shost_classdev); 183 error = class_device_add(&shost->shost_classdev);
@@ -164,15 +226,8 @@ static void scsi_host_dev_release(struct device *dev)
164 struct Scsi_Host *shost = dev_to_shost(dev); 226 struct Scsi_Host *shost = dev_to_shost(dev);
165 struct device *parent = dev->parent; 227 struct device *parent = dev->parent;
166 228
167 if (shost->ehandler) { 229 if (shost->ehandler)
168 DECLARE_COMPLETION(sem); 230 kthread_stop(shost->ehandler);
169 shost->eh_notify = &sem;
170 shost->eh_kill = 1;
171 up(shost->eh_wait);
172 wait_for_completion(&sem);
173 shost->eh_notify = NULL;
174 }
175
176 if (shost->work_q) 231 if (shost->work_q)
177 destroy_workqueue(shost->work_q); 232 destroy_workqueue(shost->work_q);
178 233
@@ -202,7 +257,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
202{ 257{
203 struct Scsi_Host *shost; 258 struct Scsi_Host *shost;
204 int gfp_mask = GFP_KERNEL, rval; 259 int gfp_mask = GFP_KERNEL, rval;
205 DECLARE_COMPLETION(complete);
206 260
207 if (sht->unchecked_isa_dma && privsize) 261 if (sht->unchecked_isa_dma && privsize)
208 gfp_mask |= __GFP_DMA; 262 gfp_mask |= __GFP_DMA;
@@ -226,6 +280,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
226 280
227 spin_lock_init(&shost->default_lock); 281 spin_lock_init(&shost->default_lock);
228 scsi_assign_lock(shost, &shost->default_lock); 282 scsi_assign_lock(shost, &shost->default_lock);
283 shost->shost_state = SHOST_CREATED;
229 INIT_LIST_HEAD(&shost->__devices); 284 INIT_LIST_HEAD(&shost->__devices);
230 INIT_LIST_HEAD(&shost->__targets); 285 INIT_LIST_HEAD(&shost->__targets);
231 INIT_LIST_HEAD(&shost->eh_cmd_q); 286 INIT_LIST_HEAD(&shost->eh_cmd_q);
@@ -307,12 +362,12 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
307 snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d", 362 snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d",
308 shost->host_no); 363 shost->host_no);
309 364
310 shost->eh_notify = &complete; 365 shost->ehandler = kthread_run(scsi_error_handler, shost,
311 rval = kernel_thread(scsi_error_handler, shost, 0); 366 "scsi_eh_%d", shost->host_no);
312 if (rval < 0) 367 if (IS_ERR(shost->ehandler)) {
368 rval = PTR_ERR(shost->ehandler);
313 goto fail_destroy_freelist; 369 goto fail_destroy_freelist;
314 wait_for_completion(&complete); 370 }
315 shost->eh_notify = NULL;
316 371
317 scsi_proc_hostdir_add(shost->hostt); 372 scsi_proc_hostdir_add(shost->hostt);
318 return shost; 373 return shost;
@@ -382,7 +437,7 @@ EXPORT_SYMBOL(scsi_host_lookup);
382 **/ 437 **/
383struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) 438struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
384{ 439{
385 if (test_bit(SHOST_DEL, &shost->shost_state) || 440 if ((shost->shost_state == SHOST_DEL) ||
386 !get_device(&shost->shost_gendev)) 441 !get_device(&shost->shost_gendev))
387 return NULL; 442 return NULL;
388 return shost; 443 return shost;