diff options
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r-- | drivers/scsi/hosts.c | 113 |
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 | **/ |
59 | static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) | 64 | int 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 | } |
129 | EXPORT_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 | **/ |
75 | void scsi_remove_host(struct Scsi_Host *shost) | 135 | void 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 | **/ |
383 | struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) | 438 | struct 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; |