notifier: Fix broken error handling pattern
The current notifiers have the following error handling pattern all
over the place:
int err, nr;
err = __foo_notifier_call_chain(&chain, val_up, v, -1, &nr);
if (err & NOTIFIER_STOP_MASK)
__foo_notifier_call_chain(&chain, val_down, v, nr-1, NULL)
And aside from the endless repetition thereof, it is broken. Consider
blocking notifiers; both calls take and drop the rwsem, this means
that the notifier list can change in between the two calls, making @nr
meaningless.
Fix this by replacing all the __foo_notifier_call_chain() functions
with foo_notifier_call_chain_robust() that embeds the above pattern,
but ensures it is inside a single lock region.
Note: I switched atomic_notifier_call_chain_robust() to use
the spinlock, since RCU cannot provide the guarantee
required for the recovery.
Note: software_resume() error handling was broken afaict.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/20200818135804.325626653@infradead.org
This commit is contained in:
committed by
Ingo Molnar
parent
f75aef392f
commit
70d9329857
@@ -15,18 +15,28 @@
|
||||
|
||||
static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain);
|
||||
|
||||
static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
|
||||
static int cpu_pm_notify(enum cpu_pm_event event)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* __atomic_notifier_call_chain has a RCU read critical section, which
|
||||
* atomic_notifier_call_chain has a RCU read critical section, which
|
||||
* could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let
|
||||
* RCU know this.
|
||||
*/
|
||||
rcu_irq_enter_irqson();
|
||||
ret = __atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
|
||||
nr_to_call, nr_calls);
|
||||
ret = atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL);
|
||||
rcu_irq_exit_irqson();
|
||||
|
||||
return notifier_to_errno(ret);
|
||||
}
|
||||
|
||||
static int cpu_pm_notify_robust(enum cpu_pm_event event_up, enum cpu_pm_event event_down)
|
||||
{
|
||||
int ret;
|
||||
|
||||
rcu_irq_enter_irqson();
|
||||
ret = atomic_notifier_call_chain_robust(&cpu_pm_notifier_chain, event_up, event_down, NULL);
|
||||
rcu_irq_exit_irqson();
|
||||
|
||||
return notifier_to_errno(ret);
|
||||
@@ -80,18 +90,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
|
||||
*/
|
||||
int cpu_pm_enter(void)
|
||||
{
|
||||
int nr_calls = 0;
|
||||
int ret = 0;
|
||||
|
||||
ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
|
||||
if (ret)
|
||||
/*
|
||||
* Inform listeners (nr_calls - 1) about failure of CPU PM
|
||||
* PM entry who are notified earlier to prepare for it.
|
||||
*/
|
||||
cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
|
||||
|
||||
return ret;
|
||||
return cpu_pm_notify_robust(CPU_PM_ENTER, CPU_PM_ENTER_FAILED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_pm_enter);
|
||||
|
||||
@@ -109,7 +108,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_enter);
|
||||
*/
|
||||
int cpu_pm_exit(void)
|
||||
{
|
||||
return cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
|
||||
return cpu_pm_notify(CPU_PM_EXIT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_pm_exit);
|
||||
|
||||
@@ -131,18 +130,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_exit);
|
||||
*/
|
||||
int cpu_cluster_pm_enter(void)
|
||||
{
|
||||
int nr_calls = 0;
|
||||
int ret = 0;
|
||||
|
||||
ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
|
||||
if (ret)
|
||||
/*
|
||||
* Inform listeners (nr_calls - 1) about failure of CPU cluster
|
||||
* PM entry who are notified earlier to prepare for it.
|
||||
*/
|
||||
cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
|
||||
|
||||
return ret;
|
||||
return cpu_pm_notify_robust(CPU_CLUSTER_PM_ENTER, CPU_CLUSTER_PM_ENTER_FAILED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
|
||||
|
||||
@@ -163,7 +151,7 @@ EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
|
||||
*/
|
||||
int cpu_cluster_pm_exit(void)
|
||||
{
|
||||
return cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
|
||||
return cpu_pm_notify(CPU_CLUSTER_PM_EXIT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user