diff options
-rw-r--r-- | kernel/time/timer_migration.c | 24 |
1 files changed, 15 insertions, 9 deletions
diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c index d85aa2afb969..8f49b6b96dfd 100644 --- a/kernel/time/timer_migration.c +++ b/kernel/time/timer_migration.c @@ -1385,11 +1385,11 @@ u64 tmigr_cpu_deactivate(u64 nextexp) * single group active on the way to top level) * * nextevt - when CPU is offline and has to handle timer on his own * or when on the way to top in every group only a single - * child is active and but @nextevt is before next_expiry - * of top level group - * * next_expiry (top) - value of top level group, when on the way to top in - * every group only a single child is active and @nextevt - * is after this value active child. + * child is active but @nextevt is before the lowest + * next_expiry encountered while walking up to top level. + * * next_expiry - value of lowest expiry encountered while walking groups + * if only a single child is active on each and @nextevt + * is after this lowest expiry. */ u64 tmigr_quick_check(u64 nextevt) { @@ -1408,10 +1408,16 @@ u64 tmigr_quick_check(u64 nextevt) do { if (!tmigr_check_lonely(group)) { return KTIME_MAX; - } else if (!group->parent) { - u64 first_global = READ_ONCE(group->next_expiry); - - return min_t(u64, nextevt, first_global); + } else { + /* + * Since current CPU is active, events may not be sorted + * from bottom to the top because the CPU's event is ignored + * up to the top and its sibling's events not propagated upwards. + * Thus keep track of the lowest observed expiry. + */ + nextevt = min_t(u64, nextevt, READ_ONCE(group->next_expiry)); + if (!group->parent) + return nextevt; } group = group->parent; } while (group); |