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 /drivers/rtc/rtc-pl030.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 'drivers/rtc/rtc-pl030.c')
| -rw-r--r-- | drivers/rtc/rtc-pl030.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c new file mode 100644 index 000000000..5caaa714f --- /dev/null +++ b/drivers/rtc/rtc-pl030.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/rtc/rtc-pl030.c + * + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. + */ +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/amba/bus.h> +#include <linux/io.h> +#include <linux/slab.h> + +#define RTC_DR (0) +#define RTC_MR (4) +#define RTC_STAT (8) +#define RTC_EOI (8) +#define RTC_LR (12) +#define RTC_CR (16) +#define RTC_CR_MIE (1 << 0) + +struct pl030_rtc { + void __iomem *base; +}; + +static irqreturn_t pl030_interrupt(int irq, void *dev_id) +{ + struct pl030_rtc *rtc = dev_id; + writel(0, rtc->base + RTC_EOI); + return IRQ_HANDLED; +} + +static int pl030_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(rtc->base + RTC_MR), &alrm->time); + return 0; +} + +static int pl030_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(&alrm->time), rtc->base + RTC_MR); + + return 0; +} + +static int pl030_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(rtc->base + RTC_DR), tm); + + return 0; +} + +/* + * Set the RTC time. Unfortunately, we can't accurately set + * the point at which the counter updates. + * + * Also, since RTC_LR is transferred to RTC_CR on next rising + * edge of the 1Hz clock, we must write the time one second + * in advance. + */ +static int pl030_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(tm) + 1, rtc->base + RTC_LR); + + return 0; +} + +static const struct rtc_class_ops pl030_ops = { + .read_time = pl030_read_time, + .set_time = pl030_set_time, + .read_alarm = pl030_read_alarm, + .set_alarm = pl030_set_alarm, +}; + +static int pl030_probe(struct amba_device *dev, const struct amba_id *id) +{ + struct pl030_rtc *rtc; + int ret; + struct rtc_device *rtc_dev; + + ret = amba_request_regions(dev, NULL); + if (ret) + goto err_req; + + rtc = devm_kzalloc(&dev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) { + ret = -ENOMEM; + goto err_rtc; + } + + rtc_dev = devm_rtc_allocate_device(&dev->dev); + if (IS_ERR(rtc_dev)) { + ret = PTR_ERR(rtc_dev); + goto err_rtc; + } + + rtc_dev->ops = &pl030_ops; + rtc_dev->range_max = U32_MAX; + rtc->base = ioremap(dev->res.start, resource_size(&dev->res)); + if (!rtc->base) { + ret = -ENOMEM; + goto err_rtc; + } + + __raw_writel(0, rtc->base + RTC_CR); + __raw_writel(0, rtc->base + RTC_EOI); + + amba_set_drvdata(dev, rtc); + + ret = request_irq(dev->irq[0], pl030_interrupt, 0, + "rtc-pl030", rtc); + if (ret) + goto err_irq; + + ret = devm_rtc_register_device(rtc_dev); + if (ret) + goto err_reg; + + return 0; + + err_reg: + free_irq(dev->irq[0], rtc); + err_irq: + iounmap(rtc->base); + err_rtc: + amba_release_regions(dev); + err_req: + return ret; +} + +static void pl030_remove(struct amba_device *dev) +{ + struct pl030_rtc *rtc = amba_get_drvdata(dev); + + writel(0, rtc->base + RTC_CR); + + free_irq(dev->irq[0], rtc); + iounmap(rtc->base); + amba_release_regions(dev); +} + +static const struct amba_id pl030_ids[] = { + { + .id = 0x00041030, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +MODULE_DEVICE_TABLE(amba, pl030_ids); + +static struct amba_driver pl030_driver = { + .drv = { + .name = "rtc-pl030", + }, + .probe = pl030_probe, + .remove = pl030_remove, + .id_table = pl030_ids, +}; + +module_amba_driver(pl030_driver); + +MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); +MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver"); +MODULE_LICENSE("GPL"); |
