From 0d6526f82c3cdefcca47f73f5fc08dc6f335eac6 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Mon, 21 Sep 2026 17:37:22 -0700 Subject: sched/cache: Keep nr_pref_llc_running in the runnable domain, to fix LLC mis-scheduling bug 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 Suggested-by: Chen Yu Signed-off-by: Tim Chen Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Kayra Cizmeci Cc: # v7.2.x Link: https://patch.msgid.link/06af61afedac32e6477f57feb4d658f6c411c3af.1790035273.git.tim.c.chen@linux.intel.com --- drivers/net/phy/bcm-phy-lib.h | 129 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 drivers/net/phy/bcm-phy-lib.h (limited to 'drivers/net/phy/bcm-phy-lib.h') diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h new file mode 100644 index 000000000..bba94ce96 --- /dev/null +++ b/drivers/net/phy/bcm-phy-lib.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2015 Broadcom Corporation + */ + +#ifndef _LINUX_BCM_PHY_LIB_H +#define _LINUX_BCM_PHY_LIB_H + +#include +#include +#include + +struct ethtool_wolinfo; + +/* 28nm only register definitions */ +#define MISC_ADDR(base, channel) base, channel + +#define DSP_TAP10 MISC_ADDR(0x0a, 0) +#define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1) +#define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2) +#define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0) + +#define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0) +#define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1) +#define AFE_RXCONFIG_2 MISC_ADDR(0x38, 2) +#define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3) +#define AFE_TX_CONFIG MISC_ADDR(0x39, 0) +#define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1) +#define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) +#define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) + + +int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val); +int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg); +int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set); +int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val); +int bcm_phy_read_exp(struct phy_device *phydev, u16 reg); +int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set); + +static inline int bcm_phy_write_exp_sel(struct phy_device *phydev, + u16 reg, u16 val) +{ + return bcm_phy_write_exp(phydev, reg | MII_BCM54XX_EXP_SEL_ER, val); +} + +static inline int bcm_phy_read_exp_sel(struct phy_device *phydev, u16 reg) +{ + return bcm_phy_read_exp(phydev, reg | MII_BCM54XX_EXP_SEL_ER); +} + +int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val); +int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum); + +int bcm_phy_write_misc(struct phy_device *phydev, + u16 reg, u16 chl, u16 value); +int bcm_phy_read_misc(struct phy_device *phydev, + u16 reg, u16 chl); + +int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, + u16 val); +int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow); + +int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val); +int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val); +int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb); +int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb); +int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, + u16 set); +int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, + u16 set); + +int bcm_phy_ack_intr(struct phy_device *phydev); +int bcm_phy_config_intr(struct phy_device *phydev); +irqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev); + +int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down); + +int bcm_phy_set_eee(struct phy_device *phydev, bool enable); + +int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count); + +int bcm_phy_downshift_set(struct phy_device *phydev, u8 count); + +int bcm_phy_get_sset_count(struct phy_device *phydev); +void bcm_phy_get_strings(struct phy_device *phydev, u8 *data); +void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, + struct ethtool_stats *stats, u64 *data); +void bcm_phy_update_stats_shadow(struct phy_device *phydev, u64 *shadow); +void bcm_phy_r_rc_cal_reset(struct phy_device *phydev); +int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev); +int bcm_phy_enable_jumbo(struct phy_device *phydev); + +int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, + bool *finished); +int bcm_phy_cable_test_start_rdb(struct phy_device *phydev); +int bcm_phy_cable_test_start(struct phy_device *phydev); +int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished); + +#if IS_ENABLED(CONFIG_BCM_NET_PHYPTP) +struct bcm_ptp_private *bcm_ptp_probe(struct phy_device *phydev); +void bcm_ptp_config_init(struct phy_device *phydev); +void bcm_ptp_stop(struct bcm_ptp_private *priv); +#else +static inline struct bcm_ptp_private *bcm_ptp_probe(struct phy_device *phydev) +{ + return NULL; +} + +static inline void bcm_ptp_config_init(struct phy_device *phydev) +{ +} + +static inline void bcm_ptp_stop(struct bcm_ptp_private *priv) +{ +} +#endif + +int bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); +void bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); +irqreturn_t bcm_phy_wol_isr(int irq, void *dev_id); + +int bcm_phy_led_brightness_set(struct phy_device *phydev, + u8 index, enum led_brightness value); + +int bcm_setup_lre_master_slave(struct phy_device *phydev); +int bcm_config_lre_aneg(struct phy_device *phydev, bool changed); +int bcm_config_lre_advert(struct phy_device *phydev); + +#endif /* _LINUX_BCM_PHY_LIB_H */ -- cgit v1.3.1