diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/hosts.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r-- | drivers/scsi/hosts.c | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c new file mode 100644 index 000000000000..ba347576d99b --- /dev/null +++ b/drivers/scsi/hosts.c | |||
@@ -0,0 +1,462 @@ | |||
1 | /* | ||
2 | * hosts.c Copyright (C) 1992 Drew Eckhardt | ||
3 | * Copyright (C) 1993, 1994, 1995 Eric Youngdale | ||
4 | * Copyright (C) 2002-2003 Christoph Hellwig | ||
5 | * | ||
6 | * mid to lowlevel SCSI driver interface | ||
7 | * Initial versions: Drew Eckhardt | ||
8 | * Subsequent revisions: Eric Youngdale | ||
9 | * | ||
10 | * <drew@colorado.edu> | ||
11 | * | ||
12 | * Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli | ||
13 | * Added QLOGIC QLA1280 SCSI controller kernel host support. | ||
14 | * August 4, 1999 Fred Lewis, Intel DuPont | ||
15 | * | ||
16 | * Updated to reflect the new initialization scheme for the higher | ||
17 | * level of scsi drivers (sd/sr/st) | ||
18 | * September 17, 2000 Torben Mathiasen <tmm@image.dk> | ||
19 | * | ||
20 | * Restructured scsi_host lists and associated functions. | ||
21 | * September 04, 2002 Mike Anderson (andmike@us.ibm.com) | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/blkdev.h> | ||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/string.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/completion.h> | ||
31 | #include <linux/transport_class.h> | ||
32 | |||
33 | #include <scsi/scsi_device.h> | ||
34 | #include <scsi/scsi_host.h> | ||
35 | #include <scsi/scsi_transport.h> | ||
36 | |||
37 | #include "scsi_priv.h" | ||
38 | #include "scsi_logging.h" | ||
39 | |||
40 | |||
41 | static int scsi_host_next_hn; /* host_no for next new host */ | ||
42 | |||
43 | |||
44 | static void scsi_host_cls_release(struct class_device *class_dev) | ||
45 | { | ||
46 | put_device(&class_to_shost(class_dev)->shost_gendev); | ||
47 | } | ||
48 | |||
49 | static struct class shost_class = { | ||
50 | .name = "scsi_host", | ||
51 | .release = scsi_host_cls_release, | ||
52 | }; | ||
53 | |||
54 | /** | ||
55 | * scsi_host_cancel - cancel outstanding IO to this host | ||
56 | * @shost: pointer to struct Scsi_Host | ||
57 | * recovery: recovery requested to run. | ||
58 | **/ | ||
59 | void scsi_host_cancel(struct Scsi_Host *shost, int recovery) | ||
60 | { | ||
61 | struct scsi_device *sdev; | ||
62 | |||
63 | set_bit(SHOST_CANCEL, &shost->shost_state); | ||
64 | shost_for_each_device(sdev, shost) { | ||
65 | scsi_device_cancel(sdev, recovery); | ||
66 | } | ||
67 | wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY, | ||
68 | &shost->shost_state))); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * scsi_remove_host - remove a scsi host | ||
73 | * @shost: a pointer to a scsi host to remove | ||
74 | **/ | ||
75 | void scsi_remove_host(struct Scsi_Host *shost) | ||
76 | { | ||
77 | scsi_forget_host(shost); | ||
78 | scsi_host_cancel(shost, 0); | ||
79 | scsi_proc_host_rm(shost); | ||
80 | |||
81 | set_bit(SHOST_DEL, &shost->shost_state); | ||
82 | |||
83 | transport_unregister_device(&shost->shost_gendev); | ||
84 | class_device_unregister(&shost->shost_classdev); | ||
85 | device_del(&shost->shost_gendev); | ||
86 | } | ||
87 | EXPORT_SYMBOL(scsi_remove_host); | ||
88 | |||
89 | /** | ||
90 | * scsi_add_host - add a scsi host | ||
91 | * @shost: scsi host pointer to add | ||
92 | * @dev: a struct device of type scsi class | ||
93 | * | ||
94 | * Return value: | ||
95 | * 0 on success / != 0 for error | ||
96 | **/ | ||
97 | int scsi_add_host(struct Scsi_Host *shost, struct device *dev) | ||
98 | { | ||
99 | struct scsi_host_template *sht = shost->hostt; | ||
100 | int error = -EINVAL; | ||
101 | |||
102 | printk(KERN_INFO "scsi%d : %s\n", shost->host_no, | ||
103 | sht->info ? sht->info(shost) : sht->name); | ||
104 | |||
105 | if (!shost->can_queue) { | ||
106 | printk(KERN_ERR "%s: can_queue = 0 no longer supported\n", | ||
107 | sht->name); | ||
108 | goto out; | ||
109 | } | ||
110 | |||
111 | if (!shost->shost_gendev.parent) | ||
112 | shost->shost_gendev.parent = dev ? dev : &platform_bus; | ||
113 | |||
114 | error = device_add(&shost->shost_gendev); | ||
115 | if (error) | ||
116 | goto out; | ||
117 | |||
118 | set_bit(SHOST_ADD, &shost->shost_state); | ||
119 | get_device(shost->shost_gendev.parent); | ||
120 | |||
121 | error = class_device_add(&shost->shost_classdev); | ||
122 | if (error) | ||
123 | goto out_del_gendev; | ||
124 | |||
125 | get_device(&shost->shost_gendev); | ||
126 | |||
127 | if (shost->transportt->host_size && | ||
128 | (shost->shost_data = kmalloc(shost->transportt->host_size, | ||
129 | GFP_KERNEL)) == NULL) | ||
130 | goto out_del_classdev; | ||
131 | |||
132 | if (shost->transportt->create_work_queue) { | ||
133 | snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d", | ||
134 | shost->host_no); | ||
135 | shost->work_q = create_singlethread_workqueue( | ||
136 | shost->work_q_name); | ||
137 | if (!shost->work_q) | ||
138 | goto out_free_shost_data; | ||
139 | } | ||
140 | |||
141 | error = scsi_sysfs_add_host(shost); | ||
142 | if (error) | ||
143 | goto out_destroy_host; | ||
144 | |||
145 | scsi_proc_host_add(shost); | ||
146 | return error; | ||
147 | |||
148 | out_destroy_host: | ||
149 | if (shost->work_q) | ||
150 | destroy_workqueue(shost->work_q); | ||
151 | out_free_shost_data: | ||
152 | kfree(shost->shost_data); | ||
153 | out_del_classdev: | ||
154 | class_device_del(&shost->shost_classdev); | ||
155 | out_del_gendev: | ||
156 | device_del(&shost->shost_gendev); | ||
157 | out: | ||
158 | return error; | ||
159 | } | ||
160 | EXPORT_SYMBOL(scsi_add_host); | ||
161 | |||
162 | static void scsi_host_dev_release(struct device *dev) | ||
163 | { | ||
164 | struct Scsi_Host *shost = dev_to_shost(dev); | ||
165 | struct device *parent = dev->parent; | ||
166 | |||
167 | if (shost->ehandler) { | ||
168 | DECLARE_COMPLETION(sem); | ||
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) | ||
177 | destroy_workqueue(shost->work_q); | ||
178 | |||
179 | scsi_proc_hostdir_rm(shost->hostt); | ||
180 | scsi_destroy_command_freelist(shost); | ||
181 | kfree(shost->shost_data); | ||
182 | |||
183 | /* | ||
184 | * Some drivers (eg aha1542) do scsi_register()/scsi_unregister() | ||
185 | * during probing without performing a scsi_set_device() in between. | ||
186 | * In this case dev->parent is NULL. | ||
187 | */ | ||
188 | if (parent) | ||
189 | put_device(parent); | ||
190 | kfree(shost); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * scsi_host_alloc - register a scsi host adapter instance. | ||
195 | * @sht: pointer to scsi host template | ||
196 | * @privsize: extra bytes to allocate for driver | ||
197 | * | ||
198 | * Note: | ||
199 | * Allocate a new Scsi_Host and perform basic initialization. | ||
200 | * The host is not published to the scsi midlayer until scsi_add_host | ||
201 | * is called. | ||
202 | * | ||
203 | * Return value: | ||
204 | * Pointer to a new Scsi_Host | ||
205 | **/ | ||
206 | struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) | ||
207 | { | ||
208 | struct Scsi_Host *shost; | ||
209 | int gfp_mask = GFP_KERNEL, rval; | ||
210 | DECLARE_COMPLETION(complete); | ||
211 | |||
212 | if (sht->unchecked_isa_dma && privsize) | ||
213 | gfp_mask |= __GFP_DMA; | ||
214 | |||
215 | /* Check to see if this host has any error handling facilities */ | ||
216 | if (!sht->eh_strategy_handler && !sht->eh_abort_handler && | ||
217 | !sht->eh_device_reset_handler && !sht->eh_bus_reset_handler && | ||
218 | !sht->eh_host_reset_handler) { | ||
219 | printk(KERN_ERR "ERROR: SCSI host `%s' has no error handling\n" | ||
220 | "ERROR: This is not a safe way to run your " | ||
221 | "SCSI host\n" | ||
222 | "ERROR: The error handling must be added to " | ||
223 | "this driver\n", sht->proc_name); | ||
224 | dump_stack(); | ||
225 | } | ||
226 | |||
227 | shost = kmalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask); | ||
228 | if (!shost) | ||
229 | return NULL; | ||
230 | memset(shost, 0, sizeof(struct Scsi_Host) + privsize); | ||
231 | |||
232 | spin_lock_init(&shost->default_lock); | ||
233 | scsi_assign_lock(shost, &shost->default_lock); | ||
234 | INIT_LIST_HEAD(&shost->__devices); | ||
235 | INIT_LIST_HEAD(&shost->__targets); | ||
236 | INIT_LIST_HEAD(&shost->eh_cmd_q); | ||
237 | INIT_LIST_HEAD(&shost->starved_list); | ||
238 | init_waitqueue_head(&shost->host_wait); | ||
239 | |||
240 | init_MUTEX(&shost->scan_mutex); | ||
241 | |||
242 | shost->host_no = scsi_host_next_hn++; /* XXX(hch): still racy */ | ||
243 | shost->dma_channel = 0xff; | ||
244 | |||
245 | /* These three are default values which can be overridden */ | ||
246 | shost->max_channel = 0; | ||
247 | shost->max_id = 8; | ||
248 | shost->max_lun = 8; | ||
249 | |||
250 | /* Give each shost a default transportt */ | ||
251 | shost->transportt = &blank_transport_template; | ||
252 | |||
253 | /* | ||
254 | * All drivers right now should be able to handle 12 byte | ||
255 | * commands. Every so often there are requests for 16 byte | ||
256 | * commands, but individual low-level drivers need to certify that | ||
257 | * they actually do something sensible with such commands. | ||
258 | */ | ||
259 | shost->max_cmd_len = 12; | ||
260 | shost->hostt = sht; | ||
261 | shost->this_id = sht->this_id; | ||
262 | shost->can_queue = sht->can_queue; | ||
263 | shost->sg_tablesize = sht->sg_tablesize; | ||
264 | shost->cmd_per_lun = sht->cmd_per_lun; | ||
265 | shost->unchecked_isa_dma = sht->unchecked_isa_dma; | ||
266 | shost->use_clustering = sht->use_clustering; | ||
267 | shost->ordered_flush = sht->ordered_flush; | ||
268 | shost->ordered_tag = sht->ordered_tag; | ||
269 | |||
270 | /* | ||
271 | * hosts/devices that do queueing must support ordered tags | ||
272 | */ | ||
273 | if (shost->can_queue > 1 && shost->ordered_flush) { | ||
274 | printk(KERN_ERR "scsi: ordered flushes don't support queueing\n"); | ||
275 | shost->ordered_flush = 0; | ||
276 | } | ||
277 | |||
278 | if (sht->max_host_blocked) | ||
279 | shost->max_host_blocked = sht->max_host_blocked; | ||
280 | else | ||
281 | shost->max_host_blocked = SCSI_DEFAULT_HOST_BLOCKED; | ||
282 | |||
283 | /* | ||
284 | * If the driver imposes no hard sector transfer limit, start at | ||
285 | * machine infinity initially. | ||
286 | */ | ||
287 | if (sht->max_sectors) | ||
288 | shost->max_sectors = sht->max_sectors; | ||
289 | else | ||
290 | shost->max_sectors = SCSI_DEFAULT_MAX_SECTORS; | ||
291 | |||
292 | /* | ||
293 | * assume a 4GB boundary, if not set | ||
294 | */ | ||
295 | if (sht->dma_boundary) | ||
296 | shost->dma_boundary = sht->dma_boundary; | ||
297 | else | ||
298 | shost->dma_boundary = 0xffffffff; | ||
299 | |||
300 | rval = scsi_setup_command_freelist(shost); | ||
301 | if (rval) | ||
302 | goto fail_kfree; | ||
303 | |||
304 | device_initialize(&shost->shost_gendev); | ||
305 | snprintf(shost->shost_gendev.bus_id, BUS_ID_SIZE, "host%d", | ||
306 | shost->host_no); | ||
307 | shost->shost_gendev.release = scsi_host_dev_release; | ||
308 | |||
309 | class_device_initialize(&shost->shost_classdev); | ||
310 | shost->shost_classdev.dev = &shost->shost_gendev; | ||
311 | shost->shost_classdev.class = &shost_class; | ||
312 | snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d", | ||
313 | shost->host_no); | ||
314 | |||
315 | shost->eh_notify = &complete; | ||
316 | rval = kernel_thread(scsi_error_handler, shost, 0); | ||
317 | if (rval < 0) | ||
318 | goto fail_destroy_freelist; | ||
319 | wait_for_completion(&complete); | ||
320 | shost->eh_notify = NULL; | ||
321 | |||
322 | scsi_proc_hostdir_add(shost->hostt); | ||
323 | return shost; | ||
324 | |||
325 | fail_destroy_freelist: | ||
326 | scsi_destroy_command_freelist(shost); | ||
327 | fail_kfree: | ||
328 | kfree(shost); | ||
329 | return NULL; | ||
330 | } | ||
331 | EXPORT_SYMBOL(scsi_host_alloc); | ||
332 | |||
333 | struct Scsi_Host *scsi_register(struct scsi_host_template *sht, int privsize) | ||
334 | { | ||
335 | struct Scsi_Host *shost = scsi_host_alloc(sht, privsize); | ||
336 | |||
337 | if (!sht->detect) { | ||
338 | printk(KERN_WARNING "scsi_register() called on new-style " | ||
339 | "template for driver %s\n", sht->name); | ||
340 | dump_stack(); | ||
341 | } | ||
342 | |||
343 | if (shost) | ||
344 | list_add_tail(&shost->sht_legacy_list, &sht->legacy_hosts); | ||
345 | return shost; | ||
346 | } | ||
347 | EXPORT_SYMBOL(scsi_register); | ||
348 | |||
349 | void scsi_unregister(struct Scsi_Host *shost) | ||
350 | { | ||
351 | list_del(&shost->sht_legacy_list); | ||
352 | scsi_host_put(shost); | ||
353 | } | ||
354 | EXPORT_SYMBOL(scsi_unregister); | ||
355 | |||
356 | /** | ||
357 | * scsi_host_lookup - get a reference to a Scsi_Host by host no | ||
358 | * | ||
359 | * @hostnum: host number to locate | ||
360 | * | ||
361 | * Return value: | ||
362 | * A pointer to located Scsi_Host or NULL. | ||
363 | **/ | ||
364 | struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) | ||
365 | { | ||
366 | struct class *class = &shost_class; | ||
367 | struct class_device *cdev; | ||
368 | struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p; | ||
369 | |||
370 | down_read(&class->subsys.rwsem); | ||
371 | list_for_each_entry(cdev, &class->children, node) { | ||
372 | p = class_to_shost(cdev); | ||
373 | if (p->host_no == hostnum) { | ||
374 | shost = scsi_host_get(p); | ||
375 | break; | ||
376 | } | ||
377 | } | ||
378 | up_read(&class->subsys.rwsem); | ||
379 | |||
380 | return shost; | ||
381 | } | ||
382 | EXPORT_SYMBOL(scsi_host_lookup); | ||
383 | |||
384 | /** | ||
385 | * scsi_host_get - inc a Scsi_Host ref count | ||
386 | * @shost: Pointer to Scsi_Host to inc. | ||
387 | **/ | ||
388 | struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) | ||
389 | { | ||
390 | if (test_bit(SHOST_DEL, &shost->shost_state) || | ||
391 | !get_device(&shost->shost_gendev)) | ||
392 | return NULL; | ||
393 | return shost; | ||
394 | } | ||
395 | EXPORT_SYMBOL(scsi_host_get); | ||
396 | |||
397 | /** | ||
398 | * scsi_host_put - dec a Scsi_Host ref count | ||
399 | * @shost: Pointer to Scsi_Host to dec. | ||
400 | **/ | ||
401 | void scsi_host_put(struct Scsi_Host *shost) | ||
402 | { | ||
403 | put_device(&shost->shost_gendev); | ||
404 | } | ||
405 | EXPORT_SYMBOL(scsi_host_put); | ||
406 | |||
407 | int scsi_init_hosts(void) | ||
408 | { | ||
409 | return class_register(&shost_class); | ||
410 | } | ||
411 | |||
412 | void scsi_exit_hosts(void) | ||
413 | { | ||
414 | class_unregister(&shost_class); | ||
415 | } | ||
416 | |||
417 | int scsi_is_host_device(const struct device *dev) | ||
418 | { | ||
419 | return dev->release == scsi_host_dev_release; | ||
420 | } | ||
421 | EXPORT_SYMBOL(scsi_is_host_device); | ||
422 | |||
423 | /** | ||
424 | * scsi_queue_work - Queue work to the Scsi_Host workqueue. | ||
425 | * @shost: Pointer to Scsi_Host. | ||
426 | * @work: Work to queue for execution. | ||
427 | * | ||
428 | * Return value: | ||
429 | * 0 on success / != 0 for error | ||
430 | **/ | ||
431 | int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work) | ||
432 | { | ||
433 | if (unlikely(!shost->work_q)) { | ||
434 | printk(KERN_ERR | ||
435 | "ERROR: Scsi host '%s' attempted to queue scsi-work, " | ||
436 | "when no workqueue created.\n", shost->hostt->name); | ||
437 | dump_stack(); | ||
438 | |||
439 | return -EINVAL; | ||
440 | } | ||
441 | |||
442 | return queue_work(shost->work_q, work); | ||
443 | } | ||
444 | EXPORT_SYMBOL_GPL(scsi_queue_work); | ||
445 | |||
446 | /** | ||
447 | * scsi_flush_work - Flush a Scsi_Host's workqueue. | ||
448 | * @shost: Pointer to Scsi_Host. | ||
449 | **/ | ||
450 | void scsi_flush_work(struct Scsi_Host *shost) | ||
451 | { | ||
452 | if (!shost->work_q) { | ||
453 | printk(KERN_ERR | ||
454 | "ERROR: Scsi host '%s' attempted to flush scsi-work, " | ||
455 | "when no workqueue created.\n", shost->hostt->name); | ||
456 | dump_stack(); | ||
457 | return; | ||
458 | } | ||
459 | |||
460 | flush_workqueue(shost->work_q); | ||
461 | } | ||
462 | EXPORT_SYMBOL_GPL(scsi_flush_work); | ||