Commit fc661f2d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sched_urgent_for_v5.16_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull scheduler fixes from Borislav Petkov:

 - Avoid touching ~100 config files in order to be able to select the
   preemption model

 - clear cluster CPU masks too, on the CPU unplug path

 - prevent use-after-free in cfs

 - Prevent a race condition when updating CPU cache domains

 - Factor out common shared part of smp_prepare_cpus() into a common
   helper which can be called by both baremetal and Xen, in order to fix
   a booting of Xen PV guests

* tag 'sched_urgent_for_v5.16_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  preempt: Restore preemption model selection configs
  arch_topology: Fix missing clear cluster_cpumask in remove_cpu_topology()
  sched/fair: Prevent dead task groups from regaining cfs_rq's
  sched/core: Mitigate race cpus_share_cache()/update_top_cache_domain()
  x86/smp: Factor out parts of native_smp_prepare_cpus()
parents f7018be2 a8b76910
...@@ -126,6 +126,7 @@ static inline void arch_send_call_function_ipi_mask(const struct cpumask *mask) ...@@ -126,6 +126,7 @@ static inline void arch_send_call_function_ipi_mask(const struct cpumask *mask)
void cpu_disable_common(void); void cpu_disable_common(void);
void native_smp_prepare_boot_cpu(void); void native_smp_prepare_boot_cpu(void);
void smp_prepare_cpus_common(void);
void native_smp_prepare_cpus(unsigned int max_cpus); void native_smp_prepare_cpus(unsigned int max_cpus);
void calculate_max_logical_packages(void); void calculate_max_logical_packages(void);
void native_smp_cpus_done(unsigned int max_cpus); void native_smp_cpus_done(unsigned int max_cpus);
......
...@@ -1350,12 +1350,7 @@ static void __init smp_get_logical_apicid(void) ...@@ -1350,12 +1350,7 @@ static void __init smp_get_logical_apicid(void)
cpu0_logical_apicid = GET_APIC_LOGICAL_ID(apic_read(APIC_LDR)); cpu0_logical_apicid = GET_APIC_LOGICAL_ID(apic_read(APIC_LDR));
} }
/* void __init smp_prepare_cpus_common(void)
* Prepare for SMP bootup.
* @max_cpus: configured maximum number of CPUs, It is a legacy parameter
* for common interface support.
*/
void __init native_smp_prepare_cpus(unsigned int max_cpus)
{ {
unsigned int i; unsigned int i;
...@@ -1386,6 +1381,17 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus) ...@@ -1386,6 +1381,17 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
set_sched_topology(x86_topology); set_sched_topology(x86_topology);
set_cpu_sibling_map(0); set_cpu_sibling_map(0);
}
/*
* Prepare for SMP bootup.
* @max_cpus: configured maximum number of CPUs, It is a legacy parameter
* for common interface support.
*/
void __init native_smp_prepare_cpus(unsigned int max_cpus)
{
smp_prepare_cpus_common();
init_freq_invariance(false, false); init_freq_invariance(false, false);
smp_sanity_check(); smp_sanity_check();
......
...@@ -225,7 +225,6 @@ static void __init xen_pv_smp_prepare_boot_cpu(void) ...@@ -225,7 +225,6 @@ static void __init xen_pv_smp_prepare_boot_cpu(void)
static void __init xen_pv_smp_prepare_cpus(unsigned int max_cpus) static void __init xen_pv_smp_prepare_cpus(unsigned int max_cpus)
{ {
unsigned cpu; unsigned cpu;
unsigned int i;
if (skip_ioapic_setup) { if (skip_ioapic_setup) {
char *m = (max_cpus == 0) ? char *m = (max_cpus == 0) ?
...@@ -238,16 +237,9 @@ static void __init xen_pv_smp_prepare_cpus(unsigned int max_cpus) ...@@ -238,16 +237,9 @@ static void __init xen_pv_smp_prepare_cpus(unsigned int max_cpus)
} }
xen_init_lock_cpu(0); xen_init_lock_cpu(0);
smp_store_boot_cpu_info(); smp_prepare_cpus_common();
cpu_data(0).x86_max_cores = 1;
for_each_possible_cpu(i) { cpu_data(0).x86_max_cores = 1;
zalloc_cpumask_var(&per_cpu(cpu_sibling_map, i), GFP_KERNEL);
zalloc_cpumask_var(&per_cpu(cpu_core_map, i), GFP_KERNEL);
zalloc_cpumask_var(&per_cpu(cpu_die_map, i), GFP_KERNEL);
zalloc_cpumask_var(&per_cpu(cpu_llc_shared_map, i), GFP_KERNEL);
}
set_cpu_sibling_map(0);
speculative_store_bypass_ht_init(); speculative_store_bypass_ht_init();
......
...@@ -677,6 +677,8 @@ void remove_cpu_topology(unsigned int cpu) ...@@ -677,6 +677,8 @@ void remove_cpu_topology(unsigned int cpu)
cpumask_clear_cpu(cpu, topology_core_cpumask(sibling)); cpumask_clear_cpu(cpu, topology_core_cpumask(sibling));
for_each_cpu(sibling, topology_sibling_cpumask(cpu)) for_each_cpu(sibling, topology_sibling_cpumask(cpu))
cpumask_clear_cpu(cpu, topology_sibling_cpumask(sibling)); cpumask_clear_cpu(cpu, topology_sibling_cpumask(sibling));
for_each_cpu(sibling, topology_cluster_cpumask(cpu))
cpumask_clear_cpu(cpu, topology_cluster_cpumask(sibling));
for_each_cpu(sibling, topology_llc_cpumask(cpu)) for_each_cpu(sibling, topology_llc_cpumask(cpu))
cpumask_clear_cpu(cpu, topology_llc_cpumask(sibling)); cpumask_clear_cpu(cpu, topology_llc_cpumask(sibling));
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
struct completion; struct completion;
struct user; struct user;
#ifdef CONFIG_PREEMPT_VOLUNTARY #ifdef CONFIG_PREEMPT_VOLUNTARY_BUILD
extern int __cond_resched(void); extern int __cond_resched(void);
# define might_resched() __cond_resched() # define might_resched() __cond_resched()
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#else #else
#define MODULE_VERMAGIC_SMP "" #define MODULE_VERMAGIC_SMP ""
#endif #endif
#ifdef CONFIG_PREEMPT #ifdef CONFIG_PREEMPT_BUILD
#define MODULE_VERMAGIC_PREEMPT "preempt " #define MODULE_VERMAGIC_PREEMPT "preempt "
#elif defined(CONFIG_PREEMPT_RT) #elif defined(CONFIG_PREEMPT_RT)
#define MODULE_VERMAGIC_PREEMPT "preempt_rt " #define MODULE_VERMAGIC_PREEMPT "preempt_rt "
......
...@@ -30,7 +30,7 @@ $(obj)/version.o: include/generated/compile.h ...@@ -30,7 +30,7 @@ $(obj)/version.o: include/generated/compile.h
quiet_cmd_compile.h = CHK $@ quiet_cmd_compile.h = CHK $@
cmd_compile.h = \ cmd_compile.h = \
$(CONFIG_SHELL) $(srctree)/scripts/mkcompile_h $@ \ $(CONFIG_SHELL) $(srctree)/scripts/mkcompile_h $@ \
"$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT)" \ "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT_BUILD)" \
"$(CONFIG_PREEMPT_RT)" $(CONFIG_CC_VERSION_TEXT) "$(LD)" "$(CONFIG_PREEMPT_RT)" $(CONFIG_CC_VERSION_TEXT) "$(LD)"
include/generated/compile.h: FORCE include/generated/compile.h: FORCE
......
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config PREEMPT_NONE_BUILD
bool
config PREEMPT_VOLUNTARY_BUILD
bool
config PREEMPT_BUILD
bool
select PREEMPTION
select UNINLINE_SPIN_UNLOCK if !ARCH_INLINE_SPIN_UNLOCK
choice choice
prompt "Preemption Model" prompt "Preemption Model"
default PREEMPT_NONE_BEHAVIOUR default PREEMPT_NONE
config PREEMPT_NONE_BEHAVIOUR config PREEMPT_NONE
bool "No Forced Preemption (Server)" bool "No Forced Preemption (Server)"
select PREEMPT_NONE if !PREEMPT_DYNAMIC select PREEMPT_NONE_BUILD if !PREEMPT_DYNAMIC
help help
This is the traditional Linux preemption model, geared towards This is the traditional Linux preemption model, geared towards
throughput. It will still provide good latencies most of the throughput. It will still provide good latencies most of the
...@@ -18,10 +29,10 @@ config PREEMPT_NONE_BEHAVIOUR ...@@ -18,10 +29,10 @@ config PREEMPT_NONE_BEHAVIOUR
raw processing power of the kernel, irrespective of scheduling raw processing power of the kernel, irrespective of scheduling
latencies. latencies.
config PREEMPT_VOLUNTARY_BEHAVIOUR config PREEMPT_VOLUNTARY
bool "Voluntary Kernel Preemption (Desktop)" bool "Voluntary Kernel Preemption (Desktop)"
depends on !ARCH_NO_PREEMPT depends on !ARCH_NO_PREEMPT
select PREEMPT_VOLUNTARY if !PREEMPT_DYNAMIC select PREEMPT_VOLUNTARY_BUILD if !PREEMPT_DYNAMIC
help help
This option reduces the latency of the kernel by adding more This option reduces the latency of the kernel by adding more
"explicit preemption points" to the kernel code. These new "explicit preemption points" to the kernel code. These new
...@@ -37,10 +48,10 @@ config PREEMPT_VOLUNTARY_BEHAVIOUR ...@@ -37,10 +48,10 @@ config PREEMPT_VOLUNTARY_BEHAVIOUR
Select this if you are building a kernel for a desktop system. Select this if you are building a kernel for a desktop system.
config PREEMPT_BEHAVIOUR config PREEMPT
bool "Preemptible Kernel (Low-Latency Desktop)" bool "Preemptible Kernel (Low-Latency Desktop)"
depends on !ARCH_NO_PREEMPT depends on !ARCH_NO_PREEMPT
select PREEMPT select PREEMPT_BUILD
help help
This option reduces the latency of the kernel by making This option reduces the latency of the kernel by making
all kernel code (that is not executing in a critical section) all kernel code (that is not executing in a critical section)
...@@ -58,7 +69,7 @@ config PREEMPT_BEHAVIOUR ...@@ -58,7 +69,7 @@ config PREEMPT_BEHAVIOUR
config PREEMPT_RT config PREEMPT_RT
bool "Fully Preemptible Kernel (Real-Time)" bool "Fully Preemptible Kernel (Real-Time)"
depends on EXPERT && ARCH_SUPPORTS_RT && !PREEMPT_DYNAMIC depends on EXPERT && ARCH_SUPPORTS_RT
select PREEMPTION select PREEMPTION
help help
This option turns the kernel into a real-time kernel by replacing This option turns the kernel into a real-time kernel by replacing
...@@ -75,17 +86,6 @@ config PREEMPT_RT ...@@ -75,17 +86,6 @@ config PREEMPT_RT
endchoice endchoice
config PREEMPT_NONE
bool
config PREEMPT_VOLUNTARY
bool
config PREEMPT
bool
select PREEMPTION
select UNINLINE_SPIN_UNLOCK if !ARCH_INLINE_SPIN_UNLOCK
config PREEMPT_COUNT config PREEMPT_COUNT
bool bool
...@@ -95,8 +95,8 @@ config PREEMPTION ...@@ -95,8 +95,8 @@ config PREEMPTION
config PREEMPT_DYNAMIC config PREEMPT_DYNAMIC
bool "Preemption behaviour defined on boot" bool "Preemption behaviour defined on boot"
depends on HAVE_PREEMPT_DYNAMIC depends on HAVE_PREEMPT_DYNAMIC && !PREEMPT_RT
select PREEMPT select PREEMPT_BUILD
default y default y
help help
This option allows to define the preemption model on the kernel This option allows to define the preemption model on the kernel
......
...@@ -31,7 +31,7 @@ static inline void autogroup_destroy(struct kref *kref) ...@@ -31,7 +31,7 @@ static inline void autogroup_destroy(struct kref *kref)
ag->tg->rt_se = NULL; ag->tg->rt_se = NULL;
ag->tg->rt_rq = NULL; ag->tg->rt_rq = NULL;
#endif #endif
sched_offline_group(ag->tg); sched_release_group(ag->tg);
sched_destroy_group(ag->tg); sched_destroy_group(ag->tg);
} }
......
...@@ -3726,6 +3726,9 @@ void wake_up_if_idle(int cpu) ...@@ -3726,6 +3726,9 @@ void wake_up_if_idle(int cpu)
bool cpus_share_cache(int this_cpu, int that_cpu) bool cpus_share_cache(int this_cpu, int that_cpu)
{ {
if (this_cpu == that_cpu)
return true;
return per_cpu(sd_llc_id, this_cpu) == per_cpu(sd_llc_id, that_cpu); return per_cpu(sd_llc_id, this_cpu) == per_cpu(sd_llc_id, that_cpu);
} }
...@@ -6625,13 +6628,13 @@ __setup("preempt=", setup_preempt_mode); ...@@ -6625,13 +6628,13 @@ __setup("preempt=", setup_preempt_mode);
static void __init preempt_dynamic_init(void) static void __init preempt_dynamic_init(void)
{ {
if (preempt_dynamic_mode == preempt_dynamic_undefined) { if (preempt_dynamic_mode == preempt_dynamic_undefined) {
if (IS_ENABLED(CONFIG_PREEMPT_NONE_BEHAVIOUR)) { if (IS_ENABLED(CONFIG_PREEMPT_NONE)) {
sched_dynamic_update(preempt_dynamic_none); sched_dynamic_update(preempt_dynamic_none);
} else if (IS_ENABLED(CONFIG_PREEMPT_VOLUNTARY_BEHAVIOUR)) { } else if (IS_ENABLED(CONFIG_PREEMPT_VOLUNTARY)) {
sched_dynamic_update(preempt_dynamic_voluntary); sched_dynamic_update(preempt_dynamic_voluntary);
} else { } else {
/* Default static call setting, nothing to do */ /* Default static call setting, nothing to do */
WARN_ON_ONCE(!IS_ENABLED(CONFIG_PREEMPT_BEHAVIOUR)); WARN_ON_ONCE(!IS_ENABLED(CONFIG_PREEMPT));
preempt_dynamic_mode = preempt_dynamic_full; preempt_dynamic_mode = preempt_dynamic_full;
pr_info("Dynamic Preempt: full\n"); pr_info("Dynamic Preempt: full\n");
} }
...@@ -9716,6 +9719,22 @@ static void sched_free_group(struct task_group *tg) ...@@ -9716,6 +9719,22 @@ static void sched_free_group(struct task_group *tg)
kmem_cache_free(task_group_cache, tg); kmem_cache_free(task_group_cache, tg);
} }
static void sched_free_group_rcu(struct rcu_head *rcu)
{
sched_free_group(container_of(rcu, struct task_group, rcu));
}
static void sched_unregister_group(struct task_group *tg)
{
unregister_fair_sched_group(tg);
unregister_rt_sched_group(tg);
/*
* We have to wait for yet another RCU grace period to expire, as
* print_cfs_stats() might run concurrently.
*/
call_rcu(&tg->rcu, sched_free_group_rcu);
}
/* allocate runqueue etc for a new task group */ /* allocate runqueue etc for a new task group */
struct task_group *sched_create_group(struct task_group *parent) struct task_group *sched_create_group(struct task_group *parent)
{ {
...@@ -9759,25 +9778,35 @@ void sched_online_group(struct task_group *tg, struct task_group *parent) ...@@ -9759,25 +9778,35 @@ void sched_online_group(struct task_group *tg, struct task_group *parent)
} }
/* rcu callback to free various structures associated with a task group */ /* rcu callback to free various structures associated with a task group */
static void sched_free_group_rcu(struct rcu_head *rhp) static void sched_unregister_group_rcu(struct rcu_head *rhp)
{ {
/* Now it should be safe to free those cfs_rqs: */ /* Now it should be safe to free those cfs_rqs: */
sched_free_group(container_of(rhp, struct task_group, rcu)); sched_unregister_group(container_of(rhp, struct task_group, rcu));
} }
void sched_destroy_group(struct task_group *tg) void sched_destroy_group(struct task_group *tg)
{ {
/* Wait for possible concurrent references to cfs_rqs complete: */ /* Wait for possible concurrent references to cfs_rqs complete: */
call_rcu(&tg->rcu, sched_free_group_rcu); call_rcu(&tg->rcu, sched_unregister_group_rcu);
} }
void sched_offline_group(struct task_group *tg) void sched_release_group(struct task_group *tg)
{ {
unsigned long flags; unsigned long flags;
/* End participation in shares distribution: */ /*
unregister_fair_sched_group(tg); * Unlink first, to avoid walk_tg_tree_from() from finding us (via
* sched_cfs_period_timer()).
*
* For this to be effective, we have to wait for all pending users of
* this task group to leave their RCU critical section to ensure no new
* user will see our dying task group any more. Specifically ensure
* that tg_unthrottle_up() won't add decayed cfs_rq's to it.
*
* We therefore defer calling unregister_fair_sched_group() to
* sched_unregister_group() which is guarantied to get called only after the
* current RCU grace period has expired.
*/
spin_lock_irqsave(&task_group_lock, flags); spin_lock_irqsave(&task_group_lock, flags);
list_del_rcu(&tg->list); list_del_rcu(&tg->list);
list_del_rcu(&tg->siblings); list_del_rcu(&tg->siblings);
...@@ -9896,7 +9925,7 @@ static void cpu_cgroup_css_released(struct cgroup_subsys_state *css) ...@@ -9896,7 +9925,7 @@ static void cpu_cgroup_css_released(struct cgroup_subsys_state *css)
{ {
struct task_group *tg = css_tg(css); struct task_group *tg = css_tg(css);
sched_offline_group(tg); sched_release_group(tg);
} }
static void cpu_cgroup_css_free(struct cgroup_subsys_state *css) static void cpu_cgroup_css_free(struct cgroup_subsys_state *css)
...@@ -9906,7 +9935,7 @@ static void cpu_cgroup_css_free(struct cgroup_subsys_state *css) ...@@ -9906,7 +9935,7 @@ static void cpu_cgroup_css_free(struct cgroup_subsys_state *css)
/* /*
* Relies on the RCU grace period between css_released() and this. * Relies on the RCU grace period between css_released() and this.
*/ */
sched_free_group(tg); sched_unregister_group(tg);
} }
/* /*
......
...@@ -11456,8 +11456,6 @@ void free_fair_sched_group(struct task_group *tg) ...@@ -11456,8 +11456,6 @@ void free_fair_sched_group(struct task_group *tg)
{ {
int i; int i;
destroy_cfs_bandwidth(tg_cfs_bandwidth(tg));
for_each_possible_cpu(i) { for_each_possible_cpu(i) {
if (tg->cfs_rq) if (tg->cfs_rq)
kfree(tg->cfs_rq[i]); kfree(tg->cfs_rq[i]);
...@@ -11534,6 +11532,8 @@ void unregister_fair_sched_group(struct task_group *tg) ...@@ -11534,6 +11532,8 @@ void unregister_fair_sched_group(struct task_group *tg)
struct rq *rq; struct rq *rq;
int cpu; int cpu;
destroy_cfs_bandwidth(tg_cfs_bandwidth(tg));
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
if (tg->se[cpu]) if (tg->se[cpu])
remove_entity_load_avg(tg->se[cpu]); remove_entity_load_avg(tg->se[cpu]);
......
...@@ -137,13 +137,17 @@ static inline struct rq *rq_of_rt_se(struct sched_rt_entity *rt_se) ...@@ -137,13 +137,17 @@ static inline struct rq *rq_of_rt_se(struct sched_rt_entity *rt_se)
return rt_rq->rq; return rt_rq->rq;
} }
void free_rt_sched_group(struct task_group *tg) void unregister_rt_sched_group(struct task_group *tg)
{ {
int i;
if (tg->rt_se) if (tg->rt_se)
destroy_rt_bandwidth(&tg->rt_bandwidth); destroy_rt_bandwidth(&tg->rt_bandwidth);
}
void free_rt_sched_group(struct task_group *tg)
{
int i;
for_each_possible_cpu(i) { for_each_possible_cpu(i) {
if (tg->rt_rq) if (tg->rt_rq)
kfree(tg->rt_rq[i]); kfree(tg->rt_rq[i]);
...@@ -250,6 +254,8 @@ static inline struct rt_rq *rt_rq_of_se(struct sched_rt_entity *rt_se) ...@@ -250,6 +254,8 @@ static inline struct rt_rq *rt_rq_of_se(struct sched_rt_entity *rt_se)
return &rq->rt; return &rq->rt;
} }
void unregister_rt_sched_group(struct task_group *tg) { }
void free_rt_sched_group(struct task_group *tg) { } void free_rt_sched_group(struct task_group *tg) { }
int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent) int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent)
......
...@@ -488,6 +488,7 @@ extern void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b); ...@@ -488,6 +488,7 @@ extern void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b);
extern void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b); extern void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b);
extern void unthrottle_cfs_rq(struct cfs_rq *cfs_rq); extern void unthrottle_cfs_rq(struct cfs_rq *cfs_rq);
extern void unregister_rt_sched_group(struct task_group *tg);
extern void free_rt_sched_group(struct task_group *tg); extern void free_rt_sched_group(struct task_group *tg);
extern int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent); extern int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent);
extern void init_tg_rt_entry(struct task_group *tg, struct rt_rq *rt_rq, extern void init_tg_rt_entry(struct task_group *tg, struct rt_rq *rt_rq,
...@@ -503,7 +504,7 @@ extern struct task_group *sched_create_group(struct task_group *parent); ...@@ -503,7 +504,7 @@ extern struct task_group *sched_create_group(struct task_group *parent);
extern void sched_online_group(struct task_group *tg, extern void sched_online_group(struct task_group *tg,
struct task_group *parent); struct task_group *parent);
extern void sched_destroy_group(struct task_group *tg); extern void sched_destroy_group(struct task_group *tg);
extern void sched_offline_group(struct task_group *tg); extern void sched_release_group(struct task_group *tg);
extern void sched_move_task(struct task_struct *tsk); extern void sched_move_task(struct task_struct *tsk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment