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/m68k/virt/ints.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/m68k/virt/ints.c')
| -rw-r--r-- | arch/m68k/virt/ints.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/arch/m68k/virt/ints.c b/arch/m68k/virt/ints.c new file mode 100644 index 000000000..896aa6eb8 --- /dev/null +++ b/arch/m68k/virt/ints.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/types.h> +#include <linux/ioport.h> + +#include <asm/hwtest.h> +#include <asm/irq.h> +#include <asm/irq_regs.h> +#include <asm/processor.h> +#include <asm/virt.h> + +#define GFPIC_REG_IRQ_PENDING 0x04 +#define GFPIC_REG_IRQ_DISABLE_ALL 0x08 +#define GFPIC_REG_IRQ_DISABLE 0x0c +#define GFPIC_REG_IRQ_ENABLE 0x10 + +static struct resource picres[6]; +static const char *picname[6] = { + "goldfish_pic.0", + "goldfish_pic.1", + "goldfish_pic.2", + "goldfish_pic.3", + "goldfish_pic.4", + "goldfish_pic.5" +}; + +/* + * 6 goldfish-pic for CPU IRQ #1 to IRQ #6 + * CPU IRQ #1 -> PIC #1 + * IRQ #1 to IRQ #31 -> unused + * IRQ #32 -> goldfish-tty + * CPU IRQ #2 -> PIC #2 + * IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32 + * CPU IRQ #3 -> PIC #3 + * IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64 + * CPU IRQ #4 -> PIC #4 + * IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96 + * CPU IRQ #5 -> PIC #5 + * IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128 + * CPU IRQ #6 -> PIC #6 + * IRQ #1 -> goldfish-timer + * IRQ #2 -> goldfish-rtc + * IRQ #3 to IRQ #32 -> unused + * CPU IRQ #7 -> NMI + */ + +static u32 gfpic_read(int pic, int reg) +{ + void __iomem *base = (void __iomem *)(virt_bi_data.pic.mmio + + pic * 0x1000); + + return ioread32be(base + reg); +} + +static void gfpic_write(u32 value, int pic, int reg) +{ + void __iomem *base = (void __iomem *)(virt_bi_data.pic.mmio + + pic * 0x1000); + + iowrite32be(value, base + reg); +} + +#define GF_PIC(irq) ((irq - IRQ_USER) / 32) +#define GF_IRQ(irq) ((irq - IRQ_USER) % 32) + +static void virt_irq_enable(struct irq_data *data) +{ + gfpic_write(BIT(GF_IRQ(data->irq)), GF_PIC(data->irq), + GFPIC_REG_IRQ_ENABLE); +} + +static void virt_irq_disable(struct irq_data *data) +{ + gfpic_write(BIT(GF_IRQ(data->irq)), GF_PIC(data->irq), + GFPIC_REG_IRQ_DISABLE); +} + +static unsigned int virt_irq_startup(struct irq_data *data) +{ + virt_irq_enable(data); + return 0; +} + +static irqreturn_t virt_nmi_handler(int irq, void *dev_id) +{ + static int in_nmi; + + if (READ_ONCE(in_nmi)) + return IRQ_HANDLED; + WRITE_ONCE(in_nmi, 1); + + pr_warn("Non-Maskable Interrupt\n"); + show_registers(get_irq_regs()); + + WRITE_ONCE(in_nmi, 0); + return IRQ_HANDLED; +} + +static struct irq_chip virt_irq_chip = { + .name = "virt", + .irq_enable = virt_irq_enable, + .irq_disable = virt_irq_disable, + .irq_startup = virt_irq_startup, + .irq_shutdown = virt_irq_disable, +}; + +static void goldfish_pic_irq(struct irq_desc *desc) +{ + u32 irq_pending; + unsigned int irq_num; + unsigned int pic = desc->irq_data.irq - 1; + + irq_pending = gfpic_read(pic, GFPIC_REG_IRQ_PENDING); + irq_num = IRQ_USER + pic * 32; + + do { + if (irq_pending & 1) + generic_handle_irq(irq_num); + ++irq_num; + irq_pending >>= 1; + } while (irq_pending); +} + +void __init virt_init_IRQ(void) +{ + unsigned int i; + + m68k_setup_irq_controller(&virt_irq_chip, handle_simple_irq, IRQ_USER, + NUM_VIRT_SOURCES - IRQ_USER); + + for (i = 0; i < 6; i++) { + + picres[i] = (struct resource) + DEFINE_RES_MEM_NAMED(virt_bi_data.pic.mmio + i * 0x1000, + 0x1000, picname[i]); + if (request_resource(&iomem_resource, &picres[i])) { + pr_err("Cannot allocate %s resource\n", picname[i]); + return; + } + + irq_set_chained_handler(virt_bi_data.pic.irq + i, + goldfish_pic_irq); + } + + if (request_irq(IRQ_AUTO_7, virt_nmi_handler, 0, "NMI", + virt_nmi_handler)) + pr_err("Couldn't register NMI\n"); +} |
