hc
2023-12-09 b22da3d8526a935aa31e086e63f60ff3246cb61c
kernel/drivers/base/power/wakeup.c
....@@ -1,10 +1,10 @@
1
+// SPDX-License-Identifier: GPL-2.0
12 /*
23 * drivers/base/power/wakeup.c - System wakeup events framework
34 *
45 * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
5
- *
6
- * This file is released under the GPLv2.
76 */
7
+#define pr_fmt(fmt) "PM: " fmt
88
99 #include <linux/device.h>
1010 #include <linux/slab.h>
....@@ -27,6 +27,9 @@
2727 #define pm_suspend_target_state (PM_SUSPEND_ON)
2828 #endif
2929
30
+#define list_for_each_entry_rcu_locked(pos, head, member) \
31
+ list_for_each_entry_rcu(pos, head, member, \
32
+ srcu_read_lock_held(&wakeup_srcu))
3033 /*
3134 * If set, the suspend/hibernate code will abort transitions to a sleep state
3235 * if wakeup events are registered during or immediately before the transition.
....@@ -34,7 +37,8 @@
3437 bool events_check_enabled __read_mostly;
3538
3639 /* First wakeup IRQ seen by the kernel in the last cycle. */
37
-unsigned int pm_wakeup_irq __read_mostly;
40
+static unsigned int wakeup_irq[2] __read_mostly;
41
+static DEFINE_RAW_SPINLOCK(wakeup_irq_lock);
3842
3943 /* If greater than 0 and the system is suspending, terminate the suspend. */
4044 static atomic_t pm_abort_suspend __read_mostly;
....@@ -253,6 +257,60 @@
253257 EXPORT_SYMBOL_GPL(wakeup_source_unregister);
254258
255259 /**
260
+ * wakeup_sources_read_lock - Lock wakeup source list for read.
261
+ *
262
+ * Returns an index of srcu lock for struct wakeup_srcu.
263
+ * This index must be passed to the matching wakeup_sources_read_unlock().
264
+ */
265
+int wakeup_sources_read_lock(void)
266
+{
267
+ return srcu_read_lock(&wakeup_srcu);
268
+}
269
+EXPORT_SYMBOL_GPL(wakeup_sources_read_lock);
270
+
271
+/**
272
+ * wakeup_sources_read_unlock - Unlock wakeup source list.
273
+ * @idx: return value from corresponding wakeup_sources_read_lock()
274
+ */
275
+void wakeup_sources_read_unlock(int idx)
276
+{
277
+ srcu_read_unlock(&wakeup_srcu, idx);
278
+}
279
+EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock);
280
+
281
+/**
282
+ * wakeup_sources_walk_start - Begin a walk on wakeup source list
283
+ *
284
+ * Returns first object of the list of wakeup sources.
285
+ *
286
+ * Note that to be safe, wakeup sources list needs to be locked by calling
287
+ * wakeup_source_read_lock() for this.
288
+ */
289
+struct wakeup_source *wakeup_sources_walk_start(void)
290
+{
291
+ struct list_head *ws_head = &wakeup_sources;
292
+
293
+ return list_entry_rcu(ws_head->next, struct wakeup_source, entry);
294
+}
295
+EXPORT_SYMBOL_GPL(wakeup_sources_walk_start);
296
+
297
+/**
298
+ * wakeup_sources_walk_next - Get next wakeup source from the list
299
+ * @ws: Previous wakeup source object
300
+ *
301
+ * Note that to be safe, wakeup sources list needs to be locked by calling
302
+ * wakeup_source_read_lock() for this.
303
+ */
304
+struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws)
305
+{
306
+ struct list_head *ws_head = &wakeup_sources;
307
+
308
+ return list_next_or_null_rcu(ws_head, &ws->entry,
309
+ struct wakeup_source, entry);
310
+}
311
+EXPORT_SYMBOL_GPL(wakeup_sources_walk_next);
312
+
313
+/**
256314 * device_wakeup_attach - Attach a wakeup source object to a device object.
257315 * @dev: Device to handle.
258316 * @ws: Wakeup source object to attach to @dev.
....@@ -356,7 +414,7 @@
356414 int srcuidx;
357415
358416 srcuidx = srcu_read_lock(&wakeup_srcu);
359
- list_for_each_entry_rcu(ws, &wakeup_sources, entry)
417
+ list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry)
360418 dev_pm_arm_wake_irq(ws->wakeirq);
361419 srcu_read_unlock(&wakeup_srcu, srcuidx);
362420 }
....@@ -372,7 +430,7 @@
372430 int srcuidx;
373431
374432 srcuidx = srcu_read_lock(&wakeup_srcu);
375
- list_for_each_entry_rcu(ws, &wakeup_sources, entry)
433
+ list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry)
376434 dev_pm_disarm_wake_irq(ws->wakeirq);
377435 srcu_read_unlock(&wakeup_srcu, srcuidx);
378436 }
....@@ -798,7 +856,7 @@
798856 EXPORT_SYMBOL_GPL(pm_wakeup_ws_event);
799857
800858 /**
801
- * pm_wakeup_event - Notify the PM core of a wakeup event.
859
+ * pm_wakeup_dev_event - Notify the PM core of a wakeup event.
802860 * @dev: Device the wakeup event is related to.
803861 * @msec: Anticipated event processing time (in milliseconds).
804862 * @hard: If set, abort suspends in progress and wake up from suspend-to-idle.
....@@ -856,9 +914,9 @@
856914 struct wakeup_source *last_activity_ws = NULL;
857915
858916 srcuidx = srcu_read_lock(&wakeup_srcu);
859
- list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
917
+ list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) {
860918 if (ws->active) {
861
- pr_debug("active wakeup source: %s\n", ws->name);
919
+ pm_pr_dbg("active wakeup source: %s\n", ws->name);
862920 active = 1;
863921 } else if (!active &&
864922 (!last_activity_ws ||
....@@ -869,7 +927,7 @@
869927 }
870928
871929 if (!active && last_activity_ws)
872
- pr_debug("last active wakeup source: %s\n",
930
+ pm_pr_dbg("last active wakeup source: %s\n",
873931 last_activity_ws->name);
874932 srcu_read_unlock(&wakeup_srcu, srcuidx);
875933 }
....@@ -900,6 +958,8 @@
900958 raw_spin_unlock_irqrestore(&events_lock, flags);
901959
902960 if (ret) {
961
+ pm_pr_dbg("Wakeup pending, aborting suspend\n");
962
+ pm_print_active_wakeup_sources();
903963 pm_get_active_wakeup_sources(suspend_abort,
904964 MAX_SUSPEND_ABORT_LEN);
905965 log_suspend_abort_reason(suspend_abort);
....@@ -921,16 +981,39 @@
921981 atomic_dec_if_positive(&pm_abort_suspend);
922982 }
923983
924
-void pm_wakeup_clear(bool reset)
984
+void pm_wakeup_clear(unsigned int irq_number)
925985 {
926
- pm_wakeup_irq = 0;
927
- if (reset)
986
+ raw_spin_lock_irq(&wakeup_irq_lock);
987
+
988
+ if (irq_number && wakeup_irq[0] == irq_number)
989
+ wakeup_irq[0] = wakeup_irq[1];
990
+ else
991
+ wakeup_irq[0] = 0;
992
+
993
+ wakeup_irq[1] = 0;
994
+
995
+ raw_spin_unlock_irq(&wakeup_irq_lock);
996
+
997
+ if (!irq_number)
928998 atomic_set(&pm_abort_suspend, 0);
929999 }
9301000
9311001 void pm_system_irq_wakeup(unsigned int irq_number)
9321002 {
933
- if (pm_wakeup_irq == 0) {
1003
+ unsigned long flags;
1004
+
1005
+ raw_spin_lock_irqsave(&wakeup_irq_lock, flags);
1006
+
1007
+ if (wakeup_irq[0] == 0)
1008
+ wakeup_irq[0] = irq_number;
1009
+ else if (wakeup_irq[1] == 0)
1010
+ wakeup_irq[1] = irq_number;
1011
+ else
1012
+ irq_number = 0;
1013
+
1014
+ raw_spin_unlock_irqrestore(&wakeup_irq_lock, flags);
1015
+
1016
+ if (irq_number) {
9341017 struct irq_desc *desc;
9351018 const char *name = "null";
9361019
....@@ -942,10 +1025,13 @@
9421025
9431026 log_irq_wakeup_reason(irq_number);
9441027 pr_warn("%s: %d triggered %s\n", __func__, irq_number, name);
945
-
946
- pm_wakeup_irq = irq_number;
9471028 pm_system_wakeup();
9481029 }
1030
+}
1031
+
1032
+unsigned int pm_wakeup_irq(void)
1033
+{
1034
+ return wakeup_irq[0];
9491035 }
9501036
9511037 /**
....@@ -1022,7 +1108,7 @@
10221108 int srcuidx;
10231109
10241110 srcuidx = srcu_read_lock(&wakeup_srcu);
1025
- list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
1111
+ list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) {
10261112 spin_lock_irq(&ws->lock);
10271113 if (ws->autosleep_enabled != set) {
10281114 ws->autosleep_enabled = set;
....@@ -1038,8 +1124,6 @@
10381124 srcu_read_unlock(&wakeup_srcu, srcuidx);
10391125 }
10401126 #endif /* CONFIG_PM_AUTOSLEEP */
1041
-
1042
-static struct dentry *wakeup_sources_stats_dentry;
10431127
10441128 /**
10451129 * print_wakeup_source_stats - Print wakeup source statistics information.
....@@ -1103,7 +1187,7 @@
11031187 }
11041188
11051189 *srcuidx = srcu_read_lock(&wakeup_srcu);
1106
- list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
1190
+ list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) {
11071191 if (n-- <= 0)
11081192 return ws;
11091193 }
....@@ -1123,6 +1207,9 @@
11231207 next_ws = ws;
11241208 break;
11251209 }
1210
+
1211
+ if (!next_ws)
1212
+ print_wakeup_source_stats(m, &deleted_ws);
11261213
11271214 return next_ws;
11281215 }
....@@ -1170,8 +1257,8 @@
11701257
11711258 static int __init wakeup_sources_debugfs_init(void)
11721259 {
1173
- wakeup_sources_stats_dentry = debugfs_create_file("wakeup_sources",
1174
- S_IRUGO, NULL, NULL, &wakeup_sources_stats_fops);
1260
+ debugfs_create_file("wakeup_sources", S_IRUGO, NULL, NULL,
1261
+ &wakeup_sources_stats_fops);
11751262 return 0;
11761263 }
11771264