linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@kernel.org>,
	"Rafael J . Wysocki" <rafael.j.wysocki@intel.com>,
	Sasha Levin <sashal@kernel.org>,
	linux-pm@vger.kernel.org
Subject: [PATCH AUTOSEL 5.9 031/111] notifier: Fix broken error handling pattern
Date: Sun, 18 Oct 2020 15:16:47 -0400	[thread overview]
Message-ID: <20201018191807.4052726-31-sashal@kernel.org> (raw)
In-Reply-To: <20201018191807.4052726-1-sashal@kernel.org>

From: Peter Zijlstra <peterz@infradead.org>

[ Upstream commit 70d932985757fbe978024db313001218e9f8fe5c ]

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
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/linux/notifier.h           |  15 ++-
 kernel/cpu_pm.c                    |  48 ++++------
 kernel/notifier.c                  | 144 ++++++++++++++++++-----------
 kernel/power/hibernate.c           |  39 ++++----
 kernel/power/main.c                |   8 +-
 kernel/power/power.h               |   3 +-
 kernel/power/suspend.c             |  14 ++-
 kernel/power/user.c                |  14 +--
 tools/power/pm-graph/sleepgraph.py |   2 +-
 9 files changed, 147 insertions(+), 140 deletions(-)

diff --git a/include/linux/notifier.h b/include/linux/notifier.h
index 018947611483e..2fb373a5c1ede 100644
--- a/include/linux/notifier.h
+++ b/include/linux/notifier.h
@@ -161,20 +161,19 @@ extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
 
 extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
 		unsigned long val, void *v);
-extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
-	unsigned long val, void *v, int nr_to_call, int *nr_calls);
 extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
 		unsigned long val, void *v);
-extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
-	unsigned long val, void *v, int nr_to_call, int *nr_calls);
 extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
 		unsigned long val, void *v);
-extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,
-	unsigned long val, void *v, int nr_to_call, int *nr_calls);
 extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
 		unsigned long val, void *v);
-extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
-	unsigned long val, void *v, int nr_to_call, int *nr_calls);
+
+extern int atomic_notifier_call_chain_robust(struct atomic_notifier_head *nh,
+		unsigned long val_up, unsigned long val_down, void *v);
+extern int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
+		unsigned long val_up, unsigned long val_down, void *v);
+extern int raw_notifier_call_chain_robust(struct raw_notifier_head *nh,
+		unsigned long val_up, unsigned long val_down, void *v);
 
 #define NOTIFY_DONE		0x0000		/* Don't care */
 #define NOTIFY_OK		0x0001		/* Suits me */
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c
index 44a259338e33d..f7e1d0eccdbc6 100644
--- a/kernel/cpu_pm.c
+++ b/kernel/cpu_pm.c
@@ -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);
 
diff --git a/kernel/notifier.c b/kernel/notifier.c
index 84c987dfbe036..1b019cbca594a 100644
--- a/kernel/notifier.c
+++ b/kernel/notifier.c
@@ -94,6 +94,34 @@ static int notifier_call_chain(struct notifier_block **nl,
 }
 NOKPROBE_SYMBOL(notifier_call_chain);
 
+/**
+ * notifier_call_chain_robust - Inform the registered notifiers about an event
+ *                              and rollback on error.
+ * @nl:		Pointer to head of the blocking notifier chain
+ * @val_up:	Value passed unmodified to the notifier function
+ * @val_down:	Value passed unmodified to the notifier function when recovering
+ *              from an error on @val_up
+ * @v		Pointer passed unmodified to the notifier function
+ *
+ * NOTE:	It is important the @nl chain doesn't change between the two
+ *		invocations of notifier_call_chain() such that we visit the
+ *		exact same notifier callbacks; this rules out any RCU usage.
+ *
+ * Returns:	the return value of the @val_up call.
+ */
+static int notifier_call_chain_robust(struct notifier_block **nl,
+				     unsigned long val_up, unsigned long val_down,
+				     void *v)
+{
+	int ret, nr = 0;
+
+	ret = notifier_call_chain(nl, val_up, v, -1, &nr);
+	if (ret & NOTIFY_STOP_MASK)
+		notifier_call_chain(nl, val_down, v, nr-1, NULL);
+
+	return ret;
+}
+
 /*
  *	Atomic notifier chain routines.  Registration and unregistration
  *	use a spinlock, and call_chain is synchronized by RCU (no locks).
@@ -144,13 +172,30 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
 }
 EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
 
+int atomic_notifier_call_chain_robust(struct atomic_notifier_head *nh,
+		unsigned long val_up, unsigned long val_down, void *v)
+{
+	unsigned long flags;
+	int ret;
+
+	/*
+	 * Musn't use RCU; because then the notifier list can
+	 * change between the up and down traversal.
+	 */
+	spin_lock_irqsave(&nh->lock, flags);
+	ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
+	spin_unlock_irqrestore(&nh->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(atomic_notifier_call_chain_robust);
+NOKPROBE_SYMBOL(atomic_notifier_call_chain_robust);
+
 /**
- *	__atomic_notifier_call_chain - Call functions in an atomic notifier chain
+ *	atomic_notifier_call_chain - Call functions in an atomic notifier chain
  *	@nh: Pointer to head of the atomic notifier chain
  *	@val: Value passed unmodified to notifier function
  *	@v: Pointer passed unmodified to notifier function
- *	@nr_to_call: See the comment for notifier_call_chain.
- *	@nr_calls: See the comment for notifier_call_chain.
  *
  *	Calls each function in a notifier chain in turn.  The functions
  *	run in an atomic context, so they must not block.
@@ -163,24 +208,16 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
  *	Otherwise the return value is the return value
  *	of the last notifier function called.
  */
-int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
-				 unsigned long val, void *v,
-				 int nr_to_call, int *nr_calls)
+int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
+			       unsigned long val, void *v)
 {
 	int ret;
 
 	rcu_read_lock();
-	ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
+	ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
 	rcu_read_unlock();
-	return ret;
-}
-EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
-NOKPROBE_SYMBOL(__atomic_notifier_call_chain);
 
-int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
-			       unsigned long val, void *v)
-{
-	return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
 NOKPROBE_SYMBOL(atomic_notifier_call_chain);
@@ -250,13 +287,30 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
 }
 EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
 
+int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
+		unsigned long val_up, unsigned long val_down, void *v)
+{
+	int ret = NOTIFY_DONE;
+
+	/*
+	 * We check the head outside the lock, but if this access is
+	 * racy then it does not matter what the result of the test
+	 * is, we re-check the list after having taken the lock anyway:
+	 */
+	if (rcu_access_pointer(nh->head)) {
+		down_read(&nh->rwsem);
+		ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
+		up_read(&nh->rwsem);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(blocking_notifier_call_chain_robust);
+
 /**
- *	__blocking_notifier_call_chain - Call functions in a blocking notifier chain
+ *	blocking_notifier_call_chain - Call functions in a blocking notifier chain
  *	@nh: Pointer to head of the blocking notifier chain
  *	@val: Value passed unmodified to notifier function
  *	@v: Pointer passed unmodified to notifier function
- *	@nr_to_call: See comment for notifier_call_chain.
- *	@nr_calls: See comment for notifier_call_chain.
  *
  *	Calls each function in a notifier chain in turn.  The functions
  *	run in a process context, so they are allowed to block.
@@ -268,9 +322,8 @@ EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
  *	Otherwise the return value is the return value
  *	of the last notifier function called.
  */
-int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
-				   unsigned long val, void *v,
-				   int nr_to_call, int *nr_calls)
+int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
+		unsigned long val, void *v)
 {
 	int ret = NOTIFY_DONE;
 
@@ -281,19 +334,11 @@ int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
 	 */
 	if (rcu_access_pointer(nh->head)) {
 		down_read(&nh->rwsem);
-		ret = notifier_call_chain(&nh->head, val, v, nr_to_call,
-					nr_calls);
+		ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
 		up_read(&nh->rwsem);
 	}
 	return ret;
 }
