Patch "clk: Evict unregistered clks from parent caches" has been added to the 4.14-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    clk: Evict unregistered clks from parent caches

to the 4.14-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     clk-evict-unregistered-clks-from-parent-caches.patch
and it can be found in the queue-4.14 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 166f501401faecdc70028c69a303d67b529f3c33
Author: Stephen Boyd <sboyd@xxxxxxxxxx>
Date:   Wed Aug 28 11:19:59 2019 -0700

    clk: Evict unregistered clks from parent caches
    
    commit bdcf1dc253248542537a742ae1e7ccafdd03f2d3 upstream.
    
    We leave a dangling pointer in each clk_core::parents array that has an
    unregistered clk as a potential parent when that clk_core pointer is
    freed by clk{_hw}_unregister(). It is impossible for the true parent of
    a clk to be set with clk_set_parent() once the dangling pointer is left
    in the cache because we compare parent pointers in
    clk_fetch_parent_index() instead of checking for a matching clk name or
    clk_hw pointer.
    
    Before commit ede77858473a ("clk: Remove global clk traversal on fetch
    parent index"), we would check clk_hw pointers, which has a higher
    chance of being the same between registration and unregistration, but it
    can still be allocated and freed by the clk provider. In fact, this has
    been a long standing problem since commit da0f0b2c3ad2 ("clk: Correct
    lookup logic in clk_fetch_parent_index()") where we stopped trying to
    compare clk names and skipped over entries in the cache that weren't
    NULL.
    
    There are good (performance) reasons to not do the global tree lookup in
    cases where the cache holds dangling pointers to parents that have been
    unregistered. Let's take the performance hit on the uncommon
    registration path instead. Loop through all the clk_core::parents arrays
    when a clk is unregistered and set the entry to NULL when the parent
    cache entry and clk being unregistered are the same pointer. This will
    fix this problem and avoid the overhead for the "normal" case.
    
    Based on a patch by Bjorn Andersson.
    
    Fixes: da0f0b2c3ad2 ("clk: Correct lookup logic in clk_fetch_parent_index()")
    Reviewed-by: Bjorn Andersson <bjorn.andersson@xxxxxxxxxx>
    Tested-by: Sai Prakash Ranjan <saiprakash.ranjan@xxxxxxxxxxxxxx>
    Signed-off-by: Stephen Boyd <sboyd@xxxxxxxxxx>
    Link: https://lkml.kernel.org/r/20190828181959.204401-1-sboyd@xxxxxxxxxx
    Tested-by: Naresh Kamboju <naresh.kamboju@xxxxxxxxxx>
    Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 44b6f23cc851d..4289c519af1be 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -39,6 +39,17 @@ static HLIST_HEAD(clk_root_list);
 static HLIST_HEAD(clk_orphan_list);
 static LIST_HEAD(clk_notifier_list);
 
+static struct hlist_head *all_lists[] = {
+	&clk_root_list,
+	&clk_orphan_list,
+	NULL,
+};
+
+static struct hlist_head *orphan_list[] = {
+	&clk_orphan_list,
+	NULL,
+};
+
 /***    private data structures    ***/
 
 struct clk_core {
@@ -1993,17 +2004,6 @@ static int inited = 0;
 static DEFINE_MUTEX(clk_debug_lock);
 static HLIST_HEAD(clk_debug_list);
 
-static struct hlist_head *all_lists[] = {
-	&clk_root_list,
-	&clk_orphan_list,
-	NULL,
-};
-
-static struct hlist_head *orphan_list[] = {
-	&clk_orphan_list,
-	NULL,
-};
-
 static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
 				 int level)
 {
@@ -2735,6 +2735,34 @@ static const struct clk_ops clk_nodrv_ops = {
 	.set_parent	= clk_nodrv_set_parent,
 };
 
+static void clk_core_evict_parent_cache_subtree(struct clk_core *root,
+						struct clk_core *target)
+{
+	int i;
+	struct clk_core *child;
+
+	for (i = 0; i < root->num_parents; i++)
+		if (root->parents[i] == target)
+			root->parents[i] = NULL;
+
+	hlist_for_each_entry(child, &root->children, child_node)
+		clk_core_evict_parent_cache_subtree(child, target);
+}
+
+/* Remove this clk from all parent caches */
+static void clk_core_evict_parent_cache(struct clk_core *core)
+{
+	struct hlist_head **lists;
+	struct clk_core *root;
+
+	lockdep_assert_held(&prepare_lock);
+
+	for (lists = all_lists; *lists; lists++)
+		hlist_for_each_entry(root, *lists, child_node)
+			clk_core_evict_parent_cache_subtree(root, core);
+
+}
+
 /**
  * clk_unregister - unregister a currently registered clock
  * @clk: clock to unregister
@@ -2773,6 +2801,8 @@ void clk_unregister(struct clk *clk)
 			clk_core_set_parent(child, NULL);
 	}
 
+	clk_core_evict_parent_cache(clk->core);
+
 	hlist_del_init(&clk->core->child_node);
 
 	if (clk->core->prepare_count)



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux