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>
	
	
This commit is contained in:
		
					parent
					
						
							
								d19d744685
							
						
					
				
			
			
				commit
				
					
						e15693ef18
					
				
			
		
					 1 changed files with 8 additions and 3 deletions
				
			
		|  | @ -1275,12 +1275,16 @@ __cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) | ||||||
| static void | static void | ||||||
| cfq_update_group_weight(struct cfq_group *cfqg) | cfq_update_group_weight(struct cfq_group *cfqg) | ||||||
| { | { | ||||||
| 	BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); |  | ||||||
| 
 |  | ||||||
| 	if (cfqg->new_weight) { | 	if (cfqg->new_weight) { | ||||||
| 		cfqg->weight = cfqg->new_weight; | 		cfqg->weight = cfqg->new_weight; | ||||||
| 		cfqg->new_weight = 0; | 		cfqg->new_weight = 0; | ||||||
| 	} | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | cfq_update_group_leaf_weight(struct cfq_group *cfqg) | ||||||
|  | { | ||||||
|  | 	BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); | ||||||
| 
 | 
 | ||||||
| 	if (cfqg->new_leaf_weight) { | 	if (cfqg->new_leaf_weight) { | ||||||
| 		cfqg->leaf_weight = cfqg->new_leaf_weight; | 		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) | ||||||
| 	/* add to the service tree */ | 	/* add to the service tree */ | ||||||
| 	BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); | 	BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); | ||||||
| 
 | 
 | ||||||
| 	cfq_update_group_weight(cfqg); | 	cfq_update_group_leaf_weight(cfqg); | ||||||
| 	__cfq_group_service_tree_add(st, cfqg); | 	__cfq_group_service_tree_add(st, cfqg); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
|  | @ -1323,6 +1327,7 @@ cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) | ||||||
| 	 */ | 	 */ | ||||||
| 	while ((parent = cfqg_parent(pos))) { | 	while ((parent = cfqg_parent(pos))) { | ||||||
| 		if (propagate) { | 		if (propagate) { | ||||||
|  | 			cfq_update_group_weight(pos); | ||||||
| 			propagate = !parent->nr_active++; | 			propagate = !parent->nr_active++; | ||||||
| 			parent->children_weight += pos->weight; | 			parent->children_weight += pos->weight; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Toshiaki Makita
				Toshiaki Makita