Merge branch 'clk-rpm' into clk-fixes
- Fix a runtime PM deadlock with clk disable unused * clk-rpm: clk: Get runtime PM before walking tree for clk_summary clk: Get runtime PM before walking tree during disable_unused clk: Initialize struct clk_core kref earlier clk: Don't hold prepare_lock when calling kref_put() clk: Remove prepare_lock hold assertion in __clk_release()
This commit is contained in:
commit
754e5287c7
@ -37,6 +37,10 @@ static HLIST_HEAD(clk_root_list);
|
|||||||
static HLIST_HEAD(clk_orphan_list);
|
static HLIST_HEAD(clk_orphan_list);
|
||||||
static LIST_HEAD(clk_notifier_list);
|
static LIST_HEAD(clk_notifier_list);
|
||||||
|
|
||||||
|
/* List of registered clks that use runtime PM */
|
||||||
|
static HLIST_HEAD(clk_rpm_list);
|
||||||
|
static DEFINE_MUTEX(clk_rpm_list_lock);
|
||||||
|
|
||||||
static const struct hlist_head *all_lists[] = {
|
static const struct hlist_head *all_lists[] = {
|
||||||
&clk_root_list,
|
&clk_root_list,
|
||||||
&clk_orphan_list,
|
&clk_orphan_list,
|
||||||
@ -59,6 +63,7 @@ struct clk_core {
|
|||||||
struct clk_hw *hw;
|
struct clk_hw *hw;
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
struct hlist_node rpm_node;
|
||||||
struct device_node *of_node;
|
struct device_node *of_node;
|
||||||
struct clk_core *parent;
|
struct clk_core *parent;
|
||||||
struct clk_parent_map *parents;
|
struct clk_parent_map *parents;
|
||||||
@ -122,6 +127,89 @@ static void clk_pm_runtime_put(struct clk_core *core)
|
|||||||
pm_runtime_put_sync(core->dev);
|
pm_runtime_put_sync(core->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clk_pm_runtime_get_all() - Runtime "get" all clk provider devices
|
||||||
|
*
|
||||||
|
* Call clk_pm_runtime_get() on all runtime PM enabled clks in the clk tree so
|
||||||
|
* that disabling unused clks avoids a deadlock where a device is runtime PM
|
||||||
|
* resuming/suspending and the runtime PM callback is trying to grab the
|
||||||
|
* prepare_lock for something like clk_prepare_enable() while
|
||||||
|
* clk_disable_unused_subtree() holds the prepare_lock and is trying to runtime
|
||||||
|
* PM resume/suspend the device as well.
|
||||||
|
*
|
||||||
|
* Context: Acquires the 'clk_rpm_list_lock' and returns with the lock held on
|
||||||
|
* success. Otherwise the lock is released on failure.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative errno otherwise.
|
||||||
|
*/
|
||||||
|
static int clk_pm_runtime_get_all(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct clk_core *core, *failed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grab the list lock to prevent any new clks from being registered
|
||||||
|
* or unregistered until clk_pm_runtime_put_all().
|
||||||
|
*/
|
||||||
|
mutex_lock(&clk_rpm_list_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Runtime PM "get" all the devices that are needed for the clks
|
||||||
|
* currently registered. Do this without holding the prepare_lock, to
|
||||||
|
* avoid the deadlock.
|
||||||
|
*/
|
||||||
|
hlist_for_each_entry(core, &clk_rpm_list, rpm_node) {
|
||||||
|
ret = clk_pm_runtime_get(core);
|
||||||
|
if (ret) {
|
||||||
|
failed = core;
|
||||||
|
pr_err("clk: Failed to runtime PM get '%s' for clk '%s'\n",
|
||||||
|
dev_name(failed->dev), failed->name);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
hlist_for_each_entry(core, &clk_rpm_list, rpm_node) {
|
||||||
|
if (core == failed)
|
||||||
|
break;
|
||||||
|
|
||||||
|
clk_pm_runtime_put(core);
|
||||||
|
}
|
||||||
|
mutex_unlock(&clk_rpm_list_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clk_pm_runtime_put_all() - Runtime "put" all clk provider devices
|
||||||
|
*
|
||||||
|
* Put the runtime PM references taken in clk_pm_runtime_get_all() and release
|
||||||
|
* the 'clk_rpm_list_lock'.
|
||||||
|
*/
|
||||||
|
static void clk_pm_runtime_put_all(void)
|
||||||
|
{
|
||||||
|
struct clk_core *core;
|
||||||
|
|
||||||
|
hlist_for_each_entry(core, &clk_rpm_list, rpm_node)
|
||||||
|
clk_pm_runtime_put(core);
|
||||||
|
mutex_unlock(&clk_rpm_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clk_pm_runtime_init(struct clk_core *core)
|
||||||
|
{
|
||||||
|
struct device *dev = core->dev;
|
||||||
|
|
||||||
|
if (dev && pm_runtime_enabled(dev)) {
|
||||||
|
core->rpm_enabled = true;
|
||||||
|
|
||||||
|
mutex_lock(&clk_rpm_list_lock);
|
||||||
|
hlist_add_head(&core->rpm_node, &clk_rpm_list);
|
||||||
|
mutex_unlock(&clk_rpm_list_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*** locking ***/
|
/*** locking ***/
|
||||||
static void clk_prepare_lock(void)
|
static void clk_prepare_lock(void)
|
||||||
{
|
{
|
||||||
@ -1381,9 +1469,6 @@ static void __init clk_unprepare_unused_subtree(struct clk_core *core)
|
|||||||
if (core->flags & CLK_IGNORE_UNUSED)
|
if (core->flags & CLK_IGNORE_UNUSED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (clk_pm_runtime_get(core))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (clk_core_is_prepared(core)) {
|
if (clk_core_is_prepared(core)) {
|
||||||
trace_clk_unprepare(core);
|
trace_clk_unprepare(core);
|
||||||
if (core->ops->unprepare_unused)
|
if (core->ops->unprepare_unused)
|
||||||
@ -1392,8 +1477,6 @@ static void __init clk_unprepare_unused_subtree(struct clk_core *core)
|
|||||||
core->ops->unprepare(core->hw);
|
core->ops->unprepare(core->hw);
|
||||||
trace_clk_unprepare_complete(core);
|
trace_clk_unprepare_complete(core);
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_pm_runtime_put(core);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init clk_disable_unused_subtree(struct clk_core *core)
|
static void __init clk_disable_unused_subtree(struct clk_core *core)
|
||||||
@ -1409,9 +1492,6 @@ static void __init clk_disable_unused_subtree(struct clk_core *core)
|
|||||||
if (core->flags & CLK_OPS_PARENT_ENABLE)
|
if (core->flags & CLK_OPS_PARENT_ENABLE)
|
||||||
clk_core_prepare_enable(core->parent);
|
clk_core_prepare_enable(core->parent);
|
||||||
|
|
||||||
if (clk_pm_runtime_get(core))
|
|
||||||
goto unprepare_out;
|
|
||||||
|
|
||||||
flags = clk_enable_lock();
|
flags = clk_enable_lock();
|
||||||
|
|
||||||
if (core->enable_count)
|
if (core->enable_count)
|
||||||
@ -1436,8 +1516,6 @@ static void __init clk_disable_unused_subtree(struct clk_core *core)
|
|||||||
|
|
||||||
unlock_out:
|
unlock_out:
|
||||||
clk_enable_unlock(flags);
|
clk_enable_unlock(flags);
|
||||||
clk_pm_runtime_put(core);
|
|
||||||
unprepare_out:
|
|
||||||
if (core->flags & CLK_OPS_PARENT_ENABLE)
|
if (core->flags & CLK_OPS_PARENT_ENABLE)
|
||||||
clk_core_disable_unprepare(core->parent);
|
clk_core_disable_unprepare(core->parent);
|
||||||
}
|
}
|
||||||
@ -1453,6 +1531,7 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup);
|
|||||||
static int __init clk_disable_unused(void)
|
static int __init clk_disable_unused(void)
|
||||||
{
|
{
|
||||||
struct clk_core *core;
|
struct clk_core *core;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (clk_ignore_unused) {
|
if (clk_ignore_unused) {
|
||||||
pr_warn("clk: Not disabling unused clocks\n");
|
pr_warn("clk: Not disabling unused clocks\n");
|
||||||
@ -1461,6 +1540,13 @@ static int __init clk_disable_unused(void)
|
|||||||
|
|
||||||
pr_info("clk: Disabling unused clocks\n");
|
pr_info("clk: Disabling unused clocks\n");
|
||||||
|
|
||||||
|
ret = clk_pm_runtime_get_all();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* Grab the prepare lock to keep the clk topology stable while iterating
|
||||||
|
* over clks.
|
||||||
|
*/
|
||||||
clk_prepare_lock();
|
clk_prepare_lock();
|
||||||
|
|
||||||
hlist_for_each_entry(core, &clk_root_list, child_node)
|
hlist_for_each_entry(core, &clk_root_list, child_node)
|
||||||
@ -1477,6 +1563,8 @@ static int __init clk_disable_unused(void)
|
|||||||
|
|
||||||
clk_prepare_unlock();
|
clk_prepare_unlock();
|
||||||
|
|
||||||
|
clk_pm_runtime_put_all();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
late_initcall_sync(clk_disable_unused);
|
late_initcall_sync(clk_disable_unused);
|
||||||
@ -3252,9 +3340,7 @@ static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
|
|||||||
{
|
{
|
||||||
struct clk_core *child;
|
struct clk_core *child;
|
||||||
|
|
||||||
clk_pm_runtime_get(c);
|
|
||||||
clk_summary_show_one(s, c, level);
|
clk_summary_show_one(s, c, level);
|
||||||
clk_pm_runtime_put(c);
|
|
||||||
|
|
||||||
hlist_for_each_entry(child, &c->children, child_node)
|
hlist_for_each_entry(child, &c->children, child_node)
|
||||||
clk_summary_show_subtree(s, child, level + 1);
|
clk_summary_show_subtree(s, child, level + 1);
|
||||||
@ -3264,11 +3350,15 @@ static int clk_summary_show(struct seq_file *s, void *data)
|
|||||||
{
|
{
|
||||||
struct clk_core *c;
|
struct clk_core *c;
|
||||||
struct hlist_head **lists = s->private;
|
struct hlist_head **lists = s->private;
|
||||||
|
int ret;
|
||||||
|
|
||||||
seq_puts(s, " enable prepare protect duty hardware connection\n");
|
seq_puts(s, " enable prepare protect duty hardware connection\n");
|
||||||
seq_puts(s, " clock count count count rate accuracy phase cycle enable consumer id\n");
|
seq_puts(s, " clock count count count rate accuracy phase cycle enable consumer id\n");
|
||||||
seq_puts(s, "---------------------------------------------------------------------------------------------------------------------------------------------\n");
|
seq_puts(s, "---------------------------------------------------------------------------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
ret = clk_pm_runtime_get_all();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
clk_prepare_lock();
|
clk_prepare_lock();
|
||||||
|
|
||||||
@ -3277,6 +3367,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
|
|||||||
clk_summary_show_subtree(s, c, 0);
|
clk_summary_show_subtree(s, c, 0);
|
||||||
|
|
||||||
clk_prepare_unlock();
|
clk_prepare_unlock();
|
||||||
|
clk_pm_runtime_put_all();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3324,8 +3415,14 @@ static int clk_dump_show(struct seq_file *s, void *data)
|
|||||||
struct clk_core *c;
|
struct clk_core *c;
|
||||||
bool first_node = true;
|
bool first_node = true;
|
||||||
struct hlist_head **lists = s->private;
|
struct hlist_head **lists = s->private;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_pm_runtime_get_all();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
seq_putc(s, '{');
|
seq_putc(s, '{');
|
||||||
|
|
||||||
clk_prepare_lock();
|
clk_prepare_lock();
|
||||||
|
|
||||||
for (; *lists; lists++) {
|
for (; *lists; lists++) {
|
||||||
@ -3338,6 +3435,7 @@ static int clk_dump_show(struct seq_file *s, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
clk_prepare_unlock();
|
clk_prepare_unlock();
|
||||||
|
clk_pm_runtime_put_all();
|
||||||
|
|
||||||
seq_puts(s, "}\n");
|
seq_puts(s, "}\n");
|
||||||
return 0;
|
return 0;
|
||||||
@ -3981,8 +4079,6 @@ static int __clk_core_init(struct clk_core *core)
|
|||||||
}
|
}
|
||||||
|
|
||||||
clk_core_reparent_orphans_nolock();
|
clk_core_reparent_orphans_nolock();
|
||||||
|
|
||||||
kref_init(&core->ref);
|
|
||||||
out:
|
out:
|
||||||
clk_pm_runtime_put(core);
|
clk_pm_runtime_put(core);
|
||||||
unlock:
|
unlock:
|
||||||
@ -4211,6 +4307,22 @@ static void clk_core_free_parent_map(struct clk_core *core)
|
|||||||
kfree(core->parents);
|
kfree(core->parents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free memory allocated for a struct clk_core */
|
||||||
|
static void __clk_release(struct kref *ref)
|
||||||
|
{
|
||||||
|
struct clk_core *core = container_of(ref, struct clk_core, ref);
|
||||||
|
|
||||||
|
if (core->rpm_enabled) {
|
||||||
|
mutex_lock(&clk_rpm_list_lock);
|
||||||
|
hlist_del(&core->rpm_node);
|
||||||
|
mutex_unlock(&clk_rpm_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_core_free_parent_map(core);
|
||||||
|
kfree_const(core->name);
|
||||||
|
kfree(core);
|
||||||
|
}
|
||||||
|
|
||||||
static struct clk *
|
static struct clk *
|
||||||
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
|
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
|
||||||
{
|
{
|
||||||
@ -4231,6 +4343,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
|
|||||||
goto fail_out;
|
goto fail_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kref_init(&core->ref);
|
||||||
|
|
||||||
core->name = kstrdup_const(init->name, GFP_KERNEL);
|
core->name = kstrdup_const(init->name, GFP_KERNEL);
|
||||||
if (!core->name) {
|
if (!core->name) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
@ -4243,9 +4357,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
|
|||||||
}
|
}
|
||||||
core->ops = init->ops;
|
core->ops = init->ops;
|
||||||
|
|
||||||
if (dev && pm_runtime_enabled(dev))
|
|
||||||
core->rpm_enabled = true;
|
|
||||||
core->dev = dev;
|
core->dev = dev;
|
||||||
|
clk_pm_runtime_init(core);
|
||||||
core->of_node = np;
|
core->of_node = np;
|
||||||
if (dev && dev->driver)
|
if (dev && dev->driver)
|
||||||
core->owner = dev->driver->owner;
|
core->owner = dev->driver->owner;
|
||||||
@ -4285,12 +4398,10 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
|
|||||||
hw->clk = NULL;
|
hw->clk = NULL;
|
||||||
|
|
||||||
fail_create_clk:
|
fail_create_clk:
|
||||||
clk_core_free_parent_map(core);
|
|
||||||
fail_parents:
|
fail_parents:
|
||||||
fail_ops:
|
fail_ops:
|
||||||
kfree_const(core->name);
|
|
||||||
fail_name:
|
fail_name:
|
||||||
kfree(core);
|
kref_put(&core->ref, __clk_release);
|
||||||
fail_out:
|
fail_out:
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
@ -4370,18 +4481,6 @@ int of_clk_hw_register(struct device_node *node, struct clk_hw *hw)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_clk_hw_register);
|
EXPORT_SYMBOL_GPL(of_clk_hw_register);
|
||||||
|
|
||||||
/* Free memory allocated for a clock. */
|
|
||||||
static void __clk_release(struct kref *ref)
|
|
||||||
{
|
|
||||||
struct clk_core *core = container_of(ref, struct clk_core, ref);
|
|
||||||
|
|
||||||
lockdep_assert_held(&prepare_lock);
|
|
||||||
|
|
||||||
clk_core_free_parent_map(core);
|
|
||||||
kfree_const(core->name);
|
|
||||||
kfree(core);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Empty clk_ops for unregistered clocks. These are used temporarily
|
* Empty clk_ops for unregistered clocks. These are used temporarily
|
||||||
* after clk_unregister() was called on a clock and until last clock
|
* after clk_unregister() was called on a clock and until last clock
|
||||||
@ -4472,7 +4571,8 @@ void clk_unregister(struct clk *clk)
|
|||||||
if (ops == &clk_nodrv_ops) {
|
if (ops == &clk_nodrv_ops) {
|
||||||
pr_err("%s: unregistered clock: %s\n", __func__,
|
pr_err("%s: unregistered clock: %s\n", __func__,
|
||||||
clk->core->name);
|
clk->core->name);
|
||||||
goto unlock;
|
clk_prepare_unlock();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Assign empty clock ops for consumers that might still hold
|
* Assign empty clock ops for consumers that might still hold
|
||||||
@ -4506,11 +4606,10 @@ void clk_unregister(struct clk *clk)
|
|||||||
if (clk->core->protect_count)
|
if (clk->core->protect_count)
|
||||||
pr_warn("%s: unregistering protected clock: %s\n",
|
pr_warn("%s: unregistering protected clock: %s\n",
|
||||||
__func__, clk->core->name);
|
__func__, clk->core->name);
|
||||||
|
clk_prepare_unlock();
|
||||||
|
|
||||||
kref_put(&clk->core->ref, __clk_release);
|
kref_put(&clk->core->ref, __clk_release);
|
||||||
free_clk(clk);
|
free_clk(clk);
|
||||||
unlock:
|
|
||||||
clk_prepare_unlock();
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(clk_unregister);
|
EXPORT_SYMBOL_GPL(clk_unregister);
|
||||||
|
|
||||||
@ -4669,13 +4768,11 @@ void __clk_put(struct clk *clk)
|
|||||||
if (clk->min_rate > 0 || clk->max_rate < ULONG_MAX)
|
if (clk->min_rate > 0 || clk->max_rate < ULONG_MAX)
|
||||||
clk_set_rate_range_nolock(clk, 0, ULONG_MAX);
|
clk_set_rate_range_nolock(clk, 0, ULONG_MAX);
|
||||||
|
|
||||||
owner = clk->core->owner;
|
|
||||||
kref_put(&clk->core->ref, __clk_release);
|
|
||||||
|
|
||||||
clk_prepare_unlock();
|
clk_prepare_unlock();
|
||||||
|
|
||||||
|
owner = clk->core->owner;
|
||||||
|
kref_put(&clk->core->ref, __clk_release);
|
||||||
module_put(owner);
|
module_put(owner);
|
||||||
|
|
||||||
free_clk(clk);
|
free_clk(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user