[PATCH] sched/deadline: Fix DL server divide-by-zero for inactive CPUs

Hui Su <[email protected]>
Newsgroups org.kernel.vger.stable,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Commit 4043f5498416 ("sched/deadline: Reject debugfs dl_server writes
for offline CPUs") rejects per-CPU DL server parameter updates once the
target CPU is offline. However, during CPU hot-unplug, the CPU is cleared
from cpu_active_mask before it is marked offline.

This leaves a window where cpu_online() is still true while
cpu_active() is already false. A debugfs write during this window passes
the cpu_online() check in sched_server_write_common() and reaches
dl_server_apply_params() with init=false.

dl_bw_cpus() counts the active CPUs in the root domain. For an isolated
CPU whose root-domain span contains only that CPU, it returns zero once
the CPU becomes inactive. If the server bandwidth is attached,
dl_server_apply_params() then passes this zero CPU count to __dl_sub()
and __dl_add(), both of which divide by the CPU count.

Using CPU1 with isolcpus=domain,1 and a temporary local hotplug pause
hook to stop the teardown after cpu_active_mask was cleared but before
the CPU became offline reproduced the state as:

dl_bw_cpus=0 attached=1 dl_b->bw=-1 total_bw=52428 span=1 active=0

Writing a new fair-server runtime while CPU1 was held in that state
triggered:

# echo 40000000 > /sys/kernel/debug/sched/fair_server/cpu1/runtime

Oops: divide error: 0000 [#1] SMP NOPTI
RIP: 0010:dl_server_apply_params+0x39d/0x400
Call Trace:
sched_server_write_common.isra.0+0x1d2/0x2d0
full_proxy_write+0x64/0x90
vfs_write+0xf7/0x540
ksys_write+0x6e/0xf0

Reject DL server parameter writes when the target CPU is inactive, not
only when it is offline.

Also update root-domain bandwidth in dl_server_apply_params() only while
the target CPU is active. This second check is necessary because CPU
hot-unplug can race with the debugfs path after its CPU state check and
before dl_server_apply_params() updates the bandwidth.

Keep the runqueue-local utilization update independent of cpu_active()
so that the local bandwidth state remains consistent if the CPU becomes
inactive during the parameter update.

With the fix, a write during the same hot-unplug window is rejected with
-EBUSY instead of reaching __dl_sub() or __dl_add() with a zero CPU
count.

Fixes: d741f297bcea ("sched/fair: Fair server interface")
Reported-by: Sashiko <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Cc: [email protected]
Signed-off-by: Hui Su <[email protected]>
---
 kernel/sched/deadline.c | 6 ++++--
 kernel/sched/debug.c    | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 200300043fa5..01adaba7ee3f 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1928,8 +1928,10 @@ int dl_server_apply_params(struct sched_dl_entity *dl_se, u64 runtime, u64 perio
 		__dl_add(dl_b, new_bw, cpus);
 		dl_se->dl_bw_attached = 1;
 	} else if (dl_se->dl_bw_attached) {
-		__dl_sub(dl_b, dl_se->dl_bw, cpus);
-		__dl_add(dl_b, new_bw, cpus);
+		if (cpu_active(cpu)) {
+			__dl_sub(dl_b, dl_se->dl_bw, cpus);
+			__dl_add(dl_b, new_bw, cpus);
+		}
 
 		dl_rq_change_utilization(rq, dl_se, new_bw);
 	}
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 40584b27ea0c..ba60ff48dc3a 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -416,7 +416,7 @@ static ssize_t sched_server_write_common(struct file *filp, const char __user *u
 			return  -EINVAL;
 		}
 
-		if (!cpu_online(cpu_of(rq)))
+		if (!cpu_active(cpu_of(rq)))
 			return -EBUSY;
 
 		update_rq_clock(rq);
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.