[PATCH stalld 44/52] stalld: use CLOCK_MONOTONIC for starvation detection

Wander Lairson Costa <[email protected]> Mon, 8 Jun 2026 15:31:54 -0300
Newsgroups org.kernel.vger.linux-rt-users
Message-ID <[email protected]>
Starvation detection relies on time(NULL) to track how long tasks
have been waiting. Wall-clock time is subject to NTP adjustments, so
a backward clock step can make elapsed durations appear negative and
prevent the daemon from ever boosting legitimately starving tasks.

Replace all time(NULL) calls with a get_monotonic_time() helper that
reads CLOCK_MONOTONIC via clock_gettime(). Monotonic time only moves
forward, making starvation tracking immune to external clock changes.

Signed-off-by: Wander Lairson Costa <[email protected]>
---
 src/queue_track.c |  2 +-
 src/sched_debug.c |  2 +-
 src/stalld.c      | 16 ++++++++--------
 src/stalld.h      |  8 ++++++++
 4 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/queue_track.c b/src/queue_track.c
index 6089277..2c1a11b 100644
--- a/src/queue_track.c
+++ b/src/queue_track.c
@@ -195,7 +195,7 @@ static int queue_track_parse(struct cpu_info *cpu_info, char *buffer, size_t buf
 
 		task->ctxsw = qtask.ctxswc;
 
-		task->since = time(NULL);
+		task->since = get_monotonic_time();
 
 		nr_running++;
 
diff --git a/src/sched_debug.c b/src/sched_debug.c
index 5e9a9e8..25a11ce 100644
--- a/src/sched_debug.c
+++ b/src/sched_debug.c
@@ -478,7 +478,7 @@ static int parse_task_lines(char *buffer, struct task_info *task_info, int nr_en
 			task->tgid = get_tgid(task->pid);
 			task->ctxsw = ctxsw;
 			task->prio = prio;
-			task->since = time(NULL);
+			task->since = get_monotonic_time();
 			/* increment the count of tasks processed */
 			tasks++;
 		}
diff --git a/src/stalld.c b/src/stalld.c
index a9e98ff..1762033 100644
--- a/src/stalld.c
+++ b/src/stalld.c
@@ -326,7 +326,7 @@ void print_waiting_tasks(struct cpu_info *cpu_info)
 	if (!config_verbose)
 		return;
 
-	now = time(NULL);
+	now = get_monotonic_time();
 	printf("CPU %d has %d waiting tasks\n", cpu_info->id, cpu_info->nr_waiting_tasks);
 	if (!cpu_info->nr_waiting_tasks)
 		return;
@@ -646,12 +646,12 @@ int check_starving_tasks(struct cpu_info *cpu)
 		task = &tasks[i];
 
 		/* Skip tasks that haven't been starving long enough */
-		if ((time(NULL) - task->since) < config_starving_threshold)
+		if ((get_monotonic_time() - task->since) < config_starving_threshold)
 			continue;
 
 		log_msg("%s-%d starved on CPU %d for %d seconds\n",
 			task->comm, task->pid, cpu->id,
-			(time(NULL) - task->since));
+			(get_monotonic_time() - task->since));
 
 		/*
 		 * Check if this task needs to be ignored from being boosted
@@ -659,7 +659,7 @@ int check_starving_tasks(struct cpu_info *cpu)
 		 * getting reported as being starved.
 		 */
 		if (config_ignore && !(check_task_ignore(task))) {
-			task->since = time(NULL);
+			task->since = get_monotonic_time();
 			continue;
 		}
 
@@ -670,7 +670,7 @@ int check_starving_tasks(struct cpu_info *cpu)
 		 * after logging.
 		 */
 		if (config_log_only) {
-			task->since = time(NULL);
+			task->since = get_monotonic_time();
 			continue;
 		}
 
@@ -693,11 +693,11 @@ int check_might_starve_tasks(struct cpu_info *cpu)
 	for (i = 0; i < cpu->nr_waiting_tasks; i++) {
 		task = &tasks[i];
 
-		if ((time(NULL) - task->since) >= config_starving_threshold/2) {
+		if ((get_monotonic_time() - task->since) >= config_starving_threshold/2) {
 
 			log_msg("%s-%d might starve on CPU %d (waiting for %d seconds)\n",
 				task->comm, task->pid, cpu->id,
-				(time(NULL) - task->since));
+				(get_monotonic_time() - task->since));
 
 			starving = 1;
 		}
@@ -933,7 +933,7 @@ int boost_cpu_starving_vector(struct cpu_starving_task_info *vector, int nr_cpus
 	int ret;
 	int i;
 
-	now = time(NULL);
+	now = get_monotonic_time();
 
 	/* Boost phase. */
 	for (i = 0; i < nr_cpus; i++) {
diff --git a/src/stalld.h b/src/stalld.h
index 8b6ab8b..2df6f94 100644
--- a/src/stalld.h
+++ b/src/stalld.h
@@ -13,6 +13,7 @@
 #include <sched.h>
 #include <stdatomic.h>
 #include <signal.h>
+#include <time.h>
 
 #define BUFFER_PAGES		10
 #define MAX_WAITING_PIDS	30
@@ -49,6 +50,13 @@
  */
 #define DAEMON_UMASK  0022
 
+static inline time_t get_monotonic_time(void)
+{
+	struct timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	return ts.tv_sec;
+}
+
 /*
  * Informnation about running tasks on a CPU.
  */
-- 
2.54.0