aboutsummaryrefslogtreecommitdiffstats
path: root/block/elevator.c
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2006-06-08 02:49:06 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-08 18:14:23 -0400
commitbc1c116974a5c3f498112a6f175d3e4a8cd5bdbc (patch)
tree69ea68db91fb871cd24a0a5c5045abbe9c77bd3a /block/elevator.c
parent26e780e8ef1cc3ef581a07aafe2346bb5a07b4f9 (diff)
[PATCH] elevator switching race
There's a race between shutting down one io scheduler and firing up the next, in which a new io could enter and cause the io scheduler to be invoked with bad or NULL data. To fix this, we need to maintain the queue lock for a bit longer. Unfortunately we cannot do that, since the elevator init requires to be run without the lock held. This isn't easily fixable, without also changing the mempool API. So split the initialization into two parts, and alloc-init operation and an attach operation. Then we can preallocate the io scheduler and related structures, and run the attach inside the lock after we detach the old one. This patch has survived 30 minutes of 1 second io scheduler switching with a very busy io load. Signed-off-by: Jens Axboe <axboe@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'block/elevator.c')
-rw-r--r--block/elevator.c55
1 files changed, 34 insertions, 21 deletions
diff --git a/block/elevator.c b/block/elevator.c
index 8768a367fdde..a0afdd317cef 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -121,16 +121,16 @@ static struct elevator_type *elevator_get(const char *name)
121 return e; 121 return e;
122} 122}
123 123
124static int elevator_attach(request_queue_t *q, struct elevator_queue *eq) 124static void *elevator_init_queue(request_queue_t *q, struct elevator_queue *eq)
125{ 125{
126 int ret = 0; 126 return eq->ops->elevator_init_fn(q, eq);
127}
127 128
129static void elevator_attach(request_queue_t *q, struct elevator_queue *eq,
130 void *data)
131{
128 q->elevator = eq; 132 q->elevator = eq;
129 133 eq->elevator_data = data;
130 if (eq->ops->elevator_init_fn)
131 ret = eq->ops->elevator_init_fn(q, eq);
132
133 return ret;
134} 134}
135 135
136static char chosen_elevator[16]; 136static char chosen_elevator[16];
@@ -181,6 +181,7 @@ int elevator_init(request_queue_t *q, char *name)
181 struct elevator_type *e = NULL; 181 struct elevator_type *e = NULL;
182 struct elevator_queue *eq; 182 struct elevator_queue *eq;
183 int ret = 0; 183 int ret = 0;
184 void *data;
184 185
185 INIT_LIST_HEAD(&q->queue_head); 186 INIT_LIST_HEAD(&q->queue_head);
186 q->last_merge = NULL; 187 q->last_merge = NULL;
@@ -202,10 +203,13 @@ int elevator_init(request_queue_t *q, char *name)
202 if (!eq) 203 if (!eq)
203 return -ENOMEM; 204 return -ENOMEM;
204 205
205 ret = elevator_attach(q, eq); 206 data = elevator_init_queue(q, eq);
206 if (ret) 207 if (!data) {
207 kobject_put(&eq->kobj); 208 kobject_put(&eq->kobj);
209 return -ENOMEM;
210 }
208 211
212 elevator_attach(q, eq, data);
209 return ret; 213 return ret;
210} 214}
211 215
@@ -722,13 +726,16 @@ int elv_register_queue(struct request_queue *q)
722 return error; 726 return error;
723} 727}
724 728
729static void __elv_unregister_queue(elevator_t *e)
730{
731 kobject_uevent(&e->kobj, KOBJ_REMOVE);
732 kobject_del(&e->kobj);
733}
734
725void elv_unregister_queue(struct request_queue *q) 735void elv_unregister_queue(struct request_queue *q)
726{ 736{
727 if (q) { 737 if (q)
728 elevator_t *e = q->elevator; 738 __elv_unregister_queue(q->elevator);
729 kobject_uevent(&e->kobj, KOBJ_REMOVE);
730 kobject_del(&e->kobj);
731 }
732} 739}
733 740
734int elv_register(struct elevator_type *e) 741int elv_register(struct elevator_type *e)
@@ -780,6 +787,7 @@ EXPORT_SYMBOL_GPL(elv_unregister);
780static int elevator_switch(request_queue_t *q, struct elevator_type *new_e) 787static int elevator_switch(request_queue_t *q, struct elevator_type *new_e)
781{ 788{
782 elevator_t *old_elevator, *e; 789 elevator_t *old_elevator, *e;
790 void *data;
783 791
784 /* 792 /*
785 * Allocate new elevator 793 * Allocate new elevator
@@ -788,6 +796,12 @@ static int elevator_switch(request_queue_t *q, struct elevator_type *new_e)
788 if (!e) 796 if (!e)
789 return 0; 797 return 0;
790 798
799 data = elevator_init_queue(q, e);
800 if (!data) {
801 kobject_put(&e->kobj);
802 return 0;
803 }
804
791 /* 805 /*
792 * Turn on BYPASS and drain all requests w/ elevator private data 806 * Turn on BYPASS and drain all requests w/ elevator private data
793 */ 807 */
@@ -806,19 +820,19 @@ static int elevator_switch(request_queue_t *q, struct elevator_type *new_e)
806 elv_drain_elevator(q); 820 elv_drain_elevator(q);
807 } 821 }
808 822
809 spin_unlock_irq(q->queue_lock);
810
811 /* 823 /*
812 * unregister old elevator data 824 * Remember old elevator.
813 */ 825 */
814 elv_unregister_queue(q);
815 old_elevator = q->elevator; 826 old_elevator = q->elevator;
816 827
817 /* 828 /*
818 * attach and start new elevator 829 * attach and start new elevator
819 */ 830 */
820 if (elevator_attach(q, e)) 831 elevator_attach(q, e, data);
821 goto fail; 832
833 spin_unlock_irq(q->queue_lock);
834
835 __elv_unregister_queue(old_elevator);
822 836
823 if (elv_register_queue(q)) 837 if (elv_register_queue(q))
824 goto fail_register; 838 goto fail_register;
@@ -837,7 +851,6 @@ fail_register:
837 */ 851 */
838 elevator_exit(e); 852 elevator_exit(e);
839 e = NULL; 853 e = NULL;
840fail:
841 q->elevator = old_elevator; 854 q->elevator = old_elevator;
842 elv_register_queue(q); 855 elv_register_queue(q);
843 clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); 856 clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags);