block, bfq: make reparent_leaf_entity actually work only on leaf entities

commit 576682fa52cbd95deb3773449566274f206acc58 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Paolo Valente 2020-03-21 10:45:20 +01:00 committed by Greg Kroah-Hartman
parent ad749ca022
commit 839b7cd1d8

View File

@ -798,39 +798,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,
static void bfq_reparent_active_queues(struct bfq_data *bfqd,
struct bfq_group *bfqg,
struct bfq_service_tree *st)
struct bfq_service_tree *st,
int ioprio_class)
{
struct rb_root *active = &st->active;
struct bfq_entity *entity = NULL;
struct bfq_entity *entity;
if (!RB_EMPTY_ROOT(&st->active))
entity = bfq_entity_of(rb_first(active));
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);
}
/**
@ -881,7 +895,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);