-EXPORT_SYMBOL_GPL(__blocking_notifier_call_chain);
-
-int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
-		unsigned long val, void *v)
-{
-	return __blocking_notifier_call_chain(nh, val, v, -1, NULL);
-}
 EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
 
 /*
@@ -335,13 +380,18 @@ int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
 }
 EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
 
+int raw_notifier_call_chain_robust(struct raw_notifier_head *nh,
+		unsigned long val_up, unsigned long val_down, void *v)
+{
+	return notifier_call_chain_robust(&nh->head, val_up, val_down, v);
+}
+EXPORT_SYMBOL_GPL(raw_notifier_call_chain_robust);
+
 /**
- *	__raw_notifier_call_chain - Call functions in a raw notifier chain
+ *	raw_notifier_call_chain - Call functions in a raw notifier chain
  *	@nh: Pointer to head of the raw notifier chain
  *	@val: Value passed unmodified to notifier function
  *	@v: Pointer passed unmodified to notifier function
- *	@nr_to_call: See comment for notifier_call_chain.
- *	@nr_calls: See comment for notifier_call_chain
  *
  *	Calls each function in a notifier chain in turn.  The functions
  *	run in an undefined context.
@@ -354,18 +404,10 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
  *	Otherwise the return value is the return value
  *	of the last notifier function called.
  */
-int __raw_notifier_call_chain(struct raw_notifier_head *nh,
-			      unsigned long val, void *v,
-			      int nr_to_call, int *nr_calls)
-{
-	return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
-}
-EXPORT_SYMBOL_GPL(__raw_notifier_call_chain);
-
 int raw_notifier_call_chain(struct raw_notifier_head *nh,
 		unsigned long val, void *v)
 {
-	return __raw_notifier_call_chain(nh, val, v, -1, NULL);
+	return notifier_call_chain(&nh->head, val, v, -1, NULL);
 }
 EXPORT_SYMBOL_GPL(raw_notifier_call_chain);
 
@@ -437,12 +479,10 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
 EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
 
 /**
- *	__srcu_notifier_call_chain - Call functions in an SRCU notifier chain
+ *	srcu_notifier_call_chain - Call functions in an SRCU notifier chain
  *	@nh: Pointer to head of the SRCU notifier chain
  *	@val: Value passed unmodified to notifier function
  *	@v: Pointer passed unmodified to notifier function
- *	@nr_to_call: See comment for notifier_call_chain.
- *	@nr_calls: See comment for notifier_call_chain
  *
  *	Calls each function in a notifier chain in turn.  The functions
  *	run in a process context, so they are allowed to block.
@@ -454,25 +494,17 @@ EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
  *	Otherwise the return value is the return value
  *	of the last notifier function called.
  */
-int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
-			       unsigned long val, void *v,
-			       int nr_to_call, int *nr_calls)
+int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
+		unsigned long val, void *v)
 {
 	int ret;
 	int idx;
 
 	idx = srcu_read_lock(&nh->srcu);
-	ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
+	ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
 	srcu_read_unlock(&nh->srcu, idx);
 	return ret;
 }
-EXPORT_SYMBOL_GPL(__srcu_notifier_call_chain);
-
-int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
-		unsigned long val, void *v)
-{
-	return __srcu_notifier_call_chain(nh, val, v, -1, NULL);
-}
 EXPORT_SYMBOL_GPL(srcu_notifier_call_chain);
 
 /**
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index e7aa57fb2fdc3..1dee70815f3cd 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -706,8 +706,8 @@ static int load_image_and_restore(void)
  */
 int hibernate(void)
 {
-	int error, nr_calls = 0;
 	bool snapshot_test = false;
+	int error;
 
 	if (!hibernation_available()) {
 		pm_pr_dbg("Hibernation not available.\n");
@@ -723,11 +723,9 @@ int hibernate(void)
 
 	pr_info("hibernation entry\n");
 	pm_prepare_console();
-	error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
-	if (error) {
-		nr_calls--;
-		goto Exit;
-	}
+	error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION);
+	if (error)
+		goto Restore;
 
 	ksys_sync_helper();
 
@@ -785,7 +783,8 @@ int hibernate(void)
 	/* Don't bother checking whether freezer_test_done is true */
 	freezer_test_done = false;
  Exit:
-	__pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
+	pm_notifier_call_chain(PM_POST_HIBERNATION);
+ Restore:
 	pm_restore_console();
 	hibernate_release();
  Unlock:
@@ -804,7 +803,7 @@ int hibernate(void)
  */
 int hibernate_quiet_exec(int (*func)(void *data), void *data)
 {
-	int error, nr_calls = 0;
+	int error;
 
 	lock_system_sleep();
 
@@ -815,11 +814,9 @@ int hibernate_quiet_exec(int (*func)(void *data), void *data)
 
 	pm_prepare_console();
 
-	error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
-	if (error) {
-		nr_calls--;
-		goto exit;
-	}
+	error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION);
+	if (error)
+		goto restore;
 
 	error = freeze_processes();
 	if (error)
@@ -880,8 +877,9 @@ int hibernate_quiet_exec(int (*func)(void *data), void *data)
 	thaw_processes();
 
 exit:
-	__pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
+	pm_notifier_call_chain(PM_POST_HIBERNATION);
 
+restore:
 	pm_restore_console();
 
 	hibernate_release();
@@ -910,7 +908,7 @@ EXPORT_SYMBOL_GPL(hibernate_quiet_exec);
  */
 static int software_resume(void)
 {
-	int error, nr_calls = 0;
+	int error;
 
 	/*
 	 * If the user said "noresume".. bail out early.
@@ -997,11 +995,9 @@ static int software_resume(void)
 
 	pr_info("resume from hibernation\n");
 	pm_prepare_console();
-	error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
-	if (error) {
-		nr_calls--;
-		goto Close_Finish;
-	}
+	error = pm_notifier_call_chain_robust(PM_RESTORE_PREPARE, PM_POST_RESTORE);
+	if (error)
+		goto Restore;
 
 	pm_pr_dbg("Preparing processes for hibernation restore.\n");
 	error = freeze_processes();
@@ -1017,7 +1013,8 @@ static int software_resume(void)
 	error = load_image_and_restore();
 	thaw_processes();
  Finish:
-	__pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
+	pm_notifier_call_chain(PM_POST_RESTORE);
+ Restore:
 	pm_restore_console();
 	pr_info("resume failed (%d)\n", error);
 	hibernate_release();
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 40f86ec4ab30d..0aefd6f57e0ac 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -80,18 +80,18 @@ int unregister_pm_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(unregister_pm_notifier);
 
-int __pm_notifier_call_chain(unsigned long val, int nr_to_call, int *nr_calls)
+int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down)
 {
 	int ret;
 
-	ret = __blocking_notifier_call_chain(&pm_chain_head, val, NULL,
-						nr_to_call, nr_calls);
+	ret = blocking_notifier_call_chain_robust(&pm_chain_head, val_up, val_down, NULL);
 
 	return notifier_to_errno(ret);
 }
+
 int pm_notifier_call_chain(unsigned long val)
 {
-	return __pm_notifier_call_chain(val, -1, NULL);
+	return blocking_notifier_call_chain(&pm_chain_head, val, NULL);
 }
 
 /* If set, devices may be suspended and resumed asynchronously. */
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 32fc89ac96c30..24f12d534515f 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -210,8 +210,7 @@ static inline void suspend_test_finish(const char *label) {}
 
 #ifdef CONFIG_PM_SLEEP
 /* kernel/power/main.c */
-extern int __pm_notifier_call_chain(unsigned long val, int nr_to_call,
-				    int *nr_calls);
+extern int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down);
 extern int pm_notifier_call_chain(unsigned long val);
 #endif
 
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 8b1bb5ee7e5d6..32391acc806bf 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -342,18 +342,16 @@ static int suspend_test(int level)
  */
 static int suspend_prepare(suspend_state_t state)
 {
-	int error, nr_calls = 0;
+	int error;
 
 	if (!sleep_state_supported(state))
 		return -EPERM;
 
 	pm_prepare_console();
 
-	error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
-	if (error) {
-		nr_calls--;
-		goto Finish;
-	}
+	error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND);
+	if (error)
+		goto Restore;
 
 	trace_suspend_resume(TPS("freeze_processes"), 0, true);
 	error = suspend_freeze_processes();
@@ -363,8 +361,8 @@ static int suspend_prepare(suspend_state_t state)
 
 	suspend_stats.failed_freeze++;
 	dpm_save_failed_step(SUSPEND_FREEZE);
- Finish:
-	__pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL);
+	pm_notifier_call_chain(PM_POST_SUSPEND);
+ Restore:
 	pm_restore_console();
 	return error;
 }
diff --git a/kernel/power/user.c b/kernel/power/user.c
index d5eedc2baa2a1..047f598f89a5c 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -46,7 +46,7 @@ int is_hibernate_resume_dev(const struct inode *bd_inode)
 static int snapshot_open(struct inode *inode, struct file *filp)
 {
 	struct snapshot_data *data;
-	int error, nr_calls = 0;
+	int error;
 
 	if (!hibernation_available())
 		return -EPERM;
@@ -73,9 +73,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
 			swap_type_of(swsusp_resume_device, 0, NULL) : -1;
 		data->mode = O_RDONLY;
 		data->free_bitmaps = false;
-		error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
-		if (error)
-			__pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL);
+		error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION);
 	} else {
 		/*
 		 * Resuming.  We may need to wait for the image device to
@@ -85,15 +83,11 @@ static int snapshot_open(struct inode *inode, struct file *filp)
 
 		data->swap = -1;
 		data->mode = O_WRONLY;
-		error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
+		error = pm_notifier_call_chain_robust(PM_RESTORE_PREPARE, PM_POST_RESTORE);
 		if (!error) {
 			error = create_basic_memory_bitmaps();
 			data->free_bitmaps = !error;
-		} else
-			nr_calls--;
-
-		if (error)
-			__pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
+		}
 	}
 	if (error)
 		hibernate_release();
diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
index 46ff97e909c6f..1bc36a1db14f6 100755
--- a/tools/power/pm-graph/sleepgraph.py
+++ b/tools/power/pm-graph/sleepgraph.py
@@ -171,7 +171,7 @@ class SystemValues:
 	tracefuncs = {
 		'sys_sync': {},
 		'ksys_sync': {},
-		'__pm_notifier_call_chain': {},
+		'pm_notifier_call_chain_robust': {},
 		'pm_prepare_console': {},
 		'pm_notifier_call_chain': {},
 		'freeze_processes': {},
-- 
2.25.1


  parent reply	other threads:[~2020-10-18 19:59 UTC|newest]

Thread overview: 126+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-18 19:16 [PATCH AUTOSEL 5.9 001/111] md/bitmap: fix memory leak of temporary bitmap Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 002/111] block: ratelimit handle_bad_sector() message Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 003/111] x86/dumpstack: Fix misleading instruction pointer error message Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 004/111] crypto: ccp - fix error handling Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 005/111] x86/asm: Replace __force_order with a memory clobber Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 006/111] x86/mce: Add Skylake quirk for patrol scrub reported errors Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 007/111] media: firewire: fix memory leak Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 008/111] media: ati_remote: sanity check for both endpoints Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 009/111] media: st-delta: Fix reference count leak in delta_run_work Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 010/111] media: sti: Fix reference count leaks Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 011/111] media: exynos4-is: Fix several reference count leaks due to pm_runtime_get_sync Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 012/111] media: exynos4-is: Fix a reference count leak " Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 013/111] media: exynos4-is: Fix a reference count leak Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 014/111] media: vsp1: Fix runtime PM imbalance on error Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 015/111] media: platform: s3c-camif: " Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 016/111] media: platform: sti: hva: " Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 017/111] media: bdisp: " Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 018/111] media: media/pci: prevent memory leak in bttv_probe Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 019/111] x86/mce: Annotate mce_rd/wrmsrl() with noinstr Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 020/111] crypto: hisilicon - fixed memory allocation error Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 021/111] spi: fsi: Fix clock running too fast Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 022/111] blk-mq: always allow reserved allocation in hctx_may_queue Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 023/111] x86/mce: Make mce_rdmsrl() panic on an inaccessible MSR Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 024/111] media: rcar_drif: Fix fwnode reference leak when parsing DT Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 025/111] media: uvcvideo: Ensure all probed info is returned to v4l2 Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 026/111] seccomp: kill process instead of thread for unknown actions Sasha Levin
2020-10-19 23:28   ` Kees Cook
2020-10-19 23:33     ` Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 027/111] mmc: sdio: Check for CISTPL_VERS_1 buffer size Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 028/111] media: saa7134: avoid a shift overflow Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 029/111] media: atomisp: fix memleak in ia_css_stream_create Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 030/111] media: venus: fixes for list corruption Sasha Levin
2020-10-18 19:16 ` Sasha Levin [this message]
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 032/111] fs: dlm: fix configfs memory leak Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 033/111] media: venus: core: Fix error handling in probe Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 034/111] media: venus: core: Fix runtime PM imbalance in venus_probe Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 035/111] ipv6/icmp: l3mdev: Perform icmp error route lookup on source device routing table (v2) Sasha Levin
2020-10-18 19:40   ` Jakub Kicinski
2020-10-19  1:40     ` David Ahern
2020-10-19 11:52       ` Sasha Levin
2020-10-19 15:33         ` Jakub Kicinski
2020-10-19 15:50           ` Sasha Levin
2020-10-19 13:19       ` Mathieu Desnoyers
2020-10-19 15:51         ` Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 036/111] ntfs: add check for mft record size in superblock Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 037/111] ip_gre: set dev->hard_header_len and dev->needed_headroom properly Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 038/111] mac80211: handle lack of sband->bitrates in rates Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 039/111] staging: wfx: fix handling of MMIC error Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 040/111] libbpf: Close map fd if init map slots failed Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 041/111] bpf: Use raw_spin_trylock() for pcpu_freelist_push/pop in NMI Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 042/111] PM: hibernate: remove the bogus call to get_gendisk() in software_resume() Sasha Levin
2020-10-18 19:16 ` [PATCH AUTOSEL 5.9 043/111] scsi: mvumi: Fix error return in mvumi_io_attach() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 044/111] scsi: target: core: Add CONTROL field for trace events Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 045/111] mic: vop: copy data to kernel space then write to io memory Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 046/111] misc: vop: add round_up(x,4) for vring_size to avoid kernel panic Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 047/111] usb: dwc3: Add splitdisable quirk for Hisilicon Kirin Soc Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 048/111] usb: gadget: function: printer: fix use-after-free in __lock_acquire Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 049/111] rtw88: pci: Power cycle device during shutdown Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 050/111] udf: Limit sparing table size Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 051/111] udf: Avoid accessing uninitialized data on failed inode read Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 052/111] rtw88: increse the size of rx buffer size Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 053/111] selftests/bpf: Fix overflow tests to reflect iter size increase Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 054/111] USB: cdc-acm: handle broken union descriptors Sasha Levin
2020-10-19  7:02   ` Johan Hovold
2020-10-19  7:10     ` Johan Hovold
2020-10-19 12:44       ` Konstantin Ryabitsev
2020-10-19  7:13     ` Greg Kroah-Hartman
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 055/111] mt76: mt7915: do not do any work in napi poll after calling napi_complete_done() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 056/111] usb: dwc3: simple: add support for Hikey 970 Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 057/111] habanalabs: cast to u64 before shift > 31 bits Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 058/111] can: flexcan: flexcan_chip_stop(): add error handling and propagate error value Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 059/111] HID: multitouch: Lenovo X1 Tablet Gen3 trackpoint and buttons Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 060/111] ath9k: hif_usb: fix race condition between usb_get_urb() and usb_kill_anchored_urbs() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 061/111] drm/panfrost: add Amlogic GPU integration quirks Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 062/111] drm/panfrost: add amlogic reset quirk callback Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 063/111] drm/panfrost: add support for vendor quirk Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 064/111] bpf: Limit caller's stack depth 256 for subprogs with tailcalls Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 065/111] dma-direct: Fix potential NULL pointer dereference Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 066/111] misc: rtsx: Fix memory leak in rtsx_pci_probe Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 067/111] reiserfs: only call unlock_new_inode() if I_NEW Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 068/111] opp: Prevent memory leak in dev_pm_opp_attach_genpd() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 069/111] xfs: make sure the rt allocator doesn't run off the end Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 070/111] usb: ohci: Default to per-port over-current protection Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 071/111] drm: fix double free for gbo in drm_gem_vram_init and drm_gem_vram_create Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 072/111] Bluetooth: Only mark socket zapped after unlocking Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 073/111] drm/msm/a6xx: fix a potential overflow issue Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 074/111] drm/xlnx: Use devm_drm_dev_alloc Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 075/111] iomap: fix WARN_ON_ONCE() from unprivileged users Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 076/111] scsi: ibmvfc: Fix error return in ibmvfc_probe() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 077/111] scsi: qla2xxx: Warn if done() or free() are called on an already freed srb Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 078/111] selftests/bpf: Fix test_sysctl_loop{1, 2} failure due to clang change Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 079/111] soundwire: cadence: fix race condition between suspend and Slave device alerts Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 080/111] brcmsmac: fix memory leak in wlc_phy_attach_lcnphy Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 081/111] rtl8xxxu: prevent potential memory leak Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 082/111] Fix use after free in get_capset_info callback Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 083/111] HID: ite: Add USB id match for Acer One S1003 keyboard dock Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 084/111] scsi: qedf: Return SUCCESS if stale rport is encountered Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 085/111] scsi: qedi: Mark all connections for recovery on link down event Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 086/111] scsi: qedi: Protect active command list to avoid list corruption Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 087/111] scsi: qedi: Fix list_del corruption while removing active I/O Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 088/111] fbmem: add margin check to fb_check_caps() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 089/111] tty: ipwireless: fix error handling Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 090/111] drm/amd/display: Fix a list corruption Sasha Levin
2020-10-19 12:40   ` Alex Deucher
2020-10-25 23:48     ` Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 091/111] Bluetooth: btusb: Fix memleak in btusb_mtk_submit_wmt_recv_urb Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 092/111] ipvs: Fix uninit-value in do_ip_vs_set_ctl() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 093/111] reiserfs: Fix memory leak in reiserfs_parse_options() Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 094/111] s390/qeth: strictly order bridge address events Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 095/111] mwifiex: don't call del_timer_sync() on uninitialized timer Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 096/111] ALSA: hda/ca0132 - Add AE-7 microphone selection commands Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 097/111] ALSA: hda/ca0132 - Add new quirk ID for SoundBlaster AE-7 Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 098/111] ASoC: SOF: Add topology filename override based on dmi data match Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 099/111] ASoC: Intel: sof_rt5682: override quirk data for tgl_max98373_rt5682 Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 100/111] scsi: smartpqi: Avoid crashing kernel for controller issues Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 101/111] brcm80211: fix possible memleak in brcmf_proto_msgbuf_attach Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 102/111] usb: core: Solve race condition in anchor cleanup functions Sasha Levin
2020-10-18 19:17 ` [PATCH AUTOSEL 5.9 103/111] soundwire: intel: reinitialize IP+DSP in .prepare(), but only when resuming Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 104/111] scsi: ufs: ufs-qcom: Fix race conditions caused by ufs_qcom_testbus_config() Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 105/111] drm/amd/display: Screen corruption on dual displays (DP+USB-C) Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 106/111] dmaengine: dw: Add DMA-channels mask cell support Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 107/111] dmaengine: dw: Activate FIFO-mode for memory peripherals only Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 108/111] drm/hisilicon: Code refactoring for hibmc_drv_de Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 109/111] drm/amd/display: Disconnect pipe separetely when disable pipe split Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 110/111] drm/panfrost: perfcnt: fix ref count leak in panfrost_perfcnt_enable_locked Sasha Levin
2020-10-18 19:18 ` [PATCH AUTOSEL 5.9 111/111] ath10k: check idx validity in __ath10k_htt_rx_ring_fill_n() Sasha Levin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201018191807.4052726-31-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=peterz@infradead.org \
    --cc=rafael.j.wysocki@intel.com \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).