aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/ftrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r--kernel/trace/ftrace.c45
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
189static int ftrace_record_suspend; 189static int ftrace_record_suspend;
190 190
191static struct dyn_ftrace *ftrace_free_records;
192
191static inline int 193static inline int
192notrace ftrace_ip_in_hash(unsigned long ip, unsigned long key) 194notrace 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
216static 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
214static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) 225static 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
363static void notrace ftrace_replace_code(int enable) 400static 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
414static int notrace __ftrace_modify_code(void *data) 453static int notrace __ftrace_modify_code(void *data)