diff options
| author | Rohit Gupta <rohgup@codeaurora.org> | 2016-05-19 17:42:04 -0700 |
|---|---|---|
| committer | Kyle Yan <kyan@codeaurora.org> | 2016-06-01 15:27:34 -0700 |
| commit | 7668a3726f0551a59865f7cb3704527fa4080c95 (patch) | |
| tree | 14513fc2890fc7abe835ff792f6754298507b7c3 | |
| parent | 8b2d1771d2a712a4718a5141bedd4fb031b0b91e (diff) | |
PM / devfreq: memlat: Prevent deadlock with hotplug in start_hwmon
When start_hwmon() runs with another thread trying to hotplug a
CPU the two threads can enter a deadlock situation as follows:
Thread A (start_hwmon()) Thread B (CPU down)
get_online_cpus()
|
atomic_inc(&cpu_hotplug.refcount)
CPU down
|
mutex_lock(&cpu_add_remove_lock)
|
cpu_hotplug_begin() waits on
cpu_hotplug.refcount to reset
register_cpu_notifier()
|
mutex_lock(&cpu_add_remove_lock)
With this change the notifers are registered and unregistered per
device rather than having a common notifier block for all the memlat
devices and unregistration only happens on stop_hwmon. This makes it
possible to move the registration outside the hotplug lock without
any race between multiple memlat devices.
Change-Id: I6ad561fe4967042e45190aea2c9b7fcfe05bafdd
Signed-off-by: Rohit Gupta <rohgup@codeaurora.org>
| -rw-r--r-- | drivers/devfreq/arm-memlat-mon.c | 46 |
1 files changed, 7 insertions, 39 deletions
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c index d69c2186c5da..370d7d95042b 100644 --- a/drivers/devfreq/arm-memlat-mon.c +++ b/drivers/devfreq/arm-memlat-mon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -57,14 +57,9 @@ static DEFINE_PER_CPU(struct memlat_hwmon_data, pm_data); struct cpu_grp_info { cpumask_t cpus; struct memlat_hwmon hw; + struct notifier_block arm_memlat_cpu_notif; }; -static DEFINE_MUTEX(notif_mutex); -static unsigned int notif_refcount; -static void notif_unregister_work_fn(struct work_struct *work); -static void hotplug_notif_unregister(void); -static DECLARE_WORK(notif_unregister_work, notif_unregister_work_fn); - static unsigned long compute_freq(struct memlat_hwmon_data *hw_data, unsigned long cyc_cnt) { @@ -155,7 +150,7 @@ static void stop_hwmon(struct memlat_hwmon *hw) for_each_cpu(cpu, &cpu_grp->cpus) { hw_data = &per_cpu(pm_data, cpu); if (hw_data->init_pending) - hotplug_notif_unregister(); + hw_data->init_pending = false; else delete_events(hw_data); @@ -167,6 +162,7 @@ static void stop_hwmon(struct memlat_hwmon *hw) } put_online_cpus(); + unregister_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif); } static struct perf_event_attr *alloc_attr(void) @@ -239,40 +235,10 @@ static int arm_memlat_cpu_callback(struct notifier_block *nb, pr_warn("Failed to create perf event for CPU%lu\n", cpu); hw_data->init_pending = false; - hotplug_notif_unregister(); return NOTIFY_OK; } -static struct notifier_block arm_memlat_cpu_notifier = { - .notifier_call = arm_memlat_cpu_callback, -}; - -static void notif_unregister_work_fn(struct work_struct *work) -{ - unregister_cpu_notifier(&arm_memlat_cpu_notifier); -} - -static void hotplug_notif_register(void) -{ - mutex_lock(¬if_mutex); - if (!notif_refcount) { - flush_work(¬if_unregister_work); - register_cpu_notifier(&arm_memlat_cpu_notifier); - } - notif_refcount++; - mutex_unlock(¬if_mutex); -} - -static void hotplug_notif_unregister(void) -{ - mutex_lock(¬if_mutex); - notif_refcount--; - if (!notif_refcount) - schedule_work(¬if_unregister_work); - mutex_unlock(¬if_mutex); -} - static int start_hwmon(struct memlat_hwmon *hw) { int cpu, ret = 0; @@ -280,13 +246,14 @@ static int start_hwmon(struct memlat_hwmon *hw) struct cpu_grp_info *cpu_grp = container_of(hw, struct cpu_grp_info, hw); + register_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif); + get_online_cpus(); for_each_cpu(cpu, &cpu_grp->cpus) { hw_data = &per_cpu(pm_data, cpu); ret = set_events(hw_data, cpu); if (ret) { if (!cpu_online(cpu)) { - hotplug_notif_register(); hw_data->init_pending = true; ret = 0; } else { @@ -337,6 +304,7 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev) cpu_grp = devm_kzalloc(dev, sizeof(*cpu_grp), GFP_KERNEL); if (!cpu_grp) return -ENOMEM; + cpu_grp->arm_memlat_cpu_notif.notifier_call = arm_memlat_cpu_callback; hw = &cpu_grp->hw; hw->dev = dev; |
