diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 45 |
1 files changed, 42 insertions, 3 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index a15e068535f8..8e02aa690b2b 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -188,6 +188,8 @@ static int ftraced_suspend; | |||
188 | 188 | ||
189 | static int ftrace_record_suspend; | 189 | static int ftrace_record_suspend; |
190 | 190 | ||
191 | static struct dyn_ftrace *ftrace_free_records; | ||
192 | |||
191 | static inline int | 193 | static inline int |
192 | notrace ftrace_ip_in_hash(unsigned long ip, unsigned long key) | 194 | notrace ftrace_ip_in_hash(unsigned long ip, unsigned long key) |
193 | { | 195 | { |
@@ -211,8 +213,35 @@ ftrace_add_hash(struct dyn_ftrace *node, unsigned long key) | |||
211 | hlist_add_head(&node->node, &ftrace_hash[key]); | 213 | hlist_add_head(&node->node, &ftrace_hash[key]); |
212 | } | 214 | } |
213 | 215 | ||
216 | static notrace void ftrace_free_rec(struct dyn_ftrace *rec) | ||
217 | { | ||
218 | /* no locking, only called from kstop_machine */ | ||
219 | |||
220 | rec->ip = (unsigned long)ftrace_free_records; | ||
221 | ftrace_free_records = rec; | ||
222 | rec->flags |= FTRACE_FL_FREE; | ||
223 | } | ||
224 | |||
214 | static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | 225 | static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) |
215 | { | 226 | { |
227 | struct dyn_ftrace *rec; | ||
228 | |||
229 | /* First check for freed records */ | ||
230 | if (ftrace_free_records) { | ||
231 | rec = ftrace_free_records; | ||
232 | |||
233 | /* todo, disable tracing altogether on this warning */ | ||
234 | if (unlikely(!(rec->flags & FTRACE_FL_FREE))) { | ||
235 | WARN_ON_ONCE(1); | ||
236 | ftrace_free_records = NULL; | ||
237 | return NULL; | ||
238 | } | ||
239 | |||
240 | ftrace_free_records = (void *)rec->ip; | ||
241 | memset(rec, 0, sizeof(*rec)); | ||
242 | return rec; | ||
243 | } | ||
244 | |||
216 | if (ftrace_pages->index == ENTRIES_PER_PAGE) { | 245 | if (ftrace_pages->index == ENTRIES_PER_PAGE) { |
217 | if (!ftrace_pages->next) | 246 | if (!ftrace_pages->next) |
218 | return NULL; | 247 | return NULL; |
@@ -356,8 +385,16 @@ __ftrace_replace_code(struct dyn_ftrace *rec, | |||
356 | } | 385 | } |
357 | 386 | ||
358 | failed = ftrace_modify_code(ip, old, new); | 387 | failed = ftrace_modify_code(ip, old, new); |
359 | if (failed) | 388 | if (failed) { |
360 | rec->flags |= FTRACE_FL_FAILED; | 389 | unsigned long key; |
390 | /* It is possible that the function hasn't been converted yet */ | ||
391 | key = hash_long(ip, FTRACE_HASHBITS); | ||
392 | if (!ftrace_ip_in_hash(ip, key)) { | ||
393 | rec->flags |= FTRACE_FL_FAILED; | ||
394 | ftrace_free_rec(rec); | ||
395 | } | ||
396 | |||
397 | } | ||
361 | } | 398 | } |
362 | 399 | ||
363 | static void notrace ftrace_replace_code(int enable) | 400 | static void notrace ftrace_replace_code(int enable) |
@@ -407,8 +444,10 @@ ftrace_code_disable(struct dyn_ftrace *rec) | |||
407 | call = ftrace_call_replace(ip, MCOUNT_ADDR); | 444 | call = ftrace_call_replace(ip, MCOUNT_ADDR); |
408 | 445 | ||
409 | failed = ftrace_modify_code(ip, call, nop); | 446 | failed = ftrace_modify_code(ip, call, nop); |
410 | if (failed) | 447 | if (failed) { |
411 | rec->flags |= FTRACE_FL_FAILED; | 448 | rec->flags |= FTRACE_FL_FAILED; |
449 | ftrace_free_rec(rec); | ||
450 | } | ||
412 | } | 451 | } |
413 | 452 | ||
414 | static int notrace __ftrace_modify_code(void *data) | 453 | static int notrace __ftrace_modify_code(void *data) |