summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ftrace.h7
-rw-r--r--kernel/trace/ftrace.c45
2 files changed, 46 insertions, 6 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index a842d96c6343..61e757bd2350 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -43,9 +43,10 @@ extern void mcount(void);
# define FTRACE_HASHSIZE (1<<FTRACE_HASHBITS)
enum {
- FTRACE_FL_FAILED = (1 << 0),
- FTRACE_FL_FILTER = (1 << 1),
- FTRACE_FL_ENABLED = (1 << 2),
+ FTRACE_FL_FREE = (1 << 0),
+ FTRACE_FL_FAILED = (1 << 1),
+ FTRACE_FL_FILTER = (1 << 2),
+ FTRACE_FL_ENABLED = (1 << 3),
};
struct dyn_ftrace {
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;
static int ftrace_record_suspend;
+static struct dyn_ftrace *ftrace_free_records;
+
static inline int
notrace ftrace_ip_in_hash(unsigned long ip, unsigned long key)
{
@@ -211,8 +213,35 @@ ftrace_add_hash(struct dyn_ftrace *node, unsigned long key)
hlist_add_head(&node->node, &ftrace_hash[key]);
}
+static notrace void ftrace_free_rec(struct dyn_ftrace *rec)
+{
+ /* no locking, only called from kstop_machine */
+
+ rec->ip = (unsigned long)ftrace_free_records;
+ ftrace_free_records = rec;
+ rec->flags |= FTRACE_FL_FREE;
+}
+
static notrace struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip)
{
+ struct dyn_ftrace *rec;
+
+ /* First check for freed records */
+ if (ftrace_free_records) {
+ rec = ftrace_free_records;
+
+ /* todo, disable tracing altogether on this warning */
+ if (unlikely(!(rec->flags & FTRACE_FL_FREE))) {
+ WARN_ON_ONCE(1);
+ ftrace_free_records = NULL;
+ return NULL;
+ }
+
+ ftrace_free_records = (void *)rec->ip;
+ memset(rec, 0, sizeof(*rec));
+ return rec;
+ }
+
if (ftrace_pages->index == ENTRIES_PER_PAGE) {
if (!ftrace_pages->next)
return NULL;
@@ -356,8 +385,16 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
}
failed = ftrace_modify_code(ip, old, new);
- if (failed)
- rec->flags |= FTRACE_FL_FAILED;
+ if (failed) {
+ unsigned long key;
+ /* It is possible that the function hasn't been converted yet */
+ key = hash_long(ip, FTRACE_HASHBITS);
+ if (!ftrace_ip_in_hash(ip, key)) {
+ rec->flags |= FTRACE_FL_FAILED;
+ ftrace_free_rec(rec);
+ }
+
+ }
}
static void notrace ftrace_replace_code(int enable)
@@ -407,8 +444,10 @@ ftrace_code_disable(struct dyn_ftrace *rec)
call = ftrace_call_replace(ip, MCOUNT_ADDR);
failed = ftrace_modify_code(ip, call, nop);
- if (failed)
+ if (failed) {
rec->flags |= FTRACE_FL_FAILED;
+ ftrace_free_rec(rec);
+ }
}
static int notrace __ftrace_modify_code(void *data)