summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBreno Leitao <leitao@debian.org>2026-08-10 04:29:25 -0700
committerPaul E. McKenney <paulmck@kernel.org>2026-08-23 17:01:46 -0700
commit38308bbd9f164873eb9bbd431ebafbc7fd783e9b (patch)
tree301ad2f90ae0060b77ed4da88175d9ff79d36696
parent07230d21630f48e74831efda665a3200c645e9c2 (diff)
downloadlinux-next-38308bbd9f164873eb9bbd431ebafbc7fd783e9b.tar.gz
linux-next-38308bbd9f164873eb9bbd431ebafbc7fd783e9b.zip
locking/csd-lock: Report how long a stuck CSD lock took to recover
The CSD lock debug output is a useful way to catch IPI stalls, but when the lock finally recovers it only says that it did: smp: csd: CSD lock (#1) got unstuck on CPU#32, CPU#123 released the lock. How long the target took to answer is left out, even though csd_lock_wait_toolong() already has the timestamp the wait started from. At Meta's fleet, that line fired 211K times in the last 24 hours, so plenty of stalls get reported with no indication of how long they lasted. Print how long the lock was stuck, and, when an IPI was re-sent, how long after that re-send the target released, as: smp: csd: CSD lock (#1) got unstuck on CPU#00, CPU#01 released the lock after 8000854660 ns, 3000825778 ns after the last IPI re-send. Signed-off-by: Breno Leitao <leitao@debian.org> Reviewed-by: Dmitry Ilvokhin <d@ilvokhin.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
-rw-r--r--kernel/smp.c23
1 files changed, 21 insertions, 2 deletions
diff --git a/kernel/smp.c b/kernel/smp.c
index 1bd8a24349e0..9092315c144a 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -227,11 +227,29 @@ bool csd_lock_is_stuck(void)
struct csd_wait_state {
u64 ts_start; /* When the wait began. */
u64 ts_report; /* When the last complaint was printed. */
+ u64 ts_resend; /* When the last IPI was re-sent, 0 if never. */
int bug_id;
unsigned long nmessages;
};
/*
+ * Report a CSD lock that came back, @ts_unstuck being when the release was
+ * noticed. Only mention the re-send delta if an IPI was actually re-sent.
+ */
+static void csd_lock_print_unstuck(struct csd_wait_state *state, int cpu, u64 ts_unstuck)
+{
+ if (state->ts_resend)
+ pr_alert("csd: CSD lock (#%d) got unstuck on CPU#%02d, CPU#%02d released the lock after %lld ns, %lld ns after the last IPI re-send.\n",
+ state->bug_id, raw_smp_processor_id(), cpu,
+ (s64)(ts_unstuck - state->ts_start),
+ (s64)(ts_unstuck - state->ts_resend));
+ else
+ pr_alert("csd: CSD lock (#%d) got unstuck on CPU#%02d, CPU#%02d released the lock after %lld ns.\n",
+ state->bug_id, raw_smp_processor_id(), cpu,
+ (s64)(ts_unstuck - state->ts_start));
+}
+
+/*
* Complain if too much time spent waiting. Note that only
* the CSD_TYPE_SYNC/ASYNC types provide the destination CPU,
* so waiting on other types gets much less information.
@@ -249,9 +267,9 @@ static bool csd_lock_wait_toolong(call_single_data_t *csd, struct csd_wait_state
if (!(flags & CSD_FLAG_LOCK)) {
if (!unlikely(state->bug_id))
return true;
+ ts_now = ktime_get_mono_fast_ns();
cpu = csd_lock_wait_getcpu(csd);
- pr_alert("csd: CSD lock (#%d) got unstuck on CPU#%02d, CPU#%02d released the lock.\n",
- state->bug_id, raw_smp_processor_id(), cpu);
+ csd_lock_print_unstuck(state, cpu, ts_now);
atomic_dec(&n_csd_lock_stuck);
return true;
}
@@ -309,6 +327,7 @@ static bool csd_lock_wait_toolong(call_single_data_t *csd, struct csd_wait_state
if (!cpu_cur_csd) {
pr_alert("csd: Re-sending CSD lock (#%d) IPI from CPU#%02d to CPU#%02d\n", state->bug_id, raw_smp_processor_id(), cpu);
arch_send_call_function_single_ipi(cpu);
+ state->ts_resend = ktime_get_mono_fast_ns();
}
}
if (firsttime)