提交 31e88909 编写于 作者: S Steven Rostedt 提交者: Ingo Molnar

ftrace: pass module struct to arch dynamic ftrace functions

Impact: allow archs more flexibility on dynamic ftrace implementations

Dynamic ftrace has largly been developed on x86. Since x86 does not
have the same limitations as other architectures, the ftrace interaction
between the generic code and the architecture specific code was not
flexible enough to handle some of the issues that other architectures
have.

Most notably, module trampolines. Due to the limited branch distance
that archs make in calling kernel core code from modules, the module
load code must create a trampoline to jump to what will make the
larger jump into core kernel code.

The problem arises when this happens to a call to mcount. Ftrace checks
all code before modifying it and makes sure the current code is what
it expects. Right now, there is not enough information to handle modifying
module trampolines.

This patch changes the API between generic dynamic ftrace code and
the arch dependent code. There is now two functions for modifying code:

  ftrace_make_nop(mod, rec, addr) - convert the code at rec->ip into
       a nop, where the original text is calling addr. (mod is the
       module struct if called by module init)

  ftrace_make_caller(rec, addr) - convert the code rec->ip that should
       be a nop into a caller to addr.

The record "rec" now has a new field called "arch" where the architecture
can add any special attributes to each call site record.
Signed-off-by: NSteven Rostedt <srostedt@redhat.com>
Signed-off-by: NIngo Molnar <mingo@elte.hu>
上级 d51ad7ac
...@@ -17,6 +17,14 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) ...@@ -17,6 +17,14 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
*/ */
return addr - 1; return addr - 1;
} }
#ifdef CONFIG_DYNAMIC_FTRACE
struct dyn_arch_ftrace {
/* No extra data needed for x86 */
};
#endif /* CONFIG_DYNAMIC_FTRACE */
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* CONFIG_FUNCTION_TRACER */ #endif /* CONFIG_FUNCTION_TRACER */
......
...@@ -166,7 +166,7 @@ static int ftrace_calc_offset(long ip, long addr) ...@@ -166,7 +166,7 @@ static int ftrace_calc_offset(long ip, long addr)
return (int)(addr - ip); return (int)(addr - ip);
} }
unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
{ {
static union ftrace_code_union calc; static union ftrace_code_union calc;
...@@ -311,12 +311,12 @@ do_ftrace_mod_code(unsigned long ip, void *new_code) ...@@ -311,12 +311,12 @@ do_ftrace_mod_code(unsigned long ip, void *new_code)
static unsigned char ftrace_nop[MCOUNT_INSN_SIZE]; static unsigned char ftrace_nop[MCOUNT_INSN_SIZE];
unsigned char *ftrace_nop_replace(void) static unsigned char *ftrace_nop_replace(void)
{ {
return ftrace_nop; return ftrace_nop;
} }
int static int
ftrace_modify_code(unsigned long ip, unsigned char *old_code, ftrace_modify_code(unsigned long ip, unsigned char *old_code,
unsigned char *new_code) unsigned char *new_code)
{ {
...@@ -349,6 +349,29 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, ...@@ -349,6 +349,29 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
return 0; return 0;
} }
int ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr)
{
unsigned char *new, *old;
unsigned long ip = rec->ip;
old = ftrace_call_replace(ip, addr);
new = ftrace_nop_replace();
return ftrace_modify_code(rec->ip, old, new);
}
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
unsigned char *new, *old;
unsigned long ip = rec->ip;
old = ftrace_nop_replace();
new = ftrace_call_replace(ip, addr);
return ftrace_modify_code(rec->ip, old, new);
}
int ftrace_update_ftrace_func(ftrace_func_t func) int ftrace_update_ftrace_func(ftrace_func_t func)
{ {
unsigned long ip = (unsigned long)(&ftrace_call); unsigned long ip = (unsigned long)(&ftrace_call);
......
...@@ -74,6 +74,9 @@ static inline void ftrace_start(void) { } ...@@ -74,6 +74,9 @@ static inline void ftrace_start(void) { }
#endif /* CONFIG_FUNCTION_TRACER */ #endif /* CONFIG_FUNCTION_TRACER */
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
#include <asm/ftrace.h>
enum { enum {
FTRACE_FL_FREE = (1 << 0), FTRACE_FL_FREE = (1 << 0),
FTRACE_FL_FAILED = (1 << 1), FTRACE_FL_FAILED = (1 << 1),
...@@ -88,6 +91,7 @@ struct dyn_ftrace { ...@@ -88,6 +91,7 @@ struct dyn_ftrace {
struct list_head list; struct list_head list;
unsigned long ip; /* address of mcount call-site */ unsigned long ip; /* address of mcount call-site */
unsigned long flags; unsigned long flags;
struct dyn_arch_ftrace arch;
}; };
int ftrace_force_update(void); int ftrace_force_update(void);
...@@ -95,22 +99,40 @@ void ftrace_set_filter(unsigned char *buf, int len, int reset); ...@@ -95,22 +99,40 @@ void ftrace_set_filter(unsigned char *buf, int len, int reset);
/* defined in arch */ /* defined in arch */
extern int ftrace_ip_converted(unsigned long ip); extern int ftrace_ip_converted(unsigned long ip);
extern unsigned char *ftrace_nop_replace(void);
extern unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr);
extern int ftrace_dyn_arch_init(void *data); extern int ftrace_dyn_arch_init(void *data);
extern int ftrace_update_ftrace_func(ftrace_func_t func); extern int ftrace_update_ftrace_func(ftrace_func_t func);
extern void ftrace_caller(void); extern void ftrace_caller(void);
extern void ftrace_call(void); extern void ftrace_call(void);
extern void mcount_call(void); extern void mcount_call(void);
/* May be defined in arch */ /**
extern int ftrace_arch_read_dyn_info(char *buf, int size); * ftrace_make_nop - convert code into top
* @mod: module structure if called by module load initialization
* @rec: the mcount call site record
* @addr: the address that the call site should be calling
*
* This is a very sensitive operation and great care needs
* to be taken by the arch. The operation should carefully
* read the location, check to see if what is read is indeed
* what we expect it to be, and then on success of the compare,
* it should write to the location.
*
* The code segment at @rec->ip should be a caller to @addr
*
* Return must be:
* 0 on success
* -EFAULT on error reading the location
* -EINVAL on a failed compare of the contents
* -EPERM on error writing to the location
* Any other value will be considered a failure.
*/
extern int ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr);
/** /**
* ftrace_modify_code - modify code segment * ftrace_make_call - convert a nop call site into a call to addr
* @ip: the address of the code segment * @rec: the mcount call site record
* @old_code: the contents of what is expected to be there * @addr: the address that the call site should call
* @new_code: the code to patch in
* *
* This is a very sensitive operation and great care needs * This is a very sensitive operation and great care needs
* to be taken by the arch. The operation should carefully * to be taken by the arch. The operation should carefully
...@@ -118,6 +140,8 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size); ...@@ -118,6 +140,8 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size);
* what we expect it to be, and then on success of the compare, * what we expect it to be, and then on success of the compare,
* it should write to the location. * it should write to the location.
* *
* The code segment at @rec->ip should be a nop
*
* Return must be: * Return must be:
* 0 on success * 0 on success
* -EFAULT on error reading the location * -EFAULT on error reading the location
...@@ -125,8 +149,11 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size); ...@@ -125,8 +149,11 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size);
* -EPERM on error writing to the location * -EPERM on error writing to the location
* Any other value will be considered a failure. * Any other value will be considered a failure.
*/ */
extern int ftrace_modify_code(unsigned long ip, unsigned char *old_code, extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr);
unsigned char *new_code);
/* May be defined in arch */
extern int ftrace_arch_read_dyn_info(char *buf, int size);
extern int skip_trace(unsigned long ip); extern int skip_trace(unsigned long ip);
...@@ -259,11 +286,13 @@ static inline void ftrace_dump(void) { } ...@@ -259,11 +286,13 @@ static inline void ftrace_dump(void) { }
#ifdef CONFIG_FTRACE_MCOUNT_RECORD #ifdef CONFIG_FTRACE_MCOUNT_RECORD
extern void ftrace_init(void); extern void ftrace_init(void);
extern void ftrace_init_module(unsigned long *start, unsigned long *end); extern void ftrace_init_module(struct module *mod,
unsigned long *start, unsigned long *end);
#else #else
static inline void ftrace_init(void) { } static inline void ftrace_init(void) { }
static inline void static inline void
ftrace_init_module(unsigned long *start, unsigned long *end) { } ftrace_init_module(struct module *mod,
unsigned long *start, unsigned long *end) { }
#endif #endif
......
...@@ -2201,7 +2201,7 @@ static noinline struct module *load_module(void __user *umod, ...@@ -2201,7 +2201,7 @@ static noinline struct module *load_module(void __user *umod,
/* sechdrs[0].sh_size is always zero */ /* sechdrs[0].sh_size is always zero */
mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc", mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc",
sizeof(*mseg), &num_mcount); sizeof(*mseg), &num_mcount);
ftrace_init_module(mseg, mseg + num_mcount); ftrace_init_module(mod, mseg, mseg + num_mcount);
err = module_finalize(hdr, sechdrs, mod); err = module_finalize(hdr, sechdrs, mod);
if (err < 0) if (err < 0)
......
...@@ -358,9 +358,7 @@ static void print_ip_ins(const char *fmt, unsigned char *p) ...@@ -358,9 +358,7 @@ static void print_ip_ins(const char *fmt, unsigned char *p)
printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]); printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]);
} }
static void ftrace_bug(int failed, unsigned long ip, static void ftrace_bug(int failed, unsigned long ip)
unsigned char *expected,
unsigned char *replace)
{ {
switch (failed) { switch (failed) {
case -EFAULT: case -EFAULT:
...@@ -372,9 +370,7 @@ static void ftrace_bug(int failed, unsigned long ip, ...@@ -372,9 +370,7 @@ static void ftrace_bug(int failed, unsigned long ip,
FTRACE_WARN_ON_ONCE(1); FTRACE_WARN_ON_ONCE(1);
pr_info("ftrace failed to modify "); pr_info("ftrace failed to modify ");
print_ip_sym(ip); print_ip_sym(ip);
print_ip_ins(" expected: ", expected);
print_ip_ins(" actual: ", (unsigned char *)ip); print_ip_ins(" actual: ", (unsigned char *)ip);
print_ip_ins(" replace: ", replace);
printk(KERN_CONT "\n"); printk(KERN_CONT "\n");
break; break;
case -EPERM: case -EPERM:
...@@ -392,8 +388,7 @@ static void ftrace_bug(int failed, unsigned long ip, ...@@ -392,8 +388,7 @@ static void ftrace_bug(int failed, unsigned long ip,
#define FTRACE_ADDR ((long)(ftrace_caller)) #define FTRACE_ADDR ((long)(ftrace_caller))
static int static int
__ftrace_replace_code(struct dyn_ftrace *rec, __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
unsigned char *old, unsigned char *new, int enable)
{ {
unsigned long ip, fl; unsigned long ip, fl;
...@@ -435,12 +430,10 @@ __ftrace_replace_code(struct dyn_ftrace *rec, ...@@ -435,12 +430,10 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
* otherwise enable it! * otherwise enable it!
*/ */
if (fl & FTRACE_FL_ENABLED) { if (fl & FTRACE_FL_ENABLED) {
/* swap new and old */ enable = 0;
new = old;
old = ftrace_call_replace(ip, FTRACE_ADDR);
rec->flags &= ~FTRACE_FL_ENABLED; rec->flags &= ~FTRACE_FL_ENABLED;
} else { } else {
new = ftrace_call_replace(ip, FTRACE_ADDR); enable = 1;
rec->flags |= FTRACE_FL_ENABLED; rec->flags |= FTRACE_FL_ENABLED;
} }
} else { } else {
...@@ -453,10 +446,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, ...@@ -453,10 +446,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED);
if (fl == FTRACE_FL_NOTRACE) if (fl == FTRACE_FL_NOTRACE)
return 0; return 0;
}
new = ftrace_call_replace(ip, FTRACE_ADDR);
} else
old = ftrace_call_replace(ip, FTRACE_ADDR);
if (enable) { if (enable) {
if (rec->flags & FTRACE_FL_ENABLED) if (rec->flags & FTRACE_FL_ENABLED)
...@@ -469,21 +459,18 @@ __ftrace_replace_code(struct dyn_ftrace *rec, ...@@ -469,21 +459,18 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
} }
} }
return ftrace_modify_code(ip, old, new); if (enable)
return ftrace_make_call(rec, FTRACE_ADDR);
else
return ftrace_make_nop(NULL, rec, FTRACE_ADDR);
} }
static void ftrace_replace_code(int enable) static void ftrace_replace_code(int enable)
{ {
int i, failed; int i, failed;
unsigned char *new = NULL, *old = NULL;
struct dyn_ftrace *rec; struct dyn_ftrace *rec;
struct ftrace_page *pg; struct ftrace_page *pg;
if (enable)
old = ftrace_nop_replace();
else
new = ftrace_nop_replace();
for (pg = ftrace_pages_start; pg; pg = pg->next) { for (pg = ftrace_pages_start; pg; pg = pg->next) {
for (i = 0; i < pg->index; i++) { for (i = 0; i < pg->index; i++) {
rec = &pg->records[i]; rec = &pg->records[i];
...@@ -504,34 +491,30 @@ static void ftrace_replace_code(int enable) ...@@ -504,34 +491,30 @@ static void ftrace_replace_code(int enable)
unfreeze_record(rec); unfreeze_record(rec);
} }
failed = __ftrace_replace_code(rec, old, new, enable); failed = __ftrace_replace_code(rec, enable);
if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
rec->flags |= FTRACE_FL_FAILED; rec->flags |= FTRACE_FL_FAILED;
if ((system_state == SYSTEM_BOOTING) || if ((system_state == SYSTEM_BOOTING) ||
!core_kernel_text(rec->ip)) { !core_kernel_text(rec->ip)) {
ftrace_free_rec(rec); ftrace_free_rec(rec);
} else } else
ftrace_bug(failed, rec->ip, old, new); ftrace_bug(failed, rec->ip);
} }
} }
} }
} }
static int static int
ftrace_code_disable(struct dyn_ftrace *rec) ftrace_code_disable(struct module *mod, struct dyn_ftrace *rec)
{ {
unsigned long ip; unsigned long ip;
unsigned char *nop, *call;
int ret; int ret;
ip = rec->ip; ip = rec->ip;
nop = ftrace_nop_replace(); ret = ftrace_make_nop(mod, rec, mcount_addr);
call = ftrace_call_replace(ip, mcount_addr);
ret = ftrace_modify_code(ip, call, nop);
if (ret) { if (ret) {
ftrace_bug(ret, ip, call, nop); ftrace_bug(ret, ip);
rec->flags |= FTRACE_FL_FAILED; rec->flags |= FTRACE_FL_FAILED;
return 0; return 0;
} }
...@@ -650,7 +633,7 @@ static cycle_t ftrace_update_time; ...@@ -650,7 +633,7 @@ static cycle_t ftrace_update_time;
static unsigned long ftrace_update_cnt; static unsigned long ftrace_update_cnt;
unsigned long ftrace_update_tot_cnt; unsigned long ftrace_update_tot_cnt;
static int ftrace_update_code(void) static int ftrace_update_code(struct module *mod)
{ {
struct dyn_ftrace *p, *t; struct dyn_ftrace *p, *t;
cycle_t start, stop; cycle_t start, stop;
...@@ -667,7 +650,7 @@ static int ftrace_update_code(void) ...@@ -667,7 +650,7 @@ static int ftrace_update_code(void)
list_del_init(&p->list); list_del_init(&p->list);
/* convert record (i.e, patch mcount-call with NOP) */ /* convert record (i.e, patch mcount-call with NOP) */
if (ftrace_code_disable(p)) { if (ftrace_code_disable(mod, p)) {
p->flags |= FTRACE_FL_CONVERTED; p->flags |= FTRACE_FL_CONVERTED;
ftrace_update_cnt++; ftrace_update_cnt++;
} else } else
...@@ -1309,7 +1292,8 @@ static __init int ftrace_init_debugfs(void) ...@@ -1309,7 +1292,8 @@ static __init int ftrace_init_debugfs(void)
fs_initcall(ftrace_init_debugfs); fs_initcall(ftrace_init_debugfs);
static int ftrace_convert_nops(unsigned long *start, static int ftrace_convert_nops(struct module *mod,
unsigned long *start,
unsigned long *end) unsigned long *end)
{ {
unsigned long *p; unsigned long *p;
...@@ -1325,18 +1309,19 @@ static int ftrace_convert_nops(unsigned long *start, ...@@ -1325,18 +1309,19 @@ static int ftrace_convert_nops(unsigned long *start,
/* disable interrupts to prevent kstop machine */ /* disable interrupts to prevent kstop machine */
local_irq_save(flags); local_irq_save(flags);
ftrace_update_code(); ftrace_update_code(mod);
local_irq_restore(flags); local_irq_restore(flags);
mutex_unlock(&ftrace_start_lock); mutex_unlock(&ftrace_start_lock);
return 0; return 0;
} }
void ftrace_init_module(unsigned long *start, unsigned long *end) void ftrace_init_module(struct module *mod,
unsigned long *start, unsigned long *end)
{ {
if (ftrace_disabled || start == end) if (ftrace_disabled || start == end)
return; return;
ftrace_convert_nops(start, end); ftrace_convert_nops(mod, start, end);
} }
extern unsigned long __start_mcount_loc[]; extern unsigned long __start_mcount_loc[];
...@@ -1366,7 +1351,8 @@ void __init ftrace_init(void) ...@@ -1366,7 +1351,8 @@ void __init ftrace_init(void)
last_ftrace_enabled = ftrace_enabled = 1; last_ftrace_enabled = ftrace_enabled = 1;
ret = ftrace_convert_nops(__start_mcount_loc, ret = ftrace_convert_nops(NULL,
__start_mcount_loc,
__stop_mcount_loc); __stop_mcount_loc);
return; return;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册