4141 * - RCU hlist traversal under disabling preempt
4242 */
4343static 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 ;
4545static 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
249250static 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. */
473471static 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}
826841EXPORT_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