@@ -5160,6 +5160,84 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
5160
5160
return ret ;
5161
5161
}
5162
5162
EXPORT_SYMBOL_GPL (unregister_ftrace_direct );
5163
+
5164
+ static struct ftrace_ops stub_ops = {
5165
+ .func = ftrace_stub ,
5166
+ };
5167
+
5168
+ /**
5169
+ * modify_ftrace_direct - Modify an existing direct call to call something else
5170
+ * @ip: The instruction pointer to modify
5171
+ * @old_addr: The address that the current @ip calls directly
5172
+ * @new_addr: The address that the @ip should call
5173
+ *
5174
+ * This modifies a ftrace direct caller at an instruction pointer without
5175
+ * having to disable it first. The direct call will switch over to the
5176
+ * @new_addr without missing anything.
5177
+ *
5178
+ * Returns: zero on success. Non zero on error, which includes:
5179
+ * -ENODEV : the @ip given has no direct caller attached
5180
+ * -EINVAL : the @old_addr does not match the current direct caller
5181
+ */
5182
+ int modify_ftrace_direct (unsigned long ip ,
5183
+ unsigned long old_addr , unsigned long new_addr )
5184
+ {
5185
+ struct ftrace_func_entry * entry ;
5186
+ struct dyn_ftrace * rec ;
5187
+ int ret = - ENODEV ;
5188
+
5189
+ mutex_lock (& direct_mutex );
5190
+ entry = __ftrace_lookup_ip (direct_functions , ip );
5191
+ if (!entry ) {
5192
+ /* OK if it is off by a little */
5193
+ rec = lookup_rec (ip , ip );
5194
+ if (!rec || rec -> ip == ip )
5195
+ goto out_unlock ;
5196
+
5197
+ entry = __ftrace_lookup_ip (direct_functions , rec -> ip );
5198
+ if (!entry )
5199
+ goto out_unlock ;
5200
+
5201
+ ip = rec -> ip ;
5202
+ WARN_ON (!(rec -> flags & FTRACE_FL_DIRECT ));
5203
+ }
5204
+
5205
+ ret = - EINVAL ;
5206
+ if (entry -> direct != old_addr )
5207
+ goto out_unlock ;
5208
+
5209
+ /*
5210
+ * By setting a stub function at the same address, we force
5211
+ * the code to call the iterator and the direct_ops helper.
5212
+ * This means that @ip does not call the direct call, and
5213
+ * we can simply modify it.
5214
+ */
5215
+ ret = ftrace_set_filter_ip (& stub_ops , ip , 0 , 0 );
5216
+ if (ret )
5217
+ goto out_unlock ;
5218
+
5219
+ ret = register_ftrace_function (& stub_ops );
5220
+ if (ret ) {
5221
+ ftrace_set_filter_ip (& stub_ops , ip , 1 , 0 );
5222
+ goto out_unlock ;
5223
+ }
5224
+
5225
+ entry -> direct = new_addr ;
5226
+
5227
+ /*
5228
+ * By removing the stub, we put back the direct call, calling
5229
+ * the @new_addr.
5230
+ */
5231
+ unregister_ftrace_function (& stub_ops );
5232
+ ftrace_set_filter_ip (& stub_ops , ip , 1 , 0 );
5233
+
5234
+ ret = 0 ;
5235
+
5236
+ out_unlock :
5237
+ mutex_unlock (& direct_mutex );
5238
+ return ret ;
5239
+ }
5240
+ EXPORT_SYMBOL_GPL (modify_ftrace_direct );
5163
5241
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
5164
5242
5165
5243
/**
0 commit comments