Skip to content

Commit 4bf2d27

Browse files
wensbebarino
authored andcommitted
clk: Use hashtable for global clk lookups
A clk lookup using clk_core_lookup() is currently somewhat expensive since it has to walk the whole clk tree to find a match. This is extremely bad in the clk_core_init() function where it is used to look for clk name conflicts, which is always the worst case of walking the whole tree. Moreover, the number of clks checked increases as more clks are registered, causing each subsequent clk registration becoming slower. Add a hashtable for doing clk lookups to replace the tree walk method. On arm64 this increases kernel memory usage by 4 KB for the hashtable, and 16 bytes (2 pointers) for |struct hlist_node| in each clk. On a platform with around 800 clks, this reduces the time spent in clk_core_lookup() significantly: | PID 0 | kworker | | before | after | before | after | ------------------------------------------- avg | 203 us | 2.7 us | 123 us | 1.5 us | ------------------------------------------- min | 4.7 us | 2.3 us | 102 us | 0.9 us | ------------------------------------------- max | 867 us | 4.8 us | 237 us | 3.5 us | ------------------------------------------- culm | 109 ms | 1.5 ms | 21 ms | 0.3 ms | This in turn reduces the time spent in clk_hw_register(), and ultimately, boot time. On a different system with close to 700 clks, This reduces boot time by around 110 ms. While this doesn't seem like a lot, this helps in cases where minimizing boot time is important. Signed-off-by: Chen-Yu Tsai <wenst@chromium.org> Tested-by: Brian Masney <bmasney@redhat.com> Reviewed-by: Brian Masney <bmasney@redhat.com> Signed-off-by: Stephen Boyd <sboyd@kernel.org>
1 parent 904bed3 commit 4bf2d27

1 file changed

Lines changed: 18 additions & 32 deletions

File tree

drivers/clk/clk.c

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/clk-provider.h>
1313
#include <linux/device.h>
1414
#include <linux/err.h>
15+
#include <linux/hashtable.h>
1516
#include <linux/init.h>
1617
#include <linux/list.h>
1718
#include <linux/module.h>
@@ -21,6 +22,8 @@
2122
#include <linux/sched.h>
2223
#include <linux/slab.h>
2324
#include <linux/spinlock.h>
25+
#include <linux/string.h>
26+
#include <linux/stringhash.h>
2427

2528
#include "clk.h"
2629

@@ -33,6 +36,9 @@ static struct task_struct *enable_owner;
3336
static int prepare_refcnt;
3437
static int enable_refcnt;
3538

39+
#define CLK_HASH_BITS 9
40+
static DEFINE_HASHTABLE(clk_hashtable, CLK_HASH_BITS);
41+
3642
static HLIST_HEAD(clk_root_list);
3743
static HLIST_HEAD(clk_orphan_list);
3844
static LIST_HEAD(clk_notifier_list);
@@ -87,6 +93,7 @@ struct clk_core {
8793
struct clk_duty duty;
8894
struct hlist_head children;
8995
struct hlist_node child_node;
96+
struct hlist_node hashtable_node;
9097
struct hlist_head clks;
9198
unsigned int notifier_count;
9299
#ifdef CONFIG_DEBUG_FS
@@ -395,45 +402,20 @@ struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw)
395402
}
396403
EXPORT_SYMBOL_GPL(clk_hw_get_parent);
397404

398-
static struct clk_core *__clk_lookup_subtree(const char *name,
399-
struct clk_core *core)
400-
{
401-
struct clk_core *child;
402-
struct clk_core *ret;
403-
404-
if (!strcmp(core->name, name))
405-
return core;
406-
407-
hlist_for_each_entry(child, &core->children, child_node) {
408-
ret = __clk_lookup_subtree(name, child);
409-
if (ret)
410-
return ret;
411-
}
412-
413-
return NULL;
414-
}
415-
416405
static struct clk_core *clk_core_lookup(const char *name)
417406
{
418-
struct clk_core *root_clk;
419-
struct clk_core *ret;
407+
struct clk_core *core;
408+
u32 hash;
420409

421410
if (!name)
422411
return NULL;
423412

424-
/* search the 'proper' clk tree first */
425-
hlist_for_each_entry(root_clk, &clk_root_list, child_node) {
426-
ret = __clk_lookup_subtree(name, root_clk);
427-
if (ret)
428-
return ret;
429-
}
413+
hash = full_name_hash(NULL, name, strlen(name));
430414

431-
/* if not found, then search the orphan tree */
432-
hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) {
433-
ret = __clk_lookup_subtree(name, root_clk);
434-
if (ret)
435-
return ret;
436-
}
415+
/* search the hashtable */
416+
hash_for_each_possible(clk_hashtable, core, hashtable_node, hash)
417+
if (!strcmp(core->name, name))
418+
return core;
437419

438420
return NULL;
439421
}
@@ -4013,6 +3995,8 @@ static int __clk_core_init(struct clk_core *core)
40133995
hlist_add_head(&core->child_node, &clk_orphan_list);
40143996
core->orphan = true;
40153997
}
3998+
hash_add(clk_hashtable, &core->hashtable_node,
3999+
full_name_hash(NULL, core->name, strlen(core->name)));
40164000

40174001
/*
40184002
* Set clk's accuracy. The preferred method is to use
@@ -4089,6 +4073,7 @@ static int __clk_core_init(struct clk_core *core)
40894073
clk_pm_runtime_put(core);
40904074
unlock:
40914075
if (ret) {
4076+
hash_del(&core->hashtable_node);
40924077
hlist_del_init(&core->child_node);
40934078
core->hw->core = NULL;
40944079
}
@@ -4610,6 +4595,7 @@ void clk_unregister(struct clk *clk)
46104595

46114596
clk_core_evict_parent_cache(clk->core);
46124597

4598+
hash_del(&clk->core->hashtable_node);
46134599
hlist_del_init(&clk->core->child_node);
46144600

46154601
if (clk->core->prepare_count)

0 commit comments

Comments
 (0)