diff --git a/include/linux/freezer.h b/include/linux/freezer.h index b9e1e4200101..3a2b6902bee8 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -8,6 +8,9 @@ #include #include #include +#if defined(CONFIG_ARM64) && !defined(__GENKSYMS__) +#include +#endif #ifdef CONFIG_FREEZER extern atomic_t system_freezing_cnt; /* nr of freezing conds in effect */ @@ -108,10 +111,15 @@ static inline bool cgroup_freezing(struct task_struct *task) * The caller shouldn't do anything which isn't allowed for a frozen task * until freezer_cont() is called. Usually, freezer[_do_not]_count() pair * wrap a scheduling operation and nothing much else. + * + * The write to current->flags uses release semantics to prevent a concurrent + * freezer_should_skip() from observing this write before a write to on_rq + * during a prior call to activate_task(), which may cause it to return true + * before deactivate_task() is called. */ static inline void freezer_do_not_count(void) { - current->flags |= PF_FREEZER_SKIP; + smp_store_release(¤t->flags, current->flags | PF_FREEZER_SKIP); } /** @@ -161,7 +169,19 @@ static inline bool freezer_should_skip(struct task_struct *p) * clearing %PF_FREEZER_SKIP. */ smp_mb(); +#ifdef CONFIG_ARM64 + return (p->flags & PF_FREEZER_SKIP) && + (!p->on_rq || task_cpu_possible_mask(p) == cpu_possible_mask); +#else + /* + * On non-aarch64, avoid depending on task_cpu_possible_mask(), which is + * defined in , because including that header from + * here exposes a tricky bug in the tracepoint headers on x86, and that + * macro would end up being defined equal to cpu_possible_mask on other + * architectures anyway. + */ return p->flags & PF_FREEZER_SKIP; +#endif } /* diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 533d81ed74d4..cd4a81114f75 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -83,7 +83,7 @@ static void __init handle_initrd(void) * In case that a resume from disk is carried out by linuxrc or one of * its children, we need to tell the freezer not to wait for us. */ - current->flags |= PF_FREEZER_SKIP; + freezer_do_not_count(); info = call_usermodehelper_setup("/linuxrc", argv, envp_init, GFP_KERNEL, init_linuxrc, NULL, NULL); diff --git a/kernel/power/main.c b/kernel/power/main.c index 7e646079fbeb..b6219c8f6d76 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -23,7 +23,7 @@ void lock_system_sleep(void) { - current->flags |= PF_FREEZER_SKIP; + freezer_do_not_count(); mutex_lock(&system_transition_mutex); } EXPORT_SYMBOL_GPL(lock_system_sleep); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index a75f99328f59..363d1c416d0a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6282,23 +6282,6 @@ pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) #endif /* CONFIG_SCHED_CORE */ -static bool __task_can_run(struct task_struct *prev) -{ - if (__fatal_signal_pending(prev)) - return true; - - if (!frozen_or_skipped(prev)) - return true; - - /* - * We can't safely go back on the runqueue if we're an asymmetric - * task skipping the freezer. Doing so can lead to migration failures - * later on if there aren't any suitable CPUs left around for us to - * move to. - */ - return task_cpu_possible_mask(prev) == cpu_possible_mask; -} - /* * Constants for the sched_mode argument of __schedule(). * @@ -6410,7 +6393,7 @@ static void __sched notrace __schedule(unsigned int sched_mode) */ prev_state = READ_ONCE(prev->__state); if (!(sched_mode & SM_MASK_PREEMPT) && prev_state) { - if (signal_pending_state(prev_state, prev) && __task_can_run(prev)) { + if (signal_pending_state(prev_state, prev)) { WRITE_ONCE(prev->__state, TASK_RUNNING); } else { prev->sched_contributes_to_load =