10
10
#include <linux/kprobes.h>
11
11
#include <linux/list.h>
12
12
#include <linux/mutex.h>
13
+ #include <linux/rhashtable.h>
13
14
#include <linux/slab.h>
14
15
#include <linux/sort.h>
15
16
41
42
* - RCU hlist traversal under disabling preempt
42
43
*/
43
44
static struct hlist_head fprobe_table [FPROBE_TABLE_SIZE ];
44
- static struct hlist_head fprobe_ip_table [ FPROBE_IP_TABLE_SIZE ] ;
45
+ static struct rhltable fprobe_ip_table ;
45
46
static DEFINE_MUTEX (fprobe_mutex );
46
47
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 )
48
+ static u32 fprobe_node_hashfn (const void * data , u32 len , u32 seed )
55
49
{
56
- struct fprobe_hlist_node * node ;
57
- struct hlist_head * head ;
50
+ return hash_ptr ( * ( unsigned long * * ) data , 32 ) ;
51
+ }
58
52
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 ;
53
+ static int fprobe_node_cmp ( struct rhashtable_compare_arg * arg ,
54
+ const void * ptr )
55
+ {
56
+ unsigned long key = * ( unsigned long * ) arg -> key ;
57
+ const struct fprobe_hlist_node * n = ptr ;
58
+
59
+ return n -> addr != key ;
66
60
}
67
- NOKPROBE_SYMBOL (find_first_fprobe_node );
68
61
69
- /* Node insertion and deletion requires the fprobe_mutex */
70
- static void insert_fprobe_node (struct fprobe_hlist_node * node )
62
+ static u32 fprobe_node_obj_hashfn (const void * data , u32 len , u32 seed )
71
63
{
72
- unsigned long ip = node -> addr ;
73
- struct fprobe_hlist_node * next ;
74
- struct hlist_head * head ;
64
+ const struct fprobe_hlist_node * n = data ;
65
+
66
+ return hash_ptr ((void * )n -> addr , 32 );
67
+ }
68
+
69
+ static const struct rhashtable_params fprobe_rht_params = {
70
+ .head_offset = offsetof(struct fprobe_hlist_node , hlist ),
71
+ .key_offset = offsetof(struct fprobe_hlist_node , addr ),
72
+ .key_len = sizeof_field (struct fprobe_hlist_node , addr ),
73
+ .hashfn = fprobe_node_hashfn ,
74
+ .obj_hashfn = fprobe_node_obj_hashfn ,
75
+ .obj_cmpfn = fprobe_node_cmp ,
76
+ .automatic_shrinking = true,
77
+ };
75
78
79
+ /* Node insertion and deletion requires the fprobe_mutex */
80
+ static int insert_fprobe_node (struct fprobe_hlist_node * node )
81
+ {
76
82
lockdep_assert_held (& fprobe_mutex );
77
83
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 );
84
+ return rhltable_insert (& fprobe_ip_table , & node -> hlist , fprobe_rht_params );
85
85
}
86
86
87
87
/* Return true if there are synonims */
@@ -92,9 +92,11 @@ static bool delete_fprobe_node(struct fprobe_hlist_node *node)
92
92
/* Avoid double deleting */
93
93
if (READ_ONCE (node -> fp ) != NULL ) {
94
94
WRITE_ONCE (node -> fp , NULL );
95
- hlist_del_rcu (& node -> hlist );
95
+ rhltable_remove (& fprobe_ip_table , & node -> hlist ,
96
+ fprobe_rht_params );
96
97
}
97
- return !!find_first_fprobe_node (node -> addr );
98
+ return !!rhltable_lookup (& fprobe_ip_table , & node -> addr ,
99
+ fprobe_rht_params );
98
100
}
99
101
100
102
/* Check existence of the fprobe */
@@ -249,9 +251,10 @@ static inline int __fprobe_kprobe_handler(unsigned long ip, unsigned long parent
249
251
static int fprobe_entry (struct ftrace_graph_ent * trace , struct fgraph_ops * gops ,
250
252
struct ftrace_regs * fregs )
251
253
{
252
- struct fprobe_hlist_node * node , * first ;
253
254
unsigned long * fgraph_data = NULL ;
254
255
unsigned long func = trace -> func ;
256
+ struct fprobe_hlist_node * node ;
257
+ struct rhlist_head * head , * pos ;
255
258
unsigned long ret_ip ;
256
259
int reserved_words ;
257
260
struct fprobe * fp ;
@@ -260,14 +263,11 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
260
263
if (WARN_ON_ONCE (!fregs ))
261
264
return 0 ;
262
265
263
- first = node = find_first_fprobe_node (func );
264
- if (unlikely (!first ))
265
- return 0 ;
266
-
266
+ head = rhltable_lookup (& fprobe_ip_table , & func , fprobe_rht_params );
267
267
reserved_words = 0 ;
268
- hlist_for_each_entry_from_rcu (node , hlist ) {
268
+ rhl_for_each_entry_rcu (node , pos , head , hlist ) {
269
269
if (node -> addr != func )
270
- break ;
270
+ continue ;
271
271
fp = READ_ONCE (node -> fp );
272
272
if (!fp || !fp -> exit_handler )
273
273
continue ;
@@ -278,13 +278,12 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
278
278
reserved_words +=
279
279
FPROBE_HEADER_SIZE_IN_LONG + SIZE_IN_LONG (fp -> entry_data_size );
280
280
}
281
- node = first ;
282
281
if (reserved_words ) {
283
282
fgraph_data = fgraph_reserve_data (gops -> idx , reserved_words * sizeof (long ));
284
283
if (unlikely (!fgraph_data )) {
285
- hlist_for_each_entry_from_rcu (node , hlist ) {
284
+ rhl_for_each_entry_rcu (node , pos , head , hlist ) {
286
285
if (node -> addr != func )
287
- break ;
286
+ continue ;
288
287
fp = READ_ONCE (node -> fp );
289
288
if (fp && !fprobe_disabled (fp ))
290
289
fp -> nmissed ++ ;
@@ -299,12 +298,12 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
299
298
*/
300
299
ret_ip = ftrace_regs_get_return_address (fregs );
301
300
used = 0 ;
302
- hlist_for_each_entry_from_rcu (node , hlist ) {
301
+ rhl_for_each_entry_rcu (node , pos , head , hlist ) {
303
302
int data_size ;
304
303
void * data ;
305
304
306
305
if (node -> addr != func )
307
- break ;
306
+ continue ;
308
307
fp = READ_ONCE (node -> fp );
309
308
if (!fp || fprobe_disabled (fp ))
310
309
continue ;
@@ -448,34 +447,31 @@ static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long ad
448
447
return 0 ;
449
448
}
450
449
451
- static void fprobe_remove_node_in_module (struct module * mod , struct hlist_head * head ,
452
- struct fprobe_addr_list * alist )
450
+ static void fprobe_remove_node_in_module (struct module * mod , struct fprobe_hlist_node * node ,
451
+ struct fprobe_addr_list * alist )
453
452
{
454
- struct fprobe_hlist_node * node ;
455
453
int ret = 0 ;
456
454
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
- }
455
+ if (!within_module (node -> addr , mod ))
456
+ return ;
457
+ if (delete_fprobe_node (node ))
458
+ return ;
459
+ /*
460
+ * If failed to update alist, just continue to update hlist.
461
+ * Therefore, at list user handler will not hit anymore.
462
+ */
463
+ if (!ret )
464
+ ret = fprobe_addr_list_add (alist , node -> addr );
470
465
}
471
466
472
467
/* Handle module unloading to manage fprobe_ip_table. */
473
468
static int fprobe_module_callback (struct notifier_block * nb ,
474
469
unsigned long val , void * data )
475
470
{
476
471
struct fprobe_addr_list alist = {.size = FPROBE_IPS_BATCH_INIT };
472
+ struct fprobe_hlist_node * node ;
473
+ struct rhashtable_iter iter ;
477
474
struct module * mod = data ;
478
- int i ;
479
475
480
476
if (val != MODULE_STATE_GOING )
481
477
return NOTIFY_DONE ;
@@ -486,8 +482,16 @@ static int fprobe_module_callback(struct notifier_block *nb,
486
482
return NOTIFY_DONE ;
487
483
488
484
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 );
485
+ rhltable_walk_enter (& fprobe_ip_table , & iter );
486
+ do {
487
+ rhashtable_walk_start (& iter );
488
+
489
+ while ((node = rhashtable_walk_next (& iter )) && !IS_ERR (node ))
490
+ fprobe_remove_node_in_module (mod , node , & alist );
491
+
492
+ rhashtable_walk_stop (& iter );
493
+ } while (node == ERR_PTR (- EAGAIN ));
494
+ rhashtable_walk_exit (& iter );
491
495
492
496
if (alist .index < alist .size && alist .index > 0 )
493
497
ftrace_set_filter_ips (& fprobe_graph_ops .ops ,
@@ -727,8 +731,16 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
727
731
ret = fprobe_graph_add_ips (addrs , num );
728
732
if (!ret ) {
729
733
add_fprobe_hash (fp );
730
- for (i = 0 ; i < hlist_array -> size ; i ++ )
731
- insert_fprobe_node (& hlist_array -> array [i ]);
734
+ for (i = 0 ; i < hlist_array -> size ; i ++ ) {
735
+ ret = insert_fprobe_node (& hlist_array -> array [i ]);
736
+ if (ret )
737
+ break ;
738
+ }
739
+ /* fallback on insert error */
740
+ if (ret ) {
741
+ for (i -- ; i >= 0 ; i -- )
742
+ delete_fprobe_node (& hlist_array -> array [i ]);
743
+ }
732
744
}
733
745
mutex_unlock (& fprobe_mutex );
734
746
@@ -824,3 +836,10 @@ int unregister_fprobe(struct fprobe *fp)
824
836
return ret ;
825
837
}
826
838
EXPORT_SYMBOL_GPL (unregister_fprobe );
839
+
840
+ static int __init fprobe_initcall (void )
841
+ {
842
+ rhltable_init (& fprobe_ip_table , & fprobe_rht_params );
843
+ return 0 ;
844
+ }
845
+ late_initcall (fprobe_initcall );
0 commit comments