| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/15 20:25 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": [
"SMP",
"NUMA",
"CPUSETS",
"SCHED_CACHE",
"MTRR",
"X86_MCE_INTEL",
"HOTPLUG_CPU"
],
"FocusSymbols": [
"build_sched_domains",
"build_overlap_sched_groups",
"build_sched_groups",
"init_overlap_sched_group",
"partition_sched_domains",
"dl_rebuild_rd_accounting",
"dl_clear_root_domain",
"dl_clear_root_domain_cpu",
"refresh_zone_stat_thresholds",
"sched_update_llc_bytes",
"mtrr_generic_state_matches",
"cmci_rediscover",
"cmci_clear"
],
"KMSANReasoning": "The patch series modifies x86 MTRR checking, CMCI bank handling during CPU offline, SMP boot cache flush logic, scheduler deadline bandwidth accounting, scheduler topology allocation/caching, and vmstat threshold calculation.\n\nNone of these changes expose uninitialized memory risks or kernel-to-user info-leaks:\n1. All newly added dynamic allocations in the scheduler topology code (such as `sched_group`, `sched_group_capacity`, and `sched_domain_shared`) use `kzalloc_node` or `alloc_percpu`, ensuring all fields are zero-initialized.\n2. All local stack variables (e.g. `val` in `mtrr_generic_state_matches()`, `total_bw`/`extra_bw` in `dl_clear_root_domain()`, and `pgdat_threshold` in `refresh_zone_stat_thresholds()`) are explicitly initialized before any read or use.\n3. CMCI state tracking uses static BSS variables (`cmci_orphaned_banks`, `cmci_orphaned_target`) that are explicitly zeroed via `bitmap_zero()`.\n4. No data structures or padding bytes are copied or leaked to user space via `copy_to_user`, ioctl outputs, netlink attributes, or socket buffers.\n5. Potential edge-case bugs that could arise from these changes (e.g., concurrency/locking issues, cpumask out-of-bounds, or NULL pointer dereferences) are covered by KASAN, LOCKDEP, and standard kernel assertions rather than KMSAN.\n\nThus, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch series touches several core kernel subsystems reachable in standard QEMU amd64 environments, including scheduler topology building (kernel/sched/topology.c), deadline scheduler bandwidth accounting (kernel/sched/deadline.c), memory management vmstat threshold updates (mm/vmstat.c), and x86 MTRR/CMCI handling. The scheduler changes rework sched_group allocations, traversal loops, and root domain accounting, which can be exercised via cpuset cgroup operations, CPU hotplug, and SCHED_DEADLINE syscalls. These non-trivial algorithmic and memory management adjustments warrant fuzzing to uncover potential memory corruptions, invariant violations, or regressions.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/15 20:25 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit dab0643a8b764d66dba17d993938331f8cba14a3\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Oct 15 20:25:44 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h\nindex 76b95bd1a4058..0527e216808c4 100644\n--- a/arch/x86/include/asm/mtrr.h\n+++ b/arch/x86/include/asm/mtrr.h\n@@ -61,6 +61,7 @@ extern int amd_special_default_mtrr(void);\n void mtrr_disable(void);\n void mtrr_enable(void);\n void mtrr_generic_set_state(void);\n+bool mtrr_generic_state_matches(void);\n # else\n static inline void guest_force_mtrr_state(struct mtrr_var_range *var,\n \t\t\t\t\t unsigned int num_var,\n@@ -106,6 +107,7 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)\n #define mtrr_disable() do {} while (0)\n #define mtrr_enable() do {} while (0)\n #define mtrr_generic_set_state() do {} while (0)\n+static inline bool mtrr_generic_state_matches(void) { return true; }\n # endif\n \n #ifdef CONFIG_COMPAT\ndiff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c\nindex 13ed16527905f..64796d5b34fdc 100644\n--- a/arch/x86/kernel/cpu/cacheinfo.c\n+++ b/arch/x86/kernel/cpu/cacheinfo.c\n@@ -770,6 +770,13 @@ static int cache_ap_online(unsigned int cpu)\n \tif (!memory_caching_control || get_cache_aps_delayed_init())\n \t\treturn 0;\n \n+\tif (!(memory_caching_control \u0026 CACHE_MTRR) ||\n+\t mtrr_generic_state_matches()) {\n+\t\tif (memory_caching_control \u0026 CACHE_PAT)\n+\t\t\tpat_cpu_init();\n+\t\treturn 0;\n+\t}\n+\n \t/*\n \t * Ideally we should hold mtrr_mutex here to avoid MTRR entries\n \t * changed, but this routine will be called in CPU boot time,\ndiff --git a/arch/x86/kernel/cpu/mce/intel.c b/arch/x86/kernel/cpu/mce/intel.c\nindex 4655223ba5606..f57718b097d9b 100644\n--- a/arch/x86/kernel/cpu/mce/intel.c\n+++ b/arch/x86/kernel/cpu/mce/intel.c\n@@ -11,6 +11,7 @@\n #include \u003clinux/percpu.h\u003e\n #include \u003clinux/sched.h\u003e\n #include \u003clinux/cpumask.h\u003e\n+#include \u003clinux/topology.h\u003e\n #include \u003casm/apic.h\u003e\n #include \u003casm/cpufeature.h\u003e\n #include \u003casm/cpu_device_id.h\u003e\n@@ -40,6 +41,8 @@\n * owned by the offlined cpu.\n */\n static DEFINE_PER_CPU(mce_banks_t, mce_banks_owned);\n+static mce_banks_t cmci_orphaned_banks;\n+static unsigned int cmci_orphaned_target;\n \n /*\n * cmci_discover_lock protects against parallel discovery attempts\n@@ -244,6 +247,7 @@ static void cmci_claim_bank(int bank, u64 val, int bios_zero_thresh, int *bios_w\n \n \t/* This CPU successfully set the enable bit. */\n \tset_bit(bank, (void *)this_cpu_ptr(\u0026mce_banks_owned));\n+\t__clear_bit(bank, cmci_orphaned_banks);\n \n \tif ((val \u0026 MCI_CTL2_CMCI_THRESHOLD_MASK) == CMCI_STORM_THRESHOLD) {\n \t\tpr_notice(\"CPU%d BANK%d CMCI inherited storm\\n\", smp_processor_id(), bank);\n@@ -328,6 +332,7 @@ static void __cmci_disable_bank(int bank)\n \tval \u0026= ~MCI_CTL2_CMCI_EN;\n \twrmsrq(MSR_IA32_MCx_CTL2(bank), val);\n \t__clear_bit(bank, this_cpu_ptr(mce_banks_owned));\n+\t__set_bit(bank, cmci_orphaned_banks);\n \n \tif ((val \u0026 MCI_CTL2_CMCI_THRESHOLD_MASK) == CMCI_STORM_THRESHOLD)\n \t\tcmci_storm_end(bank);\n@@ -339,6 +344,8 @@ static void __cmci_disable_bank(int bank)\n */\n void cmci_clear(void)\n {\n+\tunsigned int cpu = smp_processor_id();\n+\tunsigned int target;\n \tunsigned long flags;\n \tint i;\n \tint banks;\n@@ -346,8 +353,31 @@ void cmci_clear(void)\n \tif (!cmci_supported(\u0026banks))\n \t\treturn;\n \traw_spin_lock_irqsave(\u0026cmci_discover_lock, flags);\n+\tbitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);\n+\t/*\n+\t * A bank is shared by the CPUs of one SMT core, L2 cluster, die or\n+\t * package, and these scopes nest. The first online peer found from\n+\t * the innermost scope outwards is therefore part of every sharing\n+\t * scope that still has an online CPU, so it alone can claim every\n+\t * orphaned bank that is still claimable. If it already owns an index,\n+\t * the instance released here is not shared with it, hence not with\n+\t * any online CPU, and that index can be dropped from the rediscovery.\n+\t */\n+\ttarget = cpumask_any_but(topology_sibling_cpumask(cpu), cpu);\n+\tif (target \u003e= nr_cpu_ids)\n+\t\ttarget = cpumask_any_but(topology_cluster_cpumask(cpu), cpu);\n+\tif (target \u003e= nr_cpu_ids)\n+\t\ttarget = cpumask_any_but(topology_die_cpumask(cpu), cpu);\n+\tif (target \u003e= nr_cpu_ids)\n+\t\ttarget = cpumask_any_but(topology_core_cpumask(cpu), cpu);\n+\tcmci_orphaned_target = target;\n \tfor (i = 0; i \u003c banks; i++)\n \t\t__cmci_disable_bank(i);\n+\tif (target \u003c nr_cpu_ids)\n+\t\tbitmap_andnot(cmci_orphaned_banks, cmci_orphaned_banks,\n+\t\t\t per_cpu(mce_banks_owned, target), MAX_NR_BANKS);\n+\telse\n+\t\tbitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);\n \traw_spin_unlock_irqrestore(\u0026cmci_discover_lock, flags);\n }\n \n@@ -360,15 +390,38 @@ static void cmci_rediscover_work_func(void *arg)\n \t\tcmci_discover(banks);\n }\n \n-/* After a CPU went down cycle through all the others and rediscover */\n+/*\n+ * After a CPU went down, rediscover orphaned shared banks on a single\n+ * topology peer (SMT sibling, L2 cluster peer, die peer, or package peer)\n+ * selected in cmci_clear(). Core-private banks whose core is now completely\n+ * offline cannot be claimed by any other core, so probing more than one\n+ * peer is redundant.\n+ */\n void cmci_rediscover(void)\n {\n+\tunsigned int target;\n+\tunsigned long flags;\n+\tbool orphaned;\n \tint banks;\n \n \tif (!cmci_supported(\u0026banks))\n \t\treturn;\n \n-\ton_each_cpu(cmci_rediscover_work_func, NULL, 1);\n+\traw_spin_lock_irqsave(\u0026cmci_discover_lock, flags);\n+\torphaned = !bitmap_empty(cmci_orphaned_banks, MAX_NR_BANKS);\n+\ttarget = cmci_orphaned_target;\n+\traw_spin_unlock_irqrestore(\u0026cmci_discover_lock, flags);\n+\n+\tif (!orphaned)\n+\t\treturn;\n+\n+\tif (target \u003c nr_cpu_ids \u0026\u0026 cpu_online(target))\n+\t\tsmp_call_function_single(target, cmci_rediscover_work_func,\n+\t\t\t\t\t NULL, 1);\n+\n+\traw_spin_lock_irqsave(\u0026cmci_discover_lock, flags);\n+\tbitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);\n+\traw_spin_unlock_irqrestore(\u0026cmci_discover_lock, flags);\n }\n \n /*\ndiff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c\nindex 67cf69f24b005..8760a7b4dc38b 100644\n--- a/arch/x86/kernel/cpu/mtrr/generic.c\n+++ b/arch/x86/kernel/cpu/mtrr/generic.c\n@@ -981,6 +981,55 @@ void mtrr_generic_set_state(void)\n \t}\n }\n \n+bool mtrr_generic_state_matches(void)\n+{\n+\tstruct msr val;\n+\tunsigned int i;\n+\n+\tif (!cpu_feature_enabled(X86_FEATURE_MTRR))\n+\t\treturn true;\n+\n+\trdmsrq(MSR_MTRRdefType, val.q);\n+\tif ((val.l \u0026 MTRR_DEF_TYPE_TYPE) != mtrr_state.def_type ||\n+\t ((val.l \u0026 MTRR_DEF_TYPE_ENABLE) \u003e\u003e MTRR_STATE_SHIFT) != mtrr_state.enabled)\n+\t\treturn false;\n+\n+\tfor (i = 0; i \u003c num_var_ranges; i++) {\n+\t\tstruct mtrr_var_range *vr = \u0026mtrr_state.var_ranges[i];\n+\n+\t\trdmsrq(MTRRphysBase_MSR(i), val.q);\n+\t\tif ((vr-\u003ebase_lo \u0026 ~MTRR_PHYSBASE_RSVD) != (val.l \u0026 ~MTRR_PHYSBASE_RSVD) ||\n+\t\t (vr-\u003ebase_hi \u0026 ~phys_hi_rsvd) != (val.h \u0026 ~phys_hi_rsvd))\n+\t\t\treturn false;\n+\n+\t\trdmsrq(MTRRphysMask_MSR(i), val.q);\n+\t\tif ((vr-\u003emask_lo \u0026 ~MTRR_PHYSMASK_RSVD) != (val.l \u0026 ~MTRR_PHYSMASK_RSVD) ||\n+\t\t (vr-\u003emask_hi \u0026 ~phys_hi_rsvd) != (val.h \u0026 ~phys_hi_rsvd))\n+\t\t\treturn false;\n+\t}\n+\n+\tif (mtrr_state.have_fixed) {\n+\t\tu64 *saved = (u64 *)mtrr_state.fixed_ranges;\n+\t\tint block = -1, range;\n+\n+\t\t/*\n+\t\t * Ensure MtrrFixDramModEn is clear so fixed MTRR reads do not\n+\t\t * expose K8 RdMem/WrMem bits and cause false mismatches.\n+\t\t */\n+\t\tk8_check_syscfg_dram_mod_en();\n+\n+\t\twhile (fixed_range_blocks[++block].ranges) {\n+\t\t\tfor (range = 0; range \u003c fixed_range_blocks[block].ranges; range++) {\n+\t\t\t\trdmsrq(fixed_range_blocks[block].base_msr + range, val.q);\n+\t\t\t\tif (val.q != *saved++)\n+\t\t\t\t\treturn false;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n /**\n * generic_set_mtrr - set variable MTRR register on the local CPU.\n *\ndiff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c\nindex ba01a9e919b78..60a781dac9991 100644\n--- a/arch/x86/kernel/smpboot.c\n+++ b/arch/x86/kernel/smpboot.c\n@@ -1414,10 +1414,6 @@ void play_dead_common(void)\n \tlocal_irq_disable();\n }\n \n-/*\n- * We need to flush the caches before going to sleep, lest we have\n- * dirty data in our caches when we come back up.\n- */\n void __noreturn mwait_play_dead(unsigned int eax_hint)\n {\n \tstruct mwait_cpu_dead *md = this_cpu_ptr(\u0026mwait_cpu_dead);\n@@ -1426,15 +1422,21 @@ void __noreturn mwait_play_dead(unsigned int eax_hint)\n \tmd-\u003estatus = CPUDEAD_MWAIT_WAIT;\n \tmd-\u003econtrol = CPUDEAD_MWAIT_WAIT;\n \n-\twbinvd();\n+\t/*\n+\t * MWAIT C-states keep the caches coherent, so runtime CPU offline\n+\t * does not need to flush them. System sleep, however, may remove\n+\t * power from this package's caches while all of its CPUs are\n+\t * offline, and the flush done by the boot CPU before entering the\n+\t * sleep state only covers the boot CPU's own package.\n+\t */\n+\tif (cpuhp_tasks_frozen)\n+\t\twbinvd();\n \n \twhile (1) {\n \t\t/*\n \t\t * The CLFLUSH is a workaround for erratum AAI65 for\n \t\t * the Xeon 7400 series. It's not clear it is actually\n \t\t * needed, but it should be harmless in either case.\n-\t\t * The WBINVD is insufficient due to the spurious-wakeup\n-\t\t * case where we return around the loop.\n \t\t */\n \t\tmb();\n \t\tclflush(md);\ndiff --git a/arch/x86/realmode/rm/trampoline_32.S b/arch/x86/realmode/rm/trampoline_32.S\nindex 3fad907a179f1..14e56ea954160 100644\n--- a/arch/x86/realmode/rm/trampoline_32.S\n+++ b/arch/x86/realmode/rm/trampoline_32.S\n@@ -30,8 +30,6 @@\n \n \t.balign\tPAGE_SIZE\n SYM_CODE_START(trampoline_start)\n-\twbinvd\t\t\t# Needed for NUMA-Q should be harmless for others\n-\n \tLJMPW_RM(1f)\n 1:\n \tmov\t%cs, %ax\t# Code and data in the same place\ndiff --git a/arch/x86/realmode/rm/trampoline_64.S b/arch/x86/realmode/rm/trampoline_64.S\nindex 14d9c7daf90f4..e5a3f03d62217 100644\n--- a/arch/x86/realmode/rm/trampoline_64.S\n+++ b/arch/x86/realmode/rm/trampoline_64.S\n@@ -60,7 +60,6 @@\n \t.balign\tPAGE_SIZE\n SYM_CODE_START(trampoline_start)\n \tcli\t\t\t# We should be safe anyway\n-\twbinvd\n \n \tLJMPW_RM(1f)\n 1:\ndiff --git a/include/linux/cpuset.h b/include/linux/cpuset.h\nindex 914983387e955..8baa99308f49e 100644\n--- a/include/linux/cpuset.h\n+++ b/include/linux/cpuset.h\n@@ -129,7 +129,7 @@ static inline int cpuset_do_page_mem_spread(void)\n \n extern bool current_cpuset_is_being_rebound(void);\n \n-extern void dl_rebuild_rd_accounting(void);\n+void dl_rebuild_rd_accounting(void);\n extern void rebuild_sched_domains(void);\n \n extern void cpuset_print_current_mems_allowed(void);\n@@ -273,9 +273,7 @@ static inline bool current_cpuset_is_being_rebound(void)\n \treturn false;\n }\n \n-static inline void dl_rebuild_rd_accounting(void)\n-{\n-}\n+void dl_rebuild_rd_accounting(void);\n \n static inline void rebuild_sched_domains(void)\n {\ndiff --git a/include/linux/sched/deadline.h b/include/linux/sched/deadline.h\nindex 273538200a445..6f03357f05c76 100644\n--- a/include/linux/sched/deadline.h\n+++ b/include/linux/sched/deadline.h\n@@ -31,7 +31,6 @@ static inline bool dl_time_before(u64 a, u64 b)\n \n struct root_domain;\n extern void dl_add_task_root_domain(struct task_struct *p);\n-extern void dl_clear_root_domain(struct root_domain *rd);\n extern void dl_clear_root_domain_cpu(int cpu);\n /*\n * Return whether moving DL task @p to @new_mask requires moving DL\ndiff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c\nindex 857dbe3519a86..01f2f24e3b814 100644\n--- a/kernel/sched/deadline.c\n+++ b/kernel/sched/deadline.c\n@@ -1885,24 +1885,6 @@ void sched_init_dl_servers(void)\n \t}\n }\n \n-void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq)\n-{\n-\tu64 new_bw = dl_se-\u003edl_bw;\n-\tint cpu = cpu_of(rq);\n-\tstruct dl_bw *dl_b;\n-\n-\tif (!dl_se-\u003edl_bw_attached)\n-\t\treturn;\n-\n-\tdl_b = dl_bw_of(cpu_of(rq));\n-\tguard(raw_spinlock)(\u0026dl_b-\u003elock);\n-\n-\tif (!dl_bw_cpus(cpu))\n-\t\treturn;\n-\n-\t__dl_add(dl_b, new_bw, dl_bw_cpus(cpu));\n-}\n-\n int dl_server_apply_params(struct sched_dl_entity *dl_se, u64 runtime, u64 period, bool init)\n {\n \tu64 old_bw = (init || !dl_se-\u003edl_bw_attached) ? 0 :\n@@ -3448,18 +3430,22 @@ void dl_add_task_root_domain(struct task_struct *p)\n \traw_spin_unlock_irqrestore(\u0026p-\u003epi_lock, rf.flags);\n }\n \n-static void dl_server_add_bw(struct root_domain *rd, int cpu)\n+static void dl_server_sum_bw(int cpu, int cpus, u64 *total_bw, s64 *extra_bw)\n {\n \tstruct sched_dl_entity *dl_se;\n \n \tdl_se = \u0026cpu_rq(cpu)-\u003efair_server;\n-\tif (dl_server(dl_se) \u0026\u0026 dl_se-\u003edl_bw_attached \u0026\u0026 cpu_active(cpu))\n-\t\t__dl_add(\u0026rd-\u003edl_bw, dl_se-\u003edl_bw, dl_bw_cpus(cpu));\n+\tif (dl_server(dl_se) \u0026\u0026 dl_se-\u003edl_bw_attached) {\n+\t\t*total_bw += dl_se-\u003edl_bw;\n+\t\t*extra_bw -= (s32)dl_se-\u003edl_bw / cpus;\n+\t}\n \n #ifdef CONFIG_SCHED_CLASS_EXT\n \tdl_se = \u0026cpu_rq(cpu)-\u003eext_server;\n-\tif (dl_server(dl_se) \u0026\u0026 dl_se-\u003edl_bw_attached \u0026\u0026 cpu_active(cpu))\n-\t\t__dl_add(\u0026rd-\u003edl_bw, dl_se-\u003edl_bw, dl_bw_cpus(cpu));\n+\tif (dl_server(dl_se) \u0026\u0026 dl_se-\u003edl_bw_attached) {\n+\t\t*total_bw += dl_se-\u003edl_bw;\n+\t\t*extra_bw -= (s32)dl_se-\u003edl_bw / cpus;\n+\t}\n #endif\n }\n \n@@ -3480,26 +3466,32 @@ static u64 dl_server_read_bw(int cpu)\n \treturn dl_bw;\n }\n \n-void dl_clear_root_domain(struct root_domain *rd)\n+static void dl_clear_root_domain(struct root_domain *rd)\n {\n-\tint i;\n+\tu64 total_bw = 0;\n+\ts64 extra_bw = 0;\n+\tint i, cpus;\n \n \tguard(raw_spinlock_irqsave)(\u0026rd-\u003edl_bw.lock);\n \n-\t/*\n-\t * Reset total_bw to zero and extra_bw to max_bw so that next\n-\t * loop will add dl-servers contributions back properly,\n-\t */\n-\trd-\u003edl_bw.total_bw = 0;\n-\tfor_each_cpu(i, rd-\u003espan)\n-\t\tcpu_rq(i)-\u003edl.extra_bw = cpu_rq(i)-\u003edl.max_bw;\n-\n \t/*\n \t * dl_servers are not tasks. Since dl_add_task_root_domain ignores\n \t * them, we need to account for them here explicitly.\n \t */\n-\tfor_each_cpu(i, rd-\u003espan)\n-\t\tdl_server_add_bw(rd, i);\n+\tcpus = cpumask_weight_and(rd-\u003espan, cpu_active_mask);\n+\tif (cpus) {\n+\t\tfor_each_cpu_and(i, rd-\u003espan, cpu_active_mask)\n+\t\t\tdl_server_sum_bw(i, cpus, \u0026total_bw, \u0026extra_bw);\n+\t}\n+\n+\trd-\u003edl_bw.total_bw = total_bw;\n+\tfor_each_cpu(i, rd-\u003espan) {\n+\t\tstruct rq *rq = cpu_rq(i);\n+\n+\t\trq-\u003edl.extra_bw = rq-\u003edl.max_bw;\n+\t\tif (cpu_active(i))\n+\t\t\trq-\u003edl.extra_bw += extra_bw;\n+\t}\n }\n \n void dl_clear_root_domain_cpu(int cpu)\n@@ -3507,6 +3499,33 @@ void dl_clear_root_domain_cpu(int cpu)\n \tdl_clear_root_domain(cpu_rq(cpu)-\u003erd);\n }\n \n+#ifndef CONFIG_CPUSETS\n+void dl_rebuild_rd_accounting(void)\n+{\n+\tstruct task_struct *g, *p;\n+\tu64 cookie = ++dl_cookie;\n+\tint cpu;\n+\n+\tlockdep_assert_cpus_held();\n+\tlockdep_assert_held(\u0026sched_domains_mutex);\n+\n+\trcu_read_lock();\n+\tfor_each_possible_cpu(cpu) {\n+\t\tif (dl_bw_visited(cpu, cookie))\n+\t\t\tcontinue;\n+\n+\t\tdl_clear_root_domain_cpu(cpu);\n+\t}\n+\tfor_each_process_thread(g, p) {\n+\t\t/* Racy pre-check; dl_add_task_root_domain() rechecks under pi_lock. */\n+\t\tif (!dl_prio(READ_ONCE(p-\u003eprio)))\n+\t\t\tcontinue;\n+\t\tdl_add_task_root_domain(p);\n+\t}\n+\trcu_read_unlock();\n+}\n+#endif\n+\n static void switched_from_dl(struct rq *rq, struct task_struct *p)\n {\n \t/*\ndiff --git a/kernel/sched/sched.h b/kernel/sched/sched.h\nindex e656c7059bf86..e76a6876ac4e8 100644\n--- a/kernel/sched/sched.h\n+++ b/kernel/sched/sched.h\n@@ -418,7 +418,6 @@ extern void sched_init_dl_servers(void);\n \n extern void fair_server_init(struct rq *rq);\n extern void ext_server_init(struct rq *rq);\n-extern void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq);\n extern int dl_server_apply_params(struct sched_dl_entity *dl_se,\n \t\t u64 runtime, u64 period, bool init);\n extern int dl_server_attach_bw(struct sched_dl_entity *dl_se);\n@@ -2261,6 +2260,15 @@ struct sched_group_capacity {\n \n \tint\t\t\tid;\n \n+\t/*\n+\t * Build-time cache shared by all groups using this sgc while\n+\t * build_sched_domains() runs; not maintained afterwards (e.g. by\n+\t * sched_update_asym_prefer_cpu()). Use the sched_group copies.\n+\t */\n+\tunsigned int\t\tgroup_weight;\n+\tunsigned int\t\tcores;\n+\tint\t\t\tasym_prefer_cpu;\n+\n \tunsigned long\t\tcpumask[];\t\t/* Balance mask */\n };\n \ndiff --git a/kernel/sched/topology.c b/kernel/sched/topology.c\nindex 0248227d983a7..a9122dbff379d 100644\n--- a/kernel/sched/topology.c\n+++ b/kernel/sched/topology.c\n@@ -502,19 +502,6 @@ void rq_attach_root(struct rq *rq, struct root_domain *rd)\n \tif (cpumask_test_cpu(rq-\u003ecpu, cpu_active_mask))\n \t\tset_rq_online(rq);\n \n-\t/*\n-\t * Because the rq is not a task, dl_add_task_root_domain() did not\n-\t * move the fair server bw to the rd if it already started.\n-\t * Add it now.\n-\t */\n-\tif (rq-\u003efair_server.dl_server)\n-\t\t__dl_server_attach_root(\u0026rq-\u003efair_server, rq);\n-\n-#ifdef CONFIG_SCHED_CLASS_EXT\n-\tif (rq-\u003eext_server.dl_server)\n-\t\t__dl_server_attach_root(\u0026rq-\u003eext_server, rq);\n-#endif\n-\n \trq_unlock_irqrestore(rq, \u0026rf);\n \n \tif (old_rd)\n@@ -916,9 +903,15 @@ static bool alloc_sd_llc(const struct cpumask *cpu_map,\n \t\t\ttop_llc = parent;\n \n \t\tif (top_llc-\u003eflags \u0026 SD_SHARE_LLC) {\n+\t\t\tint first = cpumask_first(sched_domain_span(top_llc));\n+\n \t\t\tsd-\u003ellc_max = max_lid + 1;\n \t\t\tsd-\u003ellc_counts = p;\n-\t\t\tsd-\u003ellc_bytes = get_effective_llc_bytes(i, top_llc);\n+\t\t\t/* All CPUs in top_llc share the same LLC size and weight. */\n+\t\t\tif (first \u003c i)\n+\t\t\t\tsd-\u003ellc_bytes = (*per_cpu_ptr(d-\u003esd, first))-\u003ellc_bytes;\n+\t\t\telse\n+\t\t\t\tsd-\u003ellc_bytes = get_effective_llc_bytes(i, top_llc);\n \t\t} else {\n \t\t\t/* avoid memory leak */\n \t\t\tkfree(p);\n@@ -999,6 +992,7 @@ void sched_cache_active_set(void)\n void sched_update_llc_bytes(unsigned int cpu)\n {\n \tstruct sched_domain *sd, *sdp;\n+\tunsigned long llc_bytes;\n \tunsigned int i;\n \n \tsched_domains_mutex_lock();\n@@ -1015,10 +1009,11 @@ void sched_update_llc_bytes(unsigned int cpu)\n \t * sibling on each online event corrects this once the full\n \t * shared_cpu_map is known.\n \t */\n+\tllc_bytes = get_effective_llc_bytes(cpu, sdp);\n \tfor_each_cpu(i, sched_domain_span(sdp)) {\n \t\tsd = rcu_dereference_sched_domain(cpu_rq(i)-\u003esd);\n \t\tif (sd)\n-\t\t\tsd-\u003ellc_bytes = get_effective_llc_bytes(i, sdp);\n+\t\t\tsd-\u003ellc_bytes = llc_bytes;\n \t}\n \n unlock:\n@@ -1259,32 +1254,49 @@ build_group_from_child_sched_domain(struct sched_domain *sd, int cpu)\n \treturn sg;\n }\n \n-static void init_overlap_sched_group(struct sched_domain *sd,\n-\t\t\t\t struct sched_group *sg)\n+static int init_overlap_sched_group(struct sched_domain *sd,\n+\t\t\t\t struct sched_group *sg)\n {\n \tstruct cpumask *mask = sched_domains_tmpmask2;\n \tstruct sd_data *sdd = sd-\u003eprivate;\n-\tstruct cpumask *sg_span;\n-\tint cpu;\n+\tstruct cpumask *sg_span = sched_group_span(sg);\n+\tstruct sched_group_capacity *sgc;\n+\tint cpu = cpumask_first(sg_span);\n+\tstruct sched_domain *first_sd = *per_cpu_ptr(sdd-\u003esd, cpu);\n+\n+\tsgc = *per_cpu_ptr(sdd-\u003esgc, cpu);\n+\tif (sgc \u0026\u0026 atomic_read(\u0026sgc-\u003eref) \u003e 0 \u0026\u0026\n+\t first_sd \u0026\u0026 first_sd-\u003echild \u0026\u0026\n+\t cpumask_equal(sg_span, sched_domain_span(first_sd-\u003echild))) {\n+\t\tatomic_inc(\u0026sgc-\u003eref);\n+\t\tsg-\u003esgc = sgc;\n+\t\treturn 0;\n+\t}\n \n \tbuild_balance_mask(sd, sg, mask);\n \tcpu = cpumask_first(mask);\n \n-\tsg-\u003esgc = *per_cpu_ptr(sdd-\u003esgc, cpu);\n-\tif (atomic_inc_return(\u0026sg-\u003esgc-\u003eref) == 1)\n+\tsgc = *per_cpu_ptr(sdd-\u003esgc, cpu);\n+\tif (!sgc) {\n+\t\tsgc = kzalloc_node(sizeof(*sgc) + cpumask_size(),\n+\t\t\t\t GFP_KERNEL, cpu_to_node(cpu));\n+\t\tif (!sgc)\n+\t\t\treturn -ENOMEM;\n+\t\tsgc-\u003eid = cpu;\n+\t\t*per_cpu_ptr(sdd-\u003esgc, cpu) = sgc;\n+\t}\n+\n+\tsg-\u003esgc = sgc;\n+\tif (atomic_inc_return(\u0026sg-\u003esgc-\u003eref) == 1) {\n \t\tcpumask_copy(group_balance_mask(sg), mask);\n-\telse\n+\t\tsg-\u003esgc-\u003ecapacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);\n+\t\tsg-\u003esgc-\u003emin_capacity = SCHED_CAPACITY_SCALE;\n+\t\tsg-\u003esgc-\u003emax_capacity = SCHED_CAPACITY_SCALE;\n+\t} else {\n \t\tWARN_ON_ONCE(!cpumask_equal(group_balance_mask(sg), mask));\n+\t}\n \n-\t/*\n-\t * Initialize sgc-\u003ecapacity such that even if we mess up the\n-\t * domains and no possible iteration will get us here, we won't\n-\t * die on a /0 trap.\n-\t */\n-\tsg_span = sched_group_span(sg);\n-\tsg-\u003esgc-\u003ecapacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);\n-\tsg-\u003esgc-\u003emin_capacity = SCHED_CAPACITY_SCALE;\n-\tsg-\u003esgc-\u003emax_capacity = SCHED_CAPACITY_SCALE;\n+\treturn 0;\n }\n \n static struct sched_domain *\n@@ -1317,19 +1329,18 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)\n {\n \tstruct sched_group *first = NULL, *last = NULL, *sg;\n \tconst struct cpumask *span = sched_domain_span(sd);\n-\tstruct cpumask *covered = sched_domains_tmpmask;\n+\tstruct cpumask *uncovered = sched_domains_tmpmask;\n \tstruct sd_data *sdd = sd-\u003eprivate;\n \tstruct sched_domain *sibling;\n \tint i;\n \n-\tcpumask_clear(covered);\n+\tcpumask_copy(uncovered, span);\n \n-\tfor_each_cpu_wrap(i, span, cpu) {\n+\tfor (i = cpu; i \u003c nr_cpu_ids;\n+\t i = cpumask_next(i, uncovered),\n+\t i = i \u003c nr_cpu_ids ? i : cpumask_first(uncovered)) {\n \t\tstruct cpumask *sg_span;\n \n-\t\tif (cpumask_test_cpu(i, covered))\n-\t\t\tcontinue;\n-\n \t\tsibling = *per_cpu_ptr(sdd-\u003esd, i);\n \n \t\t/*\n@@ -1342,8 +1353,10 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)\n \t\t * Domains should always include the CPU they're built on, so\n \t\t * check that.\n \t\t */\n-\t\tif (!cpumask_test_cpu(i, sched_domain_span(sibling)))\n+\t\tif (!cpumask_test_cpu(i, sched_domain_span(sibling))) {\n+\t\t\t__cpumask_clear_cpu(i, uncovered);\n \t\t\tcontinue;\n+\t\t}\n \n \t\t/*\n \t\t * Usually we build sched_group by sibling's child sched_domain\n@@ -1385,9 +1398,12 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)\n \t\t\tgoto fail;\n \n \t\tsg_span = sched_group_span(sg);\n-\t\tcpumask_or(covered, covered, sg_span);\n+\t\tcpumask_andnot(uncovered, uncovered, sg_span);\n \n-\t\tinit_overlap_sched_group(sibling, sg);\n+\t\tif (init_overlap_sched_group(sibling, sg)) {\n+\t\t\tkfree(sg);\n+\t\t\tgoto fail;\n+\t\t}\n \n \t\tif (!first)\n \t\t\tfirst = sg;\n@@ -1489,6 +1505,25 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd)\n \t\tcpu = cpumask_first(sched_domain_span(child));\n \n \tsg = *per_cpu_ptr(sdd-\u003esg, cpu);\n+\tif (!sg) {\n+\t\tsg = kzalloc_node(sizeof(*sg) + cpumask_size(),\n+\t\t\t\t GFP_KERNEL, cpu_to_node(cpu));\n+\t\tif (!sg)\n+\t\t\treturn NULL;\n+\t\tsg-\u003enext = sg;\n+\t\t*per_cpu_ptr(sdd-\u003esg, cpu) = sg;\n+\t}\n+\n+\tif (!*per_cpu_ptr(sdd-\u003esgc, cpu)) {\n+\t\tstruct sched_group_capacity *sgc;\n+\n+\t\tsgc = kzalloc_node(sizeof(*sgc) + cpumask_size(),\n+\t\t\t\t GFP_KERNEL, cpu_to_node(cpu));\n+\t\tif (!sgc)\n+\t\t\treturn NULL;\n+\t\tsgc-\u003eid = cpu;\n+\t\t*per_cpu_ptr(sdd-\u003esgc, cpu) = sgc;\n+\t}\n \tsg-\u003esgc = *per_cpu_ptr(sdd-\u003esgc, cpu);\n \n \t/* Increase refcounts for claim_allocations: */\n@@ -1504,12 +1539,13 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd)\n \t\tcpumask_copy(sched_group_span(sg), sched_domain_span(child));\n \t\tcpumask_copy(group_balance_mask(sg), sched_group_span(sg));\n \t\tsg-\u003eflags = child-\u003eflags;\n+\t\tsg-\u003esgc-\u003ecapacity = SCHED_CAPACITY_SCALE * child-\u003espan_weight;\n \t} else {\n \t\tcpumask_set_cpu(cpu, sched_group_span(sg));\n \t\tcpumask_set_cpu(cpu, group_balance_mask(sg));\n+\t\tsg-\u003esgc-\u003ecapacity = SCHED_CAPACITY_SCALE;\n \t}\n \n-\tsg-\u003esgc-\u003ecapacity = SCHED_CAPACITY_SCALE * cpumask_weight(sched_group_span(sg));\n \tsg-\u003esgc-\u003emin_capacity = SCHED_CAPACITY_SCALE;\n \tsg-\u003esgc-\u003emax_capacity = SCHED_CAPACITY_SCALE;\n \n@@ -1529,23 +1565,24 @@ build_sched_groups(struct sched_domain *sd, int cpu)\n \tstruct sched_group *first = NULL, *last = NULL;\n \tstruct sd_data *sdd = sd-\u003eprivate;\n \tconst struct cpumask *span = sched_domain_span(sd);\n-\tstruct cpumask *covered;\n+\tstruct cpumask *uncovered;\n \tint i;\n \n \tlockdep_assert_held(\u0026sched_domains_mutex);\n-\tcovered = sched_domains_tmpmask;\n+\tuncovered = sched_domains_tmpmask;\n \n-\tcpumask_clear(covered);\n+\tcpumask_copy(uncovered, span);\n \n-\tfor_each_cpu_wrap(i, span, cpu) {\n+\tfor (i = cpu; i \u003c nr_cpu_ids;\n+\t i = cpumask_next(i, uncovered),\n+\t i = i \u003c nr_cpu_ids ? i : cpumask_first(uncovered)) {\n \t\tstruct sched_group *sg;\n \n-\t\tif (cpumask_test_cpu(i, covered))\n-\t\t\tcontinue;\n-\n \t\tsg = get_group(i, sdd);\n+\t\tif (!sg)\n+\t\t\treturn -ENOMEM;\n \n-\t\tcpumask_or(covered, covered, sched_group_span(sg));\n+\t\tcpumask_andnot(uncovered, uncovered, sched_group_span(sg));\n \n \t\tif (!first)\n \t\t\tfirst = sg;\n@@ -1577,27 +1614,40 @@ static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)\n \tWARN_ON(!sg);\n \n \tdo {\n-\t\tint cpu, cores = 0, max_cpu = -1;\n+\t\tint i, cores = 0, max_cpu = -1;\n+\n+\t\tif (sg-\u003egroup_weight)\n+\t\t\tgoto next;\n+\n+\t\tif (sg-\u003esgc-\u003egroup_weight) {\n+\t\t\tsg-\u003egroup_weight = sg-\u003esgc-\u003egroup_weight;\n+\t\t\tsg-\u003ecores = sg-\u003esgc-\u003ecores;\n+\t\t\tsg-\u003easym_prefer_cpu = sg-\u003esgc-\u003easym_prefer_cpu;\n+\t\t\tgoto next;\n+\t\t}\n \n \t\tsg-\u003egroup_weight = cpumask_weight(sched_group_span(sg));\n \n \t\tcpumask_copy(mask, sched_group_span(sg));\n-\t\tfor_each_cpu(cpu, mask) {\n+\t\tfor_each_cpu(i, mask) {\n \t\t\tcores++;\n-\t\t\tcpumask_andnot(mask, mask, cpu_smt_mask(cpu));\n+\t\t\tcpumask_andnot(mask, mask, cpu_smt_mask(i));\n \t\t}\n \t\tsg-\u003ecores = cores;\n \n-\t\tif (!(sd-\u003eflags \u0026 SD_ASYM_PACKING))\n-\t\t\tgoto next;\n-\n-\t\tfor_each_cpu(cpu, sched_group_span(sg)) {\n-\t\t\tif (max_cpu \u003c 0)\n-\t\t\t\tmax_cpu = cpu;\n-\t\t\telse if (sched_asym_prefer(cpu, max_cpu))\n-\t\t\t\tmax_cpu = cpu;\n+\t\tif (sd-\u003eflags \u0026 SD_ASYM_PACKING) {\n+\t\t\tfor_each_cpu(i, sched_group_span(sg)) {\n+\t\t\t\tif (max_cpu \u003c 0)\n+\t\t\t\t\tmax_cpu = i;\n+\t\t\t\telse if (sched_asym_prefer(i, max_cpu))\n+\t\t\t\t\tmax_cpu = i;\n+\t\t\t}\n+\t\t\tsg-\u003easym_prefer_cpu = max_cpu;\n \t\t}\n-\t\tsg-\u003easym_prefer_cpu = max_cpu;\n+\n+\t\tsg-\u003esgc-\u003egroup_weight = sg-\u003egroup_weight;\n+\t\tsg-\u003esgc-\u003ecores = sg-\u003ecores;\n+\t\tsg-\u003esgc-\u003easym_prefer_cpu = sg-\u003easym_prefer_cpu;\n \n next:\n \t\tsg = sg-\u003enext;\n@@ -1821,16 +1871,21 @@ static int __sdt_alloc(const struct cpumask *cpu_map);\n static void __sds_free(struct s_data *d, const struct cpumask *cpu_map);\n static int __sds_alloc(struct s_data *d, const struct cpumask *cpu_map);\n \n+static struct sched_domain * __percpu *sched_domains_sd;\n+\n static void __free_domain_allocs(struct s_data *d, enum s_alloc what,\n \t\t\t\t const struct cpumask *cpu_map)\n {\n+\tint j;\n+\n \tswitch (what) {\n \tcase sa_rootdomain:\n \t\tif (!atomic_read(\u0026d-\u003erd-\u003erefcount))\n \t\t\tfree_rootdomain(\u0026d-\u003erd-\u003ercu);\n \t\tfallthrough;\n \tcase sa_sd:\n-\t\tfree_percpu(d-\u003esd);\n+\t\tfor_each_cpu(j, cpu_map)\n+\t\t\t*per_cpu_ptr(d-\u003esd, j) = NULL;\n \t\tfallthrough;\n \tcase sa_sd_shared:\n \t\t__sds_free(d, cpu_map);\n@@ -1852,9 +1907,12 @@ __visit_domain_allocation_hell(struct s_data *d, const struct cpumask *cpu_map)\n \t\treturn sa_sd_storage;\n \tif (__sds_alloc(d, cpu_map))\n \t\treturn sa_sd_shared;\n-\td-\u003esd = alloc_percpu(struct sched_domain *);\n-\tif (!d-\u003esd)\n-\t\treturn sa_sd_shared;\n+\tif (!sched_domains_sd) {\n+\t\tsched_domains_sd = alloc_percpu(struct sched_domain *);\n+\t\tif (!sched_domains_sd)\n+\t\t\treturn sa_sd_shared;\n+\t}\n+\td-\u003esd = sched_domains_sd;\n \td-\u003erd = alloc_rootdomain();\n \tif (!d-\u003erd)\n \t\treturn sa_sd;\n@@ -1871,7 +1929,8 @@ static void claim_allocations(int cpu, struct s_data *d)\n {\n \tstruct sched_domain *sd;\n \n-\tif (atomic_read(\u0026(*per_cpu_ptr(d-\u003esds, cpu))-\u003eref))\n+\tif (*per_cpu_ptr(d-\u003esds, cpu) \u0026\u0026\n+\t atomic_read(\u0026(*per_cpu_ptr(d-\u003esds, cpu))-\u003eref))\n \t\t*per_cpu_ptr(d-\u003esds, cpu) = NULL;\n \n \tfor (sd = *per_cpu_ptr(d-\u003esd, cpu); sd; sd = sd-\u003eparent) {\n@@ -1880,10 +1939,12 @@ static void claim_allocations(int cpu, struct s_data *d)\n \t\tWARN_ON_ONCE(*per_cpu_ptr(sdd-\u003esd, cpu) != sd);\n \t\t*per_cpu_ptr(sdd-\u003esd, cpu) = NULL;\n \n-\t\tif (atomic_read(\u0026(*per_cpu_ptr(sdd-\u003esg, cpu))-\u003eref))\n+\t\tif (*per_cpu_ptr(sdd-\u003esg, cpu) \u0026\u0026\n+\t\t atomic_read(\u0026(*per_cpu_ptr(sdd-\u003esg, cpu))-\u003eref))\n \t\t\t*per_cpu_ptr(sdd-\u003esg, cpu) = NULL;\n \n-\t\tif (atomic_read(\u0026(*per_cpu_ptr(sdd-\u003esgc, cpu))-\u003eref))\n+\t\tif (*per_cpu_ptr(sdd-\u003esgc, cpu) \u0026\u0026\n+\t\t atomic_read(\u0026(*per_cpu_ptr(sdd-\u003esgc, cpu))-\u003eref))\n \t\t\t*per_cpu_ptr(sdd-\u003esgc, cpu) = NULL;\n \t}\n }\n@@ -1975,6 +2036,7 @@ sd_init(struct sched_domain_topology_level *tl,\n \n \t\t.last_balance\t\t= jiffies,\n \t\t.balance_interval\t= sd_weight,\n+\t\t.span_weight\t\t= sd_weight,\n \n \t\t/* 50% success rate */\n \t\t.newidle_call\t\t= 512,\n@@ -2115,6 +2177,22 @@ void __init set_sched_topology(struct sched_domain_topology_level *tl)\n }\n \n #ifdef CONFIG_NUMA\n+static void __sdt_free_percpu(void)\n+{\n+\tstruct sched_domain_topology_level *tl;\n+\n+\tfor_each_sd_topology(tl) {\n+\t\tstruct sd_data *sdd = \u0026tl-\u003edata;\n+\n+\t\tfree_percpu(sdd-\u003esd);\n+\t\tsdd-\u003esd = NULL;\n+\t\tfree_percpu(sdd-\u003esg);\n+\t\tsdd-\u003esg = NULL;\n+\t\tfree_percpu(sdd-\u003esgc);\n+\t\tsdd-\u003esgc = NULL;\n+\t}\n+}\n+\n static int cpu_numa_flags(void)\n {\n \treturn SD_NUMA;\n@@ -2405,6 +2483,7 @@ void sched_init_numa(int offline_node)\n \t/*\n \t * Copy the default topology bits..\n \t */\n+\t__sdt_free_percpu();\n \tfor (i = 0; sched_domain_topology[i].mask; i++)\n \t\ttl[i] = sched_domain_topology[i];\n \n@@ -2463,6 +2542,7 @@ static void sched_reset_numa(void)\n \t\tkfree(masks);\n \t}\n \tif (sched_domain_topology_saved) {\n+\t\t__sdt_free_percpu();\n \t\tkfree(sched_domain_topology);\n \t\tsched_domain_topology = sched_domain_topology_saved;\n \t\tsched_domain_topology_saved = NULL;\n@@ -2664,22 +2744,26 @@ static int __sdt_alloc(const struct cpumask *cpu_map)\n \tfor_each_sd_topology(tl) {\n \t\tstruct sd_data *sdd = \u0026tl-\u003edata;\n \n-\t\tsdd-\u003esd = alloc_percpu(struct sched_domain *);\n-\t\tif (!sdd-\u003esd)\n-\t\t\treturn -ENOMEM;\n+\t\tif (!sdd-\u003esd) {\n+\t\t\tsdd-\u003esd = alloc_percpu(struct sched_domain *);\n+\t\t\tif (!sdd-\u003esd)\n+\t\t\t\treturn -ENOMEM;\n+\t\t}\n \n-\t\tsdd-\u003esg = alloc_percpu(struct sched_group *);\n-\t\tif (!sdd-\u003esg)\n-\t\t\treturn -ENOMEM;\n+\t\tif (!sdd-\u003esg) {\n+\t\t\tsdd-\u003esg = alloc_percpu(struct sched_group *);\n+\t\t\tif (!sdd-\u003esg)\n+\t\t\t\treturn -ENOMEM;\n+\t\t}\n \n-\t\tsdd-\u003esgc = alloc_percpu(struct sched_group_capacity *);\n-\t\tif (!sdd-\u003esgc)\n-\t\t\treturn -ENOMEM;\n+\t\tif (!sdd-\u003esgc) {\n+\t\t\tsdd-\u003esgc = alloc_percpu(struct sched_group_capacity *);\n+\t\t\tif (!sdd-\u003esgc)\n+\t\t\t\treturn -ENOMEM;\n+\t\t}\n \n \t\tfor_each_cpu(j, cpu_map) {\n \t\t\tstruct sched_domain *sd;\n-\t\t\tstruct sched_group *sg;\n-\t\t\tstruct sched_group_capacity *sgc;\n \n \t\t\tsd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),\n \t\t\t\t\tGFP_KERNEL, cpu_to_node(j));\n@@ -2687,24 +2771,6 @@ static int __sdt_alloc(const struct cpumask *cpu_map)\n \t\t\t\treturn -ENOMEM;\n \n \t\t\t*per_cpu_ptr(sdd-\u003esd, j) = sd;\n-\n-\t\t\tsg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),\n-\t\t\t\t\tGFP_KERNEL, cpu_to_node(j));\n-\t\t\tif (!sg)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tsg-\u003enext = sg;\n-\n-\t\t\t*per_cpu_ptr(sdd-\u003esg, j) = sg;\n-\n-\t\t\tsgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),\n-\t\t\t\t\tGFP_KERNEL, cpu_to_node(j));\n-\t\t\tif (!sgc)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tsgc-\u003eid = j;\n-\n-\t\t\t*per_cpu_ptr(sdd-\u003esgc, j) = sgc;\n \t\t}\n \t}\n \n@@ -2724,43 +2790,36 @@ static void __sdt_free(const struct cpumask *cpu_map)\n \n \t\t\tif (sdd-\u003esd) {\n \t\t\t\tsd = *per_cpu_ptr(sdd-\u003esd, j);\n-\t\t\t\tif (sd \u0026\u0026 (sd-\u003eflags \u0026 SD_NUMA))\n-\t\t\t\t\tfree_sched_groups(sd-\u003egroups, 0);\n-\t\t\t\tkfree(*per_cpu_ptr(sdd-\u003esd, j));\n+\t\t\t\tif (sd) {\n+\t\t\t\t\tif (sd-\u003eflags \u0026 SD_NUMA)\n+\t\t\t\t\t\tfree_sched_groups(sd-\u003egroups, 0);\n+\t\t\t\t\tkfree(sd);\n+\t\t\t\t\t*per_cpu_ptr(sdd-\u003esd, j) = NULL;\n+\t\t\t\t}\n \t\t\t}\n \n-\t\t\tif (sdd-\u003esg)\n+\t\t\tif (sdd-\u003esg) {\n \t\t\t\tkfree(*per_cpu_ptr(sdd-\u003esg, j));\n-\t\t\tif (sdd-\u003esgc)\n+\t\t\t\t*per_cpu_ptr(sdd-\u003esg, j) = NULL;\n+\t\t\t}\n+\t\t\tif (sdd-\u003esgc) {\n \t\t\t\tkfree(*per_cpu_ptr(sdd-\u003esgc, j));\n+\t\t\t\t*per_cpu_ptr(sdd-\u003esgc, j) = NULL;\n+\t\t\t}\n \t\t}\n-\t\tfree_percpu(sdd-\u003esd);\n-\t\tsdd-\u003esd = NULL;\n-\t\tfree_percpu(sdd-\u003esg);\n-\t\tsdd-\u003esg = NULL;\n-\t\tfree_percpu(sdd-\u003esgc);\n-\t\tsdd-\u003esgc = NULL;\n \t}\n }\n \n+static struct sched_domain_shared * __percpu *sched_domains_sds;\n+\n static int __sds_alloc(struct s_data *d, const struct cpumask *cpu_map)\n {\n-\tint j;\n-\n-\td-\u003esds = alloc_percpu(struct sched_domain_shared *);\n-\tif (!d-\u003esds)\n-\t\treturn -ENOMEM;\n-\n-\tfor_each_cpu(j, cpu_map) {\n-\t\tstruct sched_domain_shared *sds;\n-\n-\t\tsds = kzalloc_node(sizeof(struct sched_domain_shared),\n-\t\t\t\tGFP_KERNEL, cpu_to_node(j));\n-\t\tif (!sds)\n+\tif (!sched_domains_sds) {\n+\t\tsched_domains_sds = alloc_percpu(struct sched_domain_shared *);\n+\t\tif (!sched_domains_sds)\n \t\t\treturn -ENOMEM;\n-\n-\t\t*per_cpu_ptr(d-\u003esds, j) = sds;\n \t}\n+\td-\u003esds = sched_domains_sds;\n \n \treturn 0;\n }\n@@ -2772,10 +2831,11 @@ static void __sds_free(struct s_data *d, const struct cpumask *cpu_map)\n \tif (!d-\u003esds)\n \t\treturn;\n \n-\tfor_each_cpu(j, cpu_map)\n+\tfor_each_cpu(j, cpu_map) {\n \t\tkfree(*per_cpu_ptr(d-\u003esds, j));\n+\t\t*per_cpu_ptr(d-\u003esds, j) = NULL;\n+\t}\n \n-\tfree_percpu(d-\u003esds);\n \td-\u003esds = NULL;\n }\n \n@@ -2799,6 +2859,7 @@ static struct sched_domain *build_sched_domain(struct sched_domain_topology_leve\n \t\t\tcpumask_or(sched_domain_span(sd),\n \t\t\t\t sched_domain_span(sd),\n \t\t\t\t sched_domain_span(child));\n+\t\t\tsd-\u003espan_weight = cpumask_weight(sched_domain_span(sd));\n \t\t}\n \n \t}\n@@ -2932,7 +2993,7 @@ static void adjust_numa_imbalance(struct sched_domain *sd_llc)\n \t}\n }\n \n-static void\n+static int\n init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)\n {\n \tstruct sched_domain_shared *sds = NULL;\n@@ -2954,6 +3015,13 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)\n \t */\n \tfor_each_cpu(cpu, sched_domain_span(sd)) {\n \t\tsds = *per_cpu_ptr(d-\u003esds, cpu);\n+\t\tif (!sds) {\n+\t\t\tsds = kzalloc_node(sizeof(*sds),\n+\t\t\t\t\t GFP_KERNEL, cpu_to_node(cpu));\n+\t\t\tif (!sds)\n+\t\t\t\treturn -ENOMEM;\n+\t\t\t*per_cpu_ptr(d-\u003esds, cpu) = sds;\n+\t\t}\n \n \t\tif (!sds-\u003ealloc_flags ||\n \t\t sd-\u003espan_weight == 1 ||\n@@ -2978,6 +3046,8 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)\n \t */\n \tatomic_set(\u0026sd-\u003eshared-\u003enr_busy_cpus, sd-\u003espan_weight);\n \tatomic_inc(\u0026sd-\u003eshared-\u003eref);\n+\n+\treturn 0;\n }\n \n /*\n@@ -2993,25 +3063,24 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)\n * degenerate into a single-capacity group. The relevant edge cases are instead\n * covered by the caveats above.\n *\n- * Return true if this CPU's asym path claimed sd-\u003eshared, false otherwise.\n+ * Return 0 on success or if not claimed, -ENOMEM on allocation failure.\n */\n-static bool claim_asym_sched_domain_shared(struct s_data *d, int cpu)\n+static int claim_asym_sched_domain_shared(struct s_data *d, int cpu)\n {\n \tstruct sched_domain *sd = *per_cpu_ptr(d-\u003esd, cpu);\n \tstruct sched_domain *sd_asym;\n \n \tif (!sd)\n-\t\treturn false;\n+\t\treturn 0;\n \n \tsd_asym = sd;\n \twhile (sd_asym \u0026\u0026 !(sd_asym-\u003eflags \u0026 SD_ASYM_CPUCAPACITY_FULL))\n \t\tsd_asym = sd_asym-\u003eparent;\n \n \tif (!sd_asym || (sd_asym-\u003eflags \u0026 SD_NUMA))\n-\t\treturn false;\n+\t\treturn 0;\n \n-\tinit_sched_domain_shared(d, sd_asym, SD_ASYM_CPUCAPACITY);\n-\treturn true;\n+\treturn init_sched_domain_shared(d, sd_asym, SD_ASYM_CPUCAPACITY);\n }\n \n static int __sched_domains_alloc_llc_id(void)\n@@ -3141,7 +3210,6 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att\n \t/* Build the groups for the domains */\n \tfor_each_cpu(i, cpu_map) {\n \t\tfor (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd-\u003eparent) {\n-\t\t\tsd-\u003espan_weight = cpumask_weight(sched_domain_span(sd));\n \t\t\tif (sd-\u003eflags \u0026 SD_NUMA) {\n \t\t\t\tif (build_overlap_sched_groups(sd, i))\n \t\t\t\t\tgoto error;\n@@ -3157,15 +3225,16 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att\n \t\tif (!sd)\n \t\t\tcontinue;\n \n-\t\tif (has_asym)\n-\t\t\tclaim_asym_sched_domain_shared(\u0026d, i);\n+\t\tif (has_asym \u0026\u0026 claim_asym_sched_domain_shared(\u0026d, i))\n+\t\t\tgoto error;\n \n \t\t/* First, find the topmost SD_SHARE_LLC domain */\n \t\twhile (sd-\u003eparent \u0026\u0026 (sd-\u003eparent-\u003eflags \u0026 SD_SHARE_LLC))\n \t\t\tsd = sd-\u003eparent;\n \n \t\tif (sd-\u003eflags \u0026 SD_SHARE_LLC) {\n-\t\t\tinit_sched_domain_shared(\u0026d, sd, SD_SHARE_LLC);\n+\t\t\tif (init_sched_domain_shared(\u0026d, sd, SD_SHARE_LLC))\n+\t\t\t\tgoto error;\n \n \t\t\t/*\n \t\t\t * In presence of higher domains, adjust the\ndiff --git a/mm/vmstat.c b/mm/vmstat.c\nindex cb57714539fb5..c671e9669b43b 100644\n--- a/mm/vmstat.c\n+++ b/mm/vmstat.c\n@@ -280,41 +280,45 @@ void refresh_zone_stat_thresholds(void)\n \tint cpu;\n \tint threshold;\n \n-\t/* Zero current pgdat thresholds */\n \tfor_each_online_pgdat(pgdat) {\n-\t\tfor_each_online_cpu(cpu) {\n-\t\t\tper_cpu_ptr(pgdat-\u003eper_cpu_nodestats, cpu)-\u003estat_threshold = 0;\n-\t\t}\n-\t}\n+\t\tint pgdat_threshold = 0;\n \n-\tfor_each_populated_zone(zone) {\n-\t\tstruct pglist_data *pgdat = zone-\u003ezone_pgdat;\n-\t\tunsigned long max_drift, tolerate_drift;\n+\t\tfor (int z = 0; z \u003c MAX_NR_ZONES; z++) {\n+\t\t\tunsigned long max_drift, tolerate_drift;\n \n-\t\tthreshold = calculate_normal_threshold(zone);\n+\t\t\tzone = \u0026pgdat-\u003enode_zones[z];\n+\t\t\tif (!populated_zone(zone))\n+\t\t\t\tcontinue;\n \n-\t\tfor_each_online_cpu(cpu) {\n-\t\t\tint pgdat_threshold;\n+\t\t\tthreshold = calculate_normal_threshold(zone);\n+\t\t\t/* Base nodestat threshold on the largest populated zone. */\n+\t\t\tpgdat_threshold = max(threshold, pgdat_threshold);\n \n-\t\t\tper_cpu_ptr(zone-\u003eper_cpu_zonestats, cpu)-\u003estat_threshold\n-\t\t\t\t\t\t\t= threshold;\n+\t\t\tfor_each_online_cpu(cpu) {\n+\t\t\t\ts8 *pz = \u0026per_cpu_ptr(zone-\u003eper_cpu_zonestats, cpu)-\u003estat_threshold;\n \n-\t\t\t/* Base nodestat threshold on the largest populated zone. */\n-\t\t\tpgdat_threshold = per_cpu_ptr(pgdat-\u003eper_cpu_nodestats, cpu)-\u003estat_threshold;\n-\t\t\tper_cpu_ptr(pgdat-\u003eper_cpu_nodestats, cpu)-\u003estat_threshold\n-\t\t\t\t= max(threshold, pgdat_threshold);\n+\t\t\t\tif (*pz != threshold)\n+\t\t\t\t\t*pz = threshold;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * Only set percpu_drift_mark if there is a danger that\n+\t\t\t * NR_FREE_PAGES reports the low watermark is ok when in fact\n+\t\t\t * the min watermark could be breached by an allocation\n+\t\t\t */\n+\t\t\ttolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);\n+\t\t\tmax_drift = num_online_cpus() * threshold;\n+\t\t\tif (max_drift \u003e tolerate_drift)\n+\t\t\t\tzone-\u003epercpu_drift_mark = high_wmark_pages(zone) +\n+\t\t\t\t\t\tmax_drift;\n \t\t}\n \n-\t\t/*\n-\t\t * Only set percpu_drift_mark if there is a danger that\n-\t\t * NR_FREE_PAGES reports the low watermark is ok when in fact\n-\t\t * the min watermark could be breached by an allocation\n-\t\t */\n-\t\ttolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);\n-\t\tmax_drift = num_online_cpus() * threshold;\n-\t\tif (max_drift \u003e tolerate_drift)\n-\t\t\tzone-\u003epercpu_drift_mark = high_wmark_pages(zone) +\n-\t\t\t\t\tmax_drift;\n+\t\tfor_each_online_cpu(cpu) {\n+\t\t\ts8 *pn = \u0026per_cpu_ptr(pgdat-\u003eper_cpu_nodestats, cpu)-\u003estat_threshold;\n+\n+\t\t\tif (*pn != pgdat_threshold)\n+\t\t\t\t*pn = pgdat_threshold;\n+\t\t}\n \t}\n }\n \n"
} |
| 2/1 | 2026/10/15 20:25 | agent | patch-evaluator |
3mModel:core Args: null Results: {
"EnableConfigs": [
"SMP",
"NUMA",
"CPUSETS",
"SCHED_CACHE",
"MTRR",
"X86_MCE_INTEL",
"HOTPLUG_CPU"
],
"FocusSymbols": [
"build_sched_domains",
"build_overlap_sched_groups",
"build_sched_groups",
"init_overlap_sched_group",
"partition_sched_domains",
"dl_rebuild_rd_accounting",
"dl_clear_root_domain",
"dl_clear_root_domain_cpu",
"refresh_zone_stat_thresholds",
"sched_update_llc_bytes",
"mtrr_generic_state_matches",
"cmci_rediscover",
"cmci_clear"
],
"Reasoning": "The patch series touches several core kernel subsystems reachable in standard QEMU amd64 environments, including scheduler topology building (kernel/sched/topology.c), deadline scheduler bandwidth accounting (kernel/sched/deadline.c), memory management vmstat threshold updates (mm/vmstat.c), and x86 MTRR/CMCI handling. The scheduler changes rework sched_group allocations, traversal loops, and root domain accounting, which can be exercised via cpuset cgroup operations, CPU hotplug, and SCHED_DEADLINE syscalls. These non-trivial algorithmic and memory management adjustments warrant fuzzing to uncover potential memory corruptions, invariant violations, or regressions.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit dab0643a8b764d66dba17d993938331f8cba14a3
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 15 20:25:44 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h
index 76b95bd1a4058..0527e216808c4 100644
--- a/arch/x86/include/asm/mtrr.h
+++ b/arch/x86/include/asm/mtrr.h
@@ -61,6 +61,7 @@ extern int amd_special_default_mtrr(void);
void mtrr_disable(void);
void mtrr_enable(void);
void mtrr_generic_set_state(void);
+bool mtrr_generic_state_matches(void);
# else
static inline void guest_force_mtrr_state(struct mtrr_var_range *var,
unsigned int num_var,
@@ -106,6 +107,7 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
#define mtrr_disable() do {} while (0)
#define mtrr_enable() do {} while (0)
#define mtrr_generic_set_state() do {} while (0)
+static inline bool mtrr_generic_state_matches(void) { return true; }
# endif
#ifdef CONFIG_COMPAT
diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c
index 13ed16527905f..64796d5b34fdc 100644
--- a/arch/x86/kernel/cpu/cacheinfo.c
+++ b/arch/x86/kernel/cpu/cacheinfo.c
@@ -770,6 +770,13 @@ static int cache_ap_online(unsigned int cpu)
if (!memory_caching_control || get_cache_aps_delayed_init())
return 0;
+ if (!(memory_caching_control & CACHE_MTRR) ||
+ mtrr_generic_state_matches()) {
+ if (memory_caching_control & CACHE_PAT)
+ pat_cpu_init();
+ return 0;
+ }
+
/*
* Ideally we should hold mtrr_mutex here to avoid MTRR entries
* changed, but this routine will be called in CPU boot time,
diff --git a/arch/x86/kernel/cpu/mce/intel.c b/arch/x86/kernel/cpu/mce/intel.c
index 4655223ba5606..f57718b097d9b 100644
--- a/arch/x86/kernel/cpu/mce/intel.c
+++ b/arch/x86/kernel/cpu/mce/intel.c
@@ -11,6 +11,7 @@
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/cpumask.h>
+#include <linux/topology.h>
#include <asm/apic.h>
#include <asm/cpufeature.h>
#include <asm/cpu_device_id.h>
@@ -40,6 +41,8 @@
* owned by the offlined cpu.
*/
static DEFINE_PER_CPU(mce_banks_t, mce_banks_owned);
+static mce_banks_t cmci_orphaned_banks;
+static unsigned int cmci_orphaned_target;
/*
* cmci_discover_lock protects against parallel discovery attempts
@@ -244,6 +247,7 @@ static void cmci_claim_bank(int bank, u64 val, int bios_zero_thresh, int *bios_w
/* This CPU successfully set the enable bit. */
set_bit(bank, (void *)this_cpu_ptr(&mce_banks_owned));
+ __clear_bit(bank, cmci_orphaned_banks);
if ((val & MCI_CTL2_CMCI_THRESHOLD_MASK) == CMCI_STORM_THRESHOLD) {
pr_notice("CPU%d BANK%d CMCI inherited storm\n", smp_processor_id(), bank);
@@ -328,6 +332,7 @@ static void __cmci_disable_bank(int bank)
val &= ~MCI_CTL2_CMCI_EN;
wrmsrq(MSR_IA32_MCx_CTL2(bank), val);
__clear_bit(bank, this_cpu_ptr(mce_banks_owned));
+ __set_bit(bank, cmci_orphaned_banks);
if ((val & MCI_CTL2_CMCI_THRESHOLD_MASK) == CMCI_STORM_THRESHOLD)
cmci_storm_end(bank);
@@ -339,6 +344,8 @@ static void __cmci_disable_bank(int bank)
*/
void cmci_clear(void)
{
+ unsigned int cpu = smp_processor_id();
+ unsigned int target;
unsigned long flags;
int i;
int banks;
@@ -346,8 +353,31 @@ void cmci_clear(void)
if (!cmci_supported(&banks))
return;
raw_spin_lock_irqsave(&cmci_discover_lock, flags);
+ bitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);
+ /*
+ * A bank is shared by the CPUs of one SMT core, L2 cluster, die or
+ * package, and these scopes nest. The first online peer found from
+ * the innermost scope outwards is therefore part of every sharing
+ * scope that still has an online CPU, so it alone can claim every
+ * orphaned bank that is still claimable. If it already owns an index,
+ * the instance released here is not shared with it, hence not with
+ * any online CPU, and that index can be dropped from the rediscovery.
+ */
+ target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu);
+ if (target >= nr_cpu_ids)
+ target = cpumask_any_but(topology_cluster_cpumask(cpu), cpu);
+ if (target >= nr_cpu_ids)
+ target = cpumask_any_but(topology_die_cpumask(cpu), cpu);
+ if (target >= nr_cpu_ids)
+ target = cpumask_any_but(topology_core_cpumask(cpu), cpu);
+ cmci_orphaned_target = target;
for (i = 0; i < banks; i++)
__cmci_disable_bank(i);
+ if (target < nr_cpu_ids)
+ bitmap_andnot(cmci_orphaned_banks, cmci_orphaned_banks,
+ per_cpu(mce_banks_owned, target), MAX_NR_BANKS);
+ else
+ bitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);
raw_spin_unlock_irqrestore(&cmci_discover_lock, flags);
}
@@ -360,15 +390,38 @@ static void cmci_rediscover_work_func(void *arg)
cmci_discover(banks);
}
-/* After a CPU went down cycle through all the others and rediscover */
+/*
+ * After a CPU went down, rediscover orphaned shared banks on a single
+ * topology peer (SMT sibling, L2 cluster peer, die peer, or package peer)
+ * selected in cmci_clear(). Core-private banks whose core is now completely
+ * offline cannot be claimed by any other core, so probing more than one
+ * peer is redundant.
+ */
void cmci_rediscover(void)
{
+ unsigned int target;
+ unsigned long flags;
+ bool orphaned;
int banks;
if (!cmci_supported(&banks))
return;
- on_each_cpu(cmci_rediscover_work_func, NULL, 1);
+ raw_spin_lock_irqsave(&cmci_discover_lock, flags);
+ orphaned = !bitmap_empty(cmci_orphaned_banks, MAX_NR_BANKS);
+ target = cmci_orphaned_target;
+ raw_spin_unlock_irqrestore(&cmci_discover_lock, flags);
+
+ if (!orphaned)
+ return;
+
+ if (target < nr_cpu_ids && cpu_online(target))
+ smp_call_function_single(target, cmci_rediscover_work_func,
+ NULL, 1);
+
+ raw_spin_lock_irqsave(&cmci_discover_lock, flags);
+ bitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);
+ raw_spin_unlock_irqrestore(&cmci_discover_lock, flags);
}
/*
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index 67cf69f24b005..8760a7b4dc38b 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -981,6 +981,55 @@ void mtrr_generic_set_state(void)
}
}
+bool mtrr_generic_state_matches(void)
+{
+ struct msr val;
+ unsigned int i;
+
+ if (!cpu_feature_enabled(X86_FEATURE_MTRR))
+ return true;
+
+ rdmsrq(MSR_MTRRdefType, val.q);
+ if ((val.l & MTRR_DEF_TYPE_TYPE) != mtrr_state.def_type ||
+ ((val.l & MTRR_DEF_TYPE_ENABLE) >> MTRR_STATE_SHIFT) != mtrr_state.enabled)
+ return false;
+
+ for (i = 0; i < num_var_ranges; i++) {
+ struct mtrr_var_range *vr = &mtrr_state.var_ranges[i];
+
+ rdmsrq(MTRRphysBase_MSR(i), val.q);
+ if ((vr->base_lo & ~MTRR_PHYSBASE_RSVD) != (val.l & ~MTRR_PHYSBASE_RSVD) ||
+ (vr->base_hi & ~phys_hi_rsvd) != (val.h & ~phys_hi_rsvd))
+ return false;
+
+ rdmsrq(MTRRphysMask_MSR(i), val.q);
+ if ((vr->mask_lo & ~MTRR_PHYSMASK_RSVD) != (val.l & ~MTRR_PHYSMASK_RSVD) ||
+ (vr->mask_hi & ~phys_hi_rsvd) != (val.h & ~phys_hi_rsvd))
+ return false;
+ }
+
+ if (mtrr_state.have_fixed) {
+ u64 *saved = (u64 *)mtrr_state.fixed_ranges;
+ int block = -1, range;
+
+ /*
+ * Ensure MtrrFixDramModEn is clear so fixed MTRR reads do not
+ * expose K8 RdMem/WrMem bits and cause false mismatches.
+ */
+ k8_check_syscfg_dram_mod_en();
+
+ while (fixed_range_blocks[++block].ranges) {
+ for (range = 0; range < fixed_range_blocks[block].ranges; range++) {
+ rdmsrq(fixed_range_blocks[block].base_msr + range, val.q);
+ if (val.q != *saved++)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
/**
* generic_set_mtrr - set variable MTRR register on the local CPU.
*
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index ba01a9e919b78..60a781dac9991 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1414,10 +1414,6 @@ void play_dead_common(void)
local_irq_disable();
}
-/*
- * We need to flush the caches before going to sleep, lest we have
- * dirty data in our caches when we come back up.
- */
void __noreturn mwait_play_dead(unsigned int eax_hint)
{
struct mwait_cpu_dead *md = this_cpu_ptr(&mwait_cpu_dead);
@@ -1426,15 +1422,21 @@ void __noreturn mwait_play_dead(unsigned int eax_hint)
md->status = CPUDEAD_MWAIT_WAIT;
md->control = CPUDEAD_MWAIT_WAIT;
- wbinvd();
+ /*
+ * MWAIT C-states keep the caches coherent, so runtime CPU offline
+ * does not need to flush them. System sleep, however, may remove
+ * power from this package's caches while all of its CPUs are
+ * offline, and the flush done by the boot CPU before entering the
+ * sleep state only covers the boot CPU's own package.
+ */
+ if (cpuhp_tasks_frozen)
+ wbinvd();
while (1) {
/*
* The CLFLUSH is a workaround for erratum AAI65 for
* the Xeon 7400 series. It's not clear it is actually
* needed, but it should be harmless in either case.
- * The WBINVD is insufficient due to the spurious-wakeup
- * case where we return around the loop.
*/
mb();
clflush(md);
diff --git a/arch/x86/realmode/rm/trampoline_32.S b/arch/x86/realmode/rm/trampoline_32.S
index 3fad907a179f1..14e56ea954160 100644
--- a/arch/x86/realmode/rm/trampoline_32.S
+++ b/arch/x86/realmode/rm/trampoline_32.S
@@ -30,8 +30,6 @@
.balign PAGE_SIZE
SYM_CODE_START(trampoline_start)
- wbinvd # Needed for NUMA-Q should be harmless for others
-
LJMPW_RM(1f)
1:
mov %cs, %ax # Code and data in the same place
diff --git a/arch/x86/realmode/rm/trampoline_64.S b/arch/x86/realmode/rm/trampoline_64.S
index 14d9c7daf90f4..e5a3f03d62217 100644
--- a/arch/x86/realmode/rm/trampoline_64.S
+++ b/arch/x86/realmode/rm/trampoline_64.S
@@ -60,7 +60,6 @@
.balign PAGE_SIZE
SYM_CODE_START(trampoline_start)
cli # We should be safe anyway
- wbinvd
LJMPW_RM(1f)
1:
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 914983387e955..8baa99308f49e 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -129,7 +129,7 @@ static inline int cpuset_do_page_mem_spread(void)
extern bool current_cpuset_is_being_rebound(void);
-extern void dl_rebuild_rd_accounting(void);
+void dl_rebuild_rd_accounting(void);
extern void rebuild_sched_domains(void);
extern void cpuset_print_current_mems_allowed(void);
@@ -273,9 +273,7 @@ static inline bool current_cpuset_is_being_rebound(void)
return false;
}
-static inline void dl_rebuild_rd_accounting(void)
-{
-}
+void dl_rebuild_rd_accounting(void);
static inline void rebuild_sched_domains(void)
{
diff --git a/include/linux/sched/deadline.h b/include/linux/sched/deadline.h
index 273538200a445..6f03357f05c76 100644
--- a/include/linux/sched/deadline.h
+++ b/include/linux/sched/deadline.h
@@ -31,7 +31,6 @@ static inline bool dl_time_before(u64 a, u64 b)
struct root_domain;
extern void dl_add_task_root_domain(struct task_struct *p);
-extern void dl_clear_root_domain(struct root_domain *rd);
extern void dl_clear_root_domain_cpu(int cpu);
/*
* Return whether moving DL task @p to @new_mask requires moving DL
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 857dbe3519a86..01f2f24e3b814 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1885,24 +1885,6 @@ void sched_init_dl_servers(void)
}
}
-void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq)
-{
- u64 new_bw = dl_se->dl_bw;
- int cpu = cpu_of(rq);
- struct dl_bw *dl_b;
-
- if (!dl_se->dl_bw_attached)
- return;
-
- dl_b = dl_bw_of(cpu_of(rq));
- guard(raw_spinlock)(&dl_b->lock);
-
- if (!dl_bw_cpus(cpu))
- return;
-
- __dl_add(dl_b, new_bw, dl_bw_cpus(cpu));
-}
-
int dl_server_apply_params(struct sched_dl_entity *dl_se, u64 runtime, u64 period, bool init)
{
u64 old_bw = (init || !dl_se->dl_bw_attached) ? 0 :
@@ -3448,18 +3430,22 @@ void dl_add_task_root_domain(struct task_struct *p)
raw_spin_unlock_irqrestore(&p->pi_lock, rf.flags);
}
-static void dl_server_add_bw(struct root_domain *rd, int cpu)
+static void dl_server_sum_bw(int cpu, int cpus, u64 *total_bw, s64 *extra_bw)
{
struct sched_dl_entity *dl_se;
dl_se = &cpu_rq(cpu)->fair_server;
- if (dl_server(dl_se) && dl_se->dl_bw_attached && cpu_active(cpu))
- __dl_add(&rd->dl_bw, dl_se->dl_bw, dl_bw_cpus(cpu));
+ if (dl_server(dl_se) && dl_se->dl_bw_attached) {
+ *total_bw += dl_se->dl_bw;
+ *extra_bw -= (s32)dl_se->dl_bw / cpus;
+ }
#ifdef CONFIG_SCHED_CLASS_EXT
dl_se = &cpu_rq(cpu)->ext_server;
- if (dl_server(dl_se) && dl_se->dl_bw_attached && cpu_active(cpu))
- __dl_add(&rd->dl_bw, dl_se->dl_bw, dl_bw_cpus(cpu));
+ if (dl_server(dl_se) && dl_se->dl_bw_attached) {
+ *total_bw += dl_se->dl_bw;
+ *extra_bw -= (s32)dl_se->dl_bw / cpus;
+ }
#endif
}
@@ -3480,26 +3466,32 @@ static u64 dl_server_read_bw(int cpu)
return dl_bw;
}
-void dl_clear_root_domain(struct root_domain *rd)
+static void dl_clear_root_domain(struct root_domain *rd)
{
- int i;
+ u64 total_bw = 0;
+ s64 extra_bw = 0;
+ int i, cpus;
guard(raw_spinlock_irqsave)(&rd->dl_bw.lock);
- /*
- * Reset total_bw to zero and extra_bw to max_bw so that next
- * loop will add dl-servers contributions back properly,
- */
- rd->dl_bw.total_bw = 0;
- for_each_cpu(i, rd->span)
- cpu_rq(i)->dl.extra_bw = cpu_rq(i)->dl.max_bw;
-
/*
* dl_servers are not tasks. Since dl_add_task_root_domain ignores
* them, we need to account for them here explicitly.
*/
- for_each_cpu(i, rd->span)
- dl_server_add_bw(rd, i);
+ cpus = cpumask_weight_and(rd->span, cpu_active_mask);
+ if (cpus) {
+ for_each_cpu_and(i, rd->span, cpu_active_mask)
+ dl_server_sum_bw(i, cpus, &total_bw, &extra_bw);
+ }
+
+ rd->dl_bw.total_bw = total_bw;
+ for_each_cpu(i, rd->span) {
+ struct rq *rq = cpu_rq(i);
+
+ rq->dl.extra_bw = rq->dl.max_bw;
+ if (cpu_active(i))
+ rq->dl.extra_bw += extra_bw;
+ }
}
void dl_clear_root_domain_cpu(int cpu)
@@ -3507,6 +3499,33 @@ void dl_clear_root_domain_cpu(int cpu)
dl_clear_root_domain(cpu_rq(cpu)->rd);
}
+#ifndef CONFIG_CPUSETS
+void dl_rebuild_rd_accounting(void)
+{
+ struct task_struct *g, *p;
+ u64 cookie = ++dl_cookie;
+ int cpu;
+
+ lockdep_assert_cpus_held();
+ lockdep_assert_held(&sched_domains_mutex);
+
+ rcu_read_lock();
+ for_each_possible_cpu(cpu) {
+ if (dl_bw_visited(cpu, cookie))
+ continue;
+
+ dl_clear_root_domain_cpu(cpu);
+ }
+ for_each_process_thread(g, p) {
+ /* Racy pre-check; dl_add_task_root_domain() rechecks under pi_lock. */
+ if (!dl_prio(READ_ONCE(p->prio)))
+ continue;
+ dl_add_task_root_domain(p);
+ }
+ rcu_read_unlock();
+}
+#endif
+
static void switched_from_dl(struct rq *rq, struct task_struct *p)
{
/*
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index e656c7059bf86..e76a6876ac4e8 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -418,7 +418,6 @@ extern void sched_init_dl_servers(void);
extern void fair_server_init(struct rq *rq);
extern void ext_server_init(struct rq *rq);
-extern void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq);
extern int dl_server_apply_params(struct sched_dl_entity *dl_se,
u64 runtime, u64 period, bool init);
extern int dl_server_attach_bw(struct sched_dl_entity *dl_se);
@@ -2261,6 +2260,15 @@ struct sched_group_capacity {
int id;
+ /*
+ * Build-time cache shared by all groups using this sgc while
+ * build_sched_domains() runs; not maintained afterwards (e.g. by
+ * sched_update_asym_prefer_cpu()). Use the sched_group copies.
+ */
+ unsigned int group_weight;
+ unsigned int cores;
+ int asym_prefer_cpu;
+
unsigned long cpumask[]; /* Balance mask */
};
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 0248227d983a7..a9122dbff379d 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -502,19 +502,6 @@ void rq_attach_root(struct rq *rq, struct root_domain *rd)
if (cpumask_test_cpu(rq->cpu, cpu_active_mask))
set_rq_online(rq);
- /*
- * Because the rq is not a task, dl_add_task_root_domain() did not
- * move the fair server bw to the rd if it already started.
- * Add it now.
- */
- if (rq->fair_server.dl_server)
- __dl_server_attach_root(&rq->fair_server, rq);
-
-#ifdef CONFIG_SCHED_CLASS_EXT
- if (rq->ext_server.dl_server)
- __dl_server_attach_root(&rq->ext_server, rq);
-#endif
-
rq_unlock_irqrestore(rq, &rf);
if (old_rd)
@@ -916,9 +903,15 @@ static bool alloc_sd_llc(const struct cpumask *cpu_map,
top_llc = parent;
if (top_llc->flags & SD_SHARE_LLC) {
+ int first = cpumask_first(sched_domain_span(top_llc));
+
sd->llc_max = max_lid + 1;
sd->llc_counts = p;
- sd->llc_bytes = get_effective_llc_bytes(i, top_llc);
+ /* All CPUs in top_llc share the same LLC size and weight. */
+ if (first < i)
+ sd->llc_bytes = (*per_cpu_ptr(d->sd, first))->llc_bytes;
+ else
+ sd->llc_bytes = get_effective_llc_bytes(i, top_llc);
} else {
/* avoid memory leak */
kfree(p);
@@ -999,6 +992,7 @@ void sched_cache_active_set(void)
void sched_update_llc_bytes(unsigned int cpu)
{
struct sched_domain *sd, *sdp;
+ unsigned long llc_bytes;
unsigned int i;
sched_domains_mutex_lock();
@@ -1015,10 +1009,11 @@ void sched_update_llc_bytes(unsigned int cpu)
* sibling on each online event corrects this once the full
* shared_cpu_map is known.
*/
+ llc_bytes = get_effective_llc_bytes(cpu, sdp);
for_each_cpu(i, sched_domain_span(sdp)) {
sd = rcu_dereference_sched_domain(cpu_rq(i)->sd);
if (sd)
- sd->llc_bytes = get_effective_llc_bytes(i, sdp);
+ sd->llc_bytes = llc_bytes;
}
unlock:
@@ -1259,32 +1254,49 @@ build_group_from_child_sched_domain(struct sched_domain *sd, int cpu)
return sg;
}
-static void init_overlap_sched_group(struct sched_domain *sd,
- struct sched_group *sg)
+static int init_overlap_sched_group(struct sched_domain *sd,
+ struct sched_group *sg)
{
struct cpumask *mask = sched_domains_tmpmask2;
struct sd_data *sdd = sd->private;
- struct cpumask *sg_span;
- int cpu;
+ struct cpumask *sg_span = sched_group_span(sg);
+ struct sched_group_capacity *sgc;
+ int cpu = cpumask_first(sg_span);
+ struct sched_domain *first_sd = *per_cpu_ptr(sdd->sd, cpu);
+
+ sgc = *per_cpu_ptr(sdd->sgc, cpu);
+ if (sgc && atomic_read(&sgc->ref) > 0 &&
+ first_sd && first_sd->child &&
+ cpumask_equal(sg_span, sched_domain_span(first_sd->child))) {
+ atomic_inc(&sgc->ref);
+ sg->sgc = sgc;
+ return 0;
+ }
build_balance_mask(sd, sg, mask);
cpu = cpumask_first(mask);
- sg->sgc = *per_cpu_ptr(sdd->sgc, cpu);
- if (atomic_inc_return(&sg->sgc->ref) == 1)
+ sgc = *per_cpu_ptr(sdd->sgc, cpu);
+ if (!sgc) {
+ sgc = kzalloc_node(sizeof(*sgc) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sgc)
+ return -ENOMEM;
+ sgc->id = cpu;
+ *per_cpu_ptr(sdd->sgc, cpu) = sgc;
+ }
+
+ sg->sgc = sgc;
+ if (atomic_inc_return(&sg->sgc->ref) == 1) {
cpumask_copy(group_balance_mask(sg), mask);
- else
+ sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
+ sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
+ sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
+ } else {
WARN_ON_ONCE(!cpumask_equal(group_balance_mask(sg), mask));
+ }
- /*
- * Initialize sgc->capacity such that even if we mess up the
- * domains and no possible iteration will get us here, we won't
- * die on a /0 trap.
- */
- sg_span = sched_group_span(sg);
- sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
- sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
- sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
+ return 0;
}
static struct sched_domain *
@@ -1317,19 +1329,18 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
{
struct sched_group *first = NULL, *last = NULL, *sg;
const struct cpumask *span = sched_domain_span(sd);
- struct cpumask *covered = sched_domains_tmpmask;
+ struct cpumask *uncovered = sched_domains_tmpmask;
struct sd_data *sdd = sd->private;
struct sched_domain *sibling;
int i;
- cpumask_clear(covered);
+ cpumask_copy(uncovered, span);
- for_each_cpu_wrap(i, span, cpu) {
+ for (i = cpu; i < nr_cpu_ids;
+ i = cpumask_next(i, uncovered),
+ i = i < nr_cpu_ids ? i : cpumask_first(uncovered)) {
struct cpumask *sg_span;
- if (cpumask_test_cpu(i, covered))
- continue;
-
sibling = *per_cpu_ptr(sdd->sd, i);
/*
@@ -1342,8 +1353,10 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
* Domains should always include the CPU they're built on, so
* check that.
*/
- if (!cpumask_test_cpu(i, sched_domain_span(sibling)))
+ if (!cpumask_test_cpu(i, sched_domain_span(sibling))) {
+ __cpumask_clear_cpu(i, uncovered);
continue;
+ }
/*
* Usually we build sched_group by sibling's child sched_domain
@@ -1385,9 +1398,12 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
goto fail;
sg_span = sched_group_span(sg);
- cpumask_or(covered, covered, sg_span);
+ cpumask_andnot(uncovered, uncovered, sg_span);
- init_overlap_sched_group(sibling, sg);
+ if (init_overlap_sched_group(sibling, sg)) {
+ kfree(sg);
+ goto fail;
+ }
if (!first)
first = sg;
@@ -1489,6 +1505,25 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd)
cpu = cpumask_first(sched_domain_span(child));
sg = *per_cpu_ptr(sdd->sg, cpu);
+ if (!sg) {
+ sg = kzalloc_node(sizeof(*sg) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sg)
+ return NULL;
+ sg->next = sg;
+ *per_cpu_ptr(sdd->sg, cpu) = sg;
+ }
+
+ if (!*per_cpu_ptr(sdd->sgc, cpu)) {
+ struct sched_group_capacity *sgc;
+
+ sgc = kzalloc_node(sizeof(*sgc) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sgc)
+ return NULL;
+ sgc->id = cpu;
+ *per_cpu_ptr(sdd->sgc, cpu) = sgc;
+ }
sg->sgc = *per_cpu_ptr(sdd->sgc, cpu);
/* Increase refcounts for claim_allocations: */
@@ -1504,12 +1539,13 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd)
cpumask_copy(sched_group_span(sg), sched_domain_span(child));
cpumask_copy(group_balance_mask(sg), sched_group_span(sg));
sg->flags = child->flags;
+ sg->sgc->capacity = SCHED_CAPACITY_SCALE * child->span_weight;
} else {
cpumask_set_cpu(cpu, sched_group_span(sg));
cpumask_set_cpu(cpu, group_balance_mask(sg));
+ sg->sgc->capacity = SCHED_CAPACITY_SCALE;
}
- sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sched_group_span(sg));
sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
@@ -1529,23 +1565,24 @@ build_sched_groups(struct sched_domain *sd, int cpu)
struct sched_group *first = NULL, *last = NULL;
struct sd_data *sdd = sd->private;
const struct cpumask *span = sched_domain_span(sd);
- struct cpumask *covered;
+ struct cpumask *uncovered;
int i;
lockdep_assert_held(&sched_domains_mutex);
- covered = sched_domains_tmpmask;
+ uncovered = sched_domains_tmpmask;
- cpumask_clear(covered);
+ cpumask_copy(uncovered, span);
- for_each_cpu_wrap(i, span, cpu) {
+ for (i = cpu; i < nr_cpu_ids;
+ i = cpumask_next(i, uncovered),
+ i = i < nr_cpu_ids ? i : cpumask_first(uncovered)) {
struct sched_group *sg;
- if (cpumask_test_cpu(i, covered))
- continue;
-
sg = get_group(i, sdd);
+ if (!sg)
+ return -ENOMEM;
- cpumask_or(covered, covered, sched_group_span(sg));
+ cpumask_andnot(uncovered, uncovered, sched_group_span(sg));
if (!first)
first = sg;
@@ -1577,27 +1614,40 @@ static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
WARN_ON(!sg);
do {
- int cpu, cores = 0, max_cpu = -1;
+ int i, cores = 0, max_cpu = -1;
+
+ if (sg->group_weight)
+ goto next;
+
+ if (sg->sgc->group_weight) {
+ sg->group_weight = sg->sgc->group_weight;
+ sg->cores = sg->sgc->cores;
+ sg->asym_prefer_cpu = sg->sgc->asym_prefer_cpu;
+ goto next;
+ }
sg->group_weight = cpumask_weight(sched_group_span(sg));
cpumask_copy(mask, sched_group_span(sg));
- for_each_cpu(cpu, mask) {
+ for_each_cpu(i, mask) {
cores++;
- cpumask_andnot(mask, mask, cpu_smt_mask(cpu));
+ cpumask_andnot(mask, mask, cpu_smt_mask(i));
}
sg->cores = cores;
- if (!(sd->flags & SD_ASYM_PACKING))
- goto next;
-
- for_each_cpu(cpu, sched_group_span(sg)) {
- if (max_cpu < 0)
- max_cpu = cpu;
- else if (sched_asym_prefer(cpu, max_cpu))
- max_cpu = cpu;
+ if (sd->flags & SD_ASYM_PACKING) {
+ for_each_cpu(i, sched_group_span(sg)) {
+ if (max_cpu < 0)
+ max_cpu = i;
+ else if (sched_asym_prefer(i, max_cpu))
+ max_cpu = i;
+ }
+ sg->asym_prefer_cpu = max_cpu;
}
- sg->asym_prefer_cpu = max_cpu;
+
+ sg->sgc->group_weight = sg->group_weight;
+ sg->sgc->cores = sg->cores;
+ sg->sgc->asym_prefer_cpu = sg->asym_prefer_cpu;
next:
sg = sg->next;
@@ -1821,16 +1871,21 @@ static int __sdt_alloc(const struct cpumask *cpu_map);
static void __sds_free(struct s_data *d, const struct cpumask *cpu_map);
static int __sds_alloc(struct s_data *d, const struct cpumask *cpu_map);
+static struct sched_domain * __percpu *sched_domains_sd;
+
static void __free_domain_allocs(struct s_data *d, enum s_alloc what,
const struct cpumask *cpu_map)
{
+ int j;
+
switch (what) {
case sa_rootdomain:
if (!atomic_read(&d->rd->refcount))
free_rootdomain(&d->rd->rcu);
fallthrough;
case sa_sd:
- free_percpu(d->sd);
+ for_each_cpu(j, cpu_map)
+ *per_cpu_ptr(d->sd, j) = NULL;
fallthrough;
case sa_sd_shared:
__sds_free(d, cpu_map);
@@ -1852,9 +1907,12 @@ __visit_domain_allocation_hell(struct s_data *d, const struct cpumask *cpu_map)
return sa_sd_storage;
if (__sds_alloc(d, cpu_map))
return sa_sd_shared;
- d->sd = alloc_percpu(struct sched_domain *);
- if (!d->sd)
- return sa_sd_shared;
+ if (!sched_domains_sd) {
+ sched_domains_sd = alloc_percpu(struct sched_domain *);
+ if (!sched_domains_sd)
+ return sa_sd_shared;
+ }
+ d->sd = sched_domains_sd;
d->rd = alloc_rootdomain();
if (!d->rd)
return sa_sd;
@@ -1871,7 +1929,8 @@ static void claim_allocations(int cpu, struct s_data *d)
{
struct sched_domain *sd;
- if (atomic_read(&(*per_cpu_ptr(d->sds, cpu))->ref))
+ if (*per_cpu_ptr(d->sds, cpu) &&
+ atomic_read(&(*per_cpu_ptr(d->sds, cpu))->ref))
*per_cpu_ptr(d->sds, cpu) = NULL;
for (sd = *per_cpu_ptr(d->sd, cpu); sd; sd = sd->parent) {
@@ -1880,10 +1939,12 @@ static void claim_allocations(int cpu, struct s_data *d)
WARN_ON_ONCE(*per_cpu_ptr(sdd->sd, cpu) != sd);
*per_cpu_ptr(sdd->sd, cpu) = NULL;
- if (atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
+ if (*per_cpu_ptr(sdd->sg, cpu) &&
+ atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
*per_cpu_ptr(sdd->sg, cpu) = NULL;
- if (atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
+ if (*per_cpu_ptr(sdd->sgc, cpu) &&
+ atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
*per_cpu_ptr(sdd->sgc, cpu) = NULL;
}
}
@@ -1975,6 +2036,7 @@ sd_init(struct sched_domain_topology_level *tl,
.last_balance = jiffies,
.balance_interval = sd_weight,
+ .span_weight = sd_weight,
/* 50% success rate */
.newidle_call = 512,
@@ -2115,6 +2177,22 @@ void __init set_sched_topology(struct sched_domain_topology_level *tl)
}
#ifdef CONFIG_NUMA
+static void __sdt_free_percpu(void)
+{
+ struct sched_domain_topology_level *tl;
+
+ for_each_sd_topology(tl) {
+ struct sd_data *sdd = &tl->data;
+
+ free_percpu(sdd->sd);
+ sdd->sd = NULL;
+ free_percpu(sdd->sg);
+ sdd->sg = NULL;
+ free_percpu(sdd->sgc);
+ sdd->sgc = NULL;
+ }
+}
+
static int cpu_numa_flags(void)
{
return SD_NUMA;
@@ -2405,6 +2483,7 @@ void sched_init_numa(int offline_node)
/*
* Copy the default topology bits..
*/
+ __sdt_free_percpu();
for (i = 0; sched_domain_topology[i].mask; i++)
tl[i] = sched_domain_topology[i];
@@ -2463,6 +2542,7 @@ static void sched_reset_numa(void)
kfree(masks);
}
if (sched_domain_topology_saved) {
+ __sdt_free_percpu();
kfree(sched_domain_topology);
sched_domain_topology = sched_domain_topology_saved;
sched_domain_topology_saved = NULL;
@@ -2664,22 +2744,26 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
for_each_sd_topology(tl) {
struct sd_data *sdd = &tl->data;
- sdd->sd = alloc_percpu(struct sched_domain *);
- if (!sdd->sd)
- return -ENOMEM;
+ if (!sdd->sd) {
+ sdd->sd = alloc_percpu(struct sched_domain *);
+ if (!sdd->sd)
+ return -ENOMEM;
+ }
- sdd->sg = alloc_percpu(struct sched_group *);
- if (!sdd->sg)
- return -ENOMEM;
+ if (!sdd->sg) {
+ sdd->sg = alloc_percpu(struct sched_group *);
+ if (!sdd->sg)
+ return -ENOMEM;
+ }
- sdd->sgc = alloc_percpu(struct sched_group_capacity *);
- if (!sdd->sgc)
- return -ENOMEM;
+ if (!sdd->sgc) {
+ sdd->sgc = alloc_percpu(struct sched_group_capacity *);
+ if (!sdd->sgc)
+ return -ENOMEM;
+ }
for_each_cpu(j, cpu_map) {
struct sched_domain *sd;
- struct sched_group *sg;
- struct sched_group_capacity *sgc;
sd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
GFP_KERNEL, cpu_to_node(j));
@@ -2687,24 +2771,6 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
return -ENOMEM;
*per_cpu_ptr(sdd->sd, j) = sd;
-
- sg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(j));
- if (!sg)
- return -ENOMEM;
-
- sg->next = sg;
-
- *per_cpu_ptr(sdd->sg, j) = sg;
-
- sgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(j));
- if (!sgc)
- return -ENOMEM;
-
- sgc->id = j;
-
- *per_cpu_ptr(sdd->sgc, j) = sgc;
}
}
@@ -2724,43 +2790,36 @@ static void __sdt_free(const struct cpumask *cpu_map)
if (sdd->sd) {
sd = *per_cpu_ptr(sdd->sd, j);
- if (sd && (sd->flags & SD_NUMA))
- free_sched_groups(sd->groups, 0);
- kfree(*per_cpu_ptr(sdd->sd, j));
+ if (sd) {
+ if (sd->flags & SD_NUMA)
+ free_sched_groups(sd->groups, 0);
+ kfree(sd);
+ *per_cpu_ptr(sdd->sd, j) = NULL;
+ }
}
- if (sdd->sg)
+ if (sdd->sg) {
kfree(*per_cpu_ptr(sdd->sg, j));
- if (sdd->sgc)
+ *per_cpu_ptr(sdd->sg, j) = NULL;
+ }
+ if (sdd->sgc) {
kfree(*per_cpu_ptr(sdd->sgc, j));
+ *per_cpu_ptr(sdd->sgc, j) = NULL;
+ }
}
- free_percpu(sdd->sd);
- sdd->sd = NULL;
- free_percpu(sdd->sg);
- sdd->sg = NULL;
- free_percpu(sdd->sgc);
- sdd->sgc = NULL;
}
}
+static struct sched_domain_shared * __percpu *sched_domains_sds;
+
static int __sds_alloc(struct s_data *d, const struct cpumask *cpu_map)
{
- int j;
-
- d->sds = alloc_percpu(struct sched_domain_shared *);
- if (!d->sds)
- return -ENOMEM;
-
- for_each_cpu(j, cpu_map) {
- struct sched_domain_shared *sds;
-
- sds = kzalloc_node(sizeof(struct sched_domain_shared),
- GFP_KERNEL, cpu_to_node(j));
- if (!sds)
+ if (!sched_domains_sds) {
+ sched_domains_sds = alloc_percpu(struct sched_domain_shared *);
+ if (!sched_domains_sds)
return -ENOMEM;
-
- *per_cpu_ptr(d->sds, j) = sds;
}
+ d->sds = sched_domains_sds;
return 0;
}
@@ -2772,10 +2831,11 @@ static void __sds_free(struct s_data *d, const struct cpumask *cpu_map)
if (!d->sds)
return;
- for_each_cpu(j, cpu_map)
+ for_each_cpu(j, cpu_map) {
kfree(*per_cpu_ptr(d->sds, j));
+ *per_cpu_ptr(d->sds, j) = NULL;
+ }
- free_percpu(d->sds);
d->sds = NULL;
}
@@ -2799,6 +2859,7 @@ static struct sched_domain *build_sched_domain(struct sched_domain_topology_leve
cpumask_or(sched_domain_span(sd),
sched_domain_span(sd),
sched_domain_span(child));
+ sd->span_weight = cpumask_weight(sched_domain_span(sd));
}
}
@@ -2932,7 +2993,7 @@ static void adjust_numa_imbalance(struct sched_domain *sd_llc)
}
}
-static void
+static int
init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
{
struct sched_domain_shared *sds = NULL;
@@ -2954,6 +3015,13 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
*/
for_each_cpu(cpu, sched_domain_span(sd)) {
sds = *per_cpu_ptr(d->sds, cpu);
+ if (!sds) {
+ sds = kzalloc_node(sizeof(*sds),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sds)
+ return -ENOMEM;
+ *per_cpu_ptr(d->sds, cpu) = sds;
+ }
if (!sds->alloc_flags ||
sd->span_weight == 1 ||
@@ -2978,6 +3046,8 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
*/
atomic_set(&sd->shared->nr_busy_cpus, sd->span_weight);
atomic_inc(&sd->shared->ref);
+
+ return 0;
}
/*
@@ -2993,25 +3063,24 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
* degenerate into a single-capacity group. The relevant edge cases are instead
* covered by the caveats above.
*
- * Return true if this CPU's asym path claimed sd->shared, false otherwise.
+ * Return 0 on success or if not claimed, -ENOMEM on allocation failure.
*/
-static bool claim_asym_sched_domain_shared(struct s_data *d, int cpu)
+static int claim_asym_sched_domain_shared(struct s_data *d, int cpu)
{
struct sched_domain *sd = *per_cpu_ptr(d->sd, cpu);
struct sched_domain *sd_asym;
if (!sd)
- return false;
+ return 0;
sd_asym = sd;
while (sd_asym && !(sd_asym->flags & SD_ASYM_CPUCAPACITY_FULL))
sd_asym = sd_asym->parent;
if (!sd_asym || (sd_asym->flags & SD_NUMA))
- return false;
+ return 0;
- init_sched_domain_shared(d, sd_asym, SD_ASYM_CPUCAPACITY);
- return true;
+ return init_sched_domain_shared(d, sd_asym, SD_ASYM_CPUCAPACITY);
}
static int __sched_domains_alloc_llc_id(void)
@@ -3141,7 +3210,6 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
/* Build the groups for the domains */
for_each_cpu(i, cpu_map) {
for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
- sd->span_weight = cpumask_weight(sched_domain_span(sd));
if (sd->flags & SD_NUMA) {
if (build_overlap_sched_groups(sd, i))
goto error;
@@ -3157,15 +3225,16 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
if (!sd)
continue;
- if (has_asym)
- claim_asym_sched_domain_shared(&d, i);
+ if (has_asym && claim_asym_sched_domain_shared(&d, i))
+ goto error;
/* First, find the topmost SD_SHARE_LLC domain */
while (sd->parent && (sd->parent->flags & SD_SHARE_LLC))
sd = sd->parent;
if (sd->flags & SD_SHARE_LLC) {
- init_sched_domain_shared(&d, sd, SD_SHARE_LLC);
+ if (init_sched_domain_shared(&d, sd, SD_SHARE_LLC))
+ goto error;
/*
* In presence of higher domains, adjust the
diff --git a/mm/vmstat.c b/mm/vmstat.c
index cb57714539fb5..c671e9669b43b 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -280,41 +280,45 @@ void refresh_zone_stat_thresholds(void)
int cpu;
int threshold;
- /* Zero current pgdat thresholds */
for_each_online_pgdat(pgdat) {
- for_each_online_cpu(cpu) {
- per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold = 0;
- }
- }
+ int pgdat_threshold = 0;
- for_each_populated_zone(zone) {
- struct pglist_data *pgdat = zone->zone_pgdat;
- unsigned long max_drift, tolerate_drift;
+ for (int z = 0; z < MAX_NR_ZONES; z++) {
+ unsigned long max_drift, tolerate_drift;
- threshold = calculate_normal_threshold(zone);
+ zone = &pgdat->node_zones[z];
+ if (!populated_zone(zone))
+ continue;
- for_each_online_cpu(cpu) {
- int pgdat_threshold;
+ threshold = calculate_normal_threshold(zone);
+ /* Base nodestat threshold on the largest populated zone. */
+ pgdat_threshold = max(threshold, pgdat_threshold);
- per_cpu_ptr(zone->per_cpu_zonestats, cpu)->stat_threshold
- = threshold;
+ for_each_online_cpu(cpu) {
+ s8 *pz = &per_cpu_ptr(zone->per_cpu_zonestats, cpu)->stat_threshold;
- /* Base nodestat threshold on the largest populated zone. */
- pgdat_threshold = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold;
- per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold
- = max(threshold, pgdat_threshold);
+ if (*pz != threshold)
+ *pz = threshold;
+ }
+
+ /*
+ * Only set percpu_drift_mark if there is a danger that
+ * NR_FREE_PAGES reports the low watermark is ok when in fact
+ * the min watermark could be breached by an allocation
+ */
+ tolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);
+ max_drift = num_online_cpus() * threshold;
+ if (max_drift > tolerate_drift)
+ zone->percpu_drift_mark = high_wmark_pages(zone) +
+ max_drift;
}
- /*
- * Only set percpu_drift_mark if there is a danger that
- * NR_FREE_PAGES reports the low watermark is ok when in fact
- * the min watermark could be breached by an allocation
- */
- tolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);
- max_drift = num_online_cpus() * threshold;
- if (max_drift > tolerate_drift)
- zone->percpu_drift_mark = high_wmark_pages(zone) +
- max_drift;
+ for_each_online_cpu(cpu) {
+ s8 *pn = &per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold;
+
+ if (*pn != pgdat_threshold)
+ *pn = pgdat_threshold;
+ }
}
}
|
| 3/2 | 2026/10/15 20:25 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18713 output: 22 thoughts: 645 Thoughts: **Evaluating the Patch Series for Syzkaller Fuzzing Potential** Okay, let's get down to brass tacks. I need to assess whether this patch series is a good candidate for syzkaller fuzzing. First, I need to make sure I'm following the right procedure. The instructions are clear: reachability is the primary concern – can these changes be executed in a standard virtualized environment like GCE or QEMU? We're dealing with amd64, so that's good. Alright, let's look at the files: 1. `arch/x86/include/asm/mtrr.h` 2. `arch/x86/kernel/cpu/cacheinfo.c` 3. `arch/x86/kernel/cpu/mce/intel.c` 4. `arch/x86/kernel/cpu/mtrr/generic.c` 5. `arch/x86/kernel/smpboot.c` 6. `arch/x86/realmode/rm/trampoline_32.S` 7. `arch/x86/realmode/rm/trampoline_64.S` 8. `include/linux/cpuset.h` 9. `include/linux/sched/deadline.h` 10. `kernel/sched/deadline.c` 11. `kernel/sched/sched.h` 12. `kernel/sched/topology.c` 13. `mm/vmstat.c` Wait a minute... these files span a few subsystems. It's not just one isolated area. This touches *x86* MTRR, cacheinfo, Intel MCE, SMP boot, realmode trampolines, and also `kernel/sched/deadline.c`, `kernel/sched/topology.c`, and `mm/vmstat.c`. This is intriguing, could potentially impact some interesting interactions. Okay, let's focus. I need to determine if the changes in those scheduler and memory management files are reachable and worth the effort for fuzzing. Actually, let's zoom in on `kernel/sched/deadline.c`, `kernel/sched/topology.c`, and `mm/vmstat.c`. Let's see what's changed there. In `kernel/sched/deadline.c`, I see functions like `dl_clear_root_domain`, `dl_server_sum_bw`, and `dl_rebuild_rd_accounting` are without `CONFIG_CPUSETS`. That's interesting, potential interactions there. `kernel/sched/topology.c` exposes functions like `build_overlap_sched_groups`, `build_sched_groups`, `get_group`, `init_overlap_sched_group`, `sched_domains_sd`, `claim_allocations`, `build_sched_domain`, `init_sched_domain_shared`, etc. When are these even called? Ah, right! CPU hotplug, cpuset changes (cgroups), `sched_init_smp`, etc. This looks like a promising area. And finally, in `mm/vmstat.c`, there's `refresh_zone_stat_thresholds()`. When is *that* function called? Let's check with `grepper`. |
| 4/2 | 2026/10/15 20:25 | tool | grepper |
0mArgs:{
"Expression": "refresh_zone_stat_thresholds"
}Results: {
"Output": "Documentation/mm/physical_memory.rst=374=General\n--\nDocumentation/mm/physical_memory.rst-539- the number of free pages to avoid per-cpu counter drift allowing watermarks\nDocumentation/mm/physical_memory.rst:540: to be breached. It is updated in ``refresh_zone_stat_thresholds()``.\nDocumentation/mm/physical_memory.rst-541-\n--\ninclude/linux/vmstat.h=310=void cpu_vm_stats_fold(int cpu);\ninclude/linux/vmstat.h:311:void refresh_zone_stat_thresholds(void);\ninclude/linux/vmstat.h-312-\n--\ninclude/linux/vmstat.h=392=static inline void __dec_node_page_state(struct page *page,\n--\ninclude/linux/vmstat.h-412-\ninclude/linux/vmstat.h:413:static inline void refresh_zone_stat_thresholds(void) { }\ninclude/linux/vmstat.h-414-static inline void cpu_vm_stats_fold(int cpu) { }\n--\nmm/page_alloc.c=6741=int __meminit init_per_zone_wmark_min(void)\n--\nmm/page_alloc.c-6744-\tsetup_per_zone_wmarks();\nmm/page_alloc.c:6745:\trefresh_zone_stat_thresholds();\nmm/page_alloc.c-6746-\tsetup_per_zone_lowmem_reserve();\n--\nmm/vmstat.c=226=int calculate_normal_threshold(struct zone *zone)\n--\nmm/vmstat.c-275- */\nmm/vmstat.c:276:void refresh_zone_stat_thresholds(void)\nmm/vmstat.c-277-{\n--\nmm/vmstat.c=2210=static int vmstat_cpu_online(unsigned int cpu)\n--\nmm/vmstat.c-2212-\tif (vmstat_late_init_done)\nmm/vmstat.c:2213:\t\trefresh_zone_stat_thresholds();\nmm/vmstat.c-2214-\n--\nmm/vmstat.c=2229=static int vmstat_cpu_dead(unsigned int cpu)\n--\nmm/vmstat.c-2235-\nmm/vmstat.c:2236:\trefresh_zone_stat_thresholds();\nmm/vmstat.c-2237-\tnode_cpus = cpumask_of_node(node);\n--\nmm/vmstat.c=2246=static int __init vmstat_late_init(void)\nmm/vmstat.c-2247-{\nmm/vmstat.c:2248:\trefresh_zone_stat_thresholds();\nmm/vmstat.c-2249-\tvmstat_late_init_done = 1;\n"
} |
| 5/2 | 2026/10/15 20:25 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20860 output: 31 thoughts: 433 Thoughts: **Investigating the `refresh_zone_stat_thresholds()` Call Chain** Okay, so I need to trace back where `refresh_zone_stat_thresholds()` gets invoked. My initial thought is to start with `init_per_zone_wmark_min`. That rings a bell... I believe that's connected to the `min_free_kbytes` sysctl handler, specifically `min_free_kbytes_sysctl_handler` or potentially `watermark_scale_factor_sysctl_handler`, and even memory hotplug. Let's dig deeper. I'll need to go through the git log, or if that's not readily available, then I'll look into commit messages. I will also inspect the `init_per_zone_wmark_min` function itself. Now, I also know that there are some interesting interactions happening in `kernel/sched/topology.c` and potentially in `kernel/sched/deadline.c`, so I'll keep those files in mind. Ideally, I'd run `git log` on HEAD, but I'm realizing that might not be available right now, so I'll need to adapt my approach. I'll start by searching for function calls or carefully inspecting the relevant files. Hmm, this commit seems like it might be a backport or related to a batch of commits from the stable branch, likely involving the scheduler, x86 architecture, or memory management. I will have to look into that. So, let's use `git grep` to investigate, or simply inspect the commit. I'm seeing if `kernel/sched/topology.c` has been modified. Alright, let's take a look. I'll need to examine all the functions within `kernel/sched/topology.c` to understand the context. Functions like `init_overlap_sched_group`, `build_overlap_sched_groups`, `get_group`, `build_sched_groups`, `init_sched_groups_capacity`, `__free_domain_allocs`, `__visit_domain_allocation_hell`, `claim_allocations`, `__sdt_free_percpu`, `sched_init_numa`, `sched_reset_numa`, `__sdt_alloc`, `__sdt_free`, `__sds_alloc`, `__sds_free`, `init_sched_domain_shared`, `claim_asym_sched_domain_shared`, and `build_sched_domains`. Finally, I want to find out when `build_sched_domains()` is called. To do this I will check the callers of `build_sched_domains`, including `partition_sched_domains_locked` and `rebuild_sched_domains_locked`. |
| 6/2 | 2026/10/15 20:26 | tool | grepper |
0mArgs:{
"Expression": "build_sched_domains\\(",
"PathPrefixes": [
"kernel/sched/"
]
}Results: {
"Output": "kernel/sched/cpufreq_schedutil.c=755=static int sugov_init(struct cpufreq_policy *policy)\n--\nkernel/sched/cpufreq_schedutil.c-812-\t */\nkernel/sched/cpufreq_schedutil.c:813:\tem_rebuild_sched_domains();\nkernel/sched/cpufreq_schedutil.c-814-\tmutex_unlock(\u0026global_tunables_lock);\n--\nkernel/sched/cpufreq_schedutil.c=836=static void sugov_exit(struct cpufreq_policy *policy)\n--\nkernel/sched/cpufreq_schedutil.c-854-\nkernel/sched/cpufreq_schedutil.c:855:\tem_rebuild_sched_domains();\nkernel/sched/cpufreq_schedutil.c-856-}\n--\nkernel/sched/rt.c=2869=static int sched_rt_handler(const struct ctl_table *table, int write, void *buffer,\n--\nkernel/sched/rt.c-2905-\t */\nkernel/sched/rt.c:2906:\trebuild_sched_domains();\nkernel/sched/rt.c-2907-\n--\nkernel/sched/sched.h=2249=struct sched_group_capacity {\n--\nkernel/sched/sched.h-2264-\t * Build-time cache shared by all groups using this sgc while\nkernel/sched/sched.h:2265:\t * build_sched_domains() runs; not maintained afterwards (e.g. by\nkernel/sched/sched.h-2266-\t * sched_update_asym_prefer_cpu()). Use the sched_group copies.\n--\nkernel/sched/topology.c=265=void rebuild_sched_domains_energy(void)\n--\nkernel/sched/topology.c-268-\tsched_energy_update = true;\nkernel/sched/topology.c:269:\trebuild_sched_domains();\nkernel/sched/topology.c-270-\tsched_energy_update = false;\n--\nkernel/sched/topology.c=844=int sysctl_sched_cache_user = 1;\n--\nkernel/sched/topology.c-853- * Returns 0 if cacheinfo is not yet populated. This happens during\nkernel/sched/topology.c:854: * early boot when build_sched_domains() runs before the generic\nkernel/sched/topology.c-855- * cacheinfo framework has been initialized (cacheinfo_cpu_online()\n--\nkernel/sched/topology.c=1328=build_overlap_sched_groups(struct sched_domain *sd, int cpu)\n--\nkernel/sched/topology.c-1350-\t\t *\nkernel/sched/topology.c:1351:\t\t * In that case build_sched_domains() will have terminated the\nkernel/sched/topology.c-1352-\t\t * iteration early and our sibling sd spans will be empty.\n--\nkernel/sched/topology.c=1804=static void asym_cpu_capacity_scan(void)\n--\nkernel/sched/topology.c-1834- * Initializers for schedule domains\nkernel/sched/topology.c:1835: * Non-inlined to reduce accumulated stack pressure in build_sched_domains()\nkernel/sched/topology.c-1836- */\n--\nkernel/sched/topology.c=3145=static int\nkernel/sched/topology.c:3146:build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *attr,\nkernel/sched/topology.c-3147-\t\t bool *multi_llcs)\n--\nkernel/sched/topology.c=3351=int __init sched_init_domains(const struct cpumask *cpu_map)\n--\nkernel/sched/topology.c-3367-\tcpumask_and(doms_cur[0], cpu_map, housekeeping_cpumask(HK_TYPE_DOMAIN));\nkernel/sched/topology.c:3368:\terr = build_sched_domains(doms_cur[0], NULL, \u0026multi_llcs);\nkernel/sched/topology.c-3369-\tif (!err)\n--\nkernel/sched/topology.c=3439=static void partition_sched_domains_locked(int ndoms_new, cpumask_var_t doms_new[],\n--\nkernel/sched/topology.c-3520-\t\t/* No match - add a new doms_new */\nkernel/sched/topology.c:3521:\t\tbuild_sched_domains(doms_new[i], dattr_new ? dattr_new + i : NULL,\nkernel/sched/topology.c-3522-\t\t\t\t \u0026multi_llcs);\n"
} |
| 7/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23128 output: 21 thoughts: 38 Thoughts: Here's my analysis of the provided thought process, tailored for an expert audience: **Analyzing Scheduler Domain Rebuilding** Okay, so I'm digging into scheduler domain management, specifically the rebuilding process. My immediate focus is on the functions `partition_sched_domains_locked` and, by extension, `rebuild_sched_domains`. The key here is understanding the call graph – who's actually *triggering* these functions? Determining the callers is absolutely crucial to grasping the overall flow and potential performance implications. Identifying the origin of these calls is the essential first step in understanding the context. I'm going to track down every instance where these function calls are occurring. |
| 8/2 | 2026/10/15 20:26 | tool | grepper |
0mArgs:{
"Expression": "rebuild_sched_domains\\("
}Results: {
"Output": "arch/s390/kernel/hiperdispatch.c-22- * hiperdispatch to make wrong decisions. This can potentially cause\narch/s390/kernel/hiperdispatch.c:23: * some overhead by leading to extra rebuild_sched_domains() calls\narch/s390/kernel/hiperdispatch.c-24- * for correction. Access to capacities within hiperdispatch has to be\n--\narch/s390/kernel/topology.c=347=static void topology_work_fn(struct work_struct *work)\narch/s390/kernel/topology.c-348-{\narch/s390/kernel/topology.c:349:\trebuild_sched_domains();\narch/s390/kernel/topology.c-350-}\n--\narch/x86/kernel/cpu/aperfmperf.c=374=bool arch_enable_hybrid_capacity_scale(void)\n--\narch/x86/kernel/cpu/aperfmperf.c-410- *\narch/x86/kernel/cpu/aperfmperf.c:411: * After calling this function for all CPUs, call arch_rebuild_sched_domains()\narch/x86/kernel/cpu/aperfmperf.c-412- * to let the scheduler know that capacity-aware scheduling can be used going\n--\narch/x86/kernel/itmt.c=42=static ssize_t sched_itmt_enabled_write(struct file *filp,\n--\narch/x86/kernel/itmt.c-55-\t\tx86_topology_update = true;\narch/x86/kernel/itmt.c:56:\t\trebuild_sched_domains();\narch/x86/kernel/itmt.c-57-\t}\n--\narch/x86/kernel/itmt.c=101=int sched_set_itmt_support(void)\n--\narch/x86/kernel/itmt.c-130-\tx86_topology_update = true;\narch/x86/kernel/itmt.c:131:\trebuild_sched_domains();\narch/x86/kernel/itmt.c-132-\n--\narch/x86/kernel/itmt.c=146=void sched_clear_itmt_support(void)\n--\narch/x86/kernel/itmt.c-163-\t\tx86_topology_update = true;\narch/x86/kernel/itmt.c:164:\t\trebuild_sched_domains();\narch/x86/kernel/itmt.c-165-\t}\n--\ndrivers/base/arch_topology.c=70=void topology_set_scale_freq_source(struct scale_freq_data *data,\n--\ndrivers/base/arch_topology.c-76-\t/*\ndrivers/base/arch_topology.c:77:\t * Avoid calling rebuild_sched_domains() unnecessarily if FIE is\ndrivers/base/arch_topology.c-78-\t * supported by cpufreq.\n--\ndrivers/base/arch_topology.c=223=static void update_topology_flags_workfn(struct work_struct *work)\n--\ndrivers/base/arch_topology.c-225-\tupdate_topology = 1;\ndrivers/base/arch_topology.c:226:\trebuild_sched_domains();\ndrivers/base/arch_topology.c-227-\tpr_debug(\"sched_domain hierarchy rebuilt, flags updated\\n\");\n--\ndrivers/cpufreq/cpufreq.c=1407=static int cpufreq_policy_online(struct cpufreq_policy *policy,\n--\ndrivers/cpufreq/cpufreq.c-1545-\t\t * Register with the energy model before\ndrivers/cpufreq/cpufreq.c:1546:\t\t * em_rebuild_sched_domains() is called, which will result\ndrivers/cpufreq/cpufreq.c-1547-\t\t * in rebuilding of the sched domains, which should only be done\n--\ndrivers/cpufreq/intel_pstate.c=1170=static void hybrid_update_capacity(struct cpudata *cpu)\n--\ndrivers/cpufreq/intel_pstate.c-1208-\tif (hybrid_register_perf_domain(cpu-\u003ecpu))\ndrivers/cpufreq/intel_pstate.c:1209:\t\tem_rebuild_sched_domains();\ndrivers/cpufreq/intel_pstate.c-1210-\n--\ninclude/linux/cpuset.h=132=void dl_rebuild_rd_accounting(void);\ninclude/linux/cpuset.h:133:extern void rebuild_sched_domains(void);\ninclude/linux/cpuset.h-134-\n--\ninclude/linux/cpuset.h=276=void dl_rebuild_rd_accounting(void);\ninclude/linux/cpuset.h-277-\ninclude/linux/cpuset.h:278:static inline void rebuild_sched_domains(void)\ninclude/linux/cpuset.h-279-{\n--\ninclude/linux/energy_model.h=189=void em_adjust_cpu_capacity(unsigned int cpu);\ninclude/linux/energy_model.h:190:void em_rebuild_sched_domains(void);\ninclude/linux/energy_model.h-191-\n--\ninclude/linux/energy_model.h=421=static inline void em_adjust_cpu_capacity(unsigned int cpu) {}\ninclude/linux/energy_model.h:422:static inline void em_rebuild_sched_domains(void) {}\ninclude/linux/energy_model.h-423-#endif\n--\nkernel/cgroup/cpuset.c=1062=static void rebuild_sched_domains_cpuslocked(void)\n--\nkernel/cgroup/cpuset.c-1068-\nkernel/cgroup/cpuset.c:1069:void rebuild_sched_domains(void)\nkernel/cgroup/cpuset.c-1070-{\n--\nkernel/cgroup/cpuset.c=4021=static void cpuset_handle_hotplug(void)\n--\nkernel/cgroup/cpuset.c-4105-\t/*\nkernel/cgroup/cpuset.c:4106:\t * rebuild_sched_domains() will always be called directly if needed\nkernel/cgroup/cpuset.c-4107-\t * to make sure that newly added or removed CPU will be reflected in\n--\nkernel/power/energy_model.c=997=static void rebuild_sd_workfn(struct work_struct *work)\n--\nkernel/power/energy_model.c-1001-\nkernel/power/energy_model.c:1002:void em_rebuild_sched_domains(void)\nkernel/power/energy_model.c-1003-{\n--\nkernel/power/energy_model.c-1008-\t * cpu_hotplug_lock is already held, so use a work item to\nkernel/power/energy_model.c:1009:\t * avoid nested locking in rebuild_sched_domains().\nkernel/power/energy_model.c-1010-\t */\n--\nkernel/sched/cpufreq_schedutil.c=755=static int sugov_init(struct cpufreq_policy *policy)\n--\nkernel/sched/cpufreq_schedutil.c-812-\t */\nkernel/sched/cpufreq_schedutil.c:813:\tem_rebuild_sched_domains();\nkernel/sched/cpufreq_schedutil.c-814-\tmutex_unlock(\u0026global_tunables_lock);\n--\nkernel/sched/cpufreq_schedutil.c=836=static void sugov_exit(struct cpufreq_policy *policy)\n--\nkernel/sched/cpufreq_schedutil.c-854-\nkernel/sched/cpufreq_schedutil.c:855:\tem_rebuild_sched_domains();\nkernel/sched/cpufreq_schedutil.c-856-}\n--\nkernel/sched/rt.c=2869=static int sched_rt_handler(const struct ctl_table *table, int write, void *buffer,\n--\nkernel/sched/rt.c-2905-\t */\nkernel/sched/rt.c:2906:\trebuild_sched_domains();\nkernel/sched/rt.c-2907-\n--\nkernel/sched/topology.c=265=void rebuild_sched_domains_energy(void)\n--\nkernel/sched/topology.c-268-\tsched_energy_update = true;\nkernel/sched/topology.c:269:\trebuild_sched_domains();\nkernel/sched/topology.c-270-\tsched_energy_update = false;\n"
} |
| 9/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25553 output: 21 thoughts: 83 Thoughts: Okay, here's my assessment of that observation, speaking as an expert: **Understanding Kernel Scheduler Domain Rebuilding** Right, so I'm seeing something interesting. Specifically, it seems that modifying certain cgroup attributes, like `cpuset.cpus` or the scheduling load balance, located in `kernel/cgroup/cpuset.c`, *or* even tweaking the sysctl `sched_rt_runtime_us` through `sched_rt_handler`, triggers a rebuild of the scheduler domains. This is definitely worth a closer look, as unnecessary rebuilding can be quite expensive, especially on large systems. The implicit recomputation is a critical performance consideration here. My immediate next step would be to delve into the `partition_sched_domains_locked` function to see exactly what's happening during this rebuild. It would be important to understand the specific criteria that necessitates this re-evaluation and how often this logic is invoked. I need to understand if this impacts realtime performance. I'll need to figure out what's driving this and if it's potentially causing any unnecessary overhead. I'm especially interested in whether there are any potential optimization points within this process. |
| 10/2 | 2026/10/15 20:26 | tool | grepper |
0mArgs:{
"Expression": "partition_sched_domains_locked"
}Results: {
"Output": "kernel/sched/topology.c=3397=static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur,\n--\nkernel/sched/topology.c-3438- */\nkernel/sched/topology.c:3439:static void partition_sched_domains_locked(int ndoms_new, cpumask_var_t doms_new[],\nkernel/sched/topology.c-3440-\t\t\t\t struct sched_domain_attr *dattr_new)\n--\nkernel/sched/topology.c=3563=void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],\n--\nkernel/sched/topology.c-3566-\tsched_domains_mutex_lock();\nkernel/sched/topology.c:3567:\tpartition_sched_domains_locked(ndoms_new, doms_new, dattr_new);\nkernel/sched/topology.c-3568-\tsched_domains_mutex_unlock();\n"
} |
| 11/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26163 output: 20 thoughts: 16 Thoughts: Okay, let's dive into this code. **Investigating the Callers of `partition_sched_domains`** Alright, so I need to understand the control flow here. The real question is: who, in the grand scheme of the kernel scheduler, is actually *using* this `partition_sched_domains` function? Knowing the calling context is absolutely critical to understanding its purpose and impact. I need to trace back from this function and see where it's being invoked. This will give me immediate insight into what is calling it and why. Let's see... time to start digging through the call graph. |
| 12/2 | 2026/10/15 20:26 | tool | grepper |
0mArgs:{
"Expression": "partition_sched_domains\\("
}Results: {
"Output": "Documentation/admin-guide/cgroup-v1/cpusets.rst=523=The scheduler remembers the currently active sched domain partitions.\nDocumentation/admin-guide/cgroup-v1/cpusets.rst:524:When the scheduler routine partition_sched_domains() is invoked from\nDocumentation/admin-guide/cgroup-v1/cpusets.rst-525-the cpuset code to update these sched domains, it compares the new\n--\ninclude/linux/cpuset.h=192=static inline void cpuset_update_active_cpus(void)\ninclude/linux/cpuset.h-193-{\ninclude/linux/cpuset.h:194:\tpartition_sched_domains(1, NULL, NULL);\ninclude/linux/cpuset.h-195-}\n--\ninclude/linux/cpuset.h=278=static inline void rebuild_sched_domains(void)\n--\ninclude/linux/cpuset.h-280-\tguard(cpus_read_lock)();\ninclude/linux/cpuset.h:281:\tpartition_sched_domains(1, NULL, NULL);\ninclude/linux/cpuset.h-282-}\n--\ninclude/linux/cpuset.h=284=static inline void cpuset_reset_sched_domains(void)\ninclude/linux/cpuset.h-285-{\ninclude/linux/cpuset.h:286:\tpartition_sched_domains(1, NULL, NULL);\ninclude/linux/cpuset.h-287-}\n--\ninclude/linux/sched/topology.h=175=static inline struct cpumask *sched_domain_span(struct sched_domain *sd)\n--\ninclude/linux/sched/topology.h-194-\ninclude/linux/sched/topology.h:195:extern void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],\ninclude/linux/sched/topology.h-196-\t\t\t\t struct sched_domain_attr *dattr_new);\ninclude/linux/sched/topology.h-197-\ninclude/linux/sched/topology.h:198:/* Allocate an array of sched domains, for partition_sched_domains(). */\ninclude/linux/sched/topology.h-199-cpumask_var_t *alloc_sched_domains(unsigned int ndoms);\n--\nkernel/cgroup/cpuset-v1.c=591=static void update_domain_attr_tree(struct sched_domain_attr *dattr,\n--\nkernel/cgroup/cpuset-v1.c-621- *\thaving the same root then form the one element of the partition\nkernel/cgroup/cpuset-v1.c:622: *\t(one sched domain) to be passed to partition_sched_domains().\nkernel/cgroup/cpuset-v1.c-623- */\nkernel/cgroup/cpuset-v1.c=624=int cpuset1_generate_sched_domains(cpumask_var_t **domains,\n--\nkernel/cgroup/cpuset-v1.c-754-\t * Fallback to the default domain if kmalloc() failed.\nkernel/cgroup/cpuset-v1.c:755:\t * See comments in partition_sched_domains().\nkernel/cgroup/cpuset-v1.c-756-\t */\n--\nkernel/cgroup/cpuset.c=746=static int validate_change(struct cpuset *cur, struct cpuset *trial)\n--\nkernel/cgroup/cpuset.c-817- * The output of this function needs to be passed to kernel/sched/core.c\nkernel/cgroup/cpuset.c:818: * partition_sched_domains() routine, which will rebuild the scheduler's\nkernel/cgroup/cpuset.c-819- * load balancing domains (sched domains) as specified by that partial\n--\nkernel/cgroup/cpuset.c-844- * doms - Conversion of 'csa' to an array of cpumasks, for passing to\nkernel/cgroup/cpuset.c:845: *\t the kernel/sched/core.c routine partition_sched_domains() in a\nkernel/cgroup/cpuset.c-846- *\t convenient format, that can be easily compared to the prior\n--\nkernel/cgroup/cpuset.c=850=static int generate_sched_domains(cpumask_var_t **domains,\n--\nkernel/cgroup/cpuset.c-944-\t * Fallback to the default domain if kmalloc() failed.\nkernel/cgroup/cpuset.c:945:\t * See comments in partition_sched_domains().\nkernel/cgroup/cpuset.c-946-\t */\n--\nkernel/cgroup/cpuset.c=1021=void rebuild_sched_domains_locked(void)\n--\nkernel/cgroup/cpuset.c-1038-\t* must not include any offline CPUs. Passing an offline CPU in the\nkernel/cgroup/cpuset.c:1039:\t* doms to partition_sched_domains() will trigger a kernel panic.\nkernel/cgroup/cpuset.c-1040-\t*\n--\nkernel/cgroup/cpuset.c-1053-\t/* Have scheduler rebuild the domains */\nkernel/cgroup/cpuset.c:1054:\tpartition_sched_domains(ndoms, doms, attr);\nkernel/cgroup/cpuset.c-1055-}\n--\nkernel/cgroup/cpuset.c=1076=void cpuset_reset_sched_domains(void)\n--\nkernel/cgroup/cpuset.c-1078-\tmutex_lock(\u0026cpuset_mutex);\nkernel/cgroup/cpuset.c:1079:\tpartition_sched_domains(1, NULL, NULL);\nkernel/cgroup/cpuset.c-1080-\tmutex_unlock(\u0026cpuset_mutex);\n--\nkernel/sched/core.c=8643=static int num_cpus_frozen;\n--\nkernel/sched/core.c-8647- * disabled, cpuset_update_active_cpus() becomes a simple wrapper\nkernel/sched/core.c:8648: * around partition_sched_domains().\nkernel/sched/core.c-8649- *\n--\nkernel/sched/topology.c=3397=static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur,\n--\nkernel/sched/topology.c-3429- * alloc call, then it can pass in doms_new == NULL \u0026\u0026 ndoms_new == 1,\nkernel/sched/topology.c:3430: * and partition_sched_domains() will fallback to the single partition\nkernel/sched/topology.c-3431- * 'fallback_doms', it also forces the domains to be rebuilt.\n--\nkernel/sched/topology.c=3439=static void partition_sched_domains_locked(int ndoms_new, cpumask_var_t doms_new[],\n--\nkernel/sched/topology.c-3562- */\nkernel/sched/topology.c:3563:void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],\nkernel/sched/topology.c-3564-\t\t\t struct sched_domain_attr *dattr_new)\n"
} |
| 13/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28166 output: 34 thoughts: 54 Thoughts: Here's my take on the thought process, as requested: **Deconstructing CPUset and Scheduling Domain Interaction** Okay, so I'm looking into how scheduling domains interact with cpusets in the kernel. The key function I need to understand is `partition_sched_domains`. I remember that's triggered when writing to cpusets, specifically within `rebuild_sched_domains_locked` in `kernel/cgroup/cpuset.c`. My next logical step is to track down all the call sites of `rebuild_sched_domains_locked`. Knowing all its uses will help me fully comprehend the circumstances under which `partition_sched_domains` is called and, ultimately, how the scheduling domain partitioning logic is applied with respect to cpusets. |
| 14/2 | 2026/10/15 20:26 | tool | grepper |
0mArgs:{
"Expression": "rebuild_sched_domains_locked",
"PathPrefixes": [
"kernel/cgroup/"
]
}Results: {
"Output": "kernel/cgroup/cpuset-internal.h=268=static inline bool cpuset_is_populated(struct cpuset *cs)\n--\nkernel/cgroup/cpuset-internal.h-301-\nkernel/cgroup/cpuset-internal.h:302:void rebuild_sched_domains_locked(void);\nkernel/cgroup/cpuset-internal.h-303-void cpuset_callback_lock_irq(void);\n--\nkernel/cgroup/cpuset-v1.c=148=static int update_relax_domain_level(struct cpuset *cs, s64 val)\n--\nkernel/cgroup/cpuset-v1.c-158-\t\t is_sched_load_balance(cs))\nkernel/cgroup/cpuset-v1.c:159:\t\t\trebuild_sched_domains_locked();\nkernel/cgroup/cpuset-v1.c-160-\t}\n--\nkernel/cgroup/cpuset.c=158=static cpumask_var_t\tisolated_hk_cpus;\t/* T */\n--\nkernel/cgroup/cpuset.c-171- * Note that update_relax_domain_level() in cpuset-v1.c can still call\nkernel/cgroup/cpuset.c:172: * rebuild_sched_domains_locked() directly without using this flag.\nkernel/cgroup/cpuset.c-173- */\n--\nkernel/cgroup/cpuset.c=971=void dl_rebuild_rd_accounting(void)\n--\nkernel/cgroup/cpuset.c-1020- */\nkernel/cgroup/cpuset.c:1021:void rebuild_sched_domains_locked(void)\nkernel/cgroup/cpuset.c-1022-{\n--\nkernel/cgroup/cpuset.c-1056-#else /* !CONFIG_SMP */\nkernel/cgroup/cpuset.c:1057:void rebuild_sched_domains_locked(void)\nkernel/cgroup/cpuset.c-1058-{\n--\nkernel/cgroup/cpuset.c=1062=static void rebuild_sched_domains_cpuslocked(void)\n--\nkernel/cgroup/cpuset.c-1064-\tmutex_lock(\u0026cpuset_mutex);\nkernel/cgroup/cpuset.c:1065:\trebuild_sched_domains_locked();\nkernel/cgroup/cpuset.c-1066-\tmutex_unlock(\u0026cpuset_mutex);\n--\nkernel/cgroup/cpuset.c=1181=static int update_partition_exclusive_flag(struct cpuset *cs, int new_prs)\n--\nkernel/cgroup/cpuset.c-1198- * Changing load balance flag will automatically call\nkernel/cgroup/cpuset.c:1199: * rebuild_sched_domains_locked().\nkernel/cgroup/cpuset.c-1200- * This function is for cgroup v2 only.\n--\nkernel/cgroup/cpuset.c=1406=static void cpuset_update_sd_hk_unlock(void)\n--\nkernel/cgroup/cpuset.c-1409-{\nkernel/cgroup/cpuset.c:1410:\t/* force_sd_rebuild will be cleared in rebuild_sched_domains_locked() */\nkernel/cgroup/cpuset.c-1411-\tif (force_sd_rebuild)\nkernel/cgroup/cpuset.c:1412:\t\trebuild_sched_domains_locked();\nkernel/cgroup/cpuset.c-1413-\n--\nkernel/cgroup/cpuset.c=2862=int cpuset_update_flag(cpuset_flagbits_t bit, struct cpuset *cs,\n--\nkernel/cgroup/cpuset.c-2895-\t\telse\nkernel/cgroup/cpuset.c:2896:\t\t\trebuild_sched_domains_locked();\nkernel/cgroup/cpuset.c-2897-\t}\n--\nkernel/cgroup/cpuset.c=2914=static int update_prstate(struct cpuset *cs, int new_prs)\n--\nkernel/cgroup/cpuset.c-3028-\tif (force_sd_rebuild)\nkernel/cgroup/cpuset.c:3029:\t\trebuild_sched_domains_locked();\nkernel/cgroup/cpuset.c-3030-\tfree_tmpmasks(\u0026tmpmask);\n--\nkernel/cgroup/cpuset.c=3639=static int cpuset_css_online(struct cgroup_subsys_state *css)\n--\nkernel/cgroup/cpuset.c-3670- * enabled, then simulate turning sched_load_balance off, which\nkernel/cgroup/cpuset.c:3671: * will call rebuild_sched_domains_locked(). That is not needed\nkernel/cgroup/cpuset.c-3672- * in the default hierarchy where only changes in partition\n"
} |
| 15/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29672 output: 22 thoughts: 1091 Thoughts: **Deep Dive into Kernel Scheduler Topology and Deadline Class Updates** Okay, here's what's running through my head as I examine this. This is *fascinating*. The fact that configuring cgroups related to CPUsets and real-time scheduling, along with modifying `/proc/sys/kernel/sched_rt_runtime_us`, directly triggers `rebuild_sched_domains()` is key. It sets off this chain of calls: `build_sched_domains()`, `build_overlap_sched_groups()`, `build_sched_groups()`, `get_group()`, and others. This means user-space configuration has a pretty direct pathway into these core scheduler components. No abstraction layer? Nice! Now, diving deeper into `kernel/sched/topology.c`, the changes really grab my attention. Dynamic allocation of `sched_group` and `sched_group_capacity` within `get_group` and `init_overlap_sched_group` suggests a more adaptive topology model. They're trying to make things dynamic, cool. Memory allocation using `kzalloc_node` during domain build is something to keep in mind. I need to keep an eye on memory usage with that. The loop traversal rewrite with the handling of `uncovered` vs `covered` and the reference counting changes for `sg->sgc` – that's potentially impactful. These changes likely mean they are trying to fix some concurrency issue, or optimize the algorithm. I'll need to explore the specific conditions under which `sgc` is incremented. Moving to `kernel/sched/deadline.c`, the rewrite of `dl_clear_root_domain()` is a notable change. The explicit accounting for DL servers, and the conditional logic related to `cpu_active_mask`, looks like a performance or correctness optimization. It is interesting that the code now explicitly sums bandwidth over a range of CPUs. The introduction of `dl_server_sum_bw` is something I need to look into - it may be critical to overall performance in particular scenarios. And then, this `dl_rebuild_rd_accounting()` function. That looks really important, because it is only included if `#ifndef CONFIG_CPUSETS`. It seems this is only used if cpusets are disabled. This suggests some special considerations for systems *without* CPUsets, where there might be a need to completely rebuild accounting on every CPU. Now I'm wondering, how *is* this called? Let me search for call-sites of `dl_rebuild_rd_accounting`. |
| 16/2 | 2026/10/15 20:26 | tool | grepper |
0mArgs:{
"Expression": "dl_rebuild_rd_accounting"
}Results: {
"Output": "include/linux/cpuset.h=130=extern bool current_cpuset_is_being_rebound(void);\ninclude/linux/cpuset.h-131-\ninclude/linux/cpuset.h:132:void dl_rebuild_rd_accounting(void);\ninclude/linux/cpuset.h-133-extern void rebuild_sched_domains(void);\n--\ninclude/linux/cpuset.h=271=static inline bool current_cpuset_is_being_rebound(void)\n--\ninclude/linux/cpuset.h-275-\ninclude/linux/cpuset.h:276:void dl_rebuild_rd_accounting(void);\ninclude/linux/cpuset.h-277-\n--\nkernel/cgroup/cpuset.c=955=static void dl_update_tasks_root_domain(struct cpuset *cs)\n--\nkernel/cgroup/cpuset.c-970-\nkernel/cgroup/cpuset.c:971:void dl_rebuild_rd_accounting(void)\nkernel/cgroup/cpuset.c-972-{\n--\nkernel/sched/deadline.c=3497=void dl_clear_root_domain_cpu(int cpu)\n--\nkernel/sched/deadline.c-3502-#ifndef CONFIG_CPUSETS\nkernel/sched/deadline.c:3503:void dl_rebuild_rd_accounting(void)\nkernel/sched/deadline.c-3504-{\n--\nkernel/sched/topology.c=3439=static void partition_sched_domains_locked(int ndoms_new, cpumask_var_t doms_new[],\n--\nkernel/sched/topology.c-3556-\tupdate_sched_domain_debugfs();\nkernel/sched/topology.c:3557:\tdl_rebuild_rd_accounting();\nkernel/sched/topology.c-3558-}\n"
} |
| 17/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31812 output: 18 thoughts: 649 Thoughts: **Analyzing Potential Race Conditions and Memory Management in the Kernel** Okay, let's break down these kernel changes. First, I noticed `kernel/sched/topology.c:3557` and the call to `dl_rebuild_rd_accounting()` within `partition_sched_domains_locked`. My focus here is potential synchronization issues within the scheduler. We need to be mindful of how these accounting functions interact, especially considering the locking involved. Then I shifted my attention to `mm/vmstat.c`. Specifically, how `refresh_zone_stat_thresholds()` gets triggered. I confirmed that changes to `min_free_kbytes`, `watermark_scale_factor`, or `percpu_pagelist_high_fraction` via sysctl all lead to `setup_per_zone_wmarks()`, which in turn, calls `refresh_zone_stat_thresholds()`. This highlights a critical link between user-space configuration (through sysctl) and low-level memory management parameters. Now, the diff itself...Let's analyze the changes: 1. **CPU Caching Control (`arch/x86/kernel/cpu/cacheinfo.c`):** The `cache_ap_online` function, called during CPU hotplug or boot, caught my eye. The addition around `CACHE_MTRR` and `CACHE_PAT` suggests changes related to memory type range registers (MTRRs) and page attribute tables (PATs). It appears this code is specifically trying to avoid holding the `mtrr_mutex` lock to prevent blocking during CPU boot. Given that this concerns MTRRs, I am concerned if it is not locking enough. 2. **Machine Check Exception (MCE) Handling (`arch/x86/kernel/cpu/mce/intel.c`):** The `cmci_clear()` and `cmci_rediscover()` functions are being called during CPU offline events. This makes me consider how this interacts with CPU hotplug, and, as syzkaller often tests CPU hotplug scenarios, this is definitely something I need to keep in mind. 3. **MTRR Configuration (`arch/x86/kernel/cpu/mtrr/generic.c`):** The `mtrr_generic_state_matches()` function reads MSRs related to MTRRs. This makes me consider how MTRRs are configured. Can MTRRs be modified from userspace? The existence of `/proc/mtrr` is a giveaway! This path provides a direct interface for userspace modification of MTRRs. This raises a red flag in my mind because of the potential for race conditions or unintended consequences when user space interacts directly with low-level hardware configuration. I need to dig deeper into the `/proc/mtrr` interface to understand the implications of this interaction. The combination of CPU hotplug, MTRRs, and user-space modification is an area ripe for subtle bugs. These changes taken together warrant a thorough review of the locking mechanisms and synchronization primitives used in these code paths. |
| 18/2 | 2026/10/15 20:26 | tool | grepper |
0mError:no matches Args: {
"Expression": "proc_mtrr"
}Results: {
"error": "no matches"
} |
| 19/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33141 output: 30 thoughts: 24 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience and presented in the first person: **Investigating MTRR Implementation** Right, let's dive in. My immediate thought is, "Where's the MTRR implementation in the Linux kernel for x86?" I need to quickly locate the relevant code. Given my experience, the `arch/x86/kernel` directory is the logical starting point for architecture-specific code. Within that, I'm pretty certain that anything related to CPU features like Memory Type Range Registers (MTRRs) would reside under `cpu`. Therefore, I'll be searching within `arch/x86/kernel/cpu/` to find the code. Specifically, the functionality related to MTRRs should be within a folder named `mtrr`. It is a quick and efficient way to narrow down the search and avoid unnecessary exploration. |
| 20/2 | 2026/10/15 20:26 | tool | codesearch-dir-index |
0mArgs:{
"Dir": "arch/x86/kernel/cpu/mtrr"
}Results: {
"Files": [
"amd.c",
"centaur.c",
"cleanup.c",
"cyrix.c",
"generic.c",
"if.c",
"legacy.c",
"mtrr.c",
"mtrr.h"
],
"Subdirs": null
} |
| 21/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33455 output: 42 thoughts: 22 Thoughts: Here's my take on the thought process, summarized as you requested: **Diving into the MTRR Implementation** Okay, so we're talking Memory Type Range Registers (MTRRs) and their implementation on x86, which, as we know, are crucial for performance optimization. Specifically, I need to get a handle on the code handling MTRR configuration, so I'm going to jump straight into `arch/x86/kernel/cpu/mtrr/if.c`. That file is the logical place to start – it's the standard interface and should give me the entry point into the MTRR management logic. I'm expecting to find the core functions that handle things like enabling, disabling, setting, and getting MTRR entries. From there, I'll be able to trace how these operations relate to the underlying hardware registers and associated data structures. This is where the magic happens, or more likely, where potential bugs lurk! Let's see what we've got. |
| 22/2 | 2026/10/15 20:26 | tool | read-file |
0mArgs:{
"File": "arch/x86/kernel/cpu/mtrr/if.c",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t// SPDX-License-Identifier: GPL-2.0\n 2:\t#include \u003clinux/capability.h\u003e\n 3:\t#include \u003clinux/seq_file.h\u003e\n 4:\t#include \u003clinux/uaccess.h\u003e\n 5:\t#include \u003clinux/proc_fs.h\u003e\n 6:\t#include \u003clinux/ctype.h\u003e\n 7:\t#include \u003clinux/string.h\u003e\n 8:\t#include \u003clinux/slab.h\u003e\n 9:\t#include \u003clinux/init.h\u003e\n 10:\t\n 11:\t#define LINE_SIZE 80\n 12:\t\n 13:\t#include \u003casm/mtrr.h\u003e\n 14:\t\n 15:\t#include \"mtrr.h\"\n 16:\t\n 17:\t#define FILE_FCOUNT(f) (((struct seq_file *)((f)-\u003eprivate_data))-\u003eprivate)\n 18:\t\n 19:\tstatic const char *const mtrr_strings[MTRR_NUM_TYPES] =\n 20:\t{\n 21:\t\t\"uncachable\",\t\t/* 0 */\n 22:\t\t\"write-combining\",\t/* 1 */\n 23:\t\t\"?\",\t\t\t/* 2 */\n 24:\t\t\"?\",\t\t\t/* 3 */\n 25:\t\t\"write-through\",\t/* 4 */\n 26:\t\t\"write-protect\",\t/* 5 */\n 27:\t\t\"write-back\",\t\t/* 6 */\n 28:\t};\n 29:\t\n 30:\tconst char *mtrr_attrib_to_str(int x)\n 31:\t{\n 32:\t\treturn (x \u003c= 6) ? mtrr_strings[x] : \"?\";\n 33:\t}\n 34:\t\n 35:\t#ifdef CONFIG_PROC_FS\n 36:\t\n 37:\tstatic int\n 38:\tmtrr_file_add(unsigned long base, unsigned long size,\n 39:\t\t unsigned int type, bool increment, struct file *file, int page)\n 40:\t{\n"
} |
| 23/2 | 2026/10/15 20:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34275 output: 22 thoughts: 2227 Thoughts: **Analyzing Kernel Changes for Fuzzing Potential** Okay, so I've got this patch in front of me, and my job is to determine if it's worth fuzzing. I see changes to `/proc/mtrr`, which, on its own, seems potentially interesting. However, it's the core subsystem changes that really grab my attention. Specifically, the modifications to the scheduler (`kernel/sched/deadline.c`, `kernel/sched/topology.c`) and memory management (`mm/vmstat.c`). These are definitely prime targets, as per the "WorthFuzzing" criteria. Logic in both subsystems is modified! The changes involve functions like `refresh_zone_stat_thresholds`, `build_sched_domains`, `dl_clear_root_domain`, and many more related to the deadline scheduler, overlap scheduler groups, topology, and bandwidth accounting. These subsystems are triggered by common operations like cpuset cgroup manipulations, sysctl configurations, task priority alterations via `sched_setattr` or `SCHED_DEADLINE`, and CPU hotplug events. This ticks all the boxes. **WorthFuzzing = True**. Now, it's time to refine this assessment. We need to identify specific functions to guide the fuzzer (FocusSymbols). I have to avoid common, ubiquitous functions like general memory allocators/deallocators, page fault and trap handlers or kernel locking primitives. I want to target the entrypoints of added or altered logic. I've gone through the code, looking at which functions are added or modified: `cache_ap_online`, `cmci_clear`, `cmci_rediscover`, `cmci_claim_bank`, `__cmci_disable_bank`, `mtrr_generic_state_matches`, `mwait_play_dead`, and a whole slew of scheduler-related functions like `dl_server_sum_bw`, `dl_clear_root_domain`, `dl_rebuild_rd_accounting`, `alloc_sd_llc`, and `sched_update_llc_bytes`. Wait! We also have to consider static inline functions, but these should be visible, as we are dealing with a .c file, and not .h files. The key is to pick functions which are not generally called and are specific to the subsystem being modified. Based on this, I've narrowed it down to: `refresh_zone_stat_thresholds`, `build_sched_domains`, `dl_rebuild_rd_accounting`, `dl_clear_root_domain`, `build_overlap_sched_groups`, `build_sched_groups`, `init_overlap_sched_group`, `mtrr_generic_state_matches`, `cmci_rediscover`. None of these functions are called during generic memory allocation, page faults or ubiquitous scheduling paths, such as `schedule()`. They are instead called during topology rebuilding, deadline bandwidth updates, vmstat thresholds, cmci rediscovery, and MTRR checks. Wait, are there more? `cache_ap_online` calls `mtrr_generic_state_matches()` `sched_update_llc_bytes` is also related. Looks like we have a good, focused set of functions to guide the fuzzing effort. |
| 24/2 | 2026/10/15 20:27 | tool | grepper |
0mArgs:{
"Expression": "sched_update_llc_bytes"
}Results: {
"Output": "drivers/base/cacheinfo.c=1033=static int cacheinfo_cpu_online(unsigned int cpu)\n--\ndrivers/base/cacheinfo.c-1044-\t\tupdate_per_cpu_data_slice_size(true, cpu, cpu_map);\ndrivers/base/cacheinfo.c:1045:\tsched_update_llc_bytes(cpu);\ndrivers/base/cacheinfo.c-1046-\treturn 0;\n--\ndrivers/base/cacheinfo.c=1052=static int cacheinfo_cpu_pre_down(unsigned int cpu)\n--\ndrivers/base/cacheinfo.c-1064-\ndrivers/base/cacheinfo.c:1065:\tsched_update_llc_bytes(cpu);\ndrivers/base/cacheinfo.c-1066-\n--\ninclude/linux/sched/topology.h=278=static inline int task_node(const struct task_struct *p)\n--\ninclude/linux/sched/topology.h-283-#ifdef CONFIG_SCHED_CACHE\ninclude/linux/sched/topology.h:284:extern void sched_update_llc_bytes(unsigned int cpu);\ninclude/linux/sched/topology.h-285-#else\ninclude/linux/sched/topology.h:286:static inline void sched_update_llc_bytes(unsigned int cpu) { }\ninclude/linux/sched/topology.h-287-#endif\n--\nkernel/sched/topology.c=844=int sysctl_sched_cache_user = 1;\n--\nkernel/sched/topology.c-856- * is a device_initcall cpuhp callback). In that case,\nkernel/sched/topology.c:857: * cacheinfo_cpu_online() will later call sched_update_llc_bytes()\nkernel/sched/topology.c-858- * to fill in the bottom domain's llc_bytes once the cache attributes\n--\nkernel/sched/topology.c=971=void sched_cache_active_set(void)\n--\nkernel/sched/topology.c-991- */\nkernel/sched/topology.c:992:void sched_update_llc_bytes(unsigned int cpu)\nkernel/sched/topology.c-993-{\n"
} |
| 25/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37790 output: 18 thoughts: 66 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into CPU Hotplug and Cache Configuration** Okay, so `sched_update_llc_bytes` is triggered when a CPU is either brought online or prepared for being taken offline – the hotplug events. That's good to know, that sets the context. Now, I see this `#ifdef CONFIG_SCHED_CACHE` guard in `include/linux/sched/topology.h`. That's interesting, as it indicates this function's behavior might be conditional on this specific kernel configuration option. Let's see, the next logical step here is to confirm whether `CONFIG_SCHED_CACHE` is even enabled. I need to check the Kconfig files related to this. Let me see where I can find the description. This seems like it should be straightforward, but I have a strong feeling it is important to understand the overall behavior. |
| 26/2 | 2026/10/15 20:27 | tool | grepper |
0mArgs:{
"Expression": "SCHED_CACHE"
}Results: {
"Output": "include/linux/mm_types.h=1619=static inline unsigned int mm_cid_size(void)\n--\ninclude/linux/mm_types.h-1625-\ninclude/linux/mm_types.h:1626:#ifdef CONFIG_SCHED_CACHE\ninclude/linux/mm_types.h-1627-void mm_init_sched(struct mm_struct *mm,\n--\ninclude/linux/mm_types.h=1644=static inline void mm_destroy_sched(struct mm_struct *mm)\n--\ninclude/linux/mm_types.h-1648-}\ninclude/linux/mm_types.h:1649:#else /* !CONFIG_SCHED_CACHE */\ninclude/linux/mm_types.h-1650-\n--\ninclude/linux/mm_types.h=1652=static inline void mm_destroy_sched(struct mm_struct *mm) { }\ninclude/linux/mm_types.h-1653-\ninclude/linux/mm_types.h:1654:#endif /* CONFIG_SCHED_CACHE */\ninclude/linux/mm_types.h-1655-\n--\ninclude/linux/sched.h=835=struct task_struct {\n--\ninclude/linux/sched.h-1433-\ninclude/linux/sched.h:1434:#ifdef CONFIG_SCHED_CACHE\ninclude/linux/sched.h-1435-\tstruct callback_head\t\tcache_work;\n--\ninclude/linux/sched.h=2390=static __always_inline int task_mm_cid(struct task_struct *t)\n--\ninclude/linux/sched.h-2400-\ninclude/linux/sched.h:2401:#ifdef CONFIG_SCHED_CACHE\ninclude/linux/sched.h-2402-\n--\ninclude/linux/sched/topology.h=66=struct sched_domain_shared {\n--\ninclude/linux/sched/topology.h-84-\t};\ninclude/linux/sched/topology.h:85:#ifdef CONFIG_SCHED_CACHE\ninclude/linux/sched/topology.h-86-\tunsigned long\tutil_avg;\n--\ninclude/linux/sched/topology.h=91=struct sched_domain {\n--\ninclude/linux/sched/topology.h-119-\ninclude/linux/sched/topology.h:120:#ifdef CONFIG_SCHED_CACHE\ninclude/linux/sched/topology.h-121-\tunsigned int llc_max;\n--\ninclude/linux/sched/topology.h=278=static inline int task_node(const struct task_struct *p)\n--\ninclude/linux/sched/topology.h-282-\ninclude/linux/sched/topology.h:283:#ifdef CONFIG_SCHED_CACHE\ninclude/linux/sched/topology.h-284-extern void sched_update_llc_bytes(unsigned int cpu);\n--\ninit/Kconfig=1021=config NUMA_BALANCING\n--\ninit/Kconfig-1033-\ninit/Kconfig:1034:config SCHED_CACHE\ninit/Kconfig-1035-\tbool \"Cache aware load balance\"\n--\ninit/init_task.c=105=struct task_struct init_task __aligned(L1_CACHE_BYTES) = {\n--\ninit/init_task.c-230-#endif\ninit/init_task.c:231:#ifdef CONFIG_SCHED_CACHE\ninit/init_task.c-232-\t.preferred_llc = -1,\n--\nkernel/exit.c=491=void mm_update_next_owner(struct mm_struct *mm)\n--\nkernel/exit.c-549-\nkernel/exit.c:550:#if defined(CONFIG_SCHED_CACHE) \u0026\u0026 defined(CONFIG_NUMA_BALANCING)\nkernel/exit.c-551-/*\n--\nkernel/exit.c=571=static inline void exit_mm_sched_cache(struct mm_struct *mm)\n--\nkernel/exit.c-573-}\nkernel/exit.c:574:#endif /* CONFIG_SCHED_CACHE CONFIG_NUMA_BALANCING */\nkernel/exit.c-575-\n--\nkernel/sched/core.c=8951=void __init sched_init(void)\n--\nkernel/sched/core.c-9095-#endif\nkernel/sched/core.c:9096:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/core.c-9097-\t\traw_spin_lock_init(\u0026rq-\u003ecpu_epoch_lock);\n--\nkernel/sched/debug.c=205=static const struct file_operations sched_scaling_fops = {\n--\nkernel/sched/debug.c-212-\nkernel/sched/debug.c:213:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/debug.c-214-static ssize_t\n--\nkernel/sched/debug.c=712=static __init int sched_init_debug(void)\n--\nkernel/sched/debug.c-746-\nkernel/sched/debug.c:747:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/debug.c-748-\tllc = debugfs_create_dir(\"llc_balancing\", debugfs_sched);\n--\nkernel/sched/fair.c=1396=static s64 update_se(struct rq *rq, struct sched_entity *se)\n--\nkernel/sched/fair.c-1437-\nkernel/sched/fair.c:1438:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-1439-\n--\nkernel/sched/fair.c=1979=void init_sched_mm(struct task_struct *p)\n--\nkernel/sched/fair.c-1991-\nkernel/sched/fair.c:1992:#else /* CONFIG_SCHED_CACHE */\nkernel/sched/fair.c-1993-\n--\nkernel/sched/fair.c=2009=static void account_llc_dequeue(struct rq *rq, struct task_struct *p) {}\nkernel/sched/fair.c-2010-\nkernel/sched/fair.c:2011:#endif /* CONFIG_SCHED_CACHE */\nkernel/sched/fair.c-2012-\n--\nkernel/sched/fair.c=3684=static void task_numa_placement(struct task_struct *p)\n--\nkernel/sched/fair.c-3767-\t\t\t}\nkernel/sched/fair.c:3768:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-3769-\t\t\t/*\n--\nkernel/sched/fair.c=10531=static inline int task_is_ineligible_on_dst_cpu(struct task_struct *p, int dest_cpu)\n--\nkernel/sched/fair.c-10541-\nkernel/sched/fair.c:10542:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-10543-/*\n--\nkernel/sched/fair.c=11381=struct sg_lb_stats {\n--\nkernel/sched/fair.c-11400-#endif\nkernel/sched/fair.c:11401:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-11402-\tunsigned int nr_pref_dst_llc;\n--\nkernel/sched/fair.c=11806=sched_reduced_capacity(struct rq *rq, struct sched_domain *sd)\n--\nkernel/sched/fair.c-11817-\nkernel/sched/fair.c:11818:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-11819-/*\n--\nkernel/sched/fair.c=11925=static inline void update_sg_lb_stats(struct lb_env *env,\n--\nkernel/sched/fair.c-11952-\nkernel/sched/fair.c:11953:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-11954-\t\tif (sched_cache_enabled()) {\n--\nkernel/sched/fair.c=12718=static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *sds)\n--\nkernel/sched/fair.c-12757-\nkernel/sched/fair.c:12758:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-12759-\tif (busiest-\u003egroup_type == group_llc_balance) {\n--\nkernel/sched/fair.c=13068=static struct rq *sched_balance_find_src_rq(struct lb_env *env,\n--\nkernel/sched/fair.c-13219-\t\tcase migrate_llc_task:\nkernel/sched/fair.c:13220:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/fair.c-13221-\t\t\tsd_tmp = rcu_dereference_all(rq-\u003esd);\n--\nkernel/sched/sched.h=1158=struct rq {\n--\nkernel/sched/sched.h-1216-#endif\nkernel/sched/sched.h:1217:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/sched.h-1218-\traw_spinlock_t\t\tcpu_epoch_lock ____cacheline_aligned;\n--\nkernel/sched/sched.h-1235-\nkernel/sched/sched.h:1236:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/sched.h-1237-\tunsigned int\t\tnr_pref_llc_running;\n--\nkernel/sched/sched.h=4138=static inline void mm_cid_switch_to(struct task_struct *prev, struct task_struct *next) { }\n--\nkernel/sched/sched.h-4140-\nkernel/sched/sched.h:4141:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/sched.h-4142-DECLARE_STATIC_KEY_FALSE(sched_cache_present);\n--\nkernel/sched/topology.c=619=static void destroy_sched_domain(struct sched_domain *sd)\n--\nkernel/sched/topology.c-628-\nkernel/sched/topology.c:629:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/topology.c-630-\t/* only the bottom sd has llc_counts array */\n--\nkernel/sched/topology.c=733=cpu_attach_domain(struct sched_domain *sd, struct root_domain *rd, int cpu)\n--\nkernel/sched/topology.c-784-\nkernel/sched/topology.c:785:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/topology.c-786-\t\t\t/* move buffer to parent as child is being destroyed */\n--\nkernel/sched/topology.c=827=enum s_alloc {\n--\nkernel/sched/topology.c-834-\nkernel/sched/topology.c:835:#ifdef CONFIG_SCHED_CACHE\nkernel/sched/topology.c-836-/* hardware support for cache aware scheduling */\n"
} |
| 27/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40890 output: 33 thoughts: 26 Thoughts: Here's a summary of my thought process, tailored for an expert like yourself: **Investigating Scheduler Cache Configuration** Okay, so I need to dive into the scheduler cache configuration. The best place to start, as usual, is the `init/Kconfig` file. I'm going to jump straight to around line 1034 – that's usually where the relevant options are defined. I'm specifically looking for `CONFIG_SCHED_CACHE`. That should be the key to understanding how the cache is enabled, its dependencies, and perhaps any related options I should consider. Knowing the Kconfig options is the foundational step. |
| 28/2 | 2026/10/15 20:27 | tool | read-file |
0mArgs:{
"File": "init/Kconfig",
"FirstLine": 1030,
"LineCount": 20
}Results: {
"Contents": "1030:\t\t it has references to the node the task is running on.\n1031:\t\n1032:\t\t This system will be inactive on UMA systems.\n1033:\t\n1034:\tconfig SCHED_CACHE\n1035:\t\tbool \"Cache aware load balance\"\n1036:\t\tdefault y\n1037:\t\tdepends on SMP\n1038:\t\thelp\n1039:\t\t When enabled, the scheduler will attempt to aggregate tasks from\n1040:\t\t the same process onto a single Last Level Cache (LLC) domain when\n1041:\t\t possible. This improves cache locality by keeping tasks that share\n1042:\t\t resources within the same cache domain, reducing cache misses and\n1043:\t\t lowering data access latency.\n1044:\t\n1045:\tconfig NUMA_BALANCING_DEFAULT_ENABLED\n1046:\t\tbool \"Automatically enable NUMA aware memory/task placement\"\n1047:\t\tdefault y\n1048:\t\tdepends on NUMA_BALANCING\n1049:\t\thelp\n"
} |
| 29/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41372 output: 19 thoughts: 28 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own internal monologue: **Quick Configuration Check** Alright, let's see, `CONFIG_SCHED_CACHE` is set to "y" by default and it's contingent on SMP being enabled. Nothing particularly surprising there. But, let's just make sure, now... I need to consider potential implications of those settings and the context they are set within. I think I will check `CONFIG_MTRR`, to look at the memory configuration. |
| 30/2 | 2026/10/15 20:27 | tool | grepper |
0mArgs:{
"Expression": "config MTRR\\b"
}Results: {
"Output": "arch/x86/Kconfig=1630=config X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK\n--\narch/x86/Kconfig-1637-\narch/x86/Kconfig:1638:config MTRR\narch/x86/Kconfig-1639-\tdef_bool y\n"
} |
| 31/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41635 output: 37 thoughts: 21 Thoughts: Here's my summary, as if I were thinking these thoughts: **Analyzing the x86 Kernel Configuration** Okay, let's dive into `arch/x86/Kconfig` and zoom in around line 1638. I'm focusing on this specific section because, based on my understanding of the system, it's likely related to [insert your hypothesis here, based on your expertise and where you think the Kconfig file might be leading]. I need to quickly identify the relevant configuration options and understand their dependencies. What's the context around this line? Is it related to a specific feature, a hardware component, or maybe a performance optimization? I'm looking for clues in the surrounding options, comments, and conditional logic. I'm trying to deduce how this configuration option impacts the overall kernel build and runtime behavior on x86 architectures. This might be key to addressing [insert a relevant problem, opportunity or question that you expect the config options might inform]. Let's see what we can find. |
| 32/2 | 2026/10/15 20:27 | tool | read-file |
0mArgs:{
"File": "arch/x86/Kconfig",
"FirstLine": 1635,
"LineCount": 20
}Results: {
"Contents": "1635:\t\t Set whether the default state of memory_corruption_check is\n1636:\t\t on or off.\n1637:\t\n1638:\tconfig MTRR\n1639:\t\tdef_bool y\n1640:\t\tprompt \"MTRR (Memory Type Range Register) support\" if EXPERT\n1641:\t\thelp\n1642:\t\t On Intel P6 family processors (Pentium Pro, Pentium II and later)\n1643:\t\t the Memory Type Range Registers (MTRRs) may be used to control\n1644:\t\t processor access to memory ranges. This is most useful if you have\n1645:\t\t a video (VGA) card on a PCI or AGP bus. Enabling write-combining\n1646:\t\t allows bus write transfers to be combined into a larger transfer\n1647:\t\t before bursting over the PCI/AGP bus. This can increase performance\n1648:\t\t of image write operations 2.5 times or more. Saying Y here creates a\n1649:\t\t /proc/mtrr file which may be used to manipulate your processor's\n1650:\t\t MTRRs. Typically the X server should use this.\n1651:\t\n1652:\t\t This code has a reasonably generic interface so that similar\n1653:\t\t control registers on other processors can be easily supported\n1654:\t\t as well:\n"
} |
| 33/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42270 output: 28 thoughts: 45 Thoughts: Okay, let's dive into this scheduling configuration. **Navigating the Scheduler Topology** Alright, I need to get a handle on how this scheduler topology is being built. My initial thought is to zero in on `kernel/sched/topology.c`. It's a key file, so let's start there. I'm immediately thinking, I need to be sure that the `CONFIG_SMP` option is enabled, because sched domains are entirely dependent on symmetric multiprocessing. It's the foundation. To double-check the compilation process of that source file I'll need to open `kernel/sched/Makefile` and see how it's being linked. That will give me a very quick overview of what needs to be enabled to allow this to build correctly. |
| 34/2 | 2026/10/15 20:27 | tool | grepper |
0mError:no matches Args: {
"Expression": "topology\\.o",
"PathPrefixes": [
"kernel/sched/"
]
}Results: {
"error": "no matches"
} |
| 35/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42518 output: 31 thoughts: 13 Thoughts: Here's my summary, as though I'm thinking through this process: **Directly Examining the Scheduler's Makefile** Okay, so I need to understand something about the scheduler, and rather than relying on my general knowledge or a vague memory of the directory structure, let's go straight to the source. The `kernel/sched/Makefile` seems like the obvious place to begin. A Makefile, especially for a core component like the scheduler, should contain all sorts of crucial information - dependencies, build rules, possibly even some comments hinting at design choices or optimization strategies. This direct approach feels right; a methodical, bottom-up investigation is likely to be more reliable in the long run than a top-down one in this situation. It’s the kind of concrete information I can work with. |
| 36/2 | 2026/10/15 20:27 | tool | read-file |
0mArgs:{
"File": "kernel/sched/Makefile",
"FirstLine": 1,
"LineCount": 50
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t\n 3:\tCONTEXT_ANALYSIS_core.o := y\n 4:\tCONTEXT_ANALYSIS_fair.o := y\n 5:\t\n 6:\t# The compilers are complaining about unused variables inside an if(0) scope\n 7:\t# block. This is daft, shut them up.\n 8:\tccflags-y += $(call cc-disable-warning, unused-but-set-variable)\n 9:\t\n 10:\t# These files are disabled because they produce non-interesting flaky coverage\n 11:\t# that is not a function of syscall inputs. E.g. involuntary context switches.\n 12:\tKCOV_INSTRUMENT := n\n 13:\t\n 14:\t# Disable KCSAN to avoid excessive noise and performance degradation. To avoid\n 15:\t# false positives ensure barriers implied by sched functions are instrumented.\n 16:\tKCSAN_SANITIZE := n\n 17:\tKCSAN_INSTRUMENT_BARRIERS := y\n 18:\t\n 19:\tifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)\n 20:\t# According to Alan Modra \u003calan@linuxcare.com.au\u003e, the -fno-omit-frame-pointer is\n 21:\t# needed for x86 only. Why this used to be enabled for all architectures is beyond\n 22:\t# me. I suspect most platforms don't need this, but until we know that for sure\n 23:\t# I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k\n 24:\t# to get a correct value for the wait-channel (WCHAN in ps). --davidm\n 25:\tCFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer\n 26:\tendif\n 27:\t\n 28:\t# Branch profiling isn't noinstr-safe\n 29:\tifdef CONFIG_TRACE_BRANCH_PROFILING\n 30:\tCFLAGS_build_policy.o += -DDISABLE_BRANCH_PROFILING\n 31:\tCFLAGS_build_utility.o += -DDISABLE_BRANCH_PROFILING\n 32:\tendif\n 33:\t#\n 34:\t# Build efficiency:\n 35:\t#\n 36:\t# These compilation units have roughly the same size and complexity - so their\n 37:\t# build parallelizes well and finishes roughly at once:\n 38:\t#\n 39:\tobj-y += core.o\n 40:\tobj-y += fair.o\n 41:\tobj-y += build_policy.o\n 42:\tobj-y += build_utility.o\n"
} |
| 37/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43424 output: 28 thoughts: 13 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Tracing the Inclusion of `topology.c`** Okay, so I need to understand the reach of `topology.c`. First things first, let's establish its inclusion points. A quick grep, or maybe a clangd search if I have it set up, will be the most efficient route. I want to see the full dependency graph originating from that file – I'm not just interested in direct includes, but also anything that transitively relies on it. Knowing the broader context is crucial, given the potential impact on core functionality if I need to make changes. This includes headers, obviously, but also other source files that may #include it as a side-effect. I’ll keep a mental note of any potentially problematic includes, circular dependencies, or unexpected uses that arise during my investigation, as this information will inform my future decisions and further steps. |
| 38/2 | 2026/10/15 20:27 | tool | grepper |
0mArgs:{
"Expression": "topology\\.c",
"PathPrefixes": [
"kernel/sched/"
]
}Results: {
"Output": "kernel/sched/build_utility.c-85-\nkernel/sched/build_utility.c:86:#include \"topology.c\"\nkernel/sched/build_utility.c-87-\n"
} |
| 39/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43718 output: 36 thoughts: 15 Thoughts: Here's a summary of my thought process, as requested: **Navigating the Kernel's Scheduling Build Utilities** Okay, so I need to understand something related to the scheduler, probably some build-time configuration or optimization. The name 'build_utility.c' immediately points to potentially interesting code. I'm thinking, likely this file contains functions related to creating data structures, perhaps defining some compile-time scheduling constants, or generating look-up tables. It's almost certainly involved in the process of setting up or tailoring the scheduler based on the specific kernel configuration and hardware architecture. I'll need to see if it's involved in tuning various schedulers if applicable, or optimizing the build to the current system. I'm going to dive right in and check that file. |
| 40/2 | 2026/10/15 20:27 | tool | read-file |
0mArgs:{
"File": "kernel/sched/build_utility.c",
"FirstLine": 75,
"LineCount": 25
}Results: {
"Contents": " 75:\t#endif\n 76:\t\n 77:\t#include \"loadavg.c\"\n 78:\t#include \"completion.c\"\n 79:\t#include \"swait.c\"\n 80:\t#include \"wait_bit.c\"\n 81:\t#include \"wait.c\"\n 82:\t\n 83:\t#include \"cpupri.c\"\n 84:\t#include \"stop_task.c\"\n 85:\t\n 86:\t#include \"topology.c\"\n 87:\t\n 88:\t#ifdef CONFIG_SCHED_CORE\n 89:\t# include \"core_sched.c\"\n 90:\t#endif\n 91:\t\n 92:\t#ifdef CONFIG_PSI\n 93:\t# include \"psi.c\"\n 94:\t#endif\n 95:\t\n 96:\t#ifdef CONFIG_MEMBARRIER\n 97:\t# include \"membarrier.c\"\n 98:\t#endif\n 99:\t\n"
} |
| 41/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44212 output: 33 thoughts: 50 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience: **Investigating the Build Dependency Chain** Right, let me see... I'm looking at `build_utility.c`, and I'm pretty sure it unconditionally includes `topology.c`. Now, even though that inclusion is direct, I need to keep in mind that the contents of `topology.c` itself *could* be subject to conditional compilation, like `#ifdef CONFIG_SMP`. So the inclusion in the build is not a guarantee of code inclusion without further investigation. I need to confirm if this inclusion behavior is relevant to my current debugging efforts, or if something might be missed because of a conditional compile. It's time to dig into the source. Let's start with `kernel/sched/topology.c` and see what the dependencies look like. I'll need to confirm what is actually included at compile time, and determine if those inclusions affect the problem I'm trying to solve. |
| 42/2 | 2026/10/15 20:27 | tool | read-file |
0mArgs:{
"File": "kernel/sched/topology.c",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t// SPDX-License-Identifier: GPL-2.0\n 2:\t/*\n 3:\t * Scheduler topology setup/handling methods\n 4:\t */\n 5:\t\n 6:\t#include \u003clinux/sched/isolation.h\u003e\n 7:\t#include \u003clinux/sched/clock.h\u003e\n 8:\t#include \u003clinux/bsearch.h\u003e\n 9:\t#include \"sched.h\"\n 10:\t\n 11:\tDEFINE_MUTEX(sched_domains_mutex);\n 12:\tvoid sched_domains_mutex_lock(void)\n 13:\t{\n 14:\t\tmutex_lock(\u0026sched_domains_mutex);\n 15:\t}\n 16:\tvoid sched_domains_mutex_unlock(void)\n 17:\t{\n 18:\t\tmutex_unlock(\u0026sched_domains_mutex);\n 19:\t}\n 20:\t\n 21:\t/* Protected by sched_domains_mutex: */\n 22:\tstatic cpumask_var_t sched_domains_llc_id_allocmask;\n 23:\tstatic cpumask_var_t sched_domains_tmpmask;\n 24:\tstatic cpumask_var_t sched_domains_tmpmask2;\n 25:\tint max_lid;\n 26:\t\n 27:\tstatic int __init sched_debug_setup(char *str)\n 28:\t{\n 29:\t\tsched_debug_verbose = true;\n 30:\t\n 31:\t\treturn 0;\n 32:\t}\n 33:\tearly_param(\"sched_verbose\", sched_debug_setup);\n 34:\t\n 35:\tstatic inline bool sched_debug(void)\n 36:\t{\n 37:\t\treturn sched_debug_verbose;\n 38:\t}\n 39:\t\n 40:\t#define SD_FLAG(_name, mflags) [__##_name] = { .meta_flags = mflags, .name = #_name },\n"
} |
| 43/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45025 output: 33 thoughts: 14 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into the `topology.c` File** Alright, time to get to the heart of the matter. I need to investigate the `SMP` check within `topology.c`. This is a critical area for understanding how the system handles multi-processor configurations, which, as we know, can have a profound impact on performance, especially in highly parallel tasks. I'll need to scrutinize the logic there. I'm looking for where and how the code determines the number and layout of CPUs, and, specifically, how it interacts with the `SMP` (Symmetric Multi-Processing) detection. I anticipate this will involve understanding how the code probes the hardware, potentially utilizing BIOS calls or other platform-specific APIs to obtain the CPU topology data, and how this information is then used to initialize and configure kernel data structures and schedulers. I'll be looking for any potential bottlenecks or areas where further optimization might be necessary, given the target workload. |
| 44/2 | 2026/10/15 20:27 | tool | grepper |
0mError:no matches Args: {
"Expression": "#ifdef CONFIG_SMP",
"PathPrefixes": [
"kernel/sched/topology.c"
]
}Results: {
"error": "no matches"
} |
| 45/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45301 output: 28 thoughts: 14 Thoughts: Okay, let's break this down. Here's how I'd approach that snippet, considering my expertise: **Checking for SMP in the Build Utility** Alright, a `SMP` check. My initial thought is, "Where exactly is this check happening within `build_utility.c`?" This instantly raises a few questions in my mind. Is this an early check? Does it gate functionality based on SMP (Symmetric Multi-Processing) capabilities? Is it a sanity check, or a critical path decision? Knowing the precise location within that file is the key to understanding its intent within the overall build process. I'd be immediately thinking about the implications. Is this related to thread configuration? Parallel build steps? Resource allocation? Is it checking for compiler flags or runtime capabilities? Or maybe, it's just ensuring the target platform can handle multi-threaded operations. From my experience, a check like this usually indicates a build system attempting to tailor its behavior to the underlying hardware. Often this would impact performance and parallelization strategy. If SMP is detected, certain optimizations or parallel tasks could be enabled. If not, the build may fall back to single-threaded modes, perhaps with a warning to the user. My next step would be to rapidly scan the surrounding code. What functions or variables are used in conjunction with this check? What conditional logic is deployed? That will offer a clearer picture of how this SMP detection impacts the build process. Only then can I truly understand its significance and potential impact. |
| 46/2 | 2026/10/15 20:27 | tool | grepper |
0mArgs:{
"Expression": "topology\\.c",
"PathPrefixes": [
"kernel/sched/"
]
}Results: {
"Output": "kernel/sched/build_utility.c-85-\nkernel/sched/build_utility.c:86:#include \"topology.c\"\nkernel/sched/build_utility.c-87-\n"
} |
| 47/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45714 output: 36 thoughts: 358 Thoughts: **Investigating the `dl_rebuild_rd_accounting` Function and Configuration Dependencies**
Okay, let's dive into this. I'm looking at lines 80-90 of `kernel/sched/build_utility.c`. I see it directly includes `topology.c`. I'm immediately thinking about SMP, particularly within `kernel/sched/topology.c`. SMP is pretty much a given on amd64 systems anyway, thanks to `CONFIG_SMP`, but it's always good to verify the interaction.
Now, I'm going to shift my focus to `CONFIG_CPUSETS`. I need to understand how this interacts with the code I'm looking at. Checking `include/linux/cpuset.h`, I find the function `void dl_rebuild_rd_accounting(void);`. I know that name. Now I'll check where it's *defined*, so I'll jump to `kernel/sched/deadline.c` and immediately see the preprocessor conditionals:
```c
#ifndef CONFIG_CPUSETS
void dl_rebuild_rd_accounting(void) { ... }
#endif
```
Then I checked `kernel/cgroup/cpuset.c`, and found this:
```c
#ifdef CONFIG_CPUSETS
void dl_rebuild_rd_accounting(void) { ... }
#endif
```
*Wait a minute*. `dl_rebuild_rd_accounting` is called within `partition_sched_domains_locked` in `kernel/sched/topology.c` regardless of whether `CONFIG_CPUSETS` is defined or not!
So here's what's happening: if `CONFIG_CPUSETS` is enabled, the actual definition of `dl_rebuild_rd_accounting` is in `kernel/cgroup/cpuset.c`. If `CONFIG_CPUSETS` isn't enabled, the definition falls back to `kernel/sched/deadline.c`. And, considering this is standard for cgroups, this is a very interesting piece of configuration logic.
Next, I need to understand how `CONFIG_X86_MCE_INTEL` interacts with this. I should check the configurations in `arch/x86/kernel/cpu/mce/intel.c`.
|
| 48/2 | 2026/10/15 20:27 | tool | grepper |
0mArgs:{
"Expression": "intel\\.o",
"PathPrefixes": [
"arch/x86/kernel/cpu/mce/"
]
}Results: {
"Output": "arch/x86/kernel/cpu/mce/Makefile=4=obj-$(CONFIG_X86_ANCIENT_MCE)\t+= winchip.o p5.o\narch/x86/kernel/cpu/mce/Makefile:5:obj-$(CONFIG_X86_MCE_INTEL)\t+= intel.o\narch/x86/kernel/cpu/mce/Makefile-6-obj-$(CONFIG_X86_MCE_AMD)\t+= amd.o\n"
} |
| 49/2 | 2026/10/15 20:27 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46740 output: 39 thoughts: 17 Thoughts: Okay, here's the summary, assuming I'm a seasoned expert working on a system configuration: **Checking `CONFIG_X86_MCE_INTEL`** Alright, time to get down to brass tacks. Let's dig into this `CONFIG_X86_MCE_INTEL` option. I need to understand its impact. It's crucial, obviously, because Machine Check Exceptions (MCEs) are a significant piece of the stability puzzle, especially on x86 architectures, and specifically Intel platforms. So, what exactly is this doing? I need to quickly ascertain what the implications are if this is enabled or disabled and how it will impact the system. Is it just Intel MCE, or does it also mean enabling something broader? Does it interact with any other configuration options or drivers that I should be aware of, perhaps those related to hardware monitoring or error handling? Specifically, how will this relate to performance and error reporting? I'll review the dependencies, the Kconfig documentation, and ideally, some relevant code snippets within the kernel to fully understand the impact. I know I need a full view, so let's start there. |
| 50/2 | 2026/10/15 20:28 | tool | grepper |
0mArgs:{
"Expression": "config X86_MCE_INTEL\\b",
"PathPrefixes": [
"arch/x86/"
]
}Results: {
"Output": "arch/x86/Kconfig=1154=config X86_MCELOG_LEGACY\n--\narch/x86/Kconfig-1161-\narch/x86/Kconfig:1162:config X86_MCE_INTEL\narch/x86/Kconfig-1163-\tdef_bool y\n"
} |
| 51/2 | 2026/10/15 20:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47141 output: 37 thoughts: 21 Thoughts: Here's my summary, as if I were thinking these thoughts: **Examining the x86 Kernel Configuration** Okay, let's dive into this Kconfig file, specifically `arch/x86/Kconfig` around line 1162. I need to understand what's being configured there. This is likely related to some specific feature or module for x86 architectures within the kernel. Given my expertise, I'm expecting to see options related to either hardware support, processor features, or potentially some core kernel functionality specific to the x86 platform. I'll be looking for dependencies, default values, and the overall logic governing how this particular section of the configuration tree behaves. I'll need to parse the relationships between various configuration symbols and see how they contribute to the overall build process. Let's see what gems are hidden in this section. |
| 52/2 | 2026/10/15 20:28 | tool | read-file |
0mArgs:{
"File": "arch/x86/Kconfig",
"FirstLine": 1160,
"LineCount": 15
}Results: {
"Contents": "1160:\t\t rasdaemon solution.\n1161:\t\n1162:\tconfig X86_MCE_INTEL\n1163:\t\tdef_bool y\n1164:\t\tprompt \"Intel MCE features\"\n1165:\t\tdepends on X86_MCE \u0026\u0026 X86_LOCAL_APIC\n1166:\t\thelp\n1167:\t\t Additional support for intel specific MCE features such as\n1168:\t\t the thermal monitor.\n1169:\t\n1170:\tconfig X86_MCE_AMD\n1171:\t\tdef_bool y\n1172:\t\tprompt \"AMD MCE features\"\n1173:\t\tdepends on X86_MCE \u0026\u0026 X86_LOCAL_APIC\n1174:\t\thelp\n"
} |
| 53/2 | 2026/10/15 20:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47598 output: 40 thoughts: 54 Thoughts: Okay, here's my take on those thoughts, keeping in mind my expertise: **Checking Dependencies in the Intel x86 MCE Implementation** Alright, let's see, `X86_MCE_INTEL` is clearly a more specific option, likely the Intel-specific Machine Check Exception support. And, as expected, it hinges on the broader `X86_MCE` feature. Makes perfect sense, that's the base enabling code. Of course, since we're dealing with x86 architecture and error handling, it's also going to need `X86_LOCAL_APIC`. Can't do much with machine checks without the APIC and its interrupt capabilities. So, where are these pieces being wired together? It's time to dig into the `arch/x86/kernel/cpu/mtrr/Makefile` and see how it fits into the build process and if anything else is configured here. |
| 54/2 | 2026/10/15 20:28 | tool | read-file |
0mArgs:{
"File": "arch/x86/kernel/cpu/mtrr/Makefile",
"FirstLine": 1,
"LineCount": 20
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tobj-y\t\t:= mtrr.o if.o generic.o cleanup.o\n 3:\tobj-$(CONFIG_X86_32) += amd.o cyrix.o centaur.o legacy.o\n 4:\t\n"
} |
| 55/2 | 2026/10/15 20:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47988 output: 33 thoughts: 68 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into the MTRR Build Process** Okay, so I'm looking at `arch/x86/kernel/cpu/mtrr/Makefile`. I see `generic.o` is flagged as `obj-y`. That's straightforward enough – it's definitely being compiled in. But hold on, when does the *directory* `arch/x86/kernel/cpu/mtrr/` itself get built? I know I can't just assume it's always included. Time to backtrack and check the parent directory's Makefile, `arch/x86/kernel/cpu/Makefile`. That'll be the key to understanding the conditional build process. Gotta figure out what triggers the build of the entire MTRR subsystem before I can really understand this. |
| 56/2 | 2026/10/15 20:28 | tool | grepper |
0mArgs:{
"Expression": "mtrr",
"PathPrefixes": [
"arch/x86/kernel/cpu/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 1659 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (14 files in total):\narch/x86/kernel/cpu/Makefile:1\narch/x86/kernel/cpu/cacheinfo.c:8\narch/x86/kernel/cpu/centaur.c:1\narch/x86/kernel/cpu/common.c:1\narch/x86/kernel/cpu/mtrr/Makefile:1\narch/x86/kernel/cpu/mtrr/amd.c:10\narch/x86/kernel/cpu/mtrr/centaur.c:8\narch/x86/kernel/cpu/mtrr/cleanup.c:118\narch/x86/kernel/cpu/mtrr/cyrix.c:6\narch/x86/kernel/cpu/mtrr/generic.c:154\narch/x86/kernel/cpu/mtrr/if.c:53\narch/x86/kernel/cpu/mtrr/legacy.c:27\narch/x86/kernel/cpu/mtrr/mtrr.c:106\narch/x86/kernel/cpu/mtrr/mtrr.h:32\n\narch/x86/kernel/cpu/Makefile=52=obj-$(CONFIG_X86_MCE)\t\t\t+= mce/\narch/x86/kernel/cpu/Makefile:53:obj-$(CONFIG_MTRR)\t\t\t+= mtrr/\narch/x86/kernel/cpu/Makefile-54-obj-$(CONFIG_MICROCODE)\t\t\t+= microcode/\n--\narch/x86/kernel/cpu/cacheinfo.c-19-#include \u003casm/cpuid/api.h\u003e\narch/x86/kernel/cpu/cacheinfo.c:20:#include \u003casm/mtrr.h\u003e\narch/x86/kernel/cpu/cacheinfo.c-21-#include \u003casm/smp.h\u003e\n--\narch/x86/kernel/cpu/cacheinfo.c=661=void cache_disable(void) __acquires(cache_disable_lock)\n--\narch/x86/kernel/cpu/cacheinfo.c-688-\tif (cpu_feature_enabled(X86_FEATURE_MTRR))\narch/x86/kernel/cpu/cacheinfo.c:689:\t\tmtrr_disable();\narch/x86/kernel/cpu/cacheinfo.c-690-\n--\narch/x86/kernel/cpu/cacheinfo.c=694=void cache_enable(void) __releases(cache_disable_lock)\n--\narch/x86/kernel/cpu/cacheinfo.c-700-\tif (cpu_feature_enabled(X86_FEATURE_MTRR))\narch/x86/kernel/cpu/cacheinfo.c:701:\t\tmtrr_enable();\narch/x86/kernel/cpu/cacheinfo.c-702-\n--\narch/x86/kernel/cpu/cacheinfo.c=713=static void cache_cpu_init(void)\n--\narch/x86/kernel/cpu/cacheinfo.c-720-\t\tcache_disable();\narch/x86/kernel/cpu/cacheinfo.c:721:\t\tmtrr_generic_set_state();\narch/x86/kernel/cpu/cacheinfo.c-722-\t\tcache_enable();\n--\narch/x86/kernel/cpu/cacheinfo.c=751=void __init cache_bp_init(void)\narch/x86/kernel/cpu/cacheinfo.c-752-{\narch/x86/kernel/cpu/cacheinfo.c:753:\tmtrr_bp_init();\narch/x86/kernel/cpu/cacheinfo.c-754-\tpat_bp_init();\n--\narch/x86/kernel/cpu/cacheinfo.c=766=static int cache_ap_online(unsigned int cpu)\n--\narch/x86/kernel/cpu/cacheinfo.c-773-\tif (!(memory_caching_control \u0026 CACHE_MTRR) ||\narch/x86/kernel/cpu/cacheinfo.c:774:\t mtrr_generic_state_matches()) {\narch/x86/kernel/cpu/cacheinfo.c-775-\t\tif (memory_caching_control \u0026 CACHE_PAT)\n--\narch/x86/kernel/cpu/cacheinfo.c-780-\t/*\narch/x86/kernel/cpu/cacheinfo.c:781:\t * Ideally we should hold mtrr_mutex here to avoid MTRR entries\narch/x86/kernel/cpu/cacheinfo.c-782-\t * changed, but this routine will be called in CPU boot time,\n--\narch/x86/kernel/cpu/cacheinfo.c-789-\t *\narch/x86/kernel/cpu/cacheinfo.c:790:\t * 2. CPU hotadd time. We let mtrr_add/del_page hold cpuhotplug\narch/x86/kernel/cpu/cacheinfo.c-791-\t * lock to prevent MTRR entry changes\n--\narch/x86/kernel/cpu/centaur.c-9-#include \u003casm/e820/api.h\u003e\narch/x86/kernel/cpu/centaur.c:10:#include \u003casm/mtrr.h\u003e\narch/x86/kernel/cpu/centaur.c-11-#include \u003casm/msr.h\u003e\n--\narch/x86/kernel/cpu/common.c-52-#include \u003casm/fpu/api.h\u003e\narch/x86/kernel/cpu/common.c:53:#include \u003casm/mtrr.h\u003e\narch/x86/kernel/cpu/common.c-54-#include \u003casm/hwcap2.h\u003e\n--\narch/x86/kernel/cpu/mtrr/Makefile-1-# SPDX-License-Identifier: GPL-2.0-only\narch/x86/kernel/cpu/mtrr/Makefile:2:obj-y\t\t:= mtrr.o if.o generic.o cleanup.o\narch/x86/kernel/cpu/mtrr/Makefile-3-obj-$(CONFIG_X86_32) += amd.o cyrix.o centaur.o legacy.o\n--\narch/x86/kernel/cpu/mtrr/amd.c-3-#include \u003clinux/mm.h\u003e\narch/x86/kernel/cpu/mtrr/amd.c:4:#include \u003casm/mtrr.h\u003e\narch/x86/kernel/cpu/mtrr/amd.c-5-#include \u003casm/msr.h\u003e\narch/x86/kernel/cpu/mtrr/amd.c-6-\narch/x86/kernel/cpu/mtrr/amd.c:7:#include \"mtrr.h\"\narch/x86/kernel/cpu/mtrr/amd.c-8-\narch/x86/kernel/cpu/mtrr/amd.c=9=static void\narch/x86/kernel/cpu/mtrr/amd.c:10:amd_get_mtrr(unsigned int reg, unsigned long *base,\narch/x86/kernel/cpu/mtrr/amd.c:11:\t unsigned long *size, mtrr_type *type)\narch/x86/kernel/cpu/mtrr/amd.c-12-{\n--\narch/x86/kernel/cpu/mtrr/amd.c-52-/**\narch/x86/kernel/cpu/mtrr/amd.c:53: * amd_set_mtrr - Set variable MTRR register on the local CPU.\narch/x86/kernel/cpu/mtrr/amd.c-54- *\n--\narch/x86/kernel/cpu/mtrr/amd.c=62=static void\narch/x86/kernel/cpu/mtrr/amd.c:63:amd_set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type type)\narch/x86/kernel/cpu/mtrr/amd.c-64-{\n--\narch/x86/kernel/cpu/mtrr/amd.c-95-\t * The writeback rule is quite specific. See the manual. Its\narch/x86/kernel/cpu/mtrr/amd.c:96:\t * disable local interrupts, write back the cache, set the mtrr\narch/x86/kernel/cpu/mtrr/amd.c-97-\t */\n--\narch/x86/kernel/cpu/mtrr/amd.c=105=amd_validate_add_page(unsigned long base, unsigned long size, unsigned int type)\n--\narch/x86/kernel/cpu/mtrr/amd.c-120-\narch/x86/kernel/cpu/mtrr/amd.c:121:const struct mtrr_ops amd_mtrr_ops = {\narch/x86/kernel/cpu/mtrr/amd.c-122-\t.var_regs = 2,\narch/x86/kernel/cpu/mtrr/amd.c:123:\t.set = amd_set_mtrr,\narch/x86/kernel/cpu/mtrr/amd.c:124:\t.get = amd_get_mtrr,\narch/x86/kernel/cpu/mtrr/amd.c-125-\t.get_free_region = generic_get_free_region,\n--\narch/x86/kernel/cpu/mtrr/centaur.c-4-\narch/x86/kernel/cpu/mtrr/centaur.c:5:#include \u003casm/mtrr.h\u003e\narch/x86/kernel/cpu/mtrr/centaur.c-6-#include \u003casm/msr.h\u003e\narch/x86/kernel/cpu/mtrr/centaur.c-7-\narch/x86/kernel/cpu/mtrr/centaur.c:8:#include \"mtrr.h\"\narch/x86/kernel/cpu/mtrr/centaur.c-9-\n--\narch/x86/kernel/cpu/mtrr/centaur.c=27=centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg)\n--\narch/x86/kernel/cpu/mtrr/centaur.c-29-\tunsigned long lbase, lsize;\narch/x86/kernel/cpu/mtrr/centaur.c:30:\tmtrr_type ltype;\narch/x86/kernel/cpu/mtrr/centaur.c-31-\tint i, max;\n--\narch/x86/kernel/cpu/mtrr/centaur.c-39-\t\t\tcontinue;\narch/x86/kernel/cpu/mtrr/centaur.c:40:\t\tmtrr_if-\u003eget(i, \u0026lbase, \u0026lsize, \u0026ltype);\narch/x86/kernel/cpu/mtrr/centaur.c-41-\t\tif (lsize == 0)\n--\narch/x86/kernel/cpu/mtrr/centaur.c=49=centaur_get_mcr(unsigned int reg, unsigned long *base,\narch/x86/kernel/cpu/mtrr/centaur.c:50:\t\tunsigned long *size, mtrr_type * type)\narch/x86/kernel/cpu/mtrr/centaur.c-51-{\n--\narch/x86/kernel/cpu/mtrr/centaur.c=65=centaur_set_mcr(unsigned int reg, unsigned long base,\narch/x86/kernel/cpu/mtrr/centaur.c:66:\t\tunsigned long size, mtrr_type type)\narch/x86/kernel/cpu/mtrr/centaur.c-67-{\n--\narch/x86/kernel/cpu/mtrr/centaur.c=91=centaur_validate_add_page(unsigned long base, unsigned long size, unsigned int type)\n--\narch/x86/kernel/cpu/mtrr/centaur.c-97-\t (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) {\narch/x86/kernel/cpu/mtrr/centaur.c:98:\t\tpr_warn(\"mtrr: only write-combining%s supported\\n\",\narch/x86/kernel/cpu/mtrr/centaur.c-99-\t\t\t centaur_mcr_type ? \" and uncacheable are\" : \" is\");\n--\narch/x86/kernel/cpu/mtrr/centaur.c-104-\narch/x86/kernel/cpu/mtrr/centaur.c:105:const struct mtrr_ops centaur_mtrr_ops = {\narch/x86/kernel/cpu/mtrr/centaur.c-106-\t.var_regs = 8,\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-17-#include \u003casm/e820/api.h\u003e\narch/x86/kernel/cpu/mtrr/cleanup.c:18:#include \u003casm/mtrr.h\u003e\narch/x86/kernel/cpu/mtrr/cleanup.c-19-#include \u003casm/msr.h\u003e\narch/x86/kernel/cpu/mtrr/cleanup.c-20-\narch/x86/kernel/cpu/mtrr/cleanup.c:21:#include \"mtrr.h\"\narch/x86/kernel/cpu/mtrr/cleanup.c-22-\narch/x86/kernel/cpu/mtrr/cleanup.c:23:struct var_mtrr_range_state {\narch/x86/kernel/cpu/mtrr/cleanup.c-24-\tunsigned long\tbase_pfn;\narch/x86/kernel/cpu/mtrr/cleanup.c-25-\tunsigned long\tsize_pfn;\narch/x86/kernel/cpu/mtrr/cleanup.c:26:\tmtrr_type\ttype;\narch/x86/kernel/cpu/mtrr/cleanup.c-27-};\narch/x86/kernel/cpu/mtrr/cleanup.c-28-\narch/x86/kernel/cpu/mtrr/cleanup.c:29:struct var_mtrr_state {\narch/x86/kernel/cpu/mtrr/cleanup.c-30-\tunsigned long\trange_startk;\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=41=static int __initdata\t\t\t\tnr_range;\narch/x86/kernel/cpu/mtrr/cleanup.c-42-\narch/x86/kernel/cpu/mtrr/cleanup.c:43:static struct var_mtrr_range_state __initdata\trange_state[RANGE_NUM];\narch/x86/kernel/cpu/mtrr/cleanup.c-44-\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=48=static int __init\narch/x86/kernel/cpu/mtrr/cleanup.c:49:x86_get_mtrr_mem_range(struct range *range, int nr_range,\narch/x86/kernel/cpu/mtrr/cleanup.c-50-\t\t unsigned long extra_remove_base,\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-53-\tunsigned long base, size;\narch/x86/kernel/cpu/mtrr/cleanup.c:54:\tmtrr_type type;\narch/x86/kernel/cpu/mtrr/cleanup.c-55-\tint i;\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-81-\t\tbase = range_state[i].base_pfn;\narch/x86/kernel/cpu/mtrr/cleanup.c:82:\t\tif (base \u003c (1\u003c\u003c(20-PAGE_SHIFT)) \u0026\u0026 mtrr_state.have_fixed \u0026\u0026\narch/x86/kernel/cpu/mtrr/cleanup.c:83:\t\t (mtrr_state.enabled \u0026 MTRR_STATE_MTRR_ENABLED) \u0026\u0026\narch/x86/kernel/cpu/mtrr/cleanup.c:84:\t\t (mtrr_state.enabled \u0026 MTRR_STATE_MTRR_FIXED_ENABLED)) {\narch/x86/kernel/cpu/mtrr/cleanup.c-85-\t\t\t/* Var MTRR contains UC entry below 1M? Skip it: */\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=120=static unsigned long __init sum_ranges(struct range *range, int nr_range)\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-130-\narch/x86/kernel/cpu/mtrr/cleanup.c:131:static int enable_mtrr_cleanup __initdata =\narch/x86/kernel/cpu/mtrr/cleanup.c-132-\tCONFIG_MTRR_SANITIZER_ENABLE_DEFAULT;\narch/x86/kernel/cpu/mtrr/cleanup.c-133-\narch/x86/kernel/cpu/mtrr/cleanup.c:134:static int __init disable_mtrr_cleanup_setup(char *str)\narch/x86/kernel/cpu/mtrr/cleanup.c-135-{\narch/x86/kernel/cpu/mtrr/cleanup.c:136:\tenable_mtrr_cleanup = 0;\narch/x86/kernel/cpu/mtrr/cleanup.c-137-\treturn 0;\narch/x86/kernel/cpu/mtrr/cleanup.c-138-}\narch/x86/kernel/cpu/mtrr/cleanup.c:139:early_param(\"disable_mtrr_cleanup\", disable_mtrr_cleanup_setup);\narch/x86/kernel/cpu/mtrr/cleanup.c-140-\narch/x86/kernel/cpu/mtrr/cleanup.c:141:static int __init enable_mtrr_cleanup_setup(char *str)\narch/x86/kernel/cpu/mtrr/cleanup.c-142-{\narch/x86/kernel/cpu/mtrr/cleanup.c:143:\tenable_mtrr_cleanup = 1;\narch/x86/kernel/cpu/mtrr/cleanup.c-144-\treturn 0;\narch/x86/kernel/cpu/mtrr/cleanup.c-145-}\narch/x86/kernel/cpu/mtrr/cleanup.c:146:early_param(\"enable_mtrr_cleanup\", enable_mtrr_cleanup_setup);\narch/x86/kernel/cpu/mtrr/cleanup.c-147-\narch/x86/kernel/cpu/mtrr/cleanup.c=148=static void __init\narch/x86/kernel/cpu/mtrr/cleanup.c:149:set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,\narch/x86/kernel/cpu/mtrr/cleanup.c-150-\t unsigned char type)\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-155-\tif (!sizek) {\narch/x86/kernel/cpu/mtrr/cleanup.c:156:\t\tfill_mtrr_var_range(reg, 0, 0, 0, 0);\narch/x86/kernel/cpu/mtrr/cleanup.c-157-\t\treturn;\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-173-\narch/x86/kernel/cpu/mtrr/cleanup.c:174:\tfill_mtrr_var_range(reg, base_lo, base_hi, mask_lo, mask_hi);\narch/x86/kernel/cpu/mtrr/cleanup.c-175-}\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=177=static void __init\narch/x86/kernel/cpu/mtrr/cleanup.c:178:save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,\narch/x86/kernel/cpu/mtrr/cleanup.c-179-\t unsigned char type)\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-185-\narch/x86/kernel/cpu/mtrr/cleanup.c:186:static void __init set_var_mtrr_all(void)\narch/x86/kernel/cpu/mtrr/cleanup.c-187-{\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-196-\narch/x86/kernel/cpu/mtrr/cleanup.c:197:\t\tset_var_mtrr(reg, basek, sizek, type);\n\n"
} |
| 57/2 | 2026/10/15 20:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 53543 output: 24 thoughts: 793 Thoughts: **Analyzing x86 Kernel Changes** Okay, let's dive in. I'm seeing some modifications related to `wbinvd` and CMCI within the x86 kernel. First, I'm noting the change in `arch/x86/kernel/smpboot.c`: the `mwait_play_dead` function now conditionally calls `wbinvd` only if `cpuhp_tasks_frozen` is true. That's a significant optimization; avoiding the `wbinvd` call when it's not strictly necessary. Simultaneously, I see that the `wbinvd` call has been removed from `trampoline_32.S` and `trampoline_64.S`. That removal makes sense, given the more targeted use in `smpboot.c`. We're likely trying to minimize the instruction footprint during certain operations, particularly if this is related to a boot path or CPU hotplugging sequence. Now, let's shift our focus to `arch/x86/kernel/cpu/mce/intel.c`, specifically the `cmci_clear()` and `cmci_rediscover()` functions. They both seem to be involved in clearing and rediscovering CMCI (Corrected Machine Check Interrupts) settings. I'm analyzing how these functions interact with the CMCI infrastructure. I'm seeing that `cmci_clear()` clears the `cmci_orphaned_banks` bitmap and attempts to find a "target" CPU, likely a sibling, cluster, die, or core CPU, to offload orphaned banks to. In `cmci_rediscover()`, the code checks if there are orphaned banks and calls `smp_call_function_single` on the target CPU if it's online. It appears to be a mechanism for redistributing CMCI bank ownership when a CPU is being taken offline or there's a configuration change. The use of `raw_spin_lock_irqsave` and `raw_spin_unlock_irqrestore` indicates that these operations are critical sections, which are likely part of a broader CPU state management system. I need to now determine where these are called from. |
| 58/2 | 2026/10/15 20:28 | tool | grepper |
0mArgs:{
"Expression": "cmci_clear|cmci_rediscover"
}Results: {
"Output": "arch/x86/include/asm/mce.h=275=void mce_intel_feature_clear(struct cpuinfo_x86 *c);\narch/x86/include/asm/mce.h:276:void cmci_clear(void);\narch/x86/include/asm/mce.h-277-void cmci_reenable(void);\narch/x86/include/asm/mce.h:278:void cmci_rediscover(void);\narch/x86/include/asm/mce.h-279-void cmci_recheck(void);\n--\narch/x86/include/asm/mce.h=282=static inline void mce_intel_feature_clear(struct cpuinfo_x86 *c) { }\narch/x86/include/asm/mce.h:283:static inline void cmci_clear(void) {}\narch/x86/include/asm/mce.h-284-static inline void cmci_reenable(void) {}\narch/x86/include/asm/mce.h:285:static inline void cmci_rediscover(void) {}\narch/x86/include/asm/mce.h-286-static inline void cmci_recheck(void) {}\n--\narch/x86/kernel/cpu/mce/core.c=2477=static void mce_disable_cmci(void *data)\n--\narch/x86/kernel/cpu/mce/core.c-2480-\t\treturn;\narch/x86/kernel/cpu/mce/core.c:2481:\tcmci_clear();\narch/x86/kernel/cpu/mce/core.c-2482-}\n--\narch/x86/kernel/cpu/mce/core.c=2728=static void mce_disable_cpu(void)\n--\narch/x86/kernel/cpu/mce/core.c-2733-\tif (!cpuhp_tasks_frozen)\narch/x86/kernel/cpu/mce/core.c:2734:\t\tcmci_clear();\narch/x86/kernel/cpu/mce/core.c-2735-\n--\narch/x86/kernel/cpu/mce/core.c=2757=static int mce_cpu_dead(unsigned int cpu)\n--\narch/x86/kernel/cpu/mce/core.c-2760-\tif (!cpuhp_tasks_frozen)\narch/x86/kernel/cpu/mce/core.c:2761:\t\tcmci_rediscover();\narch/x86/kernel/cpu/mce/core.c-2762-\treturn 0;\n--\narch/x86/kernel/cpu/mce/intel.c-36- * Note that this can change when a cpu is offlined or brought online since\narch/x86/kernel/cpu/mce/intel.c:37: * some MCA banks are shared across cpus. When a cpu is offlined, cmci_clear()\narch/x86/kernel/cpu/mce/intel.c-38- * disables CMCI on all banks owned by the cpu and clears this bitfield. At\narch/x86/kernel/cpu/mce/intel.c:39: * this point, cmci_rediscover() kicks in and a different cpu may end up\narch/x86/kernel/cpu/mce/intel.c-40- * taking ownership of some of the shared MCA banks that were previously\n--\narch/x86/kernel/cpu/mce/intel.c=105=static bool lmce_supported(void)\n--\narch/x86/kernel/cpu/mce/intel.c-138- * MCI_CTL2_CMCI_EN bit in case this happens during a\narch/x86/kernel/cpu/mce/intel.c:139: * cmci_rediscover() operation.\narch/x86/kernel/cpu/mce/intel.c-140- */\n--\narch/x86/kernel/cpu/mce/intel.c=325=static void __cmci_disable_bank(int bank)\n--\narch/x86/kernel/cpu/mce/intel.c-344- */\narch/x86/kernel/cpu/mce/intel.c:345:void cmci_clear(void)\narch/x86/kernel/cpu/mce/intel.c-346-{\n--\narch/x86/kernel/cpu/mce/intel.c-383-\narch/x86/kernel/cpu/mce/intel.c:384:static void cmci_rediscover_work_func(void *arg)\narch/x86/kernel/cpu/mce/intel.c-385-{\n--\narch/x86/kernel/cpu/mce/intel.c-395- * topology peer (SMT sibling, L2 cluster peer, die peer, or package peer)\narch/x86/kernel/cpu/mce/intel.c:396: * selected in cmci_clear(). Core-private banks whose core is now completely\narch/x86/kernel/cpu/mce/intel.c-397- * offline cannot be claimed by any other core, so probing more than one\n--\narch/x86/kernel/cpu/mce/intel.c-399- */\narch/x86/kernel/cpu/mce/intel.c:400:void cmci_rediscover(void)\narch/x86/kernel/cpu/mce/intel.c-401-{\n--\narch/x86/kernel/cpu/mce/intel.c-418-\tif (target \u003c nr_cpu_ids \u0026\u0026 cpu_online(target))\narch/x86/kernel/cpu/mce/intel.c:419:\t\tsmp_call_function_single(target, cmci_rediscover_work_func,\narch/x86/kernel/cpu/mce/intel.c-420-\t\t\t\t\t NULL, 1);\n--\narch/x86/kernel/cpu/mce/intel.c=549=void mce_intel_feature_clear(struct cpuinfo_x86 *c)\n--\narch/x86/kernel/cpu/mce/intel.c-551-\tintel_clear_lmce();\narch/x86/kernel/cpu/mce/intel.c:552:\tcmci_clear();\narch/x86/kernel/cpu/mce/intel.c-553-}\n"
} |
| 59/2 | 2026/10/15 20:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 56447 output: 23 thoughts: 4471 Thoughts: **Fuzzing Kernel CPU and Scheduling Code: A Focused Approach**
Alright, let's break this down. My goal is to determine if this patch series is ripe for fuzzing and, if so, how to best target it. My initial thought process revolves around identifying the specific code areas modified and the potential attack surfaces.
First, I see the changes touch several core subsystems: scheduler topology, deadline scheduling, memory management (vmstat), and x86 CPU management, including MTRR and MCE. The changes involve dynamic allocations, loops, reference counts, lockdep assertions, and bandwidth accounting. This signals the potential for concurrency issues, memory corruption, and subtle logic flaws – all ideal targets for fuzzing.
My primary concern, however, is to avoid being too broad. I need to pinpoint specific functions that are likely to expose new vulnerabilities or trigger unexpected behavior. I am focusing on what's changed and not on ubiquitous code paths.
I examine the code changes in `kernel/sched/topology.c`. It looks like memory management (allocating structs) and caching LLC bytes were updated, specifically related to how the scheduler handles CPU topology and resource allocation. Then, I observe changes in `kernel/sched/deadline.c`, including modifications related to bandwidth accounting and CPU active mask, along with `lockdep_assert_*` and `rcu_read_*` calls, all of which raise the chance of concurrency-related bugs. And `mm/vmstat.c`, which has threshold calculation changes based on CPU online, looks interesting.
From here, I'm identifying functions that are likely to be reached by userspace. After looking at the files, I focus on the following:
* In `kernel/sched/topology.c`:
* `build_sched_domains` (but needs to be non-static)
* `build_overlap_sched_groups`
* `build_sched_groups`
* `init_overlap_sched_group`
* `sched_update_llc_bytes`
* `partition_sched_domains`
* `rebuild_sched_domains`
* In `kernel/sched/deadline.c`:
* `dl_rebuild_rd_accounting`
* `dl_clear_root_domain_cpu`
* `dl_clear_root_domain`
* In `mm/vmstat.c`:
* `refresh_zone_stat_thresholds`
* In `arch/x86/kernel/cpu/mtrr/generic.c`:
* `mtrr_generic_state_matches`
* In `arch/x86/kernel/cpu/mce/intel.c`:
* `cmci_clear`
* `cmci_rediscover`
Then, I go over the list to look for functions that are not ubiquitous. I look at each of the above functions and list the focus symbols.
Now, it's about making sure I have the right kernel configuration. It looks like `CONFIG_SMP` is essential. In addition, `CONFIG_CPUSETS`, `CONFIG_SCHED_CACHE`, `CONFIG_MTRR`, `CONFIG_X86_MCE_INTEL`, and `CONFIG_SCHED_CLASS_EXT` need to be enabled. These configurations will ensure that the code I want to target is compiled and reachable.
Based on the nature of the changes (core system code, dynamic allocations, concurrency, and x86-specific features), I'm confident that these modifications are a good candidate for fuzzing. I'll focus on these specific symbols to maximize the chances of uncovering interesting bugs.
|
| 60/2 | 2026/10/15 20:28 | tool | grepper |
0mArgs:{
"Expression": "config SCHED_CLASS_EXT\\b"
}Results: {
"Output": "kernel/Kconfig.preempt=155=config SCHED_CORE\n--\nkernel/Kconfig.preempt-172-\nkernel/Kconfig.preempt:173:config SCHED_CLASS_EXT\nkernel/Kconfig.preempt-174-\tbool \"Extensible Scheduling Class\"\n"
} |
| 61/2 | 2026/10/15 20:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 61829 output: 22 thoughts: 80 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience: **Considering `SCHED_CLASS_EXT` and its Implications for CPU Management** Right, so `SCHED_CLASS_EXT` is the extensible scheduling class, a key piece in our system. I'm immediately thinking, what about `CONFIG_HOTPLUG_CPU`? That's the crucial link here, isn't it? CPU hotplugging is the mechanism that triggers the `CPU down` or `offline` operations. That, in turn, is directly related to functions like `mce_cpu_dead`, `cmci_rediscover`, `cache_ap_online`, and so on. These are the routines that need to be aware of any changes. So, I need to know if `CONFIG_HOTPLUG_CPU` is actually defined. I need to establish its status before I can proceed any further. Let's see if that config symbol is enabled or not. |
| 62/2 | 2026/10/15 20:29 | tool | grepper |
0mArgs:{
"Expression": "config HOTPLUG_CPU\\b"
}Results: {
"Output": "arch/arm/Kconfig=1065=config NR_CPUS\n--\narch/arm/Kconfig-1076-\narch/arm/Kconfig:1077:config HOTPLUG_CPU\narch/arm/Kconfig-1078-\tbool \"Support for hot-pluggable CPUs\"\n--\narch/arm64/Kconfig=1621=config NR_CPUS\n--\narch/arm64/Kconfig-1625-\narch/arm64/Kconfig:1626:config HOTPLUG_CPU\narch/arm64/Kconfig-1627-\tbool \"Support for hot-pluggable CPUs\"\n--\narch/csky/Kconfig=330=config DRAM_BASE\n--\narch/csky/Kconfig-333-\narch/csky/Kconfig:334:config HOTPLUG_CPU\narch/csky/Kconfig-335-\tbool \"Support for hot-pluggable CPUs\"\n--\narch/loongarch/Kconfig=515=config SMP\n--\narch/loongarch/Kconfig-532-\narch/loongarch/Kconfig:533:config HOTPLUG_CPU\narch/loongarch/Kconfig-534-\tbool \"Support for hot-pluggable CPUs\"\n--\narch/mips/Kconfig=2703=config SMP\n--\narch/mips/Kconfig-2724-\narch/mips/Kconfig:2725:config HOTPLUG_CPU\narch/mips/Kconfig-2726-\tbool \"Support for hot-pluggable CPUs\"\n--\narch/parisc/Kconfig=331=config IRQSTACKS\n--\narch/parisc/Kconfig-338-\narch/parisc/Kconfig:339:config HOTPLUG_CPU\narch/parisc/Kconfig-340-\tbool\n--\narch/powerpc/Kconfig=583=config PPC_FTRACE_OUT_OF_LINE_NUM_RESERVE\n--\narch/powerpc/Kconfig-594-\narch/powerpc/Kconfig:595:config HOTPLUG_CPU\narch/powerpc/Kconfig-596-\tbool \"Support for enabling/disabling CPUs\"\n--\narch/riscv/Kconfig=453=config NR_CPUS\n--\narch/riscv/Kconfig-462-\narch/riscv/Kconfig:463:config HOTPLUG_CPU\narch/riscv/Kconfig-464-\tbool \"Support for hot-pluggable CPUs\"\n--\narch/s390/Kconfig=535=config NR_CPUS\n--\narch/s390/Kconfig-546-\narch/s390/Kconfig:547:config HOTPLUG_CPU\narch/s390/Kconfig-548-\tdef_bool y\n--\narch/sh/Kconfig=592=config NR_CPUS\n--\narch/sh/Kconfig-605-\narch/sh/Kconfig:606:config HOTPLUG_CPU\narch/sh/Kconfig-607-\tbool \"Support for hot-pluggable CPUs (EXPERIMENTAL)\"\n--\narch/sparc/Kconfig=220=config EARLYFB\n--\narch/sparc/Kconfig-228-\narch/sparc/Kconfig:229:config HOTPLUG_CPU\narch/sparc/Kconfig-230-\tbool \"Support for hot-pluggable CPUs\"\n--\narch/x86/Kconfig=2154=config ADDRESS_MASKING\n--\narch/x86/Kconfig-2165-\narch/x86/Kconfig:2166:config HOTPLUG_CPU\narch/x86/Kconfig-2167-\tdef_bool y\n--\narch/xtensa/Kconfig=253=config NR_CPUS\n--\narch/xtensa/Kconfig-258-\narch/xtensa/Kconfig:259:config HOTPLUG_CPU\narch/xtensa/Kconfig-260-\tbool \"Enable CPU hotplug support\"\n"
} |
| 63/2 | 2026/10/15 20:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 63181 output: 337 thoughts: 1069 Thoughts: **Analyzing Scheduler Domain and CPU Management Paths on x86** Okay, so `CONFIG_HOTPLUG_CPU` is definitely enabled on x86, which is a good starting point. Let's see... looking at the code, if `CONFIG_NUMA` is enabled (which it often is), the scheduler's NUMA paths in `kernel/sched/topology.c` like `sched_init_numa` and `build_overlap_sched_groups` are definitely exercised. Good, that confirms NUMA's involvement. Now, I'm focusing on those specific symbols. I need to make sure I've got the most comprehensive list of entry points. Let's see... `build_sched_domains`, `build_overlap_sched_groups`, `build_sched_groups`, `init_overlap_sched_group`, `dl_rebuild_rd_accounting`, `dl_clear_root_domain`, `refresh_zone_stat_thresholds`, `sched_update_llc_bytes`, `mtrr_generic_state_matches`, `cmci_rediscover`, and `cmci_clear`. I need to differentiate static inline functions, to be sure I'm capturing all the relevant entry points. Okay, checking the headers... `build_sched_domains`, `build_overlap_sched_groups`, `build_sched_groups`, and `init_overlap_sched_group` are all static inline within `kernel/sched/topology.c`. `dl_rebuild_rd_accounting` is non-static and in `kernel/sched/deadline.c` (or potentially `kernel/cgroup/cpuset.c`). `dl_clear_root_domain` is static in `kernel/sched/deadline.c`. `refresh_zone_stat_thresholds` is non-static in `mm/vmstat.c`. `sched_update_llc_bytes` is non-static in `kernel/sched/topology.c`. Then, `mtrr_generic_state_matches` is non-static in `arch/x86/kernel/cpu/mtrr/generic.c`. Finally, `cmci_rediscover` and `cmci_clear` are non-static in `arch/x86/kernel/cpu/mce/intel.c`. Hold on... what about `partition_sched_domains`? That's the external entry point in `kernel/sched/topology.c` that acquires `sched_domains_mutex` and then calls `partition_sched_domains_locked`, which itself directly calls `build_sched_domains` and `dl_rebuild_rd_accounting`. Yes, that one's crucial. I need to include that in the final list, as it's a direct API entrypoint to the sub-system. And what about `dl_clear_root_domain_cpu`? Ah, that's non-static in `kernel/sched/deadline.c`, I'll add that too. Right, time to nail down the required configs. I need to consider these: `SMP`, `NUMA`, `CPUSETS`, `SCHED_CACHE`, `MTRR`, `X86_MCE_INTEL`, and `HOTPLUG_CPU`. This represents scheduler domain building, topology considerations for multi-core and NUMA setups, cpuset cgroup integration, cache-aware scheduling, MTRR state checking, and Intel CMCI clearing and rediscovery, and the CPU hotplug path, which ties everything together. Finally, the reasoning. I need a clear structure. I'll describe the key subsystems affected, like the core scheduler topology, deadline scheduler bandwidth accounting, memory management vmstat thresholds, and x86 MTRR/CMCI CPU management. Then, I need to emphasize that these paths are easily triggered in standard virtualized environments, like those that utilize CPU hotplug via sysfs, cpuset cgroups, or scheduler syscalls. These code modifications involve significant refactoring of sched domain allocation, group building loops, lockdep assertions, bandwidth calculations, and MTRR/CMCI state checks, which may have the potential for regressions, memory leaks, or race conditions. Yep, that's it. Everything is clear and verified. Time to finalize the results. |
| 64/2 | 2026/10/15 20:29 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"SMP",
"NUMA",
"CPUSETS",
"SCHED_CACHE",
"MTRR",
"X86_MCE_INTEL",
"HOTPLUG_CPU"
],
"FocusSymbols": [
"build_sched_domains",
"build_overlap_sched_groups",
"build_sched_groups",
"init_overlap_sched_group",
"partition_sched_domains",
"dl_rebuild_rd_accounting",
"dl_clear_root_domain",
"dl_clear_root_domain_cpu",
"refresh_zone_stat_thresholds",
"sched_update_llc_bytes",
"mtrr_generic_state_matches",
"cmci_rediscover",
"cmci_clear"
],
"Reasoning": "The patch series touches several core kernel subsystems reachable in standard QEMU amd64 environments, including scheduler topology building (kernel/sched/topology.c), deadline scheduler bandwidth accounting (kernel/sched/deadline.c), memory management vmstat threshold updates (mm/vmstat.c), and x86 MTRR/CMCI handling. The scheduler changes rework sched_group allocations, traversal loops, and root domain accounting, which can be exercised via cpuset cgroup operations, CPU hotplug, and SCHED_DEADLINE syscalls. These non-trivial algorithmic and memory management adjustments warrant fuzzing to uncover potential memory corruptions, invariant violations, or regressions.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"SMP",
"NUMA",
"CPUSETS",
"SCHED_CACHE",
"MTRR",
"X86_MCE_INTEL",
"HOTPLUG_CPU"
],
"FocusSymbols": [
"build_sched_domains",
"build_overlap_sched_groups",
"build_sched_groups",
"init_overlap_sched_group",
"partition_sched_domains",
"dl_rebuild_rd_accounting",
"dl_clear_root_domain",
"dl_clear_root_domain_cpu",
"refresh_zone_stat_thresholds",
"sched_update_llc_bytes",
"mtrr_generic_state_matches",
"cmci_rediscover",
"cmci_clear"
],
"Reasoning": "The patch series touches several core kernel subsystems reachable in standard QEMU amd64 environments, including scheduler topology building (kernel/sched/topology.c), deadline scheduler bandwidth accounting (kernel/sched/deadline.c), memory management vmstat threshold updates (mm/vmstat.c), and x86 MTRR/CMCI handling. The scheduler changes rework sched_group allocations, traversal loops, and root domain accounting, which can be exercised via cpuset cgroup operations, CPU hotplug, and SCHED_DEADLINE syscalls. These non-trivial algorithmic and memory management adjustments warrant fuzzing to uncover potential memory corruptions, invariant violations, or regressions.",
"WorthFuzzing": true
} |
| 65/1 | 2026/10/15 20:29 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 66/2 | 2026/10/15 20:29 | agent | kmsan-evaluator |
1mModel:core Args: null Results: {
"KMSANReasoning": "The patch series modifies x86 MTRR checking, CMCI bank handling during CPU offline, SMP boot cache flush logic, scheduler deadline bandwidth accounting, scheduler topology allocation/caching, and vmstat threshold calculation.\n\nNone of these changes expose uninitialized memory risks or kernel-to-user info-leaks:\n1. All newly added dynamic allocations in the scheduler topology code (such as `sched_group`, `sched_group_capacity`, and `sched_domain_shared`) use `kzalloc_node` or `alloc_percpu`, ensuring all fields are zero-initialized.\n2. All local stack variables (e.g. `val` in `mtrr_generic_state_matches()`, `total_bw`/`extra_bw` in `dl_clear_root_domain()`, and `pgdat_threshold` in `refresh_zone_stat_thresholds()`) are explicitly initialized before any read or use.\n3. CMCI state tracking uses static BSS variables (`cmci_orphaned_banks`, `cmci_orphaned_target`) that are explicitly zeroed via `bitmap_zero()`.\n4. No data structures or padding bytes are copied or leaked to user space via `copy_to_user`, ioctl outputs, netlink attributes, or socket buffers.\n5. Potential edge-case bugs that could arise from these changes (e.g., concurrency/locking issues, cpumask out-of-bounds, or NULL pointer dereferences) are covered by KASAN, LOCKDEP, and standard kernel assertions rather than KMSAN.\n\nThus, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit dab0643a8b764d66dba17d993938331f8cba14a3
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 15 20:25:44 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h
index 76b95bd1a4058..0527e216808c4 100644
--- a/arch/x86/include/asm/mtrr.h
+++ b/arch/x86/include/asm/mtrr.h
@@ -61,6 +61,7 @@ extern int amd_special_default_mtrr(void);
void mtrr_disable(void);
void mtrr_enable(void);
void mtrr_generic_set_state(void);
+bool mtrr_generic_state_matches(void);
# else
static inline void guest_force_mtrr_state(struct mtrr_var_range *var,
unsigned int num_var,
@@ -106,6 +107,7 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
#define mtrr_disable() do {} while (0)
#define mtrr_enable() do {} while (0)
#define mtrr_generic_set_state() do {} while (0)
+static inline bool mtrr_generic_state_matches(void) { return true; }
# endif
#ifdef CONFIG_COMPAT
diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c
index 13ed16527905f..64796d5b34fdc 100644
--- a/arch/x86/kernel/cpu/cacheinfo.c
+++ b/arch/x86/kernel/cpu/cacheinfo.c
@@ -770,6 +770,13 @@ static int cache_ap_online(unsigned int cpu)
if (!memory_caching_control || get_cache_aps_delayed_init())
return 0;
+ if (!(memory_caching_control & CACHE_MTRR) ||
+ mtrr_generic_state_matches()) {
+ if (memory_caching_control & CACHE_PAT)
+ pat_cpu_init();
+ return 0;
+ }
+
/*
* Ideally we should hold mtrr_mutex here to avoid MTRR entries
* changed, but this routine will be called in CPU boot time,
diff --git a/arch/x86/kernel/cpu/mce/intel.c b/arch/x86/kernel/cpu/mce/intel.c
index 4655223ba5606..f57718b097d9b 100644
--- a/arch/x86/kernel/cpu/mce/intel.c
+++ b/arch/x86/kernel/cpu/mce/intel.c
@@ -11,6 +11,7 @@
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/cpumask.h>
+#include <linux/topology.h>
#include <asm/apic.h>
#include <asm/cpufeature.h>
#include <asm/cpu_device_id.h>
@@ -40,6 +41,8 @@
* owned by the offlined cpu.
*/
static DEFINE_PER_CPU(mce_banks_t, mce_banks_owned);
+static mce_banks_t cmci_orphaned_banks;
+static unsigned int cmci_orphaned_target;
/*
* cmci_discover_lock protects against parallel discovery attempts
@@ -244,6 +247,7 @@ static void cmci_claim_bank(int bank, u64 val, int bios_zero_thresh, int *bios_w
/* This CPU successfully set the enable bit. */
set_bit(bank, (void *)this_cpu_ptr(&mce_banks_owned));
+ __clear_bit(bank, cmci_orphaned_banks);
if ((val & MCI_CTL2_CMCI_THRESHOLD_MASK) == CMCI_STORM_THRESHOLD) {
pr_notice("CPU%d BANK%d CMCI inherited storm\n", smp_processor_id(), bank);
@@ -328,6 +332,7 @@ static void __cmci_disable_bank(int bank)
val &= ~MCI_CTL2_CMCI_EN;
wrmsrq(MSR_IA32_MCx_CTL2(bank), val);
__clear_bit(bank, this_cpu_ptr(mce_banks_owned));
+ __set_bit(bank, cmci_orphaned_banks);
if ((val & MCI_CTL2_CMCI_THRESHOLD_MASK) == CMCI_STORM_THRESHOLD)
cmci_storm_end(bank);
@@ -339,6 +344,8 @@ static void __cmci_disable_bank(int bank)
*/
void cmci_clear(void)
{
+ unsigned int cpu = smp_processor_id();
+ unsigned int target;
unsigned long flags;
int i;
int banks;
@@ -346,8 +353,31 @@ void cmci_clear(void)
if (!cmci_supported(&banks))
return;
raw_spin_lock_irqsave(&cmci_discover_lock, flags);
+ bitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);
+ /*
+ * A bank is shared by the CPUs of one SMT core, L2 cluster, die or
+ * package, and these scopes nest. The first online peer found from
+ * the innermost scope outwards is therefore part of every sharing
+ * scope that still has an online CPU, so it alone can claim every
+ * orphaned bank that is still claimable. If it already owns an index,
+ * the instance released here is not shared with it, hence not with
+ * any online CPU, and that index can be dropped from the rediscovery.
+ */
+ target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu);
+ if (target >= nr_cpu_ids)
+ target = cpumask_any_but(topology_cluster_cpumask(cpu), cpu);
+ if (target >= nr_cpu_ids)
+ target = cpumask_any_but(topology_die_cpumask(cpu), cpu);
+ if (target >= nr_cpu_ids)
+ target = cpumask_any_but(topology_core_cpumask(cpu), cpu);
+ cmci_orphaned_target = target;
for (i = 0; i < banks; i++)
__cmci_disable_bank(i);
+ if (target < nr_cpu_ids)
+ bitmap_andnot(cmci_orphaned_banks, cmci_orphaned_banks,
+ per_cpu(mce_banks_owned, target), MAX_NR_BANKS);
+ else
+ bitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);
raw_spin_unlock_irqrestore(&cmci_discover_lock, flags);
}
@@ -360,15 +390,38 @@ static void cmci_rediscover_work_func(void *arg)
cmci_discover(banks);
}
-/* After a CPU went down cycle through all the others and rediscover */
+/*
+ * After a CPU went down, rediscover orphaned shared banks on a single
+ * topology peer (SMT sibling, L2 cluster peer, die peer, or package peer)
+ * selected in cmci_clear(). Core-private banks whose core is now completely
+ * offline cannot be claimed by any other core, so probing more than one
+ * peer is redundant.
+ */
void cmci_rediscover(void)
{
+ unsigned int target;
+ unsigned long flags;
+ bool orphaned;
int banks;
if (!cmci_supported(&banks))
return;
- on_each_cpu(cmci_rediscover_work_func, NULL, 1);
+ raw_spin_lock_irqsave(&cmci_discover_lock, flags);
+ orphaned = !bitmap_empty(cmci_orphaned_banks, MAX_NR_BANKS);
+ target = cmci_orphaned_target;
+ raw_spin_unlock_irqrestore(&cmci_discover_lock, flags);
+
+ if (!orphaned)
+ return;
+
+ if (target < nr_cpu_ids && cpu_online(target))
+ smp_call_function_single(target, cmci_rediscover_work_func,
+ NULL, 1);
+
+ raw_spin_lock_irqsave(&cmci_discover_lock, flags);
+ bitmap_zero(cmci_orphaned_banks, MAX_NR_BANKS);
+ raw_spin_unlock_irqrestore(&cmci_discover_lock, flags);
}
/*
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index 67cf69f24b005..8760a7b4dc38b 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -981,6 +981,55 @@ void mtrr_generic_set_state(void)
}
}
+bool mtrr_generic_state_matches(void)
+{
+ struct msr val;
+ unsigned int i;
+
+ if (!cpu_feature_enabled(X86_FEATURE_MTRR))
+ return true;
+
+ rdmsrq(MSR_MTRRdefType, val.q);
+ if ((val.l & MTRR_DEF_TYPE_TYPE) != mtrr_state.def_type ||
+ ((val.l & MTRR_DEF_TYPE_ENABLE) >> MTRR_STATE_SHIFT) != mtrr_state.enabled)
+ return false;
+
+ for (i = 0; i < num_var_ranges; i++) {
+ struct mtrr_var_range *vr = &mtrr_state.var_ranges[i];
+
+ rdmsrq(MTRRphysBase_MSR(i), val.q);
+ if ((vr->base_lo & ~MTRR_PHYSBASE_RSVD) != (val.l & ~MTRR_PHYSBASE_RSVD) ||
+ (vr->base_hi & ~phys_hi_rsvd) != (val.h & ~phys_hi_rsvd))
+ return false;
+
+ rdmsrq(MTRRphysMask_MSR(i), val.q);
+ if ((vr->mask_lo & ~MTRR_PHYSMASK_RSVD) != (val.l & ~MTRR_PHYSMASK_RSVD) ||
+ (vr->mask_hi & ~phys_hi_rsvd) != (val.h & ~phys_hi_rsvd))
+ return false;
+ }
+
+ if (mtrr_state.have_fixed) {
+ u64 *saved = (u64 *)mtrr_state.fixed_ranges;
+ int block = -1, range;
+
+ /*
+ * Ensure MtrrFixDramModEn is clear so fixed MTRR reads do not
+ * expose K8 RdMem/WrMem bits and cause false mismatches.
+ */
+ k8_check_syscfg_dram_mod_en();
+
+ while (fixed_range_blocks[++block].ranges) {
+ for (range = 0; range < fixed_range_blocks[block].ranges; range++) {
+ rdmsrq(fixed_range_blocks[block].base_msr + range, val.q);
+ if (val.q != *saved++)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
/**
* generic_set_mtrr - set variable MTRR register on the local CPU.
*
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index ba01a9e919b78..60a781dac9991 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1414,10 +1414,6 @@ void play_dead_common(void)
local_irq_disable();
}
-/*
- * We need to flush the caches before going to sleep, lest we have
- * dirty data in our caches when we come back up.
- */
void __noreturn mwait_play_dead(unsigned int eax_hint)
{
struct mwait_cpu_dead *md = this_cpu_ptr(&mwait_cpu_dead);
@@ -1426,15 +1422,21 @@ void __noreturn mwait_play_dead(unsigned int eax_hint)
md->status = CPUDEAD_MWAIT_WAIT;
md->control = CPUDEAD_MWAIT_WAIT;
- wbinvd();
+ /*
+ * MWAIT C-states keep the caches coherent, so runtime CPU offline
+ * does not need to flush them. System sleep, however, may remove
+ * power from this package's caches while all of its CPUs are
+ * offline, and the flush done by the boot CPU before entering the
+ * sleep state only covers the boot CPU's own package.
+ */
+ if (cpuhp_tasks_frozen)
+ wbinvd();
while (1) {
/*
* The CLFLUSH is a workaround for erratum AAI65 for
* the Xeon 7400 series. It's not clear it is actually
* needed, but it should be harmless in either case.
- * The WBINVD is insufficient due to the spurious-wakeup
- * case where we return around the loop.
*/
mb();
clflush(md);
diff --git a/arch/x86/realmode/rm/trampoline_32.S b/arch/x86/realmode/rm/trampoline_32.S
index 3fad907a179f1..14e56ea954160 100644
--- a/arch/x86/realmode/rm/trampoline_32.S
+++ b/arch/x86/realmode/rm/trampoline_32.S
@@ -30,8 +30,6 @@
.balign PAGE_SIZE
SYM_CODE_START(trampoline_start)
- wbinvd # Needed for NUMA-Q should be harmless for others
-
LJMPW_RM(1f)
1:
mov %cs, %ax # Code and data in the same place
diff --git a/arch/x86/realmode/rm/trampoline_64.S b/arch/x86/realmode/rm/trampoline_64.S
index 14d9c7daf90f4..e5a3f03d62217 100644
--- a/arch/x86/realmode/rm/trampoline_64.S
+++ b/arch/x86/realmode/rm/trampoline_64.S
@@ -60,7 +60,6 @@
.balign PAGE_SIZE
SYM_CODE_START(trampoline_start)
cli # We should be safe anyway
- wbinvd
LJMPW_RM(1f)
1:
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 914983387e955..8baa99308f49e 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -129,7 +129,7 @@ static inline int cpuset_do_page_mem_spread(void)
extern bool current_cpuset_is_being_rebound(void);
-extern void dl_rebuild_rd_accounting(void);
+void dl_rebuild_rd_accounting(void);
extern void rebuild_sched_domains(void);
extern void cpuset_print_current_mems_allowed(void);
@@ -273,9 +273,7 @@ static inline bool current_cpuset_is_being_rebound(void)
return false;
}
-static inline void dl_rebuild_rd_accounting(void)
-{
-}
+void dl_rebuild_rd_accounting(void);
static inline void rebuild_sched_domains(void)
{
diff --git a/include/linux/sched/deadline.h b/include/linux/sched/deadline.h
index 273538200a445..6f03357f05c76 100644
--- a/include/linux/sched/deadline.h
+++ b/include/linux/sched/deadline.h
@@ -31,7 +31,6 @@ static inline bool dl_time_before(u64 a, u64 b)
struct root_domain;
extern void dl_add_task_root_domain(struct task_struct *p);
-extern void dl_clear_root_domain(struct root_domain *rd);
extern void dl_clear_root_domain_cpu(int cpu);
/*
* Return whether moving DL task @p to @new_mask requires moving DL
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 857dbe3519a86..01f2f24e3b814 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1885,24 +1885,6 @@ void sched_init_dl_servers(void)
}
}
-void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq)
-{
- u64 new_bw = dl_se->dl_bw;
- int cpu = cpu_of(rq);
- struct dl_bw *dl_b;
-
- if (!dl_se->dl_bw_attached)
- return;
-
- dl_b = dl_bw_of(cpu_of(rq));
- guard(raw_spinlock)(&dl_b->lock);
-
- if (!dl_bw_cpus(cpu))
- return;
-
- __dl_add(dl_b, new_bw, dl_bw_cpus(cpu));
-}
-
int dl_server_apply_params(struct sched_dl_entity *dl_se, u64 runtime, u64 period, bool init)
{
u64 old_bw = (init || !dl_se->dl_bw_attached) ? 0 :
@@ -3448,18 +3430,22 @@ void dl_add_task_root_domain(struct task_struct *p)
raw_spin_unlock_irqrestore(&p->pi_lock, rf.flags);
}
-static void dl_server_add_bw(struct root_domain *rd, int cpu)
+static void dl_server_sum_bw(int cpu, int cpus, u64 *total_bw, s64 *extra_bw)
{
struct sched_dl_entity *dl_se;
dl_se = &cpu_rq(cpu)->fair_server;
- if (dl_server(dl_se) && dl_se->dl_bw_attached && cpu_active(cpu))
- __dl_add(&rd->dl_bw, dl_se->dl_bw, dl_bw_cpus(cpu));
+ if (dl_server(dl_se) && dl_se->dl_bw_attached) {
+ *total_bw += dl_se->dl_bw;
+ *extra_bw -= (s32)dl_se->dl_bw / cpus;
+ }
#ifdef CONFIG_SCHED_CLASS_EXT
dl_se = &cpu_rq(cpu)->ext_server;
- if (dl_server(dl_se) && dl_se->dl_bw_attached && cpu_active(cpu))
- __dl_add(&rd->dl_bw, dl_se->dl_bw, dl_bw_cpus(cpu));
+ if (dl_server(dl_se) && dl_se->dl_bw_attached) {
+ *total_bw += dl_se->dl_bw;
+ *extra_bw -= (s32)dl_se->dl_bw / cpus;
+ }
#endif
}
@@ -3480,26 +3466,32 @@ static u64 dl_server_read_bw(int cpu)
return dl_bw;
}
-void dl_clear_root_domain(struct root_domain *rd)
+static void dl_clear_root_domain(struct root_domain *rd)
{
- int i;
+ u64 total_bw = 0;
+ s64 extra_bw = 0;
+ int i, cpus;
guard(raw_spinlock_irqsave)(&rd->dl_bw.lock);
- /*
- * Reset total_bw to zero and extra_bw to max_bw so that next
- * loop will add dl-servers contributions back properly,
- */
- rd->dl_bw.total_bw = 0;
- for_each_cpu(i, rd->span)
- cpu_rq(i)->dl.extra_bw = cpu_rq(i)->dl.max_bw;
-
/*
* dl_servers are not tasks. Since dl_add_task_root_domain ignores
* them, we need to account for them here explicitly.
*/
- for_each_cpu(i, rd->span)
- dl_server_add_bw(rd, i);
+ cpus = cpumask_weight_and(rd->span, cpu_active_mask);
+ if (cpus) {
+ for_each_cpu_and(i, rd->span, cpu_active_mask)
+ dl_server_sum_bw(i, cpus, &total_bw, &extra_bw);
+ }
+
+ rd->dl_bw.total_bw = total_bw;
+ for_each_cpu(i, rd->span) {
+ struct rq *rq = cpu_rq(i);
+
+ rq->dl.extra_bw = rq->dl.max_bw;
+ if (cpu_active(i))
+ rq->dl.extra_bw += extra_bw;
+ }
}
void dl_clear_root_domain_cpu(int cpu)
@@ -3507,6 +3499,33 @@ void dl_clear_root_domain_cpu(int cpu)
dl_clear_root_domain(cpu_rq(cpu)->rd);
}
+#ifndef CONFIG_CPUSETS
+void dl_rebuild_rd_accounting(void)
+{
+ struct task_struct *g, *p;
+ u64 cookie = ++dl_cookie;
+ int cpu;
+
+ lockdep_assert_cpus_held();
+ lockdep_assert_held(&sched_domains_mutex);
+
+ rcu_read_lock();
+ for_each_possible_cpu(cpu) {
+ if (dl_bw_visited(cpu, cookie))
+ continue;
+
+ dl_clear_root_domain_cpu(cpu);
+ }
+ for_each_process_thread(g, p) {
+ /* Racy pre-check; dl_add_task_root_domain() rechecks under pi_lock. */
+ if (!dl_prio(READ_ONCE(p->prio)))
+ continue;
+ dl_add_task_root_domain(p);
+ }
+ rcu_read_unlock();
+}
+#endif
+
static void switched_from_dl(struct rq *rq, struct task_struct *p)
{
/*
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index e656c7059bf86..e76a6876ac4e8 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -418,7 +418,6 @@ extern void sched_init_dl_servers(void);
extern void fair_server_init(struct rq *rq);
extern void ext_server_init(struct rq *rq);
-extern void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq);
extern int dl_server_apply_params(struct sched_dl_entity *dl_se,
u64 runtime, u64 period, bool init);
extern int dl_server_attach_bw(struct sched_dl_entity *dl_se);
@@ -2261,6 +2260,15 @@ struct sched_group_capacity {
int id;
+ /*
+ * Build-time cache shared by all groups using this sgc while
+ * build_sched_domains() runs; not maintained afterwards (e.g. by
+ * sched_update_asym_prefer_cpu()). Use the sched_group copies.
+ */
+ unsigned int group_weight;
+ unsigned int cores;
+ int asym_prefer_cpu;
+
unsigned long cpumask[]; /* Balance mask */
};
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 0248227d983a7..a9122dbff379d 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -502,19 +502,6 @@ void rq_attach_root(struct rq *rq, struct root_domain *rd)
if (cpumask_test_cpu(rq->cpu, cpu_active_mask))
set_rq_online(rq);
- /*
- * Because the rq is not a task, dl_add_task_root_domain() did not
- * move the fair server bw to the rd if it already started.
- * Add it now.
- */
- if (rq->fair_server.dl_server)
- __dl_server_attach_root(&rq->fair_server, rq);
-
-#ifdef CONFIG_SCHED_CLASS_EXT
- if (rq->ext_server.dl_server)
- __dl_server_attach_root(&rq->ext_server, rq);
-#endif
-
rq_unlock_irqrestore(rq, &rf);
if (old_rd)
@@ -916,9 +903,15 @@ static bool alloc_sd_llc(const struct cpumask *cpu_map,
top_llc = parent;
if (top_llc->flags & SD_SHARE_LLC) {
+ int first = cpumask_first(sched_domain_span(top_llc));
+
sd->llc_max = max_lid + 1;
sd->llc_counts = p;
- sd->llc_bytes = get_effective_llc_bytes(i, top_llc);
+ /* All CPUs in top_llc share the same LLC size and weight. */
+ if (first < i)
+ sd->llc_bytes = (*per_cpu_ptr(d->sd, first))->llc_bytes;
+ else
+ sd->llc_bytes = get_effective_llc_bytes(i, top_llc);
} else {
/* avoid memory leak */
kfree(p);
@@ -999,6 +992,7 @@ void sched_cache_active_set(void)
void sched_update_llc_bytes(unsigned int cpu)
{
struct sched_domain *sd, *sdp;
+ unsigned long llc_bytes;
unsigned int i;
sched_domains_mutex_lock();
@@ -1015,10 +1009,11 @@ void sched_update_llc_bytes(unsigned int cpu)
* sibling on each online event corrects this once the full
* shared_cpu_map is known.
*/
+ llc_bytes = get_effective_llc_bytes(cpu, sdp);
for_each_cpu(i, sched_domain_span(sdp)) {
sd = rcu_dereference_sched_domain(cpu_rq(i)->sd);
if (sd)
- sd->llc_bytes = get_effective_llc_bytes(i, sdp);
+ sd->llc_bytes = llc_bytes;
}
unlock:
@@ -1259,32 +1254,49 @@ build_group_from_child_sched_domain(struct sched_domain *sd, int cpu)
return sg;
}
-static void init_overlap_sched_group(struct sched_domain *sd,
- struct sched_group *sg)
+static int init_overlap_sched_group(struct sched_domain *sd,
+ struct sched_group *sg)
{
struct cpumask *mask = sched_domains_tmpmask2;
struct sd_data *sdd = sd->private;
- struct cpumask *sg_span;
- int cpu;
+ struct cpumask *sg_span = sched_group_span(sg);
+ struct sched_group_capacity *sgc;
+ int cpu = cpumask_first(sg_span);
+ struct sched_domain *first_sd = *per_cpu_ptr(sdd->sd, cpu);
+
+ sgc = *per_cpu_ptr(sdd->sgc, cpu);
+ if (sgc && atomic_read(&sgc->ref) > 0 &&
+ first_sd && first_sd->child &&
+ cpumask_equal(sg_span, sched_domain_span(first_sd->child))) {
+ atomic_inc(&sgc->ref);
+ sg->sgc = sgc;
+ return 0;
+ }
build_balance_mask(sd, sg, mask);
cpu = cpumask_first(mask);
- sg->sgc = *per_cpu_ptr(sdd->sgc, cpu);
- if (atomic_inc_return(&sg->sgc->ref) == 1)
+ sgc = *per_cpu_ptr(sdd->sgc, cpu);
+ if (!sgc) {
+ sgc = kzalloc_node(sizeof(*sgc) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sgc)
+ return -ENOMEM;
+ sgc->id = cpu;
+ *per_cpu_ptr(sdd->sgc, cpu) = sgc;
+ }
+
+ sg->sgc = sgc;
+ if (atomic_inc_return(&sg->sgc->ref) == 1) {
cpumask_copy(group_balance_mask(sg), mask);
- else
+ sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
+ sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
+ sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
+ } else {
WARN_ON_ONCE(!cpumask_equal(group_balance_mask(sg), mask));
+ }
- /*
- * Initialize sgc->capacity such that even if we mess up the
- * domains and no possible iteration will get us here, we won't
- * die on a /0 trap.
- */
- sg_span = sched_group_span(sg);
- sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
- sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
- sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
+ return 0;
}
static struct sched_domain *
@@ -1317,19 +1329,18 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
{
struct sched_group *first = NULL, *last = NULL, *sg;
const struct cpumask *span = sched_domain_span(sd);
- struct cpumask *covered = sched_domains_tmpmask;
+ struct cpumask *uncovered = sched_domains_tmpmask;
struct sd_data *sdd = sd->private;
struct sched_domain *sibling;
int i;
- cpumask_clear(covered);
+ cpumask_copy(uncovered, span);
- for_each_cpu_wrap(i, span, cpu) {
+ for (i = cpu; i < nr_cpu_ids;
+ i = cpumask_next(i, uncovered),
+ i = i < nr_cpu_ids ? i : cpumask_first(uncovered)) {
struct cpumask *sg_span;
- if (cpumask_test_cpu(i, covered))
- continue;
-
sibling = *per_cpu_ptr(sdd->sd, i);
/*
@@ -1342,8 +1353,10 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
* Domains should always include the CPU they're built on, so
* check that.
*/
- if (!cpumask_test_cpu(i, sched_domain_span(sibling)))
+ if (!cpumask_test_cpu(i, sched_domain_span(sibling))) {
+ __cpumask_clear_cpu(i, uncovered);
continue;
+ }
/*
* Usually we build sched_group by sibling's child sched_domain
@@ -1385,9 +1398,12 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
goto fail;
sg_span = sched_group_span(sg);
- cpumask_or(covered, covered, sg_span);
+ cpumask_andnot(uncovered, uncovered, sg_span);
- init_overlap_sched_group(sibling, sg);
+ if (init_overlap_sched_group(sibling, sg)) {
+ kfree(sg);
+ goto fail;
+ }
if (!first)
first = sg;
@@ -1489,6 +1505,25 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd)
cpu = cpumask_first(sched_domain_span(child));
sg = *per_cpu_ptr(sdd->sg, cpu);
+ if (!sg) {
+ sg = kzalloc_node(sizeof(*sg) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sg)
+ return NULL;
+ sg->next = sg;
+ *per_cpu_ptr(sdd->sg, cpu) = sg;
+ }
+
+ if (!*per_cpu_ptr(sdd->sgc, cpu)) {
+ struct sched_group_capacity *sgc;
+
+ sgc = kzalloc_node(sizeof(*sgc) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sgc)
+ return NULL;
+ sgc->id = cpu;
+ *per_cpu_ptr(sdd->sgc, cpu) = sgc;
+ }
sg->sgc = *per_cpu_ptr(sdd->sgc, cpu);
/* Increase refcounts for claim_allocations: */
@@ -1504,12 +1539,13 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd)
cpumask_copy(sched_group_span(sg), sched_domain_span(child));
cpumask_copy(group_balance_mask(sg), sched_group_span(sg));
sg->flags = child->flags;
+ sg->sgc->capacity = SCHED_CAPACITY_SCALE * child->span_weight;
} else {
cpumask_set_cpu(cpu, sched_group_span(sg));
cpumask_set_cpu(cpu, group_balance_mask(sg));
+ sg->sgc->capacity = SCHED_CAPACITY_SCALE;
}
- sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sched_group_span(sg));
sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
@@ -1529,23 +1565,24 @@ build_sched_groups(struct sched_domain *sd, int cpu)
struct sched_group *first = NULL, *last = NULL;
struct sd_data *sdd = sd->private;
const struct cpumask *span = sched_domain_span(sd);
- struct cpumask *covered;
+ struct cpumask *uncovered;
int i;
lockdep_assert_held(&sched_domains_mutex);
- covered = sched_domains_tmpmask;
+ uncovered = sched_domains_tmpmask;
- cpumask_clear(covered);
+ cpumask_copy(uncovered, span);
- for_each_cpu_wrap(i, span, cpu) {
+ for (i = cpu; i < nr_cpu_ids;
+ i = cpumask_next(i, uncovered),
+ i = i < nr_cpu_ids ? i : cpumask_first(uncovered)) {
struct sched_group *sg;
- if (cpumask_test_cpu(i, covered))
- continue;
-
sg = get_group(i, sdd);
+ if (!sg)
+ return -ENOMEM;
- cpumask_or(covered, covered, sched_group_span(sg));
+ cpumask_andnot(uncovered, uncovered, sched_group_span(sg));
if (!first)
first = sg;
@@ -1577,27 +1614,40 @@ static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
WARN_ON(!sg);
do {
- int cpu, cores = 0, max_cpu = -1;
+ int i, cores = 0, max_cpu = -1;
+
+ if (sg->group_weight)
+ goto next;
+
+ if (sg->sgc->group_weight) {
+ sg->group_weight = sg->sgc->group_weight;
+ sg->cores = sg->sgc->cores;
+ sg->asym_prefer_cpu = sg->sgc->asym_prefer_cpu;
+ goto next;
+ }
sg->group_weight = cpumask_weight(sched_group_span(sg));
cpumask_copy(mask, sched_group_span(sg));
- for_each_cpu(cpu, mask) {
+ for_each_cpu(i, mask) {
cores++;
- cpumask_andnot(mask, mask, cpu_smt_mask(cpu));
+ cpumask_andnot(mask, mask, cpu_smt_mask(i));
}
sg->cores = cores;
- if (!(sd->flags & SD_ASYM_PACKING))
- goto next;
-
- for_each_cpu(cpu, sched_group_span(sg)) {
- if (max_cpu < 0)
- max_cpu = cpu;
- else if (sched_asym_prefer(cpu, max_cpu))
- max_cpu = cpu;
+ if (sd->flags & SD_ASYM_PACKING) {
+ for_each_cpu(i, sched_group_span(sg)) {
+ if (max_cpu < 0)
+ max_cpu = i;
+ else if (sched_asym_prefer(i, max_cpu))
+ max_cpu = i;
+ }
+ sg->asym_prefer_cpu = max_cpu;
}
- sg->asym_prefer_cpu = max_cpu;
+
+ sg->sgc->group_weight = sg->group_weight;
+ sg->sgc->cores = sg->cores;
+ sg->sgc->asym_prefer_cpu = sg->asym_prefer_cpu;
next:
sg = sg->next;
@@ -1821,16 +1871,21 @@ static int __sdt_alloc(const struct cpumask *cpu_map);
static void __sds_free(struct s_data *d, const struct cpumask *cpu_map);
static int __sds_alloc(struct s_data *d, const struct cpumask *cpu_map);
+static struct sched_domain * __percpu *sched_domains_sd;
+
static void __free_domain_allocs(struct s_data *d, enum s_alloc what,
const struct cpumask *cpu_map)
{
+ int j;
+
switch (what) {
case sa_rootdomain:
if (!atomic_read(&d->rd->refcount))
free_rootdomain(&d->rd->rcu);
fallthrough;
case sa_sd:
- free_percpu(d->sd);
+ for_each_cpu(j, cpu_map)
+ *per_cpu_ptr(d->sd, j) = NULL;
fallthrough;
case sa_sd_shared:
__sds_free(d, cpu_map);
@@ -1852,9 +1907,12 @@ __visit_domain_allocation_hell(struct s_data *d, const struct cpumask *cpu_map)
return sa_sd_storage;
if (__sds_alloc(d, cpu_map))
return sa_sd_shared;
- d->sd = alloc_percpu(struct sched_domain *);
- if (!d->sd)
- return sa_sd_shared;
+ if (!sched_domains_sd) {
+ sched_domains_sd = alloc_percpu(struct sched_domain *);
+ if (!sched_domains_sd)
+ return sa_sd_shared;
+ }
+ d->sd = sched_domains_sd;
d->rd = alloc_rootdomain();
if (!d->rd)
return sa_sd;
@@ -1871,7 +1929,8 @@ static void claim_allocations(int cpu, struct s_data *d)
{
struct sched_domain *sd;
- if (atomic_read(&(*per_cpu_ptr(d->sds, cpu))->ref))
+ if (*per_cpu_ptr(d->sds, cpu) &&
+ atomic_read(&(*per_cpu_ptr(d->sds, cpu))->ref))
*per_cpu_ptr(d->sds, cpu) = NULL;
for (sd = *per_cpu_ptr(d->sd, cpu); sd; sd = sd->parent) {
@@ -1880,10 +1939,12 @@ static void claim_allocations(int cpu, struct s_data *d)
WARN_ON_ONCE(*per_cpu_ptr(sdd->sd, cpu) != sd);
*per_cpu_ptr(sdd->sd, cpu) = NULL;
- if (atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
+ if (*per_cpu_ptr(sdd->sg, cpu) &&
+ atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
*per_cpu_ptr(sdd->sg, cpu) = NULL;
- if (atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
+ if (*per_cpu_ptr(sdd->sgc, cpu) &&
+ atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
*per_cpu_ptr(sdd->sgc, cpu) = NULL;
}
}
@@ -1975,6 +2036,7 @@ sd_init(struct sched_domain_topology_level *tl,
.last_balance = jiffies,
.balance_interval = sd_weight,
+ .span_weight = sd_weight,
/* 50% success rate */
.newidle_call = 512,
@@ -2115,6 +2177,22 @@ void __init set_sched_topology(struct sched_domain_topology_level *tl)
}
#ifdef CONFIG_NUMA
+static void __sdt_free_percpu(void)
+{
+ struct sched_domain_topology_level *tl;
+
+ for_each_sd_topology(tl) {
+ struct sd_data *sdd = &tl->data;
+
+ free_percpu(sdd->sd);
+ sdd->sd = NULL;
+ free_percpu(sdd->sg);
+ sdd->sg = NULL;
+ free_percpu(sdd->sgc);
+ sdd->sgc = NULL;
+ }
+}
+
static int cpu_numa_flags(void)
{
return SD_NUMA;
@@ -2405,6 +2483,7 @@ void sched_init_numa(int offline_node)
/*
* Copy the default topology bits..
*/
+ __sdt_free_percpu();
for (i = 0; sched_domain_topology[i].mask; i++)
tl[i] = sched_domain_topology[i];
@@ -2463,6 +2542,7 @@ static void sched_reset_numa(void)
kfree(masks);
}
if (sched_domain_topology_saved) {
+ __sdt_free_percpu();
kfree(sched_domain_topology);
sched_domain_topology = sched_domain_topology_saved;
sched_domain_topology_saved = NULL;
@@ -2664,22 +2744,26 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
for_each_sd_topology(tl) {
struct sd_data *sdd = &tl->data;
- sdd->sd = alloc_percpu(struct sched_domain *);
- if (!sdd->sd)
- return -ENOMEM;
+ if (!sdd->sd) {
+ sdd->sd = alloc_percpu(struct sched_domain *);
+ if (!sdd->sd)
+ return -ENOMEM;
+ }
- sdd->sg = alloc_percpu(struct sched_group *);
- if (!sdd->sg)
- return -ENOMEM;
+ if (!sdd->sg) {
+ sdd->sg = alloc_percpu(struct sched_group *);
+ if (!sdd->sg)
+ return -ENOMEM;
+ }
- sdd->sgc = alloc_percpu(struct sched_group_capacity *);
- if (!sdd->sgc)
- return -ENOMEM;
+ if (!sdd->sgc) {
+ sdd->sgc = alloc_percpu(struct sched_group_capacity *);
+ if (!sdd->sgc)
+ return -ENOMEM;
+ }
for_each_cpu(j, cpu_map) {
struct sched_domain *sd;
- struct sched_group *sg;
- struct sched_group_capacity *sgc;
sd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
GFP_KERNEL, cpu_to_node(j));
@@ -2687,24 +2771,6 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
return -ENOMEM;
*per_cpu_ptr(sdd->sd, j) = sd;
-
- sg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(j));
- if (!sg)
- return -ENOMEM;
-
- sg->next = sg;
-
- *per_cpu_ptr(sdd->sg, j) = sg;
-
- sgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(j));
- if (!sgc)
- return -ENOMEM;
-
- sgc->id = j;
-
- *per_cpu_ptr(sdd->sgc, j) = sgc;
}
}
@@ -2724,43 +2790,36 @@ static void __sdt_free(const struct cpumask *cpu_map)
if (sdd->sd) {
sd = *per_cpu_ptr(sdd->sd, j);
- if (sd && (sd->flags & SD_NUMA))
- free_sched_groups(sd->groups, 0);
- kfree(*per_cpu_ptr(sdd->sd, j));
+ if (sd) {
+ if (sd->flags & SD_NUMA)
+ free_sched_groups(sd->groups, 0);
+ kfree(sd);
+ *per_cpu_ptr(sdd->sd, j) = NULL;
+ }
}
- if (sdd->sg)
+ if (sdd->sg) {
kfree(*per_cpu_ptr(sdd->sg, j));
- if (sdd->sgc)
+ *per_cpu_ptr(sdd->sg, j) = NULL;
+ }
+ if (sdd->sgc) {
kfree(*per_cpu_ptr(sdd->sgc, j));
+ *per_cpu_ptr(sdd->sgc, j) = NULL;
+ }
}
- free_percpu(sdd->sd);
- sdd->sd = NULL;
- free_percpu(sdd->sg);
- sdd->sg = NULL;
- free_percpu(sdd->sgc);
- sdd->sgc = NULL;
}
}
+static struct sched_domain_shared * __percpu *sched_domains_sds;
+
static int __sds_alloc(struct s_data *d, const struct cpumask *cpu_map)
{
- int j;
-
- d->sds = alloc_percpu(struct sched_domain_shared *);
- if (!d->sds)
- return -ENOMEM;
-
- for_each_cpu(j, cpu_map) {
- struct sched_domain_shared *sds;
-
- sds = kzalloc_node(sizeof(struct sched_domain_shared),
- GFP_KERNEL, cpu_to_node(j));
- if (!sds)
+ if (!sched_domains_sds) {
+ sched_domains_sds = alloc_percpu(struct sched_domain_shared *);
+ if (!sched_domains_sds)
return -ENOMEM;
-
- *per_cpu_ptr(d->sds, j) = sds;
}
+ d->sds = sched_domains_sds;
return 0;
}
@@ -2772,10 +2831,11 @@ static void __sds_free(struct s_data *d, const struct cpumask *cpu_map)
if (!d->sds)
return;
- for_each_cpu(j, cpu_map)
+ for_each_cpu(j, cpu_map) {
kfree(*per_cpu_ptr(d->sds, j));
+ *per_cpu_ptr(d->sds, j) = NULL;
+ }
- free_percpu(d->sds);
d->sds = NULL;
}
@@ -2799,6 +2859,7 @@ static struct sched_domain *build_sched_domain(struct sched_domain_topology_leve
cpumask_or(sched_domain_span(sd),
sched_domain_span(sd),
sched_domain_span(child));
+ sd->span_weight = cpumask_weight(sched_domain_span(sd));
}
}
@@ -2932,7 +2993,7 @@ static void adjust_numa_imbalance(struct sched_domain *sd_llc)
}
}
-static void
+static int
init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
{
struct sched_domain_shared *sds = NULL;
@@ -2954,6 +3015,13 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
*/
for_each_cpu(cpu, sched_domain_span(sd)) {
sds = *per_cpu_ptr(d->sds, cpu);
+ if (!sds) {
+ sds = kzalloc_node(sizeof(*sds),
+ GFP_KERNEL, cpu_to_node(cpu));
+ if (!sds)
+ return -ENOMEM;
+ *per_cpu_ptr(d->sds, cpu) = sds;
+ }
if (!sds->alloc_flags ||
sd->span_weight == 1 ||
@@ -2978,6 +3046,8 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
*/
atomic_set(&sd->shared->nr_busy_cpus, sd->span_weight);
atomic_inc(&sd->shared->ref);
+
+ return 0;
}
/*
@@ -2993,25 +3063,24 @@ init_sched_domain_shared(struct s_data *d, struct sched_domain *sd, int flags)
* degenerate into a single-capacity group. The relevant edge cases are instead
* covered by the caveats above.
*
- * Return true if this CPU's asym path claimed sd->shared, false otherwise.
+ * Return 0 on success or if not claimed, -ENOMEM on allocation failure.
*/
-static bool claim_asym_sched_domain_shared(struct s_data *d, int cpu)
+static int claim_asym_sched_domain_shared(struct s_data *d, int cpu)
{
struct sched_domain *sd = *per_cpu_ptr(d->sd, cpu);
struct sched_domain *sd_asym;
if (!sd)
- return false;
+ return 0;
sd_asym = sd;
while (sd_asym && !(sd_asym->flags & SD_ASYM_CPUCAPACITY_FULL))
sd_asym = sd_asym->parent;
if (!sd_asym || (sd_asym->flags & SD_NUMA))
- return false;
+ return 0;
- init_sched_domain_shared(d, sd_asym, SD_ASYM_CPUCAPACITY);
- return true;
+ return init_sched_domain_shared(d, sd_asym, SD_ASYM_CPUCAPACITY);
}
static int __sched_domains_alloc_llc_id(void)
@@ -3141,7 +3210,6 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
/* Build the groups for the domains */
for_each_cpu(i, cpu_map) {
for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
- sd->span_weight = cpumask_weight(sched_domain_span(sd));
if (sd->flags & SD_NUMA) {
if (build_overlap_sched_groups(sd, i))
goto error;
@@ -3157,15 +3225,16 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
if (!sd)
continue;
- if (has_asym)
- claim_asym_sched_domain_shared(&d, i);
+ if (has_asym && claim_asym_sched_domain_shared(&d, i))
+ goto error;
/* First, find the topmost SD_SHARE_LLC domain */
while (sd->parent && (sd->parent->flags & SD_SHARE_LLC))
sd = sd->parent;
if (sd->flags & SD_SHARE_LLC) {
- init_sched_domain_shared(&d, sd, SD_SHARE_LLC);
+ if (init_sched_domain_shared(&d, sd, SD_SHARE_LLC))
+ goto error;
/*
* In presence of higher domains, adjust the
diff --git a/mm/vmstat.c b/mm/vmstat.c
index cb57714539fb5..c671e9669b43b 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -280,41 +280,45 @@ void refresh_zone_stat_thresholds(void)
int cpu;
int threshold;
- /* Zero current pgdat thresholds */
for_each_online_pgdat(pgdat) {
- for_each_online_cpu(cpu) {
- per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold = 0;
- }
- }
+ int pgdat_threshold = 0;
- for_each_populated_zone(zone) {
- struct pglist_data *pgdat = zone->zone_pgdat;
- unsigned long max_drift, tolerate_drift;
+ for (int z = 0; z < MAX_NR_ZONES; z++) {
+ unsigned long max_drift, tolerate_drift;
- threshold = calculate_normal_threshold(zone);
+ zone = &pgdat->node_zones[z];
+ if (!populated_zone(zone))
+ continue;
- for_each_online_cpu(cpu) {
- int pgdat_threshold;
+ threshold = calculate_normal_threshold(zone);
+ /* Base nodestat threshold on the largest populated zone. */
+ pgdat_threshold = max(threshold, pgdat_threshold);
- per_cpu_ptr(zone->per_cpu_zonestats, cpu)->stat_threshold
- = threshold;
+ for_each_online_cpu(cpu) {
+ s8 *pz = &per_cpu_ptr(zone->per_cpu_zonestats, cpu)->stat_threshold;
- /* Base nodestat threshold on the largest populated zone. */
- pgdat_threshold = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold;
- per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold
- = max(threshold, pgdat_threshold);
+ if (*pz != threshold)
+ *pz = threshold;
+ }
+
+ /*
+ * Only set percpu_drift_mark if there is a danger that
+ * NR_FREE_PAGES reports the low watermark is ok when in fact
+ * the min watermark could be breached by an allocation
+ */
+ tolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);
+ max_drift = num_online_cpus() * threshold;
+ if (max_drift > tolerate_drift)
+ zone->percpu_drift_mark = high_wmark_pages(zone) +
+ max_drift;
}
- /*
- * Only set percpu_drift_mark if there is a danger that
- * NR_FREE_PAGES reports the low watermark is ok when in fact
- * the min watermark could be breached by an allocation
- */
- tolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);
- max_drift = num_online_cpus() * threshold;
- if (max_drift > tolerate_drift)
- zone->percpu_drift_mark = high_wmark_pages(zone) +
- max_drift;
+ for_each_online_cpu(cpu) {
+ s8 *pn = &per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold;
+
+ if (*pn != pgdat_threshold)
+ *pn = pgdat_threshold;
+ }
}
}
|
| 67/3 | 2026/10/15 20:29 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 17925 output: 19 thoughts: 307 Thoughts: **Evaluating the Need for KMSAN Fuzzing**
Okay, so I'm looking at this patch series and the primary question is whether this justifies spinning up a whole new KMSAN fuzzing session, in addition to our KASAN runs. I need to be methodical here.
The first thing I'm doing is carefully reviewing the changes, to see what's being touched. We've got `arch/x86/include/asm/mtrr.h` and `arch/x86/kernel/cpu/mtrr/generic.c`, which makes me immediately think this is related to Memory Type Range Registers (MTRRs).
Alright, I see a new function `mtrr_generic_state_matches(void)` being introduced. It's checking if the generic MTRR MSRs align with `mtrr_state`, so it appears to be some kind of state validation. I'm seeing `rdmsrq(...)`, using an `msr val` struct, where we're reading values. Hold on a second! I need to ensure this is sound. `rdmsrq` looks like the likely call to read a Model Specific Register, but I have to make sure it initializes that `val.q` field. Does it? I'll need to check the definition or usage. I don't want to chase a false negative.
Let's look more closely at `mtrr_generic_state_matches()`, specifically this block:
```c
struct msr val;
rdmsrq(MSR_MTRRdefType, val.q);
if ((val.l & MTRR_DEF_TYPE_TYPE) != mtrr_state.def_type ||
((val.l & MTRR_DEF_TYPE_ENABLE) >> MTRR_STATE_SHIFT) != mtrr_state.enabled)
```
Right, the `val.l` is what we're interested in, but what is `rdmsrq` really doing? That's the crux here. We need to be confident that it properly initializes the contents of `val`, so I'm going to quickly check with grepper to clarify the operation of that function. I've got to ensure the MSR read operation's behavior is as expected before I can form an opinion on if this is likely to introduce new memory errors we're not catching.
|
| 68/3 | 2026/10/15 20:29 | tool | grepper |
0mArgs:{
"Expression": "#define rdmsrq"
}Results: {
"Output": "arch/x86/include/asm/msr.h=189=static inline void wrmsr(u32 msr, u32 low, u32 high)\n--\narch/x86/include/asm/msr.h-193-\narch/x86/include/asm/msr.h:194:#define rdmsrq(msr, val)\t\t\t\\\narch/x86/include/asm/msr.h-195-\t((val) = native_read_msr((msr)))\n--\narch/x86/include/asm/paravirt.h=160=static __always_inline void wrmsr(u32 msr, u32 low, u32 high)\n--\narch/x86/include/asm/paravirt.h-164-\narch/x86/include/asm/paravirt.h:165:#define rdmsrq(msr, val)\t\t\t\\\narch/x86/include/asm/paravirt.h-166-do {\t\t\t\t\t\t\\\n"
} |
| 69/3 | 2026/10/15 20:29 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19036 output: 18 thoughts: 11 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Diving into the `struct msr` Definition** Alright, let's get down to brass tacks. I need a quick refresher on the `struct msr` definition. It's time to verify my assumptions about the MSR (Model Specific Register) structure. I want to make sure I'm fully up to speed before I dig any deeper into the code. Gotta make sure I'm clear on the bitfields, offsets, and any potential alignment issues. Let's see if anything unexpected jumps out, or if this aligns perfectly with what I already know. Let's make sure nothing's changed and that my understanding is current with any recent specifications. Best to be meticulous and confirm the details before proceeding. |
| 70/3 | 2026/10/15 20:29 | tool | grepper |
0mArgs:{
"Expression": "struct msr \\{"
}Results: {
"Output": "arch/x86/include/asm/shared/msr.h-4-\narch/x86/include/asm/shared/msr.h:5:struct msr {\narch/x86/include/asm/shared/msr.h-6-\tunion {\n"
} |
| 71/3 | 2026/10/15 20:29 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19314 output: 42 thoughts: 22 Thoughts: Okay, let's dive into this. Here's my take on those initial thoughts: **Peering into the x86 Architecture - A Deep Dive into MSRs** Alright, time to get my hands dirty. It looks like we're heading into `arch/x86/include/asm/shared/msr.h`. Excellent. This is where the magic happens, the low-level stuff. I'm guessing this file holds the definitions and structures related to Model-Specific Registers (MSRs) for x86. Gotta brush up on my MSR lore, I always enjoy a refresher course on this stuff. These registers are the architecturally defined performance counters, configuration options, and control points. I'm expecting to find things like definitions for the MSR addresses, bitfields for the control values, and maybe some macros to make working with these registers a bit less tedious. Definitely expecting a bunch of `#define` statements and perhaps some inline assembly snippets. This should be interesting. Let's see what secrets this header file reveals. Time to put on my systems programmer hat. |
| 72/3 | 2026/10/15 20:29 | tool | read-file |
0mArgs:{
"File": "arch/x86/include/asm/shared/msr.h",
"FirstLine": 1,
"LineCount": 25
}Results: {
"Contents": " 1:\t/* SPDX-License-Identifier: GPL-2.0 */\n 2:\t#ifndef _ASM_X86_SHARED_MSR_H\n 3:\t#define _ASM_X86_SHARED_MSR_H\n 4:\t\n 5:\tstruct msr {\n 6:\t\tunion {\n 7:\t\t\tstruct {\n 8:\t\t\t\tu32 l;\n 9:\t\t\t\tu32 h;\n 10:\t\t\t};\n 11:\t\t\tu64 q;\n 12:\t\t};\n 13:\t};\n 14:\t\n 15:\t/*\n 16:\t * The kernel proper already defines rdmsr()/wrmsr(), but they are not for the\n 17:\t * boot kernel since they rely on tracepoint/exception handling infrastructure\n 18:\t * that's not available here.\n 19:\t */\n 20:\tstatic inline void raw_rdmsr(unsigned int reg, struct msr *m)\n 21:\t{\n 22:\t\tasm volatile(\"rdmsr\" : \"=a\" (m-\u003el), \"=d\" (m-\u003eh) : \"c\" (reg));\n 23:\t}\n 24:\t\n 25:\tstatic inline void raw_wrmsr(unsigned int reg, const struct msr *m)\n"
} |
| 73/3 | 2026/10/15 20:29 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19947 output: 34 thoughts: 1873 Thoughts: **Analyzing Potential Uninitialized Memory Issues** Okay, let's break this down systematically. My initial concern was centered around potential uninitialized memory reads, so that's where I'll focus. First, with the `struct msr` and `rdmsrq` call, my expectation is that the `val` variable, including both `l`, `h` (and, subsequently, `q`) within the union, should be fully initialized by the `rdmsrq` call, as the MSR read operation populates all 64 bits. Then there's the `mtrr_state` – that's something to investigate further, but I'll set it aside for now. Moving on to `arch/x86/kernel/cpu/cacheinfo.c`, the hotplug/online caching control code seems pretty straightforward, with no obvious initializations that would raise immediate concerns. The same is true of the MCE code in `arch/x86/kernel/cpu/mce/intel.c`. All the operations on `cmci_orphaned_banks` and `cmci_orphaned_target` use static memory (like bitmaps), zeroed bitmaps, and local variables. It appears that the memory is initialized on usage. Then the `mwait_play_dead` function in `arch/x86/kernel/smpboot.c` and the trampoline code in `arch/x86/realmode` that removes the `wbinvd` looks fine, as it is just removing instructions. This, again, doesn't jump out as a problem area. Next, I looked at the schedulers. Checking `include/linux/cpuset.h`, `include/linux/sched/deadline.h` and `kernel/sched/deadline.c`, `dl_rebuild_rd_accounting` has `cookie` initialized. And, through macro loops and the other loops, `g`, `p`, and `cpu` are initialized. No uninitialized memory issues here. Finally, in `kernel/sched/sched.h` and `kernel/sched/topology.c`, the `alloc_sd_llc` is the one that's a bit more interesting, specifically the `sd->llc_bytes` calculation. If `first == i` then `get_effective_llc_bytes(i, top_llc)` is used. Otherwise it's the `(*per_cpu_ptr(d->sd, first))->llc_bytes`. This potentially requires the `llc_bytes` to be initialized. But is it? The code traverses CPUs in order (it appears, at least). The key question is whether the `(*per_cpu_ptr(d->sd, first))->llc_bytes` is initialized before it's read when `first < i`. That's the next thing to investigate. I need to trace the calling paths of `alloc_sd_llc` to ensure that this value is properly set before it is ever accessed, in every possible execution path. |
| 74/3 | 2026/10/15 20:29 | tool | grepper |
0mArgs:{
"Expression": "alloc_sd_llc",
"PathPrefixes": [
"kernel/sched/topology.c"
]
}Results: {
"Output": "kernel/sched/topology.c=861=static unsigned long get_effective_llc_bytes(int cpu,\n--\nkernel/sched/topology.c-877-\nkernel/sched/topology.c:878:static bool alloc_sd_llc(const struct cpumask *cpu_map,\nkernel/sched/topology.c-879-\t\t\t struct s_data *d)\n--\nkernel/sched/topology.c=1023=static void sched_cache_set(bool has_multi_llcs)\n--\nkernel/sched/topology.c-1036-#else\nkernel/sched/topology.c:1037:static bool alloc_sd_llc(const struct cpumask *cpu_map,\nkernel/sched/topology.c-1038-\t\t\t struct s_data *d)\n--\nkernel/sched/topology.c=3146=build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *attr,\n--\nkernel/sched/topology.c-3263-\nkernel/sched/topology.c:3264:\talloc_sd_llc(cpu_map, \u0026d);\nkernel/sched/topology.c-3265-\n"
} |
| 75/3 | 2026/10/15 20:29 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22820 output: 35 thoughts: 32 Thoughts: Okay, let's dive into this. Here's my take on those lines of code: **Analyzing `alloc_sd_llc` in `kernel/sched/topology.c`** Alright, time to get my hands dirty. I'm going to focus my attention on `alloc_sd_llc`, specifically around lines 878 to 930 in `kernel/sched/topology.c`. Given the context, I'm expecting this function to be involved in the allocation of sched_domains, likely related to how the scheduler handles Last Level Cache (LLC) affinity. My initial thoughts are, this is where the *magic* happens, the nitty-gritty of mapping tasks to CPUs, while considering shared cache behavior. It's probably where the data structures that model cache relationships are built or updated. I bet it involves iterating through the available CPUs and the cache hierarchy. I'll need to see how it's using the `sd` (sched_domain) struct, and what other data structures it interacts with to make those decisions. I anticipate it deals with the logic of assigning CPUs to a specific LLC level. It's likely that a structure like the *sched_domain* is being created to model this. I'm curious to see how the LLC is represented, how the function detects or assumes the topology of the system, and how the algorithm accounts for NUMA (Non-Uniform Memory Access) effects, if relevant. Are they using the cacheline size to group the CPUs for an efficient cache? What about the data structures used here? Is this using a linked-list, a tree, or even a graph structure? How do they handle the *weight* of this LLC relation? And what if there are multiple LLC domains? I hope to observe how it handles NUMA, or heterogeneous systems. In essence, I'm looking for the algorithm's core, the heuristics, and the data structures used to effectively manage the cache resources for efficient task scheduling, within the larger picture of the scheduler's overall goal of load balancing. Let's see how it's designed. |
| 76/3 | 2026/10/15 20:30 | tool | read-file |
0mArgs:{
"File": "kernel/sched/topology.c",
"FirstLine": 875,
"LineCount": 60
}Results: {
"Contents": " 875:\t\treturn div_u64((u64)ci-\u003esize * sd-\u003espan_weight, hw_weight);\n 876:\t}\n 877:\t\n 878:\tstatic bool alloc_sd_llc(const struct cpumask *cpu_map,\n 879:\t\t\t\t struct s_data *d)\n 880:\t{\n 881:\t\tstruct sched_domain *sd, *top_llc, *parent;\n 882:\t\tunsigned int *p;\n 883:\t\tint i;\n 884:\t\n 885:\t\tfor_each_cpu(i, cpu_map) {\n 886:\t\t\tsd = *per_cpu_ptr(d-\u003esd, i);\n 887:\t\t\tif (!sd)\n 888:\t\t\t\tgoto err;\n 889:\t\n 890:\t\t\tp = kcalloc_node(max_lid + 1, sizeof(unsigned int),\n 891:\t\t\t\t\t GFP_KERNEL, cpu_to_node(i));\n 892:\t\t\tif (!p)\n 893:\t\t\t\tgoto err;\n 894:\t\n 895:\t\t\ttop_llc = sd;\n 896:\t\t\t/*\n 897:\t\t\t * Find the topmost SD_SHARE_LLC domain.\n 898:\t\t\t * Not yet attached to the CPU, so per_cpu(sd_llc, i)\n 899:\t\t\t * can not be used.\n 900:\t\t\t */\n 901:\t\t\twhile ((parent = rcu_dereference_protected(top_llc-\u003eparent, true)) \u0026\u0026\n 902:\t\t\t (parent-\u003eflags \u0026 SD_SHARE_LLC))\n 903:\t\t\t\ttop_llc = parent;\n 904:\t\n 905:\t\t\tif (top_llc-\u003eflags \u0026 SD_SHARE_LLC) {\n 906:\t\t\t\tint first = cpumask_first(sched_domain_span(top_llc));\n 907:\t\n 908:\t\t\t\tsd-\u003ellc_max = max_lid + 1;\n 909:\t\t\t\tsd-\u003ellc_counts = p;\n 910:\t\t\t\t/* All CPUs in top_llc share the same LLC size and weight. */\n 911:\t\t\t\tif (first \u003c i)\n 912:\t\t\t\t\tsd-\u003ellc_bytes = (*per_cpu_ptr(d-\u003esd, first))-\u003ellc_bytes;\n 913:\t\t\t\telse\n 914:\t\t\t\t\tsd-\u003ellc_bytes = get_effective_llc_bytes(i, top_llc);\n 915:\t\t\t} else {\n 916:\t\t\t\t/* avoid memory leak */\n 917:\t\t\t\tkfree(p);\n 918:\t\t\t}\n 919:\t\t}\n 920:\t\n 921:\t\treturn true;\n 922:\terr:\n 923:\t\tfor_each_cpu(i, cpu_map) {\n 924:\t\t\tsd = *per_cpu_ptr(d-\u003esd, i);\n 925:\t\t\tif (sd) {\n 926:\t\t\t\tkfree(sd-\u003ellc_counts);\n 927:\t\t\t\tsd-\u003ellc_counts = NULL;\n 928:\t\t\t\tsd-\u003ellc_max = 0;\n 929:\t\t\t\tsd-\u003ellc_bytes = 0;\n 930:\t\t\t}\n 931:\t\t}\n 932:\t\n 933:\t\treturn false;\n 934:\t}\n"
} |
| 77/3 | 2026/10/15 20:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24236 output: 31 thoughts: 3941 Thoughts: **Deep Dive into Potential Uninitialized Memory** Okay, let's break this down. My initial scan focused on the LLC bytes calculation and determined there wasn't a risk, because the CPU map iteration ensured that we'd have values for the first CPU. But I need to be more thorough and review all changes in this patch series. I'm focusing on potential uninitialized memory reads, especially in kernel structures that might get passed to userspace or involved in conditional logic. I'm going through the files methodically. First, I looked into the `sched_update_llc_bytes` and related functions. I verified the LLC byte calculation and assignment are sound: the loop structure ensures that `first`, which holds a CPU, has its `llc_bytes` set before its data is read. No issue there. Next, I moved on to `init_overlap_sched_group`, `build_overlap_sched_groups`, and `get_group`. I noted the use of `kzalloc_node` for allocating structures like `sgc` and `sg`. This is a very good sign because `kzalloc_node` guarantees zero-initialization. This includes the members like `group_weight`, `cores`, and `asym_prefer_cpu`. However, I have to trace how those values are used, to make sure no uninitialized memory gets used. And the `init_sched_groups_capacity`, does initialize these values. I then considered `sched_domains_sd` and `sched_domains_sds`. The use of `alloc_percpu` and `kzalloc_node` here again points to zero-initialization, so this is unlikely to be a problem. All good. Next, I examined the `mm/vmstat.c` changes within `refresh_zone_stat_thresholds`. I carefully analyzed how `per_cpu_zonestats` and `per_cpu_nodestats` are handled. I confirmed that these structures are allocated and initialized either at boot time or during hotplug. The previous code set `stat_threshold` to 0. It is a good thing that the new code retains this initialization, and any later reads of those fields will see valid, initialized values. I also made sure that the logic dealing with `pgdat_threshold` is correct, and that it is recomputed and used. So far, so good. No uninitialized memory issues. Now, let's read `arch/x86/kernel/cpu/mtrr/generic.c`. Specifically, I am concerned about the `mtrr_generic_state_matches(void)` function. This function uses a global variable named `mtrr_state`, and the potential exists for the memory read in the `fixed_ranges` element. I'm going to look at where this variable is defined and how it is initialized. Alright, that covers the core logic. I need to make sure none of these changes introduce vulnerabilities based on the KMSAN guidelines. Based on my analysis, I am reasonably confident that these changes do *not* introduce a risk of uninitialized memory use or leaks, and that the code does not need KMSAN. |
| 78/3 | 2026/10/15 20:30 | tool | grepper |
0mArgs:{
"Expression": "mtrr_state",
"PathPrefixes": [
"arch/x86/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 371 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (10 files in total):\narch/x86/hyperv/ivm.c:1\narch/x86/include/asm/kvm_host.h:1\narch/x86/include/asm/mtrr.h:4\narch/x86/kernel/cpu/mtrr/cleanup.c:7\narch/x86/kernel/cpu/mtrr/generic.c:68\narch/x86/kernel/cpu/mtrr/mtrr.c:5\narch/x86/kernel/cpu/mtrr/mtrr.h:3\narch/x86/kernel/kvm.c:1\narch/x86/kvm/mtrr.c:5\narch/x86/xen/enlighten_pv.c:3\n\narch/x86/hyperv/ivm.c=857=void __init hv_vtom_init(void)\n--\narch/x86/hyperv/ivm.c-898-\t/* Set WB as the default cache mode. */\narch/x86/hyperv/ivm.c:899:\tguest_force_mtrr_state(NULL, 0, MTRR_TYPE_WRBACK);\narch/x86/hyperv/ivm.c-900-}\n--\narch/x86/include/asm/kvm_host.h=718=struct kvm_vcpu_arch {\n--\narch/x86/include/asm/kvm_host.h-903-\narch/x86/include/asm/kvm_host.h:904:\tstruct kvm_mtrr mtrr_state;\narch/x86/include/asm/kvm_host.h-905-\tu64 pat;\n--\narch/x86/include/asm/mtrr.h-33-\narch/x86/include/asm/mtrr.h:34:struct mtrr_state_type {\narch/x86/include/asm/mtrr.h-35-\tstruct mtrr_var_range var_ranges[MTRR_MAX_VAR_RANGES];\n--\narch/x86/include/asm/mtrr.h=47=void mtrr_bp_init(void);\narch/x86/include/asm/mtrr.h:48:void guest_force_mtrr_state(struct mtrr_var_range *var, unsigned int num_var,\narch/x86/include/asm/mtrr.h-49-\t\t\t mtrr_type def_type);\n--\narch/x86/include/asm/mtrr.h=64=bool mtrr_generic_state_matches(void);\narch/x86/include/asm/mtrr.h-65-# else\narch/x86/include/asm/mtrr.h:66:static inline void guest_force_mtrr_state(struct mtrr_var_range *var,\narch/x86/include/asm/mtrr.h-67-\t\t\t\t\t unsigned int num_var,\n--\narch/x86/include/asm/mtrr.h=122=struct mtrr_gentry32 {\n--\narch/x86/include/asm/mtrr.h-143-\narch/x86/include/asm/mtrr.h:144:/* Bit fields for enabled in struct mtrr_state_type */\narch/x86/include/asm/mtrr.h-145-#define MTRR_STATE_SHIFT\t\t10\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=23=struct var_mtrr_range_state {\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-28-\narch/x86/kernel/cpu/mtrr/cleanup.c:29:struct var_mtrr_state {\narch/x86/kernel/cpu/mtrr/cleanup.c-30-\tunsigned long\trange_startk;\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=49=x86_get_mtrr_mem_range(struct range *range, int nr_range,\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-81-\t\tbase = range_state[i].base_pfn;\narch/x86/kernel/cpu/mtrr/cleanup.c:82:\t\tif (base \u003c (1\u003c\u003c(20-PAGE_SHIFT)) \u0026\u0026 mtrr_state.have_fixed \u0026\u0026\narch/x86/kernel/cpu/mtrr/cleanup.c:83:\t\t (mtrr_state.enabled \u0026 MTRR_STATE_MTRR_ENABLED) \u0026\u0026\narch/x86/kernel/cpu/mtrr/cleanup.c:84:\t\t (mtrr_state.enabled \u0026 MTRR_STATE_MTRR_FIXED_ENABLED)) {\narch/x86/kernel/cpu/mtrr/cleanup.c-85-\t\t\t/* Var MTRR contains UC entry below 1M? Skip it: */\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=268=static unsigned __init\narch/x86/kernel/cpu/mtrr/cleanup.c:269:range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,\narch/x86/kernel/cpu/mtrr/cleanup.c-270-\t\t\tunsigned long sizek)\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=382=static void __init\narch/x86/kernel/cpu/mtrr/cleanup.c:383:set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn,\narch/x86/kernel/cpu/mtrr/cleanup.c-384-\t\t unsigned long size_pfn)\n--\narch/x86/kernel/cpu/mtrr/cleanup.c=448=x86_setup_var_mtrrs(struct range *range, int nr_range,\n--\narch/x86/kernel/cpu/mtrr/cleanup.c-450-{\narch/x86/kernel/cpu/mtrr/cleanup.c:451:\tstruct var_mtrr_state var_state;\narch/x86/kernel/cpu/mtrr/cleanup.c-452-\tint num_reg;\n--\narch/x86/kernel/cpu/mtrr/generic.c=87=static unsigned long smp_changes_mask;\narch/x86/kernel/cpu/mtrr/generic.c:88:static int mtrr_state_set;\narch/x86/kernel/cpu/mtrr/generic.c-89-u64 mtrr_tom2;\narch/x86/kernel/cpu/mtrr/generic.c-90-\narch/x86/kernel/cpu/mtrr/generic.c:91:struct mtrr_state_type mtrr_state;\narch/x86/kernel/cpu/mtrr/generic.c-92-\n--\narch/x86/kernel/cpu/mtrr/generic.c=126=static u64 get_mtrr_size(u64 mask)\n--\narch/x86/kernel/cpu/mtrr/generic.c-135-\narch/x86/kernel/cpu/mtrr/generic.c:136:static u8 get_var_mtrr_state(unsigned int reg, u64 *start, u64 *size)\narch/x86/kernel/cpu/mtrr/generic.c-137-{\narch/x86/kernel/cpu/mtrr/generic.c:138:\tstruct mtrr_var_range *mtrr = mtrr_state.var_ranges + reg;\narch/x86/kernel/cpu/mtrr/generic.c-139-\n--\narch/x86/kernel/cpu/mtrr/generic.c=305=static void map_add_var(void)\n--\narch/x86/kernel/cpu/mtrr/generic.c-321-\tfor (i = 0; i \u003c num_var_ranges; i++) {\narch/x86/kernel/cpu/mtrr/generic.c:322:\t\ttype = get_var_mtrr_state(i, \u0026start, \u0026size);\narch/x86/kernel/cpu/mtrr/generic.c-323-\t\tif (type != MTRR_TYPE_INVALID)\n--\narch/x86/kernel/cpu/mtrr/generic.c=350=void __init mtrr_build_map(void)\n--\narch/x86/kernel/cpu/mtrr/generic.c-356-\t/* Add fixed MTRRs, optimize for adjacent entries with same type. */\narch/x86/kernel/cpu/mtrr/generic.c:357:\tif (mtrr_state.enabled \u0026 MTRR_STATE_MTRR_FIXED_ENABLED) {\narch/x86/kernel/cpu/mtrr/generic.c-358-\t\t/*\n--\narch/x86/kernel/cpu/mtrr/generic.c-363-\t\tend = size = 0x10000;\narch/x86/kernel/cpu/mtrr/generic.c:364:\t\ttype = mtrr_state.fixed_ranges[0];\narch/x86/kernel/cpu/mtrr/generic.c-365-\n--\narch/x86/kernel/cpu/mtrr/generic.c-370-\narch/x86/kernel/cpu/mtrr/generic.c:371:\t\t\tif (mtrr_state.fixed_ranges[i] != type) {\narch/x86/kernel/cpu/mtrr/generic.c-372-\t\t\t\tadd_map_entry(start, end, type);\narch/x86/kernel/cpu/mtrr/generic.c-373-\t\t\t\tstart = end;\narch/x86/kernel/cpu/mtrr/generic.c:374:\t\t\t\ttype = mtrr_state.fixed_ranges[i];\narch/x86/kernel/cpu/mtrr/generic.c-375-\t\t\t}\n--\narch/x86/kernel/cpu/mtrr/generic.c=402=void __init mtrr_copy_map(void)\n--\narch/x86/kernel/cpu/mtrr/generic.c-405-\narch/x86/kernel/cpu/mtrr/generic.c:406:\tif (!mtrr_state.enabled || !new_size) {\narch/x86/kernel/cpu/mtrr/generic.c-407-\t\tcache_map = NULL;\n--\narch/x86/kernel/cpu/mtrr/generic.c-418-\t} else {\narch/x86/kernel/cpu/mtrr/generic.c:419:\t\tmtrr_state.enabled = 0;\narch/x86/kernel/cpu/mtrr/generic.c-420-\t\tpr_err(\"MTRRs disabled due to allocation failure for lookup map.\\n\");\n--\narch/x86/kernel/cpu/mtrr/generic.c-426-/**\narch/x86/kernel/cpu/mtrr/generic.c:427: * guest_force_mtrr_state - set static MTRR state for a guest\narch/x86/kernel/cpu/mtrr/generic.c-428- *\n--\narch/x86/kernel/cpu/mtrr/generic.c-439- */\narch/x86/kernel/cpu/mtrr/generic.c:440:void guest_force_mtrr_state(struct mtrr_var_range *var, unsigned int num_var,\narch/x86/kernel/cpu/mtrr/generic.c-441-\t\t\t mtrr_type def_type)\n--\narch/x86/kernel/cpu/mtrr/generic.c-445-\t/* Only allowed to be called once before mtrr_bp_init(). */\narch/x86/kernel/cpu/mtrr/generic.c:446:\tif (WARN_ON_ONCE(mtrr_state_set))\narch/x86/kernel/cpu/mtrr/generic.c-447-\t\treturn;\n--\narch/x86/kernel/cpu/mtrr/generic.c-475-\t\tfor (i = 0; i \u003c num_var; i++)\narch/x86/kernel/cpu/mtrr/generic.c:476:\t\t\tmtrr_state.var_ranges[i] = var[i];\narch/x86/kernel/cpu/mtrr/generic.c-477-\t\tnum_var_ranges = num_var;\n--\narch/x86/kernel/cpu/mtrr/generic.c-479-\narch/x86/kernel/cpu/mtrr/generic.c:480:\tmtrr_state.def_type = def_type;\narch/x86/kernel/cpu/mtrr/generic.c:481:\tmtrr_state.enabled |= MTRR_STATE_MTRR_ENABLED;\narch/x86/kernel/cpu/mtrr/generic.c-482-\narch/x86/kernel/cpu/mtrr/generic.c:483:\tmtrr_state_set = 1;\narch/x86/kernel/cpu/mtrr/generic.c-484-}\n--\narch/x86/kernel/cpu/mtrr/generic.c=513=u8 mtrr_type_lookup(u64 start, u64 end, u8 *uniform)\n--\narch/x86/kernel/cpu/mtrr/generic.c-517-\narch/x86/kernel/cpu/mtrr/generic.c:518:\tif (!mtrr_state_set) {\narch/x86/kernel/cpu/mtrr/generic.c-519-\t\t/* Uniformity is unknown. */\n--\narch/x86/kernel/cpu/mtrr/generic.c-525-\narch/x86/kernel/cpu/mtrr/generic.c:526:\tif (!(mtrr_state.enabled \u0026 MTRR_STATE_MTRR_ENABLED))\narch/x86/kernel/cpu/mtrr/generic.c-527-\t\treturn MTRR_TYPE_UNCACHABLE;\n--\narch/x86/kernel/cpu/mtrr/generic.c-536-\t\t\t/* At least some part of region has default type. */\narch/x86/kernel/cpu/mtrr/generic.c:537:\t\t\ttype = type_merge(type, mtrr_state.def_type, uniform);\narch/x86/kernel/cpu/mtrr/generic.c-538-\t\t\t/* End of region not covered, too? -\u003e lookup done. */\n--\narch/x86/kernel/cpu/mtrr/generic.c-550-\tif (start \u003c end)\narch/x86/kernel/cpu/mtrr/generic.c:551:\t\ttype = type_merge(type, mtrr_state.def_type, uniform);\narch/x86/kernel/cpu/mtrr/generic.c-552-\n--\narch/x86/kernel/cpu/mtrr/generic.c=571=void fill_mtrr_var_range(unsigned int index,\n--\narch/x86/kernel/cpu/mtrr/generic.c-575-\narch/x86/kernel/cpu/mtrr/generic.c:576:\tvr = mtrr_state.var_ranges;\narch/x86/kernel/cpu/mtrr/generic.c-577-\n--\narch/x86/kernel/cpu/mtrr/generic.c=599=void mtrr_save_fixed_ranges(void *info)\narch/x86/kernel/cpu/mtrr/generic.c-600-{\narch/x86/kernel/cpu/mtrr/generic.c:601:\tif (mtrr_state.have_fixed)\narch/x86/kernel/cpu/mtrr/generic.c:602:\t\tget_fixed_ranges(mtrr_state.fixed_ranges);\narch/x86/kernel/cpu/mtrr/generic.c-603-}\n--\narch/x86/kernel/cpu/mtrr/generic.c=629=print_fixed(unsigned base, unsigned step, const mtrr_type *types)\n--\narch/x86/kernel/cpu/mtrr/generic.c-647-\narch/x86/kernel/cpu/mtrr/generic.c:648:static void __init print_mtrr_state(void)\narch/x86/kernel/cpu/mtrr/generic.c-649-{\n--\narch/x86/kernel/cpu/mtrr/generic.c-653-\tpr_info(\"MTRR default type: %s\\n\",\narch/x86/kernel/cpu/mtrr/generic.c:654:\t\tmtrr_attrib_to_str(mtrr_state.def_type));\narch/x86/kernel/cpu/mtrr/generic.c:655:\tif (mtrr_state.have_fixed) {\narch/x86/kernel/cpu/mtrr/generic.c-656-\t\tpr_info(\"MTRR fixed ranges %s:\\n\",\narch/x86/kernel/cpu/mtrr/generic.c-657-\t\t\tstr_enabled_disabled(\narch/x86/kernel/cpu/mtrr/generic.c:658:\t\t\t (mtrr_state.enabled \u0026 MTRR_STATE_MTRR_ENABLED) \u0026\u0026\narch/x86/kernel/cpu/mtrr/generic.c:659:\t\t\t (mtrr_state.enabled \u0026 MTRR_STATE_MTRR_FIXED_ENABLED)));\narch/x86/kernel/cpu/mtrr/generic.c:660:\t\tprint_fixed(0x00000, 0x10000, mtrr_state.fixed_ranges + 0);\narch/x86/kernel/cpu/mtrr/generic.c-661-\t\tfor (i = 0; i \u003c 2; ++i)\narch/x86/kernel/cpu/mtrr/generic.c-662-\t\t\tprint_fixed(0x80000 + i * 0x20000, 0x04000,\narch/x86/kernel/cpu/mtrr/generic.c:663:\t\t\t\t mtrr_state.fixed_ranges + (i + 1) * 8);\narch/x86/kernel/cpu/mtrr/generic.c-664-\t\tfor (i = 0; i \u003c 8; ++i)\narch/x86/kernel/cpu/mtrr/generic.c-665-\t\t\tprint_fixed(0xC0000 + i * 0x08000, 0x01000,\narch/x86/kernel/cpu/mtrr/generic.c:666:\t\t\t\t mtrr_state.fixed_ranges + (i + 3) * 8);\narch/x86/kernel/cpu/mtrr/generic.c-667-\n--\narch/x86/kernel/cpu/mtrr/generic.c-671-\tpr_info(\"MTRR variable ranges %s:\\n\",\narch/x86/kernel/cpu/mtrr/generic.c:672:\t\tstr_enabled_disabled(mtrr_state.enabled \u0026 MTRR_STATE_MTRR_ENABLED));\narch/x86/kernel/cpu/mtrr/generic.c-673-\thigh_width = (boot_cpu_data.x86_phys_bits - (32 - PAGE_SHIFT) + 3) / 4;\n--\narch/x86/kernel/cpu/mtrr/generic.c-675-\tfor (i = 0; i \u003c num_var_ranges; ++i) {\narch/x86/kernel/cpu/mtrr/generic.c:676:\t\tif (mtrr_state.var_ranges[i].mask_lo \u0026 MTRR_PHYSMASK_V)\narch/x86/kernel/cpu/mtrr/generic.c-677-\t\t\tpr_info(\" %u base %0*X%05X000 mask %0*X%05X000 %s\\n\",\n--\narch/x86/kernel/cpu/mtrr/generic.c-679-\t\t\t\thigh_width,\narch/x86/kernel/cpu/mtrr/generic.c:680:\t\t\t\tmtrr_state.var_ranges[i].base_hi,\narch/x86/kernel/cpu/mtrr/generic.c:681:\t\t\t\tmtrr_state.var_ranges[i].base_lo \u003e\u003e 12,\narch/x86/kernel/cpu/mtrr/generic.c-682-\t\t\t\thigh_width,\narch/x86/kernel/cpu/mtrr/generic.c:683:\t\t\t\tmtrr_state.var_ranges[i].mask_hi,\narch/x86/kernel/cpu/mtrr/generic.c:684:\t\t\t\tmtrr_state.var_ranges[i].mask_lo \u003e\u003e 12,\narch/x86/kernel/cpu/mtrr/generic.c:685:\t\t\t\tmtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo \u0026\narch/x86/kernel/cpu/mtrr/generic.c-686-\t\t\t\t\t\t MTRR_PHYSBASE_TYPE));\n--\narch/x86/kernel/cpu/mtrr/generic.c-694-/* Grab all of the MTRR state for this CPU into *state */\n\n"
} |
| 79/3 | 2026/10/15 20:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34386 output: 44 thoughts: 36 Thoughts: Okay, let's dive into this. I need to understand how memory type range registers (MTRRs) are managed, specifically the generic implementation. My focus is on the `mtrr_generic_set_state()` function. I'll need to open up `arch/x86/kernel/cpu/mtrr/generic.c` to see the actual implementation. From there, I'll be able to trace the flow, see how it interacts with the underlying hardware, and understand the logic behind setting the state. I'll be looking for potential optimization opportunities, and areas that might impact performance. The goal is to deeply grok this function so I have a mental map of its execution pathway. |
| 80/3 | 2026/10/15 20:30 | tool | read-file |
0mArgs:{
"File": "arch/x86/kernel/cpu/mtrr/generic.c",
"FirstLine": 950,
"LineCount": 45
}Results: {
"Contents": " 950:\tvoid mtrr_disable(void)\n 951:\t{\n 952:\t\tstruct msr val;\n 953:\t\n 954:\t\t/* Save MTRR state */\n 955:\t\trdmsrq(MSR_MTRRdefType, val.q);\n 956:\t\tdeftype_lo = val.l;\n 957:\t\tdeftype_hi = val.h;\n 958:\t\n 959:\t\t/* Disable MTRRs, and set the default type to uncached */\n 960:\t\tmtrr_wrmsr(MSR_MTRRdefType, deftype_lo \u0026 MTRR_DEF_TYPE_DISABLE, deftype_hi);\n 961:\t}\n 962:\t\n 963:\tvoid mtrr_enable(void)\n 964:\t{\n 965:\t\t/* Intel (P6) standard MTRRs */\n 966:\t\tmtrr_wrmsr(MSR_MTRRdefType, deftype_lo, deftype_hi);\n 967:\t}\n 968:\t\n 969:\tvoid mtrr_generic_set_state(void)\n 970:\t{\n 971:\t\tunsigned long mask, count;\n 972:\t\n 973:\t\t/* Actually set the state */\n 974:\t\tmask = set_mtrr_state();\n 975:\t\n 976:\t\t/* Use the atomic bitops to update the global mask */\n 977:\t\tfor (count = 0; count \u003c sizeof(mask) * 8; ++count) {\n 978:\t\t\tif (mask \u0026 0x01)\n 979:\t\t\t\tset_bit(count, \u0026smp_changes_mask);\n 980:\t\t\tmask \u003e\u003e= 1;\n 981:\t\t}\n 982:\t}\n 983:\t\n 984:\tbool mtrr_generic_state_matches(void)\n 985:\t{\n 986:\t\tstruct msr val;\n 987:\t\tunsigned int i;\n 988:\t\n 989:\t\tif (!cpu_feature_enabled(X86_FEATURE_MTRR))\n 990:\t\t\treturn true;\n 991:\t\n 992:\t\trdmsrq(MSR_MTRRdefType, val.q);\n 993:\t\tif ((val.l \u0026 MTRR_DEF_TYPE_TYPE) != mtrr_state.def_type ||\n 994:\t\t ((val.l \u0026 MTRR_DEF_TYPE_ENABLE) \u003e\u003e MTRR_STATE_SHIFT) != mtrr_state.enabled)\n"
} |
| 81/3 | 2026/10/15 20:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35312 output: 374 thoughts: 2517 Thoughts: **Analysis of Kernel Memory Initialization and KMSAN Requirement** Alright, let's break this down. My analysis started by examining the code modifications across several areas: MTRR handling, CMCI management, SMP boot, the deadline scheduler, topology management, and VM stats. The goal? To determine if these changes introduce any uninitialized memory risks that KMSAN would uniquely catch. My first observation was that `mtrr_generic_state_matches` compares hardware MSRs against a boot-time initialized state – no uninitialized memory there. Then I moved to `intel.c` to look into `cmci_orphaned_banks`, and `cmci_orphaned_target` and confirmed those are initialized in the `.bss` section by the compiler, and explicitly zeroed during runtime. Next, I took a look at `deadline.c` and saw that all variables were initialized before their use. The same result for all structures and variables in `topology.c` (like `sched_group_capacity`) which are allocated with `kzalloc_node`, or `alloc_percpu`. That means everything is zero-initialized. I made sure to check the code path for the assignment of each member of the structure and found it properly initialized. Lastly, the `vmstat.c` change only involved recalculating thresholds within existing data structures – no new allocations or uninitialized memory concerns. Now, the crucial check: do any of these changes touch user-space interaction points, like `copy_to_user`, netlink SKBs, ioctls, BPF buffers, or introduce any conditional logic based on potentially uninitialized variables? The answer is a resounding no. This review was to check any information leaks. This patch series doesn't introduce any risk of uninitialized memory reads or information leaks to userspace. Every allocated structure is initialized using `kzalloc_node` or `alloc_percpu`. All the relevant variables were explicitly initialized. Based on the established rules, a KMSAN session is only required if a bug can't be caught by KASAN, LOCKDEP, or other standard bug detectors. I considered that these changes *might* have out-of-bounds accesses, array overflows, null pointer dereferences, locking deadlocks, or use-after-free bugs (which are caught by KASAN). The main goal of this analysis was to see if the patch introduced any changes related to uninitialized variables. Given these observations, the modifications are: 1. Adding MTRR consistency checks (no uninitialized memory). 2. Improving CMCI orphaned bank tracking (variables are zeroed, explicit initialization). 3. Improving the scheduler accounting (all variables are initialized). 4. Re-factoring group capacity and topology metrics, with allocation and zero-initialization with `kzalloc_node` or `alloc_percpu`. 5. Optimizing threshold recalculation (no new allocations or related issues). Therefore, any problems here are the domain of KASAN, LOCKDEP, and standard debugging. Thus, `NeedsKMSAN` is definitively set to `false`. |
| 82/3 | 2026/10/15 20:31 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series modifies x86 MTRR checking, CMCI bank handling during CPU offline, SMP boot cache flush logic, scheduler deadline bandwidth accounting, scheduler topology allocation/caching, and vmstat threshold calculation.\n\nNone of these changes expose uninitialized memory risks or kernel-to-user info-leaks:\n1. All newly added dynamic allocations in the scheduler topology code (such as `sched_group`, `sched_group_capacity`, and `sched_domain_shared`) use `kzalloc_node` or `alloc_percpu`, ensuring all fields are zero-initialized.\n2. All local stack variables (e.g. `val` in `mtrr_generic_state_matches()`, `total_bw`/`extra_bw` in `dl_clear_root_domain()`, and `pgdat_threshold` in `refresh_zone_stat_thresholds()`) are explicitly initialized before any read or use.\n3. CMCI state tracking uses static BSS variables (`cmci_orphaned_banks`, `cmci_orphaned_target`) that are explicitly zeroed via `bitmap_zero()`.\n4. No data structures or padding bytes are copied or leaked to user space via `copy_to_user`, ioctl outputs, netlink attributes, or socket buffers.\n5. Potential edge-case bugs that could arise from these changes (e.g., concurrency/locking issues, cpumask out-of-bounds, or NULL pointer dereferences) are covered by KASAN, LOCKDEP, and standard kernel assertions rather than KMSAN.\n\nThus, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series modifies x86 MTRR checking, CMCI bank handling during CPU offline, SMP boot cache flush logic, scheduler deadline bandwidth accounting, scheduler topology allocation/caching, and vmstat threshold calculation.\n\nNone of these changes expose uninitialized memory risks or kernel-to-user info-leaks:\n1. All newly added dynamic allocations in the scheduler topology code (such as `sched_group`, `sched_group_capacity`, and `sched_domain_shared`) use `kzalloc_node` or `alloc_percpu`, ensuring all fields are zero-initialized.\n2. All local stack variables (e.g. `val` in `mtrr_generic_state_matches()`, `total_bw`/`extra_bw` in `dl_clear_root_domain()`, and `pgdat_threshold` in `refresh_zone_stat_thresholds()`) are explicitly initialized before any read or use.\n3. CMCI state tracking uses static BSS variables (`cmci_orphaned_banks`, `cmci_orphaned_target`) that are explicitly zeroed via `bitmap_zero()`.\n4. No data structures or padding bytes are copied or leaked to user space via `copy_to_user`, ioctl outputs, netlink attributes, or socket buffers.\n5. Potential edge-case bugs that could arise from these changes (e.g., concurrency/locking issues, cpumask out-of-bounds, or NULL pointer dereferences) are covered by KASAN, LOCKDEP, and standard kernel assertions rather than KMSAN.\n\nThus, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|