@@ -6121,21 +6121,10 @@ static struct ftrace_hash *hash_add(struct ftrace_hash *a, struct ftrace_hash *b
61216121 return NULL ;
61226122}
61236123
6124- static void reset_direct_hash (struct ftrace_ops * ops , struct ftrace_hash * * hashp )
6125- {
6126- struct ftrace_hash * free_hash = direct_functions ;
6127-
6128- rcu_assign_pointer (direct_functions , * hashp );
6129- * hashp = free_hash ;
6130-
6131- /* cleanup for possible another register call */
6132- ops -> func = NULL ;
6133- ops -> trampoline = 0 ;
6134- }
6135-
61366124int update_ftrace_direct_add (struct ftrace_ops * ops , struct ftrace_hash * hash )
61376125{
6138- struct ftrace_hash * filter_hash = NULL , * new_hash = NULL , * free_hash = NULL ;
6126+ struct ftrace_hash * old_direct_functions = NULL , * new_direct_functions = NULL ;
6127+ struct ftrace_hash * old_filter_hash = NULL , * new_filter_hash = NULL ;
61396128 struct ftrace_func_entry * entry ;
61406129 int i , size , err = - EINVAL ;
61416130 bool reg ;
@@ -6154,10 +6143,11 @@ int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
61546143 }
61556144 }
61566145
6157- filter_hash = ops -> func_hash ? ops -> func_hash -> filter_hash : NULL ;
6146+ old_filter_hash = ops -> func_hash ? ops -> func_hash -> filter_hash : NULL ;
6147+ old_direct_functions = direct_functions ;
61586148
61596149 /* If there's nothing in filter_hash we need to register the ops. */
6160- reg = hash_count (filter_hash ) == 0 ;
6150+ reg = hash_count (old_filter_hash ) == 0 ;
61616151 if (reg ) {
61626152 if (ops -> func || ops -> trampoline )
61636153 goto out_unlock ;
@@ -6166,40 +6156,54 @@ int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
61666156 }
61676157
61686158 err = - ENOMEM ;
6169- filter_hash = hash_add (filter_hash , hash );
6170- if (!filter_hash )
6159+ new_filter_hash = hash_add (old_filter_hash , hash );
6160+ if (!new_filter_hash )
61716161 goto out_unlock ;
61726162
6173- new_hash = hash_add (direct_functions , hash );
6174- if (!new_hash )
6163+ new_direct_functions = hash_add (old_direct_functions , hash );
6164+ if (!new_direct_functions )
61756165 goto out_unlock ;
61766166
6177- free_hash = direct_functions ;
6178- rcu_assign_pointer (direct_functions , new_hash );
6167+ rcu_assign_pointer (direct_functions , new_direct_functions );
61796168
61806169 if (reg ) {
61816170 ops -> func = call_direct_funcs ;
6182- ops -> flags = MULTI_FLAGS ;
6171+ ops -> flags | = MULTI_FLAGS ;
61836172 ops -> trampoline = FTRACE_REGS_ADDR ;
6184- ops -> local_hash .filter_hash = filter_hash ;
6173+ ops -> local_hash .filter_hash = new_filter_hash ;
61856174
61866175 err = register_ftrace_function_nolock (ops );
6187- if (!err )
6188- filter_hash = NULL ;
6176+ if (err ) {
6177+ /* restore old filter on error */
6178+ ops -> local_hash .filter_hash = old_filter_hash ;
6179+ old_filter_hash = new_filter_hash ;
6180+
6181+ /* cleanup for possible another register call */
6182+ ops -> func = NULL ;
6183+ ops -> trampoline = 0 ;
6184+ }
61896185 } else {
6190- err = ftrace_update_ops (ops , filter_hash , EMPTY_HASH );
6186+ err = ftrace_update_ops (ops , new_filter_hash , EMPTY_HASH );
6187+ /*
6188+ * new_filter_hash is dup-ed, so we need to release it anyway,
6189+ * old_filter_hash either stays on error or is released already
6190+ */
6191+ old_filter_hash = new_filter_hash ;
61916192 }
61926193
6193- if (err )
6194- reset_direct_hash (ops , & free_hash );
6194+ if (err ) {
6195+ /* reset direct_functions and free the new one */
6196+ rcu_assign_pointer (direct_functions , old_direct_functions );
6197+ old_direct_functions = new_direct_functions ;
6198+ }
61956199
61966200 out_unlock :
61976201 mutex_unlock (& direct_mutex );
61986202
6199- if (free_hash && free_hash != EMPTY_HASH )
6200- call_rcu_tasks (& free_hash -> rcu , register_ftrace_direct_cb );
6201- if (filter_hash )
6202- free_ftrace_hash (filter_hash );
6203+ if (old_direct_functions && old_direct_functions != EMPTY_HASH )
6204+ call_rcu_tasks (& old_direct_functions -> rcu , register_ftrace_direct_cb );
6205+ if (old_filter_hash )
6206+ free_ftrace_hash (old_filter_hash );
62036207
62046208 return err ;
62056209}
0 commit comments