Skip to content

Commit b01db23

Browse files
Benjamin SegallPeter Zijlstra
authored andcommitted
sched/eevdf: Fix pick_eevdf()
The old pick_eevdf() could fail to find the actual earliest eligible deadline when it descended to the right looking for min_deadline, but it turned out that that min_deadline wasn't actually eligible. In that case we need to go back and search through any left branches we skipped looking for the actual best _eligible_ min_deadline. This is more expensive, but still O(log n), and at worst should only involve descending two branches of the rbtree. I've run this through a userspace stress test (thank you tools/lib/rbtree.c), so hopefully this implementation doesn't miss any corner cases. Fixes: 147f3ef ("sched/fair: Implement an EEVDF-like scheduling policy") Signed-off-by: Ben Segall <bsegall@google.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lkml.kernel.org/r/xm261qego72d.fsf_-_@google.com
1 parent 8dafa9d commit b01db23

File tree

1 file changed

+58
-14
lines changed

1 file changed

+58
-14
lines changed

kernel/sched/fair.c

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -872,14 +872,16 @@ struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq)
872872
*
873873
* Which allows an EDF like search on (sub)trees.
874874
*/
875-
static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq)
875+
static struct sched_entity *__pick_eevdf(struct cfs_rq *cfs_rq)
876876
{
877877
struct rb_node *node = cfs_rq->tasks_timeline.rb_root.rb_node;
878878
struct sched_entity *curr = cfs_rq->curr;
879879
struct sched_entity *best = NULL;
880+
struct sched_entity *best_left = NULL;
880881

881882
if (curr && (!curr->on_rq || !entity_eligible(cfs_rq, curr)))
882883
curr = NULL;
884+
best = curr;
883885

884886
/*
885887
* Once selected, run a task until it either becomes non-eligible or
@@ -900,41 +902,83 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq)
900902
}
901903

902904
/*
903-
* If this entity has an earlier deadline than the previous
904-
* best, take this one. If it also has the earliest deadline
905-
* of its subtree, we're done.
905+
* Now we heap search eligible trees for the best (min_)deadline
906906
*/
907-
if (!best || deadline_gt(deadline, best, se)) {
907+
if (!best || deadline_gt(deadline, best, se))
908908
best = se;
909-
if (best->deadline == best->min_deadline)
910-
break;
911-
}
912909

913910
/*
914-
* If the earlest deadline in this subtree is in the fully
915-
* eligible left half of our space, go there.
911+
* Every se in a left branch is eligible, keep track of the
912+
* branch with the best min_deadline
916913
*/
914+
if (node->rb_left) {
915+
struct sched_entity *left = __node_2_se(node->rb_left);
916+
917+
if (!best_left || deadline_gt(min_deadline, best_left, left))
918+
best_left = left;
919+
920+
/*
921+
* min_deadline is in the left branch. rb_left and all
922+
* descendants are eligible, so immediately switch to the second
923+
* loop.
924+
*/
925+
if (left->min_deadline == se->min_deadline)
926+
break;
927+
}
928+
929+
/* min_deadline is at this node, no need to look right */
930+
if (se->deadline == se->min_deadline)
931+
break;
932+
933+
/* else min_deadline is in the right branch. */
934+
node = node->rb_right;
935+
}
936+
937+
/*
938+
* We ran into an eligible node which is itself the best.
939+
* (Or nr_running == 0 and both are NULL)
940+
*/
941+
if (!best_left || (s64)(best_left->min_deadline - best->deadline) > 0)
942+
return best;
943+
944+
/*
945+
* Now best_left and all of its children are eligible, and we are just
946+
* looking for deadline == min_deadline
947+
*/
948+
node = &best_left->run_node;
949+
while (node) {
950+
struct sched_entity *se = __node_2_se(node);
951+
952+
/* min_deadline is the current node */
953+
if (se->deadline == se->min_deadline)
954+
return se;
955+
956+
/* min_deadline is in the left branch */
917957
if (node->rb_left &&
918958
__node_2_se(node->rb_left)->min_deadline == se->min_deadline) {
919959
node = node->rb_left;
920960
continue;
921961
}
922962

963+
/* else min_deadline is in the right branch */
923964
node = node->rb_right;
924965
}
966+
return NULL;
967+
}
925968

926-
if (!best || (curr && deadline_gt(deadline, best, curr)))
927-
best = curr;
969+
static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq)
970+
{
971+
struct sched_entity *se = __pick_eevdf(cfs_rq);
928972

929-
if (unlikely(!best)) {
973+
if (!se) {
930974
struct sched_entity *left = __pick_first_entity(cfs_rq);
931975
if (left) {
932976
pr_err("EEVDF scheduling fail, picking leftmost\n");
933977
return left;
934978
}
935979
}
936980

937-
return best;
981+
return se;
938982
}
939983

940984
#ifdef CONFIG_SCHED_DEBUG

0 commit comments

Comments
 (0)