[PATCH 2/2] drivers: android: binder: add preferred CPU wake-up nudge for waiting threads
From: trieu2.huynh
Date: Tue Jul 14 2026 - 14:37:10 EST
Implement a wake-up nudge mechanism that hints the scheduler to wake up
the selected Binder thread on its preferred CPU. This maximizes cache
locality for the incoming transaction.
The preferred CPU is recorded during thread selection and stored in the
thread structure. Right before waking up the thread, this value is
safely injected into the task's 'wake_cpu' field.
Signed-off-by: trieu2.huynh <trieu2.huynh@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
---
drivers/android/binder.c | 39 +++++++++++++++++++++++++++++--
drivers/android/binder_internal.h | 4 ++--
2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 2a7be45368c1..580889b773b7 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -638,6 +638,7 @@ binder_select_thread_topology_ilocked(struct binder_proc *proc, int caller_cpu)
struct binder_thread *thread, *best = NULL;
int caller_cluster = -1;
int best_score = -1;
+ int chosen_cpu = -1;
assert_spin_locked(&proc->inner_lock);
@@ -648,9 +649,14 @@ binder_select_thread_topology_ilocked(struct binder_proc *proc, int caller_cpu)
int tcpu = READ_ONCE(thread->last_cpu);
int score;
- /* Highest priority: exactly the same CPU */
+ /* Highest priority: exactly the same CPU. No wake_cpu nudge
+ * needed here: __set_task_cpu() already keeps task->wake_cpu
+ * in sync with task_cpu(p) (== tcpu), so writing
+ * chosen_cpu = tcpu back would be a no-op.
+ */
if (tcpu == caller_cpu && caller_cpu >= 0) {
best = thread;
+ chosen_cpu = -1;
goto found_best;
}
@@ -667,12 +673,29 @@ binder_select_thread_topology_ilocked(struct binder_proc *proc, int caller_cpu)
if (score > best_score) {
best_score = score;
best = thread;
+ /* Nudge toward caller_cpu (not the thread's own stale
+ * tcpu) so the wake_cpu hint conveys new information:
+ * the transaction data was just touched on caller_cpu,
+ * and it shares an L2/L3 with tcpu within the cluster,
+ * so migrating there is cheap and cache-friendly.
+ */
+ if (score == 1 && caller_cpu >= 0) {
+ /* Only nudge if caller_cpu is idle */
+ if (available_idle_cpu(caller_cpu))
+ chosen_cpu = caller_cpu;
+ else
+ chosen_cpu = -1;
+ } else {
+ chosen_cpu = -1;
+ }
}
}
found_best:
- if (best)
+ if (best) {
list_del_init(&best->waiting_thread_node);
+ best->preferred_cpu = chosen_cpu;
+ }
return best;
}
@@ -700,6 +723,16 @@ static void binder_wakeup_thread_ilocked(struct binder_proc *proc,
assert_spin_locked(&proc->inner_lock);
if (thread) {
+#ifdef CONFIG_SMP
+ /* CTABS nudge: hint scheduler to wake thread on preferred CPU.
+ * This improves cache locality for the transaction. Only do
+ * this for sync wakeups, for oneway/async work the caller
+ * keeps running on caller_cpu.
+ */
+ if (sync && thread->preferred_cpu >= 0 && thread->task)
+ WRITE_ONCE(thread->task->wake_cpu, thread->preferred_cpu);
+#endif
+ thread->preferred_cpu = -1;
if (sync)
wake_up_interruptible_sync(&thread->wait);
else
@@ -5363,6 +5396,8 @@ static struct binder_thread *binder_get_thread_ilocked(
thread->ee.command = BR_OK;
/* CTABS: initialize last_cpu to -1 (unknown) */
WRITE_ONCE(thread->last_cpu, -1);
+ /* CTABS: initialize preferred_cpu to -1 (no nudge) */
+ WRITE_ONCE(thread->preferred_cpu, -1);
INIT_LIST_HEAD(&new_thread->waiting_thread_node);
return thread;
}
diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
index d01688cf0961..285846e6068f 100644
--- a/drivers/android/binder_internal.h
+++ b/drivers/android/binder_internal.h
@@ -506,8 +506,8 @@ struct binder_thread {
struct binder_stats stats;
atomic_t tmp_ref;
bool is_dead;
- /* CTABS: last CPU this thread ran on (smp_processor_id()), -1 if unknown */
- int last_cpu;
+ int last_cpu; /* CTABS: last CPU this thread ran on */
+ int preferred_cpu; /* CTABS: preferred CPU for wake-up nudge */
};
/**
--
2.43.0