summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRohit Gupta <rohgup@codeaurora.org>2016-05-19 17:42:04 -0700
committerKyle Yan <kyan@codeaurora.org>2016-06-01 15:27:34 -0700
commit7668a3726f0551a59865f7cb3704527fa4080c95 (patch)
tree14513fc2890fc7abe835ff792f6754298507b7c3
parent8b2d1771d2a712a4718a5141bedd4fb031b0b91e (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.c46
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(&notif_mutex);
- if (!notif_refcount) {
- flush_work(&notif_unregister_work);
- register_cpu_notifier(&arm_memlat_cpu_notifier);
- }
- notif_refcount++;
- mutex_unlock(&notif_mutex);
-}
-
-static void hotplug_notif_unregister(void)
-{
- mutex_lock(&notif_mutex);
- notif_refcount--;
- if (!notif_refcount)
- schedule_work(&notif_unregister_work);
- mutex_unlock(&notif_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;