Skip to content

Commit

Permalink
PM / wakeirq: Convert to SRCU
Browse files Browse the repository at this point in the history
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: 4990d4f (PM / Wakeirq: Add automated device wake IRQ handling)
Reported-by: Brian Norris <briannorris@chromium.org>
Suggested-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Tested-by: Tony Lindgren <tony@atomide.com>
Tested-by: Brian Norris <briannorris@chromium.org>
Cc: 4.2+ <stable@vger.kernel.org> # 4.2+
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
KAGA-KOKO authored and rafaeljw committed Jun 26, 2017
1 parent c0bc126 commit ea0212f
Showing 1 changed file with 18 additions and 14 deletions.
32 changes: 18 additions & 14 deletions drivers/base/power/wakeup.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ static LIST_HEAD(wakeup_sources);

static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);

DEFINE_STATIC_SRCU(wakeup_srcu);

static struct wakeup_source deleted_ws = {
.name = "deleted",
.lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
Expand Down Expand Up @@ -198,7 +200,7 @@ void wakeup_source_remove(struct wakeup_source *ws)
spin_lock_irqsave(&events_lock, flags);
list_del_rcu(&ws->entry);
spin_unlock_irqrestore(&events_lock, flags);
synchronize_rcu();
synchronize_srcu(&wakeup_srcu);
}
EXPORT_SYMBOL_GPL(wakeup_source_remove);

Expand Down Expand Up @@ -332,12 +334,12 @@ void device_wakeup_detach_irq(struct device *dev)
void device_wakeup_arm_wake_irqs(void)
{
struct wakeup_source *ws;
int srcuidx;

rcu_read_lock();
srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry)
dev_pm_arm_wake_irq(ws->wakeirq);

rcu_read_unlock();
srcu_read_unlock(&wakeup_srcu, srcuidx);
}

/**
Expand All @@ -348,12 +350,12 @@ void device_wakeup_arm_wake_irqs(void)
void device_wakeup_disarm_wake_irqs(void)
{
struct wakeup_source *ws;
int srcuidx;

rcu_read_lock();
srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry)
dev_pm_disarm_wake_irq(ws->wakeirq);

rcu_read_unlock();
srcu_read_unlock(&wakeup_srcu, srcuidx);
}

/**
Expand Down Expand Up @@ -804,10 +806,10 @@ EXPORT_SYMBOL_GPL(pm_wakeup_dev_event);
void pm_print_active_wakeup_sources(void)
{
struct wakeup_source *ws;
int active = 0;
int srcuidx, active = 0;
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) {
if (ws->active) {
pr_debug("active wakeup source: %s\n", ws->name);
Expand All @@ -823,7 +825,7 @@ void pm_print_active_wakeup_sources(void)
if (!active && last_activity_ws)
pr_debug("last active wakeup source: %s\n",
last_activity_ws->name);
rcu_read_unlock();
srcu_read_unlock(&wakeup_srcu, srcuidx);
}
EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources);

Expand Down Expand Up @@ -950,8 +952,9 @@ void pm_wakep_autosleep_enabled(bool set)
{
struct wakeup_source *ws;
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) {
spin_lock_irq(&ws->lock);
if (ws->autosleep_enabled != set) {
Expand All @@ -965,7 +968,7 @@ void pm_wakep_autosleep_enabled(bool set)
}
spin_unlock_irq(&ws->lock);
}
rcu_read_unlock();
srcu_read_unlock(&wakeup_srcu, srcuidx);
}
#endif /* CONFIG_PM_AUTOSLEEP */

Expand Down Expand Up @@ -1026,15 +1029,16 @@ static int print_wakeup_source_stats(struct seq_file *m,
static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
{
struct wakeup_source *ws;
int srcuidx;

seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"
"expire_count\tactive_since\ttotal_time\tmax_time\t"
"last_change\tprevent_suspend_time\n");

rcu_read_lock();
srcuidx = srcu_read_lock(&wakeup_srcu);
list_for_each_entry_rcu(ws, &wakeup_sources, entry)
print_wakeup_source_stats(m, ws);
rcu_read_unlock();
srcu_read_unlock(&wakeup_srcu, srcuidx);

print_wakeup_source_stats(m, &deleted_ws);

Expand Down

0 comments on commit ea0212f

Please sign in to comment.