diff --git a/include/linux/freezer.h b/include/linux/freezer.h index f753c307b8b3..181aac4b5187 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 d6140ed15d0b..f985c3ccbf2f 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 f805a67f877e..5000f595fac0 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4617,23 +4617,6 @@ restart: BUG(); } -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; -} - /* * __schedule() is the main scheduler function. * @@ -4727,7 +4710,7 @@ static void __sched notrace __schedule(bool preempt) */ prev_state = prev->state; if (!preempt && prev_state) { - if (signal_pending_state(prev_state, prev) && __task_can_run(prev)) { + if (signal_pending_state(prev_state, prev)) { prev->state = TASK_RUNNING; } else { prev->sched_contributes_to_load =