diff options
| author | Tim Chen <tim.c.chen@linux.intel.com> | 2026-09-21 17:37:22 -0700 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-09-22 10:49:53 +0200 |
| commit | 0d6526f82c3cdefcca47f73f5fc08dc6f335eac6 (patch) | |
| tree | 176dc2d685debcfb082c151ee94f2425ac96b364 /arch/mips/generic/init.c | |
| download | linux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.tar.gz linux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.zip | |
sched/cache: Keep nr_pref_llc_running in the runnable domain, to fix LLC mis-scheduling buggrafted
alb_break_llc() decides whether to break LLC preference during active
load balance. It does so by testing that every runnable fair task on the
source rq prefers its LLC:
env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable
But the two counters cover different sets. nr_pref_llc_running is updated
in account_llc_enqueue()/account_llc_dequeue(), next to cfs_rq->nr_queued,
so it follows queued tasks. h_nr_runnable is updated in set_delayed()/
clear_delayed() and drops delay-dequeued tasks.
So under DELAY_DEQUEUE, a preferring task that goes to sleep stays counted
in nr_pref_llc_running while h_nr_runnable falls. The equality then breaks,
alb_break_llc() returns false, and active balance is free to pull a task
off its preferred LLC. Active balance only moves runnable tasks, and this
is the only LLC check it consults: once the stopper runs, LBF_ACTIVE_LB
skips the per-task test in can_migrate_task(). The runnable set is the one
we want.
Fix it on the counter side. A task should be counted in
nr_pref_llc_running exactly while it is both queued on its preferred LLC
(pref_llc_queued) and runnable (!sched_delayed). Define that membership
once in task_pref_llc_runnable(), and adjust the counter only through
pref_llc_running_inc()/pref_llc_running_dec() from the four sites that
change either input: account_llc_enqueue(), account_llc_dequeue(),
set_delayed() and clear_delayed(). Gating every update on the same
predicate keeps the delay, wake and dequeue paths from double-counting
or underflowing; see the comments at those sites for the ordering.
nr_llc_running and sd->llc_counts are not touched and stay on queued
semantics.
Fixes: 714059f79ff0 ("sched/cache: Handle moving single tasks to/from their preferred LLC")
Closes: https://lore.kernel.org/lkml/20260827135000.735138-1-zhanxusheng@xiaomi.com/
Reported-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
Suggested-by: Chen Yu <yu.c.chen@intel.com>
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Kayra Cizmeci <kayracizmeci@gmail.com>
Cc: <stable@kernel.org> # v7.2.x
Link: https://patch.msgid.link/06af61afedac32e6477f57feb4d658f6c411c3af.1790035273.git.tim.c.chen@linux.intel.com
Diffstat (limited to 'arch/mips/generic/init.c')
| -rw-r--r-- | arch/mips/generic/init.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/arch/mips/generic/init.c b/arch/mips/generic/init.c new file mode 100644 index 000000000..1d712eac1 --- /dev/null +++ b/arch/mips/generic/init.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Imagination Technologies + * Author: Paul Burton <paul.burton@mips.com> + */ + +#include <linux/clk.h> +#include <linux/clocksource.h> +#include <linux/init.h> +#include <linux/irqchip.h> +#include <linux/of_clk.h> +#include <linux/of_fdt.h> + +#include <asm/bootinfo.h> +#include <asm/fw/fw.h> +#include <asm/irq_cpu.h> +#include <asm/machine.h> +#include <asm/mips-cps.h> +#include <asm/prom.h> +#include <asm/smp-ops.h> +#include <asm/time.h> + +static __initconst const void *fdt; +static __initconst const struct mips_machine *mach; +static __initconst const void *mach_match_data; + +void __init prom_init(void) +{ + plat_get_fdt(); + BUG_ON(!fdt); +} + +void __init *plat_get_fdt(void) +{ + const struct mips_machine *check_mach; + const struct of_device_id *match; + + if (fdt) + /* Already set up */ + return (void *)fdt; + + fdt = (void *)get_fdt(); + if (fdt && !fdt_check_header(fdt)) { + /* + * We have been provided with the appropriate device tree for + * the board. Make use of it & search for any machine struct + * based upon the root compatible string. + */ + for_each_mips_machine(check_mach) { + match = mips_machine_is_compatible(check_mach, fdt); + if (match) { + mach = check_mach; + mach_match_data = match->data; + break; + } + } + } else if (IS_ENABLED(CONFIG_LEGACY_BOARDS)) { + /* + * We weren't booted using the UHI boot protocol, but do + * support some number of boards with legacy boot protocols. + * Attempt to find the right one. + */ + for_each_mips_machine(check_mach) { + if (!check_mach->detect) + continue; + + if (!check_mach->detect()) + continue; + + mach = check_mach; + } + + /* + * If we don't recognise the machine then we can't continue, so + * die here. + */ + BUG_ON(!mach); + + /* Retrieve the machine's FDT */ + fdt = mach->fdt; + } + return (void *)fdt; +} + +#ifdef CONFIG_RELOCATABLE + +void __init plat_fdt_relocated(void *new_location) +{ + /* + * reset fdt as the cached value would point to the location + * before relocations happened and update the location argument + * if it was passed using UHI + */ + fdt = NULL; + + if (fw_arg0 == -2) + fw_arg1 = (unsigned long)new_location; +} + +#endif /* CONFIG_RELOCATABLE */ + +void __init plat_mem_setup(void) +{ + if (mach && mach->fixup_fdt) + fdt = mach->fixup_fdt(fdt, mach_match_data); + + fw_init_cmdline(); + __dt_setup_arch((void *)fdt); +} + +void __init device_tree_init(void) +{ + unflatten_and_copy_device_tree(); + mips_cpc_probe(); + + if (!register_cps_smp_ops()) + return; + if (!register_vsmp_smp_ops()) + return; + + register_up_smp_ops(); +} + +int __init apply_mips_fdt_fixups(void *fdt_out, size_t fdt_out_size, + const void *fdt_in, + const struct mips_fdt_fixup *fixups) +{ + int err; + + err = fdt_open_into(fdt_in, fdt_out, fdt_out_size); + if (err) { + pr_err("Failed to open FDT\n"); + return err; + } + + for (; fixups->apply; fixups++) { + err = fixups->apply(fdt_out); + if (err) { + pr_err("Failed to apply FDT fixup \"%s\"\n", + fixups->description); + return err; + } + } + + err = fdt_pack(fdt_out); + if (err) + pr_err("Failed to pack FDT\n"); + return err; +} + +void __init plat_time_init(void) +{ + struct device_node *np; + struct clk *clk; + + of_clk_init(NULL); + + if (!cpu_has_counter) { + mips_hpt_frequency = 0; + } else if (mach && mach->measure_hpt_freq) { + mips_hpt_frequency = mach->measure_hpt_freq(); + } else { + np = of_get_cpu_node(0, NULL); + if (!np) { + pr_err("Failed to get CPU node\n"); + return; + } + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk)); + return; + } + + mips_hpt_frequency = clk_get_rate(clk); + clk_put(clk); + + switch (boot_cpu_type()) { + case CPU_20KC: + case CPU_25KF: + /* The counter runs at the CPU clock rate */ + break; + default: + /* The counter runs at half the CPU clock rate */ + mips_hpt_frequency /= 2; + break; + } + } + + timer_probe(); +} + +void __init arch_init_irq(void) +{ + struct device_node *intc_node; + + intc_node = of_find_compatible_node(NULL, NULL, + "mti,cpu-interrupt-controller"); + if (!cpu_has_veic && !intc_node) + mips_cpu_irq_init(); + of_node_put(intc_node); + + irqchip_init(); +} |
