ftrace: Allow for function tracing to record init functions on boot up
authorSteven Rostedt (VMware) <rostedt@goodmis.org>
Fri, 3 Mar 2017 21:15:39 +0000 (16:15 -0500)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Sat, 25 Mar 2017 00:51:49 +0000 (20:51 -0400)
Adding a hook into free_reserve_area() that informs ftrace that boot up init
text is being free, lets ftrace safely remove those init functions from its
records, which keeps ftrace from trying to modify text that no longer
exists.

Note, this still does not allow for tracing .init text of modules, as
modules require different work for freeing its init code.

Link: http://lkml.kernel.org/r/1488502497.7212.24.camel@linux.intel.com
Cc: linux-mm@kvack.org
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Requested-by: Todd Brandt <todd.e.brandt@linux.intel.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
include/linux/ftrace.h
include/linux/init.h
kernel/trace/ftrace.c
mm/page_alloc.c
scripts/recordmcount.c
scripts/recordmcount.pl

index 569db5589851f0b5ec5d79a91f42d542a1aa4c13..0276a2c487e6276ab085a8de7276982d4a549e29 100644 (file)
@@ -146,6 +146,10 @@ struct ftrace_ops_hash {
        struct ftrace_hash              *filter_hash;
        struct mutex                    regex_lock;
 };
+
+void ftrace_free_mem(void *start, void *end);
+#else
+static inline void ftrace_free_mem(void *start, void *end) { }
 #endif
 
 /*
@@ -262,6 +266,7 @@ static inline int ftrace_nr_registered_ops(void)
 }
 static inline void clear_ftrace_function(void) { }
 static inline void ftrace_kill(void) { }
+static inline void ftrace_free_mem(void *start, void *end) { }
 #endif /* CONFIG_FUNCTION_TRACER */
 
 #ifdef CONFIG_STACK_TRACER
index 79af0962fd525980064241c653d748f2f9be63c0..94769d687cf07d8b41081f3e966d7319b5a71ee9 100644 (file)
@@ -39,7 +39,7 @@
 
 /* These are for everybody (although not all archs will actually
    discard it in modules) */
-#define __init         __section(.init.text) __cold notrace __latent_entropy
+#define __init         __section(.init.text) __cold __inittrace __latent_entropy
 #define __initdata     __section(.init.data)
 #define __initconst    __section(.init.rodata)
 #define __exitdata     __section(.exit.data)
 
 #ifdef MODULE
 #define __exitused
+#define __inittrace notrace
 #else
 #define __exitused  __used
+#define __inittrace
 #endif
 
 #define __exit          __section(.exit.text) __exitused __cold notrace
index b9691ee8f6c182cfee1af7308555b9291f3730bd..0556a202c055f3df34b8ef7690deeed4b50f14fd 100644 (file)
@@ -5262,6 +5262,50 @@ void ftrace_module_init(struct module *mod)
 }
 #endif /* CONFIG_MODULES */
 
+void ftrace_free_mem(void *start_ptr, void *end_ptr)
+{
+       unsigned long start = (unsigned long)start_ptr;
+       unsigned long end = (unsigned long)end_ptr;
+       struct ftrace_page **last_pg = &ftrace_pages_start;
+       struct ftrace_page *pg;
+       struct dyn_ftrace *rec;
+       struct dyn_ftrace key;
+       int order;
+
+       key.ip = start;
+       key.flags = end;        /* overload flags, as it is unsigned long */
+
+       mutex_lock(&ftrace_lock);
+
+       for (pg = ftrace_pages_start; pg; last_pg = &pg->next, pg = *last_pg) {
+               if (end < pg->records[0].ip ||
+                   start >= (pg->records[pg->index - 1].ip + MCOUNT_INSN_SIZE))
+                       continue;
+ again:
+               rec = bsearch(&key, pg->records, pg->index,
+                             sizeof(struct dyn_ftrace),
+                             ftrace_cmp_recs);
+               if (!rec)
+                       continue;
+               pg->index--;
+               if (!pg->index) {
+                       *last_pg = pg->next;
+                       order = get_count_order(pg->size / ENTRIES_PER_PAGE);
+                       free_pages((unsigned long)pg->records, order);
+                       kfree(pg);
+                       pg = container_of(last_pg, struct ftrace_page, next);
+                       if (!(*last_pg))
+                               ftrace_pages = pg;
+                       continue;
+               }
+               memmove(rec, rec + 1,
+                       (pg->index - (rec - pg->records)) * sizeof(*rec));
+               /* More than one function may be in this block */
+               goto again;
+       }
+       mutex_unlock(&ftrace_lock);
+}
+
 void __init ftrace_init(void)
 {
        extern unsigned long __start_mcount_loc[];
index 6cbde310abed8df22f9cd6ed80fcc252f4c80f43..eee82bfb7cd8e4c456298810e22520b3bfc5c2d5 100644 (file)
@@ -65,6 +65,7 @@
 #include <linux/page_owner.h>
 #include <linux/kthread.h>
 #include <linux/memcontrol.h>
+#include <linux/ftrace.h>
 
 #include <asm/sections.h>
 #include <asm/tlbflush.h>
@@ -6605,6 +6606,9 @@ unsigned long free_reserved_area(void *start, void *end, int poison, char *s)
        void *pos;
        unsigned long pages = 0;
 
+       /* This may be .init text, inform ftrace to remove it */
+       ftrace_free_mem(start, end);
+
        start = (void *)PAGE_ALIGN((unsigned long)start);
        end = (void *)((unsigned long)end & PAGE_MASK);
        for (pos = start; pos < end; pos += PAGE_SIZE, pages++) {
index aeb34223167c1a5ac9a2e46a7e2c346360cf93f7..16e086dcc56762dd7bc8e6ccd78324e43320d829 100644 (file)
@@ -412,6 +412,7 @@ static int
 is_mcounted_section_name(char const *const txtname)
 {
        return strcmp(".text",           txtname) == 0 ||
+               strcmp(".init.text",     txtname) == 0 ||
                strcmp(".ref.text",      txtname) == 0 ||
                strcmp(".sched.text",    txtname) == 0 ||
                strcmp(".spinlock.text", txtname) == 0 ||
index 0b6002b36f204f89fa0b0f1c23390fb54307fd21..1633c3e6c0b95af1933339b195b2f68527c639d8 100755 (executable)
@@ -130,6 +130,7 @@ if ($inputfile =~ m,kernel/trace/ftrace\.o$,) {
 # Acceptable sections to record.
 my %text_sections = (
      ".text" => 1,
+     ".init.text" => 1,
      ".ref.text" => 1,
      ".sched.text" => 1,
      ".spinlock.text" => 1,