From 4db8d24081c16913487ad5982e8a414a13d5ed42 Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Tue, 9 Dec 2025 20:28:34 +0800 Subject: [PATCH 1/3] interference: Add fallback to sched_clock when TSC is unavailable hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICAOAT ----------------------------------------- Currently, if TSC is not available, IFS will be directly disabled, which prevents enabling IFS even in environments like qemu where higher overhead is tolerable. To address this, add a fallback logic to cgroup_ifs_time_counter. Even if TSC is unavailable on x86/aarch64, it can now fall back to sched_clock. Note: This change also incidentally fixes a null pointer issue in cgroup_addrm_files when TSC is unavailable. Fixes: 9c735dcd118d ("interference: Use hardware timer counter") Signed-off-by: Tengda Wu --- include/linux/cgroup.h | 25 +++++++++++++++++-------- kernel/cgroup/ifs.c | 29 +++++++++++++++-------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 794cf8953b7f..05a9cef2edac 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -920,6 +920,12 @@ static inline bool cgroup_ifs_enabled(void) return static_branch_unlikely(&cgrp_ifs_enabled); } +DECLARE_STATIC_KEY_TRUE(cgrp_ifs_tsc_available); +static inline bool cgroup_ifs_tsc_available(void) +{ + return static_branch_likely(&cgrp_ifs_tsc_available); +} + static inline struct cgroup_ifs *cgroup_ifs(struct cgroup *cgrp) { return cgroup_ino(cgrp) == 1 ? &cgroup_root_ifs : cgrp->ifs; @@ -955,19 +961,22 @@ static inline void cgroup_ifs_account_delta(struct cgroup_ifs_cpu *ifsc, static inline u64 cgroup_ifs_time_counter(void) { + if (cgroup_ifs_tsc_available()) { #if defined(__aarch64__) - u64 counter; + u64 counter; - asm volatile("mrs %0, cntvct_el0" : "=r" (counter) :: "memory"); - return counter; + asm volatile("mrs %0, cntvct_el0" : "=r" (counter) :: "memory"); + return counter; #elif defined(__x86_64__) - unsigned int lo, hi; + unsigned int lo, hi; - asm volatile("rdtsc" : "=a"(lo), "=d"(hi) :: "memory"); - return ((u64)hi << 32) | lo; -#else - return sched_clock(); + asm volatile("rdtsc" : "=a"(lo), "=d"(hi) :: "memory"); + return ((u64)hi << 32) | lo; #endif + } + + /* fallback */ + return sched_clock(); } static inline void cgroup_ifs_enter_lock(u64 *clock) diff --git a/kernel/cgroup/ifs.c b/kernel/cgroup/ifs.c index e6419899d725..4ffd3b5c9db0 100644 --- a/kernel/cgroup/ifs.c +++ b/kernel/cgroup/ifs.c @@ -49,6 +49,7 @@ struct cgroup_ifs cgroup_root_ifs = { }; DEFINE_STATIC_KEY_FALSE(cgrp_ifs_enabled); +DEFINE_STATIC_KEY_TRUE(cgrp_ifs_tsc_available); #ifdef CONFIG_CGROUP_IFS_DEFAULT_ENABLED static bool ifs_enable = true; @@ -315,7 +316,8 @@ static void tdesc_init(struct ifs_tdesc *desc, u64 freq) static void cgroup_ifs_tdesc_init(void) { tdesc_init(&ifs_tdesc[IFS_TIMER_CLK], NSEC_PER_SEC); - tdesc_init(&ifs_tdesc[IFS_TIMER_TSC], this_cpu_read(ifs_tsc_freq)); + if (cgroup_ifs_tsc_available()) + tdesc_init(&ifs_tdesc[IFS_TIMER_TSC], this_cpu_read(ifs_tsc_freq)); } static u64 tsc_cycles_to_nsec(u64 tsc_cycles) @@ -327,7 +329,7 @@ static u64 tsc_cycles_to_nsec(u64 tsc_cycles) #endif } -static int cgroup_ifs_tsc_init(void) +static void cgroup_ifs_tsc_init(void) { u64 freq = 0; int cpu; @@ -339,14 +341,13 @@ static int cgroup_ifs_tsc_init(void) freq = tsc_khz * 1000; #endif if (!freq) { - pr_warn("Disable CGROUP IFS: no constant tsc\n"); - return -1; + pr_warn("IFS: no constant tsc, use default clocksource as time source\n"); + static_branch_disable(&cgrp_ifs_tsc_available); + return; } for_each_possible_cpu(cpu) per_cpu(ifs_tsc_freq, cpu) = freq; - - return 0; } static bool should_print(int type) @@ -362,6 +363,11 @@ static bool should_print(int type) return true; } +static bool use_tsc(enum ifs_types t) +{ + return cgroup_ifs_tsc_available() && (t == IFS_SPINLOCK || t == IFS_MUTEX); +} + static int print_sum_time(struct cgroup_ifs *ifs, struct seq_file *seq) { u64 time[NR_IFS_TYPES] = { 0 }; @@ -381,7 +387,7 @@ static int print_sum_time(struct cgroup_ifs *ifs, struct seq_file *seq) for (i = 0; i < NR_IFS_TYPES; i++) { if (!should_print(i)) continue; - if (i == IFS_SPINLOCK || i == IFS_MUTEX) + if (use_tsc(i)) time[i] = tsc_cycles_to_nsec(time[i]); seq_printf(seq, "%-18s%llu\n", ifs_type_name(i), time[i]); } @@ -425,7 +431,7 @@ static int print_hist_count(struct cgroup_ifs *ifs, struct seq_file *seq) if (!should_print(i)) continue; - if (i == IFS_SPINLOCK || i == IFS_MUTEX) + if (use_tsc(i)) desc = &ifs_tdesc[IFS_TIMER_TSC]; else desc = &ifs_tdesc[IFS_TIMER_CLK]; @@ -532,9 +538,7 @@ void cgroup_ifs_init(void) if (!ifs_enable) return; - if (cgroup_ifs_tsc_init() < 0) - return; - + cgroup_ifs_tsc_init(); BUG_ON(cgroup_init_cftypes(NULL, cgroup_ifs_files)); cgroup_ifs_tdesc_init(); } @@ -580,9 +584,6 @@ static __init int cgroup_ifs_enable(void) if (!ifs_enable) return 0; - if (!this_cpu_read(ifs_tsc_freq)) - return 0; - static_branch_enable(&cgrp_ifs_enabled); return 0; } -- Gitee From e8e2abd928a95d223036e8ff6e99d6b2457271d5 Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Tue, 9 Dec 2025 20:28:35 +0800 Subject: [PATCH 2/3] interference: Fix missing interference.stat file in v1 hierarchy hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICAOAT ----------------------------------------- The 'interference.stat' file was missing from the cgroup v1 hierarchy when booting with cgroup v1 enabled. This occurred because both cgroup_ifs_enable and cgroup_v1_ifs_init were registered using late_initcall_sync. The cgroup_v1_ifs_init function depends on the 'cgrp_ifs_enabled' flag being set by cgroup_ifs_enable. Without an explicit priority distinction between the two late_initcall_sync registrations, cgroup_v1_ifs_init executed before cgroup_ifs_enable during boot, resulting in 'interference.stat' not being added to the v1 directories. Fix this by changing the registration of cgroup_ifs_enable to device_initcall. This ensures cgroup_ifs_enable (Level 6) runs strictly before cgroup_v1_ifs_init (Level 7 / late_initcall_sync), correctly setting 'cgrp_ifs_enabled' before the v1 interfaces are initialized. Fixes: c27ddc47dc1b ("ifs: Defer cgroup ifs enable") Signed-off-by: Tengda Wu --- kernel/cgroup/ifs.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/ifs.c b/kernel/cgroup/ifs.c index 4ffd3b5c9db0..9d9405e94b6a 100644 --- a/kernel/cgroup/ifs.c +++ b/kernel/cgroup/ifs.c @@ -587,4 +587,14 @@ static __init int cgroup_ifs_enable(void) static_branch_enable(&cgrp_ifs_enabled); return 0; } -late_initcall_sync(cgroup_ifs_enable); + +/* + * Execution Timing Constraints: + * 1. Must be late enough (e.g., after SUBSYS_INITCALL) to avoid the + * intermediate state of the core cgroup subsystem initialization, ensuring + * all internal structures are stable. + * 2. Must execute strictly before cgroup_v1_ifs_init(), which runs at + * LATE_INITCALL_SYNC, as cgroup_v1_ifs_init() relies on 'cgrp_ifs_enabled' + * being set before its execution begins. + */ +device_initcall(cgroup_ifs_enable); -- Gitee From 21959a17278b3aff07ed26f111dd90af3dfa15bb Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Tue, 9 Dec 2025 20:28:36 +0800 Subject: [PATCH 3/3] interference: add version compatibility check between subsystem and cgroup hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICAOAT ----------------------------------------- Add a sanity check to ensure the cgroup subsystem and the target cgroup are using the same cgroup version (either both v1 or both v2). This prevents operations that mix incompatible cgroup hierarchies, which could lead to incorrect accounting for interference. Fixes: 28d4e4995d76 ("interference: Add cgroup v1 support for CGROUP_IFS") Signed-off-by: Tengda Wu --- kernel/cgroup/ifs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/cgroup/ifs.c b/kernel/cgroup/ifs.c index 9d9405e94b6a..d7a3e4214143 100644 --- a/kernel/cgroup/ifs.c +++ b/kernel/cgroup/ifs.c @@ -477,6 +477,16 @@ static int cgroup_ifs_show(struct seq_file *seq, void *v) return -EINVAL; } +#ifdef CONFIG_CGROUP_CPUACCT + if ((!cgroup_subsys_on_dfl(cpuacct_cgrp_subsys) && cgroup_on_dfl(cgrp)) || + (cgroup_subsys_on_dfl(cpuacct_cgrp_subsys) && !cgroup_on_dfl(cgrp))) { + pr_info("cgroup version mismatch: subsystem %s, cgroup %s\n", + cgroup_subsys_on_dfl(cpuacct_cgrp_subsys) ? "v2" : "v1", + cgroup_on_dfl(cgrp) ? "v2" : "v1"); + return -EOPNOTSUPP; + } +#endif + ret = print_sum_time(ifs, seq); if (ret) return ret; -- Gitee