@@ -1020,12 +1020,6 @@ static bool update_all_ops;
1020
1020
# error Dynamic ftrace depends on MCOUNT_RECORD
1021
1021
#endif
1022
1022
1023
- struct ftrace_func_entry {
1024
- struct hlist_node hlist ;
1025
- unsigned long ip ;
1026
- unsigned long direct ; /* for direct lookup only */
1027
- };
1028
-
1029
1023
struct ftrace_func_probe {
1030
1024
struct ftrace_probe_ops * probe_ops ;
1031
1025
struct ftrace_ops ops ;
@@ -5112,7 +5106,8 @@ int register_ftrace_direct(unsigned long ip, unsigned long addr)
5112
5106
}
5113
5107
EXPORT_SYMBOL_GPL (register_ftrace_direct );
5114
5108
5115
- static struct ftrace_func_entry * find_direct_entry (unsigned long * ip )
5109
+ static struct ftrace_func_entry * find_direct_entry (unsigned long * ip ,
5110
+ struct dyn_ftrace * * recp )
5116
5111
{
5117
5112
struct ftrace_func_entry * entry ;
5118
5113
struct dyn_ftrace * rec ;
@@ -5132,6 +5127,9 @@ static struct ftrace_func_entry *find_direct_entry(unsigned long *ip)
5132
5127
/* Passed in ip just needs to be on the call site */
5133
5128
* ip = rec -> ip ;
5134
5129
5130
+ if (recp )
5131
+ * recp = rec ;
5132
+
5135
5133
return entry ;
5136
5134
}
5137
5135
@@ -5143,7 +5141,7 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
5143
5141
5144
5142
mutex_lock (& direct_mutex );
5145
5143
5146
- entry = find_direct_entry (& ip );
5144
+ entry = find_direct_entry (& ip , NULL );
5147
5145
if (!entry )
5148
5146
goto out_unlock ;
5149
5147
@@ -5179,6 +5177,75 @@ static struct ftrace_ops stub_ops = {
5179
5177
.func = ftrace_stub ,
5180
5178
};
5181
5179
5180
+ /**
5181
+ * ftrace_modify_direct_caller - modify ftrace nop directly
5182
+ * @entry: The ftrace hash entry of the direct helper for @rec
5183
+ * @rec: The record representing the function site to patch
5184
+ * @old_addr: The location that the site at @rec->ip currently calls
5185
+ * @new_addr: The location that the site at @rec->ip should call
5186
+ *
5187
+ * An architecture may overwrite this function to optimize the
5188
+ * changing of the direct callback on an ftrace nop location.
5189
+ * This is called with the ftrace_lock mutex held, and no other
5190
+ * ftrace callbacks are on the associated record (@rec). Thus,
5191
+ * it is safe to modify the ftrace record, where it should be
5192
+ * currently calling @old_addr directly, to call @new_addr.
5193
+ *
5194
+ * Safety checks should be made to make sure that the code at
5195
+ * @rec->ip is currently calling @old_addr. And this must
5196
+ * also update entry->direct to @new_addr.
5197
+ */
5198
+ int __weak ftrace_modify_direct_caller (struct ftrace_func_entry * entry ,
5199
+ struct dyn_ftrace * rec ,
5200
+ unsigned long old_addr ,
5201
+ unsigned long new_addr )
5202
+ {
5203
+ unsigned long ip = rec -> ip ;
5204
+ int ret ;
5205
+
5206
+ /*
5207
+ * The ftrace_lock was used to determine if the record
5208
+ * had more than one registered user to it. If it did,
5209
+ * we needed to prevent that from changing to do the quick
5210
+ * switch. But if it did not (only a direct caller was attached)
5211
+ * then this function is called. But this function can deal
5212
+ * with attached callers to the rec that we care about, and
5213
+ * since this function uses standard ftrace calls that take
5214
+ * the ftrace_lock mutex, we need to release it.
5215
+ */
5216
+ mutex_unlock (& ftrace_lock );
5217
+
5218
+ /*
5219
+ * By setting a stub function at the same address, we force
5220
+ * the code to call the iterator and the direct_ops helper.
5221
+ * This means that @ip does not call the direct call, and
5222
+ * we can simply modify it.
5223
+ */
5224
+ ret = ftrace_set_filter_ip (& stub_ops , ip , 0 , 0 );
5225
+ if (ret )
5226
+ goto out_lock ;
5227
+
5228
+ ret = register_ftrace_function (& stub_ops );
5229
+ if (ret ) {
5230
+ ftrace_set_filter_ip (& stub_ops , ip , 1 , 0 );
5231
+ goto out_lock ;
5232
+ }
5233
+
5234
+ entry -> direct = new_addr ;
5235
+
5236
+ /*
5237
+ * By removing the stub, we put back the direct call, calling
5238
+ * the @new_addr.
5239
+ */
5240
+ unregister_ftrace_function (& stub_ops );
5241
+ ftrace_set_filter_ip (& stub_ops , ip , 1 , 0 );
5242
+
5243
+ out_lock :
5244
+ mutex_lock (& ftrace_lock );
5245
+
5246
+ return ret ;
5247
+ }
5248
+
5182
5249
/**
5183
5250
* modify_ftrace_direct - Modify an existing direct call to call something else
5184
5251
* @ip: The instruction pointer to modify
@@ -5197,11 +5264,13 @@ int modify_ftrace_direct(unsigned long ip,
5197
5264
unsigned long old_addr , unsigned long new_addr )
5198
5265
{
5199
5266
struct ftrace_func_entry * entry ;
5267
+ struct dyn_ftrace * rec ;
5200
5268
int ret = - ENODEV ;
5201
5269
5202
5270
mutex_lock (& direct_mutex );
5203
5271
5204
- entry = find_direct_entry (& ip );
5272
+ mutex_lock (& ftrace_lock );
5273
+ entry = find_direct_entry (& ip , & rec );
5205
5274
if (!entry )
5206
5275
goto out_unlock ;
5207
5276
@@ -5210,33 +5279,20 @@ int modify_ftrace_direct(unsigned long ip,
5210
5279
goto out_unlock ;
5211
5280
5212
5281
/*
5213
- * By setting a stub function at the same address, we force
5214
- * the code to call the iterator and the direct_ops helper .
5215
- * This means that @ip does not call the direct call, and
5216
- * we can simply modify it .
5282
+ * If there's no other ftrace callback on the rec->ip location,
5283
+ * then it can be changed directly by the architecture .
5284
+ * If there is another caller, then we just need to change the
5285
+ * direct caller helper to point to @new_addr .
5217
5286
*/
5218
- ret = ftrace_set_filter_ip (& stub_ops , ip , 0 , 0 );
5219
- if (ret )
5220
- goto out_unlock ;
5221
-
5222
- ret = register_ftrace_function (& stub_ops );
5223
- if (ret ) {
5224
- ftrace_set_filter_ip (& stub_ops , ip , 1 , 0 );
5225
- goto out_unlock ;
5287
+ if (ftrace_rec_count (rec ) == 1 ) {
5288
+ ret = ftrace_modify_direct_caller (entry , rec , old_addr , new_addr );
5289
+ } else {
5290
+ entry -> direct = new_addr ;
5291
+ ret = 0 ;
5226
5292
}
5227
5293
5228
- entry -> direct = new_addr ;
5229
-
5230
- /*
5231
- * By removing the stub, we put back the direct call, calling
5232
- * the @new_addr.
5233
- */
5234
- unregister_ftrace_function (& stub_ops );
5235
- ftrace_set_filter_ip (& stub_ops , ip , 1 , 0 );
5236
-
5237
- ret = 0 ;
5238
-
5239
5294
out_unlock :
5295
+ mutex_unlock (& ftrace_lock );
5240
5296
mutex_unlock (& direct_mutex );
5241
5297
return ret ;
5242
5298
}
0 commit comments