block, bfq: make reparent_leaf_entity actually work only on leaf entities
authorPaolo Valente <paolo.valente@linaro.org>
Sat, 21 Mar 2020 09:45:20 +0000 (10:45 +0100)
committerJens Axboe <axboe@kernel.dk>
Sat, 21 Mar 2020 20:31:02 +0000 (14:31 -0600)
bfq_reparent_leaf_entity() reparents the input leaf entity (a leaf
entity represents just a bfq_queue in an entity tree). Yet, the input
entity is guaranteed to always be a leaf entity only in two-level
entity trees. In this respect, because of the error fixed by
commit 14afc5936197 ("block, bfq: fix overwrite of bfq_group pointer
in bfq_find_set_group()"), all (wrongly collapsed) entity trees happened
to actually have only two levels. After the latter commit, this does not
hold any longer.

This commit fixes this problem by modifying
bfq_reparent_leaf_entity(), so that it searches an active leaf entity
down the path that stems from the input entity. Such a leaf entity is
guaranteed to exist when bfq_reparent_leaf_entity() is invoked.

Tested-by: cki-project@redhat.com
Signed-off-by: Paolo Valente <paolo.valente@linaro.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/bfq-cgroup.c

index 72c6151..efb89db 100644 (file)
@@ -815,39 +815,53 @@ static void bfq_flush_idle_tree(struct bfq_service_tree *st)
 /**
  * bfq_reparent_leaf_entity - move leaf entity to the root_group.
  * @bfqd: the device data structure with the root group.
- * @entity: the entity to move.
+ * @entity: the entity to move, if entity is a leaf; or the parent entity
+ *         of an active leaf entity to move, if entity is not a leaf.
  */
 static void bfq_reparent_leaf_entity(struct bfq_data *bfqd,
-                                    struct bfq_entity *entity)
+                                    struct bfq_entity *entity,
+                                    int ioprio_class)
 {
-       struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
+       struct bfq_queue *bfqq;
+       struct bfq_entity *child_entity = entity;
+
+       while (child_entity->my_sched_data) { /* leaf not reached yet */
+               struct bfq_sched_data *child_sd = child_entity->my_sched_data;
+               struct bfq_service_tree *child_st = child_sd->service_tree +
+                       ioprio_class;
+               struct rb_root *child_active = &child_st->active;
 
+               child_entity = bfq_entity_of(rb_first(child_active));
+
+               if (!child_entity)
+                       child_entity = child_sd->in_service_entity;
+       }
+
+       bfqq = bfq_entity_to_bfqq(child_entity);
        bfq_bfqq_move(bfqd, bfqq, bfqd->root_group);
 }
 
 /**
- * bfq_reparent_active_entities - move to the root group all active
- *                                entities.
+ * bfq_reparent_active_queues - move to the root group all active queues.
  * @bfqd: the device data structure with the root group.
  * @bfqg: the group to move from.
- * @st: the service tree with the entities.
+ * @st: the service tree to start the search from.
  */
-static void bfq_reparent_active_entities(struct bfq_data *bfqd,
-                                        struct bfq_group *bfqg,
-                                        struct bfq_service_tree *st)
+static void bfq_reparent_active_queues(struct bfq_data *bfqd,
+                                      struct bfq_group *bfqg,
+                                      struct bfq_service_tree *st,
+                                      int ioprio_class)
 {
        struct rb_root *active = &st->active;
-       struct bfq_entity *entity = NULL;
-
-       if (!RB_EMPTY_ROOT(&st->active))
-               entity = bfq_entity_of(rb_first(active));
+       struct bfq_entity *entity;
 
-       for (; entity ; entity = bfq_entity_of(rb_first(active)))
-               bfq_reparent_leaf_entity(bfqd, entity);
+       while ((entity = bfq_entity_of(rb_first(active))))
+               bfq_reparent_leaf_entity(bfqd, entity, ioprio_class);
 
        if (bfqg->sched_data.in_service_entity)
                bfq_reparent_leaf_entity(bfqd,
-                       bfqg->sched_data.in_service_entity);
+                                        bfqg->sched_data.in_service_entity,
+                                        ioprio_class);
 }
 
 /**
@@ -898,7 +912,7 @@ static void bfq_pd_offline(struct blkg_policy_data *pd)
                 * There is no need to put the sync queues, as the
                 * scheduler has taken no reference.
                 */
-               bfq_reparent_active_entities(bfqd, bfqg, st);
+               bfq_reparent_active_queues(bfqd, bfqg, st, i);
        }
 
        __bfq_deactivate_entity(entity, false);