Skip to content

Commit 1b38cd9

Browse files
image-dragonKernel Patches Daemon
authored andcommitted
fprobe: use rhltable for fprobe_ip_table
For now, all the kernel functions who are hooked by the fprobe will be added to the hash table "fprobe_ip_table". The key of it is the function address, and the value of it is "struct fprobe_hlist_node". The budget of the hash table is FPROBE_IP_TABLE_SIZE, which is 256. And this means the overhead of the hash table lookup will grow linearly if the count of the functions in the fprobe more than 256. When we try to hook all the kernel functions, the overhead will be huge. Therefore, replace the hash table with rhltable to reduce the overhead. Signed-off-by: Menglong Dong <[email protected]>
1 parent 9db1c0b commit 1b38cd9

File tree

2 files changed

+90
-67
lines changed

2 files changed

+90
-67
lines changed

include/linux/fprobe.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <linux/ftrace.h>
88
#include <linux/rcupdate.h>
99
#include <linux/refcount.h>
10+
#include <linux/rhashtable.h>
1011
#include <linux/slab.h>
1112

1213
struct fprobe;
@@ -26,7 +27,7 @@ typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip,
2627
* @fp: The fprobe which owns this.
2728
*/
2829
struct fprobe_hlist_node {
29-
struct hlist_node hlist;
30+
struct rhlist_head hlist;
3031
unsigned long addr;
3132
struct fprobe *fp;
3233
};

kernel/trace/fprobe.c

Lines changed: 88 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -41,47 +41,46 @@
4141
* - RCU hlist traversal under disabling preempt
4242
*/
4343
static struct hlist_head fprobe_table[FPROBE_TABLE_SIZE];
44-
static struct hlist_head fprobe_ip_table[FPROBE_IP_TABLE_SIZE];
44+
static struct rhltable fprobe_ip_table;
4545
static DEFINE_MUTEX(fprobe_mutex);
4646

47-
/*
48-
* Find first fprobe in the hlist. It will be iterated twice in the entry
49-
* probe, once for correcting the total required size, the second time is
50-
* calling back the user handlers.
51-
* Thus the hlist in the fprobe_table must be sorted and new probe needs to
52-
* be added *before* the first fprobe.
53-
*/
54-
static struct fprobe_hlist_node *find_first_fprobe_node(unsigned long ip)
47+
static u32 fprobe_node_hashfn(const void *data, u32 len, u32 seed)
5548
{
56-
struct fprobe_hlist_node *node;
57-
struct hlist_head *head;
49+
return hash_ptr(*(unsigned long **)data, 32);
50+
}
5851

59-
head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)];
60-
hlist_for_each_entry_rcu(node, head, hlist,
61-
lockdep_is_held(&fprobe_mutex)) {
62-
if (node->addr == ip)
63-
return node;
64-
}
65-
return NULL;
52+
static int fprobe_node_cmp(struct rhashtable_compare_arg *arg,
53+
const void *ptr)
54+
{
55+
unsigned long key = *(unsigned long *)arg->key;
56+
const struct fprobe_hlist_node *n = ptr;
57+
58+
return n->addr != key;
6659
}
67-
NOKPROBE_SYMBOL(find_first_fprobe_node);
6860

69-
/* Node insertion and deletion requires the fprobe_mutex */
70-
static void insert_fprobe_node(struct fprobe_hlist_node *node)
61+
static u32 fprobe_node_obj_hashfn(const void *data, u32 len, u32 seed)
7162
{
72-
unsigned long ip = node->addr;
73-
struct fprobe_hlist_node *next;
74-
struct hlist_head *head;
63+
const struct fprobe_hlist_node *n = data;
64+
65+
return hash_ptr((void *)n->addr, 32);
66+
}
67+
68+
static const struct rhashtable_params fprobe_rht_params = {
69+
.head_offset = offsetof(struct fprobe_hlist_node, hlist),
70+
.key_offset = offsetof(struct fprobe_hlist_node, addr),
71+
.key_len = sizeof_field(struct fprobe_hlist_node, addr),
72+
.hashfn = fprobe_node_hashfn,
73+
.obj_hashfn = fprobe_node_obj_hashfn,
74+
.obj_cmpfn = fprobe_node_cmp,
75+
.automatic_shrinking = true,
76+
};
7577

