Commit 5480437f authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Greg Kroah-Hartman

PM / wakeirq: Convert to SRCU

commit ea0212f4 upstream.

The wakeirq infrastructure uses RCU to protect the list of wakeirqs. That
breaks the irq bus locking infrastructure, which is allows sleeping
functions to be called so interrupt controllers behind slow busses,
e.g. i2c, can be handled.

The wakeirq functions hold rcu_read_lock and call into irq functions, which
in case of interrupts using the irq bus locking will trigger a
might_sleep() splat.

Convert the wakeirq infrastructure to Sleepable RCU and unbreak it.

Fixes: 4990d4fe (PM / Wakeirq: Add automated device wake IRQ handling)
Reported-by: default avatarBrian Norris <briannorris@chromium.org>
Suggested-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Tested-by: default avatarTony Lindgren <tony@atomide.com>
Tested-by: default avatarBrian Norris <briannorris@chromium.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 758dc6a8
...@@ -60,6 +60,8 @@ static LIST_HEAD(wakeup_sources); ...@@ -60,6 +60,8 @@ static LIST_HEAD(wakeup_sources);
static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
DEFINE_STATIC_SRCU(wakeup_srcu);
static struct wakeup_source deleted_ws = { static struct wakeup_source deleted_ws = {
.name = "deleted", .name = "deleted",
.lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
...@@ -198,7 +200,7 @@ void wakeup_source_remove(struct wakeup_source *ws) ...@@ -198,7 +200,7 @@ void wakeup_source_remove(struct wakeup_source *ws)
spin_lock_irqsave(&events_lock, flags); spin_lock_irqsave(&events_lock, flags);
list_del_rcu(&ws->entry); list_del_rcu(&ws->entry);
spin_unlock_irqrestore(&events_lock, flags); spin_unlock_irqrestore(&events_lock, flags);
synchronize_rcu(); synchronize_srcu(&wakeup_srcu);
} }
EXPORT_SYMBOL_GPL(wakeup_source_remove); EXPORT_SYMBOL_GPL(wakeup_source_remove);
...@@ -332,12 +334,12 @@ void device_wakeup_detach_irq(struct device *dev) ...@@ -332,12 +334,12 @@ void device_wakeup_detach_irq(struct device *dev)
void device_wakeup_arm_wake_irqs(void) void device_wakeup_arm_wake_irqs(void)
{ {
struct wakeup_source *ws; struct wakeup_source *ws;
int srcuidx;
rcu_read_lock(); srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) list_for_each_entry_rcu(ws, &wakeup_sources, entry)
dev_pm_arm_wake_irq(ws->wakeirq); dev_pm_arm_wake_irq(ws->wakeirq);
srcu_read_unlock(&wakeup_srcu, srcuidx);
rcu_read_unlock();
} }
/** /**
...@@ -348,12 +350,12 @@ void device_wakeup_arm_wake_irqs(void) ...@@ -348,12 +350,12 @@ void device_wakeup_arm_wake_irqs(void)
void device_wakeup_disarm_wake_irqs(void) void device_wakeup_disarm_wake_irqs(void)
{ {
struct wakeup_source *ws; struct wakeup_source *ws;
int srcuidx;
rcu_read_lock(); srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) list_for_each_entry_rcu(ws, &wakeup_sources, entry)
dev_pm_disarm_wake_irq(ws->wakeirq); dev_pm_disarm_wake_irq(ws->wakeirq);
srcu_read_unlock(&wakeup_srcu, srcuidx);
rcu_read_unlock();
} }
/** /**
...@@ -805,10 +807,10 @@ EXPORT_SYMBOL_GPL(pm_wakeup_event); ...@@ -805,10 +807,10 @@ EXPORT_SYMBOL_GPL(pm_wakeup_event);
void pm_print_active_wakeup_sources(void) void pm_print_active_wakeup_sources(void)
{ {
struct wakeup_source *ws; struct wakeup_source *ws;
int active = 0; int srcuidx, active = 0;
struct wakeup_source *last_activity_ws = NULL; struct wakeup_source *last_activity_ws = NULL;
rcu_read_lock(); srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) { list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
if (ws->active) { if (ws->active) {
pr_info("active wakeup source: %s\n", ws->name); pr_info("active wakeup source: %s\n", ws->name);
...@@ -824,7 +826,7 @@ void pm_print_active_wakeup_sources(void) ...@@ -824,7 +826,7 @@ void pm_print_active_wakeup_sources(void)
if (!active && last_activity_ws) if (!active && last_activity_ws)
pr_info("last active wakeup source: %s\n", pr_info("last active wakeup source: %s\n",
last_activity_ws->name); last_activity_ws->name);
rcu_read_unlock(); srcu_read_unlock(&wakeup_srcu, srcuidx);
} }
EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources); EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources);
...@@ -951,8 +953,9 @@ void pm_wakep_autosleep_enabled(bool set) ...@@ -951,8 +953,9 @@ void pm_wakep_autosleep_enabled(bool set)
{ {
struct wakeup_source *ws; struct wakeup_source *ws;
ktime_t now = ktime_get(); ktime_t now = ktime_get();
int srcuidx;
rcu_read_lock(); srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) { list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
spin_lock_irq(&ws->lock); spin_lock_irq(&ws->lock);
if (ws->autosleep_enabled != set) { if (ws->autosleep_enabled != set) {
...@@ -966,7 +969,7 @@ void pm_wakep_autosleep_enabled(bool set) ...@@ -966,7 +969,7 @@ void pm_wakep_autosleep_enabled(bool set)
} }
spin_unlock_irq(&ws->lock); spin_unlock_irq(&ws->lock);
} }
rcu_read_unlock(); srcu_read_unlock(&wakeup_srcu, srcuidx);
} }
#endif /* CONFIG_PM_AUTOSLEEP */ #endif /* CONFIG_PM_AUTOSLEEP */
...@@ -1027,15 +1030,16 @@ static int print_wakeup_source_stats(struct seq_file *m, ...@@ -1027,15 +1030,16 @@ static int print_wakeup_source_stats(struct seq_file *m,
static int wakeup_sources_stats_show(struct seq_file *m, void *unused) static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
{ {
struct wakeup_source *ws; struct wakeup_source *ws;
int srcuidx;
seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"
"expire_count\tactive_since\ttotal_time\tmax_time\t" "expire_count\tactive_since\ttotal_time\tmax_time\t"
"last_change\tprevent_suspend_time\n"); "last_change\tprevent_suspend_time\n");
rcu_read_lock(); srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry) list_for_each_entry_rcu(ws, &wakeup_sources, entry)
print_wakeup_source_stats(m, ws); print_wakeup_source_stats(m, ws);
rcu_read_unlock(); srcu_read_unlock(&wakeup_srcu, srcuidx);
print_wakeup_source_stats(m, &deleted_ws); print_wakeup_source_stats(m, &deleted_ws);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment