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/block/elevator.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/block/elevator.c')
-rw-r--r-- | drivers/block/elevator.c | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c new file mode 100644 index 000000000000..6b79b4314622 --- /dev/null +++ b/drivers/block/elevator.c | |||
@@ -0,0 +1,705 @@ | |||
1 | /* | ||
2 | * linux/drivers/block/elevator.c | ||
3 | * | ||
4 | * Block device elevator/IO-scheduler. | ||
5 | * | ||
6 | * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE | ||
7 | * | ||
8 | * 30042000 Jens Axboe <axboe@suse.de> : | ||
9 | * | ||
10 | * Split the elevator a bit so that it is possible to choose a different | ||
11 | * one or even write a new "plug in". There are three pieces: | ||
12 | * - elevator_fn, inserts a new request in the queue list | ||
13 | * - elevator_merge_fn, decides whether a new buffer can be merged with | ||
14 | * an existing request | ||
15 | * - elevator_dequeue_fn, called when a request is taken off the active list | ||
16 | * | ||
17 | * 20082000 Dave Jones <davej@suse.de> : | ||
18 | * Removed tests for max-bomb-segments, which was breaking elvtune | ||
19 | * when run without -bN | ||
20 | * | ||
21 | * Jens: | ||
22 | * - Rework again to work with bio instead of buffer_heads | ||
23 | * - loose bi_dev comparisons, partition handling is right now | ||
24 | * - completely modularize elevator setup and teardown | ||
25 | * | ||
26 | */ | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/fs.h> | ||
29 | #include <linux/blkdev.h> | ||
30 | #include <linux/elevator.h> | ||
31 | #include <linux/bio.h> | ||
32 | #include <linux/config.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/compiler.h> | ||
37 | |||
38 | #include <asm/uaccess.h> | ||
39 | |||
40 | static DEFINE_SPINLOCK(elv_list_lock); | ||
41 | static LIST_HEAD(elv_list); | ||
42 | |||
43 | /* | ||
44 | * can we safely merge with this request? | ||
45 | */ | ||
46 | inline int elv_rq_merge_ok(struct request *rq, struct bio *bio) | ||
47 | { | ||
48 | if (!rq_mergeable(rq)) | ||
49 | return 0; | ||
50 | |||
51 | /* | ||
52 | * different data direction or already started, don't merge | ||
53 | */ | ||
54 | if (bio_data_dir(bio) != rq_data_dir(rq)) | ||
55 | return 0; | ||
56 | |||
57 | /* | ||
58 | * same device and no special stuff set, merge is ok | ||
59 | */ | ||
60 | if (rq->rq_disk == bio->bi_bdev->bd_disk && | ||
61 | !rq->waiting && !rq->special) | ||
62 | return 1; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | EXPORT_SYMBOL(elv_rq_merge_ok); | ||
67 | |||
68 | inline int elv_try_merge(struct request *__rq, struct bio *bio) | ||
69 | { | ||
70 | int ret = ELEVATOR_NO_MERGE; | ||
71 | |||
72 | /* | ||
73 | * we can merge and sequence is ok, check if it's possible | ||
74 | */ | ||
75 | if (elv_rq_merge_ok(__rq, bio)) { | ||
76 | if (__rq->sector + __rq->nr_sectors == bio->bi_sector) | ||
77 | ret = ELEVATOR_BACK_MERGE; | ||
78 | else if (__rq->sector - bio_sectors(bio) == bio->bi_sector) | ||
79 | ret = ELEVATOR_FRONT_MERGE; | ||
80 | } | ||
81 | |||
82 | return ret; | ||
83 | } | ||
84 | EXPORT_SYMBOL(elv_try_merge); | ||
85 | |||
86 | inline int elv_try_last_merge(request_queue_t *q, struct bio *bio) | ||
87 | { | ||
88 | if (q->last_merge) | ||
89 | return elv_try_merge(q->last_merge, bio); | ||
90 | |||
91 | return ELEVATOR_NO_MERGE; | ||
92 | } | ||
93 | EXPORT_SYMBOL(elv_try_last_merge); | ||
94 | |||
95 | static struct elevator_type *elevator_find(const char *name) | ||
96 | { | ||
97 | struct elevator_type *e = NULL; | ||
98 | struct list_head *entry; | ||
99 | |||
100 | spin_lock_irq(&elv_list_lock); | ||
101 | list_for_each(entry, &elv_list) { | ||
102 | struct elevator_type *__e; | ||
103 | |||
104 | __e = list_entry(entry, struct elevator_type, list); | ||
105 | |||
106 | if (!strcmp(__e->elevator_name, name)) { | ||
107 | e = __e; | ||
108 | break; | ||
109 | } | ||
110 | } | ||
111 | spin_unlock_irq(&elv_list_lock); | ||
112 | |||
113 | return e; | ||
114 | } | ||
115 | |||
116 | static void elevator_put(struct elevator_type *e) | ||
117 | { | ||
118 | module_put(e->elevator_owner); | ||
119 | } | ||
120 | |||
121 | static struct elevator_type *elevator_get(const char *name) | ||
122 | { | ||
123 | struct elevator_type *e = elevator_find(name); | ||
124 | |||
125 | if (!e) | ||
126 | return NULL; | ||
127 | if (!try_module_get(e->elevator_owner)) | ||
128 | return NULL; | ||
129 | |||
130 | return e; | ||
131 | } | ||
132 | |||
133 | static int elevator_attach(request_queue_t *q, struct elevator_type *e, | ||
134 | struct elevator_queue *eq) | ||
135 | { | ||
136 | int ret = 0; | ||
137 | |||
138 | memset(eq, 0, sizeof(*eq)); | ||
139 | eq->ops = &e->ops; | ||
140 | eq->elevator_type = e; | ||
141 | |||
142 | INIT_LIST_HEAD(&q->queue_head); | ||
143 | q->last_merge = NULL; | ||
144 | q->elevator = eq; | ||
145 | |||
146 | if (eq->ops->elevator_init_fn) | ||
147 | ret = eq->ops->elevator_init_fn(q, eq); | ||
148 | |||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | static char chosen_elevator[16]; | ||
153 | |||
154 | static void elevator_setup_default(void) | ||
155 | { | ||
156 | /* | ||
157 | * check if default is set and exists | ||
158 | */ | ||
159 | if (chosen_elevator[0] && elevator_find(chosen_elevator)) | ||
160 | return; | ||
161 | |||
162 | #if defined(CONFIG_IOSCHED_AS) | ||
163 | strcpy(chosen_elevator, "anticipatory"); | ||
164 | #elif defined(CONFIG_IOSCHED_DEADLINE) | ||
165 | strcpy(chosen_elevator, "deadline"); | ||
166 | #elif defined(CONFIG_IOSCHED_CFQ) | ||
167 | strcpy(chosen_elevator, "cfq"); | ||
168 | #elif defined(CONFIG_IOSCHED_NOOP) | ||
169 | strcpy(chosen_elevator, "noop"); | ||
170 | #else | ||
171 | #error "You must build at least 1 IO scheduler into the kernel" | ||
172 | #endif | ||
173 | } | ||
174 | |||
175 | static int __init elevator_setup(char *str) | ||
176 | { | ||
177 | strncpy(chosen_elevator, str, sizeof(chosen_elevator) - 1); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | __setup("elevator=", elevator_setup); | ||
182 | |||
183 | int elevator_init(request_queue_t *q, char *name) | ||
184 | { | ||
185 | struct elevator_type *e = NULL; | ||
186 | struct elevator_queue *eq; | ||
187 | int ret = 0; | ||
188 | |||
189 | elevator_setup_default(); | ||
190 | |||
191 | if (!name) | ||
192 | name = chosen_elevator; | ||
193 | |||
194 | e = elevator_get(name); | ||
195 | if (!e) | ||
196 | return -EINVAL; | ||
197 | |||
198 | eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL); | ||
199 | if (!eq) { | ||
200 | elevator_put(e->elevator_type); | ||
201 | return -ENOMEM; | ||
202 | } | ||
203 | |||
204 | ret = elevator_attach(q, e, eq); | ||
205 | if (ret) { | ||
206 | kfree(eq); | ||
207 | elevator_put(e->elevator_type); | ||
208 | } | ||
209 | |||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | void elevator_exit(elevator_t *e) | ||
214 | { | ||
215 | if (e->ops->elevator_exit_fn) | ||
216 | e->ops->elevator_exit_fn(e); | ||
217 | |||
218 | elevator_put(e->elevator_type); | ||
219 | e->elevator_type = NULL; | ||
220 | kfree(e); | ||
221 | } | ||
222 | |||
223 | static int elevator_global_init(void) | ||
224 | { | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) | ||
229 | { | ||
230 | elevator_t *e = q->elevator; | ||
231 | |||
232 | if (e->ops->elevator_merge_fn) | ||
233 | return e->ops->elevator_merge_fn(q, req, bio); | ||
234 | |||
235 | return ELEVATOR_NO_MERGE; | ||
236 | } | ||
237 | |||
238 | void elv_merged_request(request_queue_t *q, struct request *rq) | ||
239 | { | ||
240 | elevator_t *e = q->elevator; | ||
241 | |||
242 | if (e->ops->elevator_merged_fn) | ||
243 | e->ops->elevator_merged_fn(q, rq); | ||
244 | } | ||
245 | |||
246 | void elv_merge_requests(request_queue_t *q, struct request *rq, | ||
247 | struct request *next) | ||
248 | { | ||
249 | elevator_t *e = q->elevator; | ||
250 | |||
251 | if (q->last_merge == next) | ||
252 | q->last_merge = NULL; | ||
253 | |||
254 | if (e->ops->elevator_merge_req_fn) | ||
255 | e->ops->elevator_merge_req_fn(q, rq, next); | ||
256 | } | ||
257 | |||
258 | /* | ||
259 | * For careful internal use by the block layer. Essentially the same as | ||
260 | * a requeue in that it tells the io scheduler that this request is not | ||
261 | * active in the driver or hardware anymore, but we don't want the request | ||
262 | * added back to the scheduler. Function is not exported. | ||
263 | */ | ||
264 | void elv_deactivate_request(request_queue_t *q, struct request *rq) | ||
265 | { | ||
266 | elevator_t *e = q->elevator; | ||
267 | |||
268 | /* | ||
269 | * it already went through dequeue, we need to decrement the | ||
270 | * in_flight count again | ||
271 | */ | ||
272 | if (blk_account_rq(rq)) | ||
273 | q->in_flight--; | ||
274 | |||
275 | rq->flags &= ~REQ_STARTED; | ||
276 | |||
277 | if (e->ops->elevator_deactivate_req_fn) | ||
278 | e->ops->elevator_deactivate_req_fn(q, rq); | ||
279 | } | ||
280 | |||
281 | void elv_requeue_request(request_queue_t *q, struct request *rq) | ||
282 | { | ||
283 | elv_deactivate_request(q, rq); | ||
284 | |||
285 | /* | ||
286 | * if this is the flush, requeue the original instead and drop the flush | ||
287 | */ | ||
288 | if (rq->flags & REQ_BAR_FLUSH) { | ||
289 | clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); | ||
290 | rq = rq->end_io_data; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * if iosched has an explicit requeue hook, then use that. otherwise | ||
295 | * just put the request at the front of the queue | ||
296 | */ | ||
297 | if (q->elevator->ops->elevator_requeue_req_fn) | ||
298 | q->elevator->ops->elevator_requeue_req_fn(q, rq); | ||
299 | else | ||
300 | __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); | ||
301 | } | ||
302 | |||
303 | void __elv_add_request(request_queue_t *q, struct request *rq, int where, | ||
304 | int plug) | ||
305 | { | ||
306 | /* | ||
307 | * barriers implicitly indicate back insertion | ||
308 | */ | ||
309 | if (rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER) && | ||
310 | where == ELEVATOR_INSERT_SORT) | ||
311 | where = ELEVATOR_INSERT_BACK; | ||
312 | |||
313 | if (plug) | ||
314 | blk_plug_device(q); | ||
315 | |||
316 | rq->q = q; | ||
317 | |||
318 | if (!test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) { | ||
319 | q->elevator->ops->elevator_add_req_fn(q, rq, where); | ||
320 | |||
321 | if (blk_queue_plugged(q)) { | ||
322 | int nrq = q->rq.count[READ] + q->rq.count[WRITE] | ||
323 | - q->in_flight; | ||
324 | |||
325 | if (nrq == q->unplug_thresh) | ||
326 | __generic_unplug_device(q); | ||
327 | } | ||
328 | } else | ||
329 | /* | ||
330 | * if drain is set, store the request "locally". when the drain | ||
331 | * is finished, the requests will be handed ordered to the io | ||
332 | * scheduler | ||
333 | */ | ||
334 | list_add_tail(&rq->queuelist, &q->drain_list); | ||
335 | } | ||
336 | |||
337 | void elv_add_request(request_queue_t *q, struct request *rq, int where, | ||
338 | int plug) | ||
339 | { | ||
340 | unsigned long flags; | ||
341 | |||
342 | spin_lock_irqsave(q->queue_lock, flags); | ||
343 | __elv_add_request(q, rq, where, plug); | ||
344 | spin_unlock_irqrestore(q->queue_lock, flags); | ||
345 | } | ||
346 | |||
347 | static inline struct request *__elv_next_request(request_queue_t *q) | ||
348 | { | ||
349 | struct request *rq = q->elevator->ops->elevator_next_req_fn(q); | ||
350 | |||
351 | /* | ||
352 | * if this is a barrier write and the device has to issue a | ||
353 | * flush sequence to support it, check how far we are | ||
354 | */ | ||
355 | if (rq && blk_fs_request(rq) && blk_barrier_rq(rq)) { | ||
356 | BUG_ON(q->ordered == QUEUE_ORDERED_NONE); | ||
357 | |||
358 | if (q->ordered == QUEUE_ORDERED_FLUSH && | ||
359 | !blk_barrier_preflush(rq)) | ||
360 | rq = blk_start_pre_flush(q, rq); | ||
361 | } | ||
362 | |||
363 | return rq; | ||
364 | } | ||
365 | |||
366 | struct request *elv_next_request(request_queue_t *q) | ||
367 | { | ||
368 | struct request *rq; | ||
369 | int ret; | ||
370 | |||
371 | while ((rq = __elv_next_request(q)) != NULL) { | ||
372 | /* | ||
373 | * just mark as started even if we don't start it, a request | ||
374 | * that has been delayed should not be passed by new incoming | ||
375 | * requests | ||
376 | */ | ||
377 | rq->flags |= REQ_STARTED; | ||
378 | |||
379 | if (rq == q->last_merge) | ||
380 | q->last_merge = NULL; | ||
381 | |||
382 | if ((rq->flags & REQ_DONTPREP) || !q->prep_rq_fn) | ||
383 | break; | ||
384 | |||
385 | ret = q->prep_rq_fn(q, rq); | ||
386 | if (ret == BLKPREP_OK) { | ||
387 | break; | ||
388 | } else if (ret == BLKPREP_DEFER) { | ||
389 | rq = NULL; | ||
390 | break; | ||
391 | } else if (ret == BLKPREP_KILL) { | ||
392 | int nr_bytes = rq->hard_nr_sectors << 9; | ||
393 | |||
394 | if (!nr_bytes) | ||
395 | nr_bytes = rq->data_len; | ||
396 | |||
397 | blkdev_dequeue_request(rq); | ||
398 | rq->flags |= REQ_QUIET; | ||
399 | end_that_request_chunk(rq, 0, nr_bytes); | ||
400 | end_that_request_last(rq); | ||
401 | } else { | ||
402 | printk(KERN_ERR "%s: bad return=%d\n", __FUNCTION__, | ||
403 | ret); | ||
404 | break; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | return rq; | ||
409 | } | ||
410 | |||
411 | void elv_remove_request(request_queue_t *q, struct request *rq) | ||
412 | { | ||
413 | elevator_t *e = q->elevator; | ||
414 | |||
415 | /* | ||
416 | * the time frame between a request being removed from the lists | ||
417 | * and to it is freed is accounted as io that is in progress at | ||
418 | * the driver side. note that we only account requests that the | ||
419 | * driver has seen (REQ_STARTED set), to avoid false accounting | ||
420 | * for request-request merges | ||
421 | */ | ||
422 | if (blk_account_rq(rq)) | ||
423 | q->in_flight++; | ||
424 | |||
425 | /* | ||
426 | * the main clearing point for q->last_merge is on retrieval of | ||
427 | * request by driver (it calls elv_next_request()), but it _can_ | ||
428 | * also happen here if a request is added to the queue but later | ||
429 | * deleted without ever being given to driver (merged with another | ||
430 | * request). | ||
431 | */ | ||
432 | if (rq == q->last_merge) | ||
433 | q->last_merge = NULL; | ||
434 | |||
435 | if (e->ops->elevator_remove_req_fn) | ||
436 | e->ops->elevator_remove_req_fn(q, rq); | ||
437 | } | ||
438 | |||
439 | int elv_queue_empty(request_queue_t *q) | ||
440 | { | ||
441 | elevator_t *e = q->elevator; | ||
442 | |||
443 | if (e->ops->elevator_queue_empty_fn) | ||
444 | return e->ops->elevator_queue_empty_fn(q); | ||
445 | |||
446 | return list_empty(&q->queue_head); | ||
447 | } | ||
448 | |||
449 | struct request *elv_latter_request(request_queue_t *q, struct request *rq) | ||
450 | { | ||
451 | struct list_head *next; | ||
452 | |||
453 | elevator_t *e = q->elevator; | ||
454 | |||
455 | if (e->ops->elevator_latter_req_fn) | ||
456 | return e->ops->elevator_latter_req_fn(q, rq); | ||
457 | |||
458 | next = rq->queuelist.next; | ||
459 | if (next != &q->queue_head && next != &rq->queuelist) | ||
460 | return list_entry_rq(next); | ||
461 | |||
462 | return NULL; | ||
463 | } | ||
464 | |||
465 | struct request *elv_former_request(request_queue_t *q, struct request *rq) | ||
466 | { | ||
467 | struct list_head *prev; | ||
468 | |||
469 | elevator_t *e = q->elevator; | ||
470 | |||
471 | if (e->ops->elevator_former_req_fn) | ||
472 | return e->ops->elevator_former_req_fn(q, rq); | ||
473 | |||
474 | prev = rq->queuelist.prev; | ||
475 | if (prev != &q->queue_head && prev != &rq->queuelist) | ||
476 | return list_entry_rq(prev); | ||
477 | |||
478 | return NULL; | ||
479 | } | ||
480 | |||
481 | int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask) | ||
482 | { | ||
483 | elevator_t *e = q->elevator; | ||
484 | |||
485 | if (e->ops->elevator_set_req_fn) | ||
486 | return e->ops->elevator_set_req_fn(q, rq, gfp_mask); | ||
487 | |||
488 | rq->elevator_private = NULL; | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | void elv_put_request(request_queue_t *q, struct request *rq) | ||
493 | { | ||
494 | elevator_t *e = q->elevator; | ||
495 | |||
496 | if (e->ops->elevator_put_req_fn) | ||
497 | e->ops->elevator_put_req_fn(q, rq); | ||
498 | } | ||
499 | |||
500 | int elv_may_queue(request_queue_t *q, int rw) | ||
501 | { | ||
502 | elevator_t *e = q->elevator; | ||
503 | |||
504 | if (e->ops->elevator_may_queue_fn) | ||
505 | return e->ops->elevator_may_queue_fn(q, rw); | ||
506 | |||
507 | return ELV_MQUEUE_MAY; | ||
508 | } | ||
509 | |||
510 | void elv_completed_request(request_queue_t *q, struct request *rq) | ||
511 | { | ||
512 | elevator_t *e = q->elevator; | ||
513 | |||
514 | /* | ||
515 | * request is released from the driver, io must be done | ||
516 | */ | ||
517 | if (blk_account_rq(rq)) | ||
518 | q->in_flight--; | ||
519 | |||
520 | if (e->ops->elevator_completed_req_fn) | ||
521 | e->ops->elevator_completed_req_fn(q, rq); | ||
522 | } | ||
523 | |||
524 | int elv_register_queue(struct request_queue *q) | ||
525 | { | ||
526 | elevator_t *e = q->elevator; | ||
527 | |||
528 | e->kobj.parent = kobject_get(&q->kobj); | ||
529 | if (!e->kobj.parent) | ||
530 | return -EBUSY; | ||
531 | |||
532 | snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); | ||
533 | e->kobj.ktype = e->elevator_type->elevator_ktype; | ||
534 | |||
535 | return kobject_register(&e->kobj); | ||
536 | } | ||
537 | |||
538 | void elv_unregister_queue(struct request_queue *q) | ||
539 | { | ||
540 | if (q) { | ||
541 | elevator_t *e = q->elevator; | ||
542 | kobject_unregister(&e->kobj); | ||
543 | kobject_put(&q->kobj); | ||
544 | } | ||
545 | } | ||
546 | |||
547 | int elv_register(struct elevator_type *e) | ||
548 | { | ||
549 | if (elevator_find(e->elevator_name)) | ||
550 | BUG(); | ||
551 | |||
552 | spin_lock_irq(&elv_list_lock); | ||
553 | list_add_tail(&e->list, &elv_list); | ||
554 | spin_unlock_irq(&elv_list_lock); | ||
555 | |||
556 | printk(KERN_INFO "io scheduler %s registered", e->elevator_name); | ||
557 | if (!strcmp(e->elevator_name, chosen_elevator)) | ||
558 | printk(" (default)"); | ||
559 | printk("\n"); | ||
560 | return 0; | ||
561 | } | ||
562 | EXPORT_SYMBOL_GPL(elv_register); | ||
563 | |||
564 | void elv_unregister(struct elevator_type *e) | ||
565 | { | ||
566 | spin_lock_irq(&elv_list_lock); | ||
567 | list_del_init(&e->list); | ||
568 | spin_unlock_irq(&elv_list_lock); | ||
569 | } | ||
570 | EXPORT_SYMBOL_GPL(elv_unregister); | ||
571 | |||
572 | /* | ||
573 | * switch to new_e io scheduler. be careful not to introduce deadlocks - | ||
574 | * we don't free the old io scheduler, before we have allocated what we | ||
575 | * need for the new one. this way we have a chance of going back to the old | ||
576 | * one, if the new one fails init for some reason. we also do an intermediate | ||
577 | * switch to noop to ensure safety with stack-allocated requests, since they | ||
578 | * don't originate from the block layer allocator. noop is safe here, because | ||
579 | * it never needs to touch the elevator itself for completion events. DRAIN | ||
580 | * flags will make sure we don't touch it for additions either. | ||
581 | */ | ||
582 | static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) | ||
583 | { | ||
584 | elevator_t *e = kmalloc(sizeof(elevator_t), GFP_KERNEL); | ||
585 | struct elevator_type *noop_elevator = NULL; | ||
586 | elevator_t *old_elevator; | ||
587 | |||
588 | if (!e) | ||
589 | goto error; | ||
590 | |||
591 | /* | ||
592 | * first step, drain requests from the block freelist | ||
593 | */ | ||
594 | blk_wait_queue_drained(q, 0); | ||
595 | |||
596 | /* | ||
597 | * unregister old elevator data | ||
598 | */ | ||
599 | elv_unregister_queue(q); | ||
600 | old_elevator = q->elevator; | ||
601 | |||
602 | /* | ||
603 | * next step, switch to noop since it uses no private rq structures | ||
604 | * and doesn't allocate any memory for anything. then wait for any | ||
605 | * non-fs requests in-flight | ||
606 | */ | ||
607 | noop_elevator = elevator_get("noop"); | ||
608 | spin_lock_irq(q->queue_lock); | ||
609 | elevator_attach(q, noop_elevator, e); | ||
610 | spin_unlock_irq(q->queue_lock); | ||
611 | |||
612 | blk_wait_queue_drained(q, 1); | ||
613 | |||
614 | /* | ||
615 | * attach and start new elevator | ||
616 | */ | ||
617 | if (elevator_attach(q, new_e, e)) | ||
618 | goto fail; | ||
619 | |||
620 | if (elv_register_queue(q)) | ||
621 | goto fail_register; | ||
622 | |||
623 | /* | ||
624 | * finally exit old elevator and start queue again | ||
625 | */ | ||
626 | elevator_exit(old_elevator); | ||
627 | blk_finish_queue_drain(q); | ||
628 | elevator_put(noop_elevator); | ||
629 | return; | ||
630 | |||
631 | fail_register: | ||
632 | /* | ||
633 | * switch failed, exit the new io scheduler and reattach the old | ||
634 | * one again (along with re-adding the sysfs dir) | ||
635 | */ | ||
636 | elevator_exit(e); | ||
637 | fail: | ||
638 | q->elevator = old_elevator; | ||
639 | elv_register_queue(q); | ||
640 | blk_finish_queue_drain(q); | ||
641 | error: | ||
642 | if (noop_elevator) | ||
643 | elevator_put(noop_elevator); | ||
644 | elevator_put(new_e); | ||
645 | printk(KERN_ERR "elevator: switch to %s failed\n",new_e->elevator_name); | ||
646 | } | ||
647 | |||
648 | ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count) | ||
649 | { | ||
650 | char elevator_name[ELV_NAME_MAX]; | ||
651 | struct elevator_type *e; | ||
652 | |||
653 | memset(elevator_name, 0, sizeof(elevator_name)); | ||
654 | strncpy(elevator_name, name, sizeof(elevator_name)); | ||
655 | |||
656 | if (elevator_name[strlen(elevator_name) - 1] == '\n') | ||
657 | elevator_name[strlen(elevator_name) - 1] = '\0'; | ||
658 | |||
659 | e = elevator_get(elevator_name); | ||
660 | if (!e) { | ||
661 | printk(KERN_ERR "elevator: type %s not found\n", elevator_name); | ||
662 | return -EINVAL; | ||
663 | } | ||
664 | |||
665 | if (!strcmp(elevator_name, q->elevator->elevator_type->elevator_name)) | ||
666 | return count; | ||
667 | |||
668 | elevator_switch(q, e); | ||
669 | return count; | ||
670 | } | ||
671 | |||
672 | ssize_t elv_iosched_show(request_queue_t *q, char *name) | ||
673 | { | ||
674 | elevator_t *e = q->elevator; | ||
675 | struct elevator_type *elv = e->elevator_type; | ||
676 | struct list_head *entry; | ||
677 | int len = 0; | ||
678 | |||
679 | spin_lock_irq(q->queue_lock); | ||
680 | list_for_each(entry, &elv_list) { | ||
681 | struct elevator_type *__e; | ||
682 | |||
683 | __e = list_entry(entry, struct elevator_type, list); | ||
684 | if (!strcmp(elv->elevator_name, __e->elevator_name)) | ||
685 | len += sprintf(name+len, "[%s] ", elv->elevator_name); | ||
686 | else | ||
687 | len += sprintf(name+len, "%s ", __e->elevator_name); | ||
688 | } | ||
689 | spin_unlock_irq(q->queue_lock); | ||
690 | |||
691 | len += sprintf(len+name, "\n"); | ||
692 | return len; | ||
693 | } | ||
694 | |||
695 | module_init(elevator_global_init); | ||
696 | |||
697 | EXPORT_SYMBOL(elv_add_request); | ||
698 | EXPORT_SYMBOL(__elv_add_request); | ||
699 | EXPORT_SYMBOL(elv_requeue_request); | ||
700 | EXPORT_SYMBOL(elv_next_request); | ||
701 | EXPORT_SYMBOL(elv_remove_request); | ||
702 | EXPORT_SYMBOL(elv_queue_empty); | ||
703 | EXPORT_SYMBOL(elv_completed_request); | ||
704 | EXPORT_SYMBOL(elevator_exit); | ||
705 | EXPORT_SYMBOL(elevator_init); | ||