aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
context:
space:
mode:
authorFabio Checconi <fabio@gandalf.sssup.i>2008-04-10 02:28:01 -0400
committerJens Axboe <jens.axboe@oracle.com>2008-04-10 02:28:01 -0400
commit4faa3c8150c1d4f7b38d962eda7851083e218e3f (patch)
treea9110e7d49bc5f76d51d437a684844b47c31be3a /block
parent8191ecd1d14c6914c660dfa007154860a7908857 (diff)
cfq-iosched: do not leak ioc_data across iosched switches
When switching scheduler from cfq, cfq_exit_queue() does not clear ioc->ioc_data, leaving a dangling pointer that can deceive the following lookups when the iosched is switched back to cfq. The pattern that can trigger that is the following: - elevator switch from cfq to something else; - module unloading, with elv_unregister() that calls cfq_free_io_context() on ioc freeing the cic (via the .trim op); - module gets reloaded and the elevator switches back to cfq; - reallocation of a cic at the same address as before (with a valid key). To fix it just assign NULL to ioc_data in __cfq_exit_single_io_context(), that is called from the regular exit path and from the elevator switching code. The only path that frees a cic and is not covered is the error handling one, but cic's freed in this way are never cached in ioc_data. Signed-off-by: Fabio Checconi <fabio@gandalf.sssup.it> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'block')
-rw-r--r--block/cfq-iosched.c9
1 files changed, 6 insertions, 3 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index f26da2bfcc15..f4e1006c253d 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1214,6 +1214,8 @@ static void cfq_exit_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq)
1214static void __cfq_exit_single_io_context(struct cfq_data *cfqd, 1214static void __cfq_exit_single_io_context(struct cfq_data *cfqd,
1215 struct cfq_io_context *cic) 1215 struct cfq_io_context *cic)
1216{ 1216{
1217 struct io_context *ioc = cic->ioc;
1218
1217 list_del_init(&cic->queue_list); 1219 list_del_init(&cic->queue_list);
1218 1220
1219 /* 1221 /*
@@ -1223,6 +1225,9 @@ static void __cfq_exit_single_io_context(struct cfq_data *cfqd,
1223 cic->dead_key = (unsigned long) cic->key; 1225 cic->dead_key = (unsigned long) cic->key;
1224 cic->key = NULL; 1226 cic->key = NULL;
1225 1227
1228 if (ioc->ioc_data == cic)
1229 rcu_assign_pointer(ioc->ioc_data, NULL);
1230
1226 if (cic->cfqq[ASYNC]) { 1231 if (cic->cfqq[ASYNC]) {
1227 cfq_exit_cfqq(cfqd, cic->cfqq[ASYNC]); 1232 cfq_exit_cfqq(cfqd, cic->cfqq[ASYNC]);
1228 cic->cfqq[ASYNC] = NULL; 1233 cic->cfqq[ASYNC] = NULL;
@@ -1255,7 +1260,6 @@ static void cfq_exit_single_io_context(struct io_context *ioc,
1255 */ 1260 */
1256static void cfq_exit_io_context(struct io_context *ioc) 1261static void cfq_exit_io_context(struct io_context *ioc)
1257{ 1262{
1258 rcu_assign_pointer(ioc->ioc_data, NULL);
1259 call_for_each_cic(ioc, cfq_exit_single_io_context); 1263 call_for_each_cic(ioc, cfq_exit_single_io_context);
1260} 1264}
1261 1265
@@ -1478,8 +1482,7 @@ cfq_drop_dead_cic(struct cfq_data *cfqd, struct io_context *ioc,
1478 1482
1479 spin_lock_irqsave(&ioc->lock, flags); 1483 spin_lock_irqsave(&ioc->lock, flags);
1480 1484
1481 if (ioc->ioc_data == cic) 1485 BUG_ON(ioc->ioc_data == cic);
1482 rcu_assign_pointer(ioc->ioc_data, NULL);
1483 1486
1484 radix_tree_delete(&ioc->radix_root, (unsigned long) cfqd); 1487 radix_tree_delete(&ioc->radix_root, (unsigned long) cfqd);
1485 hlist_del_rcu(&cic->cic_list); 1488 hlist_del_rcu(&cic->cic_list);