diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c index fb9a7402f88a..80fd26e84565 100644 --- a/drivers/clk/qcom/clk-debug.c +++ b/drivers/clk/qcom/clk-debug.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2016, 2019-2021, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -23,7 +24,9 @@ static struct clk_hw *measure; static bool debug_suspend; +static bool debug_suspend_atomic; static struct dentry *clk_debugfs_suspend; +static struct dentry *clk_debugfs_suspend_atomic; struct hw_debug_clk { struct list_head list; @@ -788,7 +791,8 @@ static int clock_debug_print_clock(struct hw_debug_clk *dclk, struct seq_file *s struct clk_hw *clk_hw; unsigned long clk_rate; bool clk_prepared, clk_enabled; - int vdd_level; + int vdd_level = 0; + bool atomic; if (!dclk || !dclk->clk_hw) { pr_err("clk param error\n"); @@ -802,13 +806,23 @@ static int clock_debug_print_clock(struct hw_debug_clk *dclk, struct seq_file *s clk = dclk->clk_hw->clk; + /* + * In order to prevent running into "scheduling while atomic" + * due to grabbing contested mutexes, avoid making any calls + * that grab a mutex in the debug_suspend path when the + * variable atomic is true. + */ + atomic = debug_suspend_atomic && !s; + do { clk_hw = __clk_get_hw(clk); if (!clk_hw) break; clk_rate = clk_hw_get_rate(clk_hw); - vdd_level = clk_list_rate_vdd_level(clk_hw, clk_rate); + + if (!atomic) + vdd_level = clk_list_rate_vdd_level(clk_hw, clk_rate); if (s) { /* @@ -847,6 +861,9 @@ static int clock_debug_print_clock(struct hw_debug_clk *dclk, struct seq_file *s clk_rate); } + if (atomic) + break; + start = " -> "; } while ((clk = clk_get_parent(clk_hw->clk))); @@ -981,6 +998,24 @@ static int clk_debug_suspend_enable_set(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(clk_debug_suspend_enable_fops, clk_debug_suspend_enable_get, clk_debug_suspend_enable_set, "%llu\n"); + +static int clk_debug_suspend_atomic_enable_get(void *data, u64 *val) +{ + *val = debug_suspend_atomic; + + return 0; +} + +static int clk_debug_suspend_atomic_enable_set(void *data, u64 val) +{ + debug_suspend_atomic = !!val; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(clk_debug_suspend_atomic_enable_fops, + clk_debug_suspend_atomic_enable_get, clk_debug_suspend_atomic_enable_set, "%llu\n"); + static void clk_hw_debug_remove(struct hw_debug_clk *dclk) { if (dclk) { @@ -1053,6 +1088,11 @@ int clk_debug_init(void) clk_debugfs_suspend = debugfs_create_file_unsafe("debug_suspend", 0644, rootdir, NULL, &clk_debug_suspend_enable_fops); + + clk_debugfs_suspend_atomic = debugfs_create_file_unsafe("debug_suspend_atomic", + 0644, rootdir, NULL, + &clk_debug_suspend_atomic_enable_fops); + dput(rootdir); if (IS_ERR(clk_debugfs_suspend)) { ret = PTR_ERR(clk_debugfs_suspend); @@ -1060,15 +1100,23 @@ int clk_debug_init(void) __func__, ret); } + if (IS_ERR(clk_debugfs_suspend_atomic)) { + ret = PTR_ERR(clk_debugfs_suspend_atomic); + pr_err("%s: unable to create clock debug_suspend_atomic debugfs directory, ret=%d\n", + __func__, ret); + } + return ret; } void clk_debug_exit(void) { debugfs_remove(clk_debugfs_suspend); + debugfs_remove(clk_debugfs_suspend_atomic); if (debug_suspend) unregister_trace_suspend_resume( clk_debug_suspend_trace_probe, NULL); + clk_debug_unregister(); }