78+
/* Node insertion and deletion requires the fprobe_mutex */
79+
static int insert_fprobe_node(struct fprobe_hlist_node *node)
80+
{
7681
lockdep_assert_held(&fprobe_mutex);
7782

78-
next = find_first_fprobe_node(ip);
79-
if (next) {
80-
hlist_add_before_rcu(&node->hlist, &next->hlist);
81-
return;
82-
}
83-
head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)];
84-
hlist_add_head_rcu(&node->hlist, head);
83+
return rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params);
8584
}
8685

8786
/* Return true if there are synonims */
@@ -92,9 +91,11 @@ static bool delete_fprobe_node(struct fprobe_hlist_node *node)
9291
/* Avoid double deleting */
9392
if (READ_ONCE(node->fp) != NULL) {
9493
WRITE_ONCE(node->fp, NULL);
95-
hlist_del_rcu(&node->hlist);
94+
rhltable_remove(&fprobe_ip_table, &node->hlist,
95+
fprobe_rht_params);
9696
}
97-
return !!find_first_fprobe_node(node->addr);
97+
return !!rhltable_lookup(&fprobe_ip_table, &node->addr,
98+
fprobe_rht_params);
9899
}
99100

100101
/* Check existence of the fprobe */
@@ -249,9 +250,10 @@ static inline int __fprobe_kprobe_handler(unsigned long ip, unsigned long parent
249250
static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
250251
struct ftrace_regs *fregs)
251252
{
252-
struct fprobe_hlist_node *node, *first;
253253
unsigned long *fgraph_data = NULL;
254254
unsigned long func = trace->func;
255+
struct fprobe_hlist_node *node;
256+
struct rhlist_head *head, *pos;
255257
unsigned long ret_ip;
256258
int reserved_words;
257259
struct fprobe *fp;
@@ -260,14 +262,12 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
260262
if (WARN_ON_ONCE(!fregs))
261263
return 0;
262264

263-
first = node = find_first_fprobe_node(func);
264-
if (unlikely(!first))
265-
return 0;
266-
265+
rcu_read_lock();
266+
head = rhltable_lookup(&fprobe_ip_table, &func, fprobe_rht_params);
267267
reserved_words = 0;
268-
hlist_for_each_entry_from_rcu(node, hlist) {
268+
rhl_for_each_entry_rcu(node, pos, head, hlist) {
269269
if (node->addr != func)
270-
break;
270+
continue;
271271
fp = READ_ONCE(node->fp);
272272
if (!fp || !fp->exit_handler)
273273
continue;
@@ -278,17 +278,19 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
278278
reserved_words +=
279279
FPROBE_HEADER_SIZE_IN_LONG + SIZE_IN_LONG(fp->entry_data_size);
280280
}
281-
node = first;
281+
rcu_read_unlock();
282282
if (reserved_words) {
283283
fgraph_data = fgraph_reserve_data(gops->idx, reserved_words * sizeof(long));
284284
if (unlikely(!fgraph_data)) {
285-
hlist_for_each_entry_from_rcu(node, hlist) {
285+
rcu_read_lock();
286+
rhl_for_each_entry_rcu(node, pos, head, hlist) {
286287
if (node->addr != func)
287-
break;
288+
continue;
288289
fp = READ_ONCE(node->fp);
289290
if (fp && !fprobe_disabled(fp))
290291
fp->nmissed++;
291292
}
293+
rcu_read_unlock();
292294
return 0;
293295
}
294296
}
@@ -299,12 +301,12 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
299301
*/
300302
ret_ip = ftrace_regs_get_return_address(fregs);
301303
used = 0;
302-
hlist_for_each_entry_from_rcu(node, hlist) {
304+
rhl_for_each_entry_rcu(node, pos, head, hlist) {
303305
int data_size;
304306
void *data;
305307

306308
if (node->addr != func)
307-
break;
309+
continue;
308310
fp = READ_ONCE(node->fp);
309311
if (!fp || fprobe_disabled(fp))
310312
continue;
@@ -448,34 +450,31 @@ static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long ad
448450
return 0;
449451
}
450452

451-
static void fprobe_remove_node_in_module(struct module *mod, struct hlist_head *head,
452-
struct fprobe_addr_list *alist)
453+
static void fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node,
454+
struct fprobe_addr_list *alist)
453455
{
454-
struct fprobe_hlist_node *node;
455456
int ret = 0;
456457

457-
hlist_for_each_entry_rcu(node, head, hlist,
458-
lockdep_is_held(&fprobe_mutex)) {
459-
if (!within_module(node->addr, mod))
460-
continue;
461-
if (delete_fprobe_node(node))
462-
continue;
463-
/*
464-
* If failed to update alist, just continue to update hlist.
465-
* Therefore, at list user handler will not hit anymore.
466-
*/
467-
if (!ret)
468-
ret = fprobe_addr_list_add(alist, node->addr);
469-
}
458+
if (!within_module(node->addr, mod))
459+
return;
460+
if (delete_fprobe_node(node))
461+
return;
462+
/*
463+
* If failed to update alist, just continue to update hlist.
464+
* Therefore, at list user handler will not hit anymore.
465+
*/
466+
if (!ret)
467+
ret = fprobe_addr_list_add(alist, node->addr);
470468
}
471469

472470
/* Handle module unloading to manage fprobe_ip_table. */
473471
static int fprobe_module_callback(struct notifier_block *nb,
474472
unsigned long val, void *data)
475473
{
476474
struct fprobe_addr_list alist = {.size = FPROBE_IPS_BATCH_INIT};
475+
struct fprobe_hlist_node *node;
476+
struct rhashtable_iter iter;
477477
struct module *mod = data;
478-
int i;
479478

480479
if (val != MODULE_STATE_GOING)
481480
return NOTIFY_DONE;
@@ -486,8 +485,16 @@ static int fprobe_module_callback(struct notifier_block *nb,
486485
return NOTIFY_DONE;
487486

488487
mutex_lock(&fprobe_mutex);
489-
for (i = 0; i < FPROBE_IP_TABLE_SIZE; i++)
490-
fprobe_remove_node_in_module(mod, &fprobe_ip_table[i], &alist);
488+
rhashtable_walk_enter(&fprobe_ip_table.ht, &iter);
489+
do {
490+
rhashtable_walk_start(&iter);
491+
492+
while ((node = rhashtable_walk_next(&iter)) && !IS_ERR(node))
493+
fprobe_remove_node_in_module(mod, node, &alist);
494+
495+
rhashtable_walk_stop(&iter);
496+
} while (node == ERR_PTR(-EAGAIN));
497+
rhashtable_walk_exit(&iter);
491498

492499
if (alist.index < alist.size && alist.index > 0)
493500
ftrace_set_filter_ips(&fprobe_graph_ops.ops,
@@ -727,8 +734,16 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
727734
ret = fprobe_graph_add_ips(addrs, num);
728735
if (!ret) {
729736
add_fprobe_hash(fp);
730-
for (i = 0; i < hlist_array->size; i++)
731-
insert_fprobe_node(&hlist_array->array[i]);
737+
for (i = 0; i < hlist_array->size; i++) {
738+
ret = insert_fprobe_node(&hlist_array->array[i]);
739+
if (ret)
740+
break;
741+
}
742+
/* fallback on insert error */
743+
if (ret) {
744+
for (i--; i >= 0; i--)
745+
delete_fprobe_node(&hlist_array->array[i]);
746+
}
732747
}
733748
mutex_unlock(&fprobe_mutex);
734749

@@ -824,3 +839,10 @@ int unregister_fprobe(struct fprobe *fp)
824839
return ret;
825840
}
826841
EXPORT_SYMBOL_GPL(unregister_fprobe);
842+
843+
static int __init fprobe_initcall(void)
844+
{
845+
rhltable_init(&fprobe_ip_table, &fprobe_rht_params);
846+
return 0;
847+
}
848+
late_initcall(fprobe_initcall);

0 commit comments

Comments
 (0)