diff options
author | Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> | 2014-08-26 07:56:36 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2014-08-26 12:17:30 -0400 |
commit | e15693ef18e13e3e6bffe891fe140f18b8ff6d07 (patch) | |
tree | 5e11af3b57e34cb9d5f85854c3213a37b775dd61 /block/cfq-iosched.c | |
parent | d19d744685f47f1bb3d39b3ec51eb50afe5ff47d (diff) |
cfq-iosched: Fix wrong children_weight calculation
cfq_group_service_tree_add() is applying new_weight at the beginning of
the function via cfq_update_group_weight().
This actually allows weight to change between adding it to and subtracting
it from children_weight, and triggers WARN_ON_ONCE() in
cfq_group_service_tree_del(), or even causes oops by divide error during
vfr calculation in cfq_group_service_tree_add().
The detailed scenario is as follows:
1. Create blkio cgroups X and Y as a child of X.
Set X's weight to 500 and perform some I/O to apply new_weight.
This X's I/O completes before starting Y's I/O.
2. Y starts I/O and cfq_group_service_tree_add() is called with Y.
3. cfq_group_service_tree_add() walks up the tree during children_weight
calculation and adds parent X's weight (500) to children_weight of root.
children_weight becomes 500.
4. Set X's weight to 1000.
5. X starts I/O and cfq_group_service_tree_add() is called with X.
6. cfq_group_service_tree_add() applies its new_weight (1000).
7. I/O of Y completes and cfq_group_service_tree_del() is called with Y.
8. I/O of X completes and cfq_group_service_tree_del() is called with X.
9. cfq_group_service_tree_del() subtracts X's weight (1000) from
children_weight of root. children_weight becomes -500.
This triggers WARN_ON_ONCE().
10. Set X's weight to 500.
11. X starts I/O and cfq_group_service_tree_add() is called with X.
12. cfq_group_service_tree_add() applies its new_weight (500) and adds it
to children_weight of root. children_weight becomes 0. Calcularion of
vfr triggers oops by divide error.
weight should be updated right before adding it to children_weight.
Reported-by: Ruki Sekiya <sekiya.ruki@lab.ntt.co.jp>
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'block/cfq-iosched.c')
-rw-r--r-- | block/cfq-iosched.c | 11 |
1 files changed, 8 insertions, 3 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index cadc37841744..d7494637c5db 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c | |||
@@ -1275,12 +1275,16 @@ __cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) | |||
1275 | static void | 1275 | static void |
1276 | cfq_update_group_weight(struct cfq_group *cfqg) | 1276 | cfq_update_group_weight(struct cfq_group *cfqg) |
1277 | { | 1277 | { |
1278 | BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); | ||
1279 | |||
1280 | if (cfqg->new_weight) { | 1278 | if (cfqg->new_weight) { |
1281 | cfqg->weight = cfqg->new_weight; | 1279 | cfqg->weight = cfqg->new_weight; |
1282 | cfqg->new_weight = 0; | 1280 | cfqg->new_weight = 0; |
1283 | } | 1281 | } |
1282 | } | ||
1283 | |||
1284 | static void | ||
1285 | cfq_update_group_leaf_weight(struct cfq_group *cfqg) | ||
1286 | { | ||
1287 | BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); | ||
1284 | 1288 | ||
1285 | if (cfqg->new_leaf_weight) { | 1289 | if (cfqg->new_leaf_weight) { |
1286 | cfqg->leaf_weight = cfqg->new_leaf_weight; | 1290 | cfqg->leaf_weight = cfqg->new_leaf_weight; |
@@ -1299,7 +1303,7 @@ cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) | |||
1299 | /* add to the service tree */ | 1303 | /* add to the service tree */ |
1300 | BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); | 1304 | BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); |
1301 | 1305 | ||
1302 | cfq_update_group_weight(cfqg); | 1306 | cfq_update_group_leaf_weight(cfqg); |
1303 | __cfq_group_service_tree_add(st, cfqg); | 1307 | __cfq_group_service_tree_add(st, cfqg); |
1304 | 1308 | ||
1305 | /* | 1309 | /* |
@@ -1323,6 +1327,7 @@ cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) | |||
1323 | */ | 1327 | */ |
1324 | while ((parent = cfqg_parent(pos))) { | 1328 | while ((parent = cfqg_parent(pos))) { |
1325 | if (propagate) { | 1329 | if (propagate) { |
1330 | cfq_update_group_weight(pos); | ||
1326 | propagate = !parent->nr_active++; | 1331 | propagate = !parent->nr_active++; |
1327 | parent->children_weight += pos->weight; | 1332 | parent->children_weight += pos->weight; |
1328 | } | 1333 | } |