summaryrefslogtreecommitdiffstats
path: root/kernel/time/timer_migration.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/time/timer_migration.c')
-rw-r--r--kernel/time/timer_migration.c24
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);