Two fixes in the tracing utility.
The first is a fix for the way the ring buffer stores timestamps. After a restructure of the code was done, the ring buffer timestamp logic missed the fact that the first event on a sub buffer is to have a zero delta, as the full timestamp is stored on the sub buffer itself. But because the delta was not cleared to zero, the timestamp for that event will be calculated as the real timestamp + the delta from the last timestamp. This can skew the timestamps of the events and have them say they happened when they didn't really happen. That's bad. The second fix is for modifying the function graph caller site. When the stop machine was removed from updating the function tracing code, it missed updating the function graph call site location. It is still modified as if it is being done via stop machine. But it's not. This can lead to a GPF and kernel crash if the function graph call site happens to lie between cache lines and one CPU is executing it while another CPU is doing the update. It would be a very hard condition to hit, but the result is sever enough to have it fixed ASAP. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQEcBAABAgAGBQJS/2leAAoJEKQekfcNnQGu7nYH/AltUO19AgM2sFLOLM7Q0dp4 Lg7vE8CLKtFq0fjtv/ri//fJ56Lr+/WNHiiD06aIrgnMVBbWynS0m0RO+9bhFl8/ rELiUpXTTruqljmlT2T5lPxk+ZKgtLbxK8hNywU99eLgkTwyaOwrSUol30E8pw41 UwtKg4OAn1LbjQ8/sddVynGlFDNRdqFiGTIDvhHqI6F6/QlaEX81EeZbLThDU4D/ l86fMuIdw5pb+efa29Rr0s7O4Xol7SJgnSMVgd0OYADRFmp4sg+MKxuJAUjPsHk7 9vvbylOb4w5H6lo5h7kUee3w7kG+FjYVoEx+Sqq9936+KlwtN0kbiNvl0DkrXnY= =kUmM -----END PGP SIGNATURE----- Merge tag 'trace-fixes-v3.14-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace Pull twi tracing fixes from Steven Rostedt: "Two urgent fixes in the tracing utility. The first is a fix for the way the ring buffer stores timestamps. After a restructure of the code was done, the ring buffer timestamp logic missed the fact that the first event on a sub buffer is to have a zero delta, as the full timestamp is stored on the sub buffer itself. But because the delta was not cleared to zero, the timestamp for that event will be calculated as the real timestamp + the delta from the last timestamp. This can skew the timestamps of the events and have them say they happened when they didn't really happen. That's bad. The second fix is for modifying the function graph caller site. When the stop machine was removed from updating the function tracing code, it missed updating the function graph call site location. It is still modified as if it is being done via stop machine. But it's not. This can lead to a GPF and kernel crash if the function graph call site happens to lie between cache lines and one CPU is executing it while another CPU is doing the update. It would be a very hard condition to hit, but the result is severe enough to have it fixed ASAP" * tag 'trace-fixes-v3.14-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: ftrace/x86: Use breakpoints for converting function graph caller ring-buffer: Fix first commit on sub-buffer having non-zero delta
This commit is contained in:
commit
9bd01b9bbd
2 changed files with 54 additions and 36 deletions
|
@ -77,8 +77,7 @@ within(unsigned long addr, unsigned long start, unsigned long end)
|
|||
return addr >= start && addr < end;
|
||||
}
|
||||
|
||||
static int
|
||||
do_ftrace_mod_code(unsigned long ip, const void *new_code)
|
||||
static unsigned long text_ip_addr(unsigned long ip)
|
||||
{
|
||||
/*
|
||||
* On x86_64, kernel text mappings are mapped read-only with
|
||||
|
@ -91,7 +90,7 @@ do_ftrace_mod_code(unsigned long ip, const void *new_code)
|
|||
if (within(ip, (unsigned long)_text, (unsigned long)_etext))
|
||||
ip = (unsigned long)__va(__pa_symbol(ip));
|
||||
|
||||
return probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE);
|
||||
return ip;
|
||||
}
|
||||
|
||||
static const unsigned char *ftrace_nop_replace(void)
|
||||
|
@ -123,8 +122,10 @@ ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code,
|
|||
if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ip = text_ip_addr(ip);
|
||||
|
||||
/* replace the text with the new text */
|
||||
if (do_ftrace_mod_code(ip, new_code))
|
||||
if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE))
|
||||
return -EPERM;
|
||||
|
||||
sync_core();
|
||||
|
@ -221,37 +222,51 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ftrace_update_ftrace_func(ftrace_func_t func)
|
||||
static unsigned long ftrace_update_func;
|
||||
|
||||
static int update_ftrace_func(unsigned long ip, void *new)
|
||||
{
|
||||
unsigned long ip = (unsigned long)(&ftrace_call);
|
||||
unsigned char old[MCOUNT_INSN_SIZE], *new;
|
||||
unsigned char old[MCOUNT_INSN_SIZE];
|
||||
int ret;
|
||||
|
||||
memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
|
||||
new = ftrace_call_replace(ip, (unsigned long)func);
|
||||
memcpy(old, (void *)ip, MCOUNT_INSN_SIZE);
|
||||
|
||||
ftrace_update_func = ip;
|
||||
/* Make sure the breakpoints see the ftrace_update_func update */
|
||||
smp_wmb();
|
||||
|
||||
/* See comment above by declaration of modifying_ftrace_code */
|
||||
atomic_inc(&modifying_ftrace_code);
|
||||
|
||||
ret = ftrace_modify_code(ip, old, new);
|
||||
|
||||
atomic_dec(&modifying_ftrace_code);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ftrace_update_ftrace_func(ftrace_func_t func)
|
||||
{
|
||||
unsigned long ip = (unsigned long)(&ftrace_call);
|
||||
unsigned char *new;
|
||||
int ret;
|
||||
|
||||
new = ftrace_call_replace(ip, (unsigned long)func);
|
||||
ret = update_ftrace_func(ip, new);
|
||||
|
||||
/* Also update the regs callback function */
|
||||
if (!ret) {
|
||||
ip = (unsigned long)(&ftrace_regs_call);
|
||||
memcpy(old, &ftrace_regs_call, MCOUNT_INSN_SIZE);
|
||||
new = ftrace_call_replace(ip, (unsigned long)func);
|
||||
ret = ftrace_modify_code(ip, old, new);
|
||||
ret = update_ftrace_func(ip, new);
|
||||
}
|
||||
|
||||
atomic_dec(&modifying_ftrace_code);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int is_ftrace_caller(unsigned long ip)
|
||||
{
|
||||
if (ip == (unsigned long)(&ftrace_call) ||
|
||||
ip == (unsigned long)(&ftrace_regs_call))
|
||||
if (ip == ftrace_update_func)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -677,45 +692,41 @@ int __init ftrace_dyn_arch_init(void *data)
|
|||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
extern void ftrace_graph_call(void);
|
||||
|
||||
static int ftrace_mod_jmp(unsigned long ip,
|
||||
int old_offset, int new_offset)
|
||||
static unsigned char *ftrace_jmp_replace(unsigned long ip, unsigned long addr)
|
||||
{
|
||||
unsigned char code[MCOUNT_INSN_SIZE];
|
||||
static union ftrace_code_union calc;
|
||||
|
||||
if (probe_kernel_read(code, (void *)ip, MCOUNT_INSN_SIZE))
|
||||
return -EFAULT;
|
||||
/* Jmp not a call (ignore the .e8) */
|
||||
calc.e8 = 0xe9;
|
||||
calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr);
|
||||
|
||||
if (code[0] != 0xe9 || old_offset != *(int *)(&code[1]))
|
||||
return -EINVAL;
|
||||
/*
|
||||
* ftrace external locks synchronize the access to the static variable.
|
||||
*/
|
||||
return calc.code;
|
||||
}
|
||||
|
||||
*(int *)(&code[1]) = new_offset;
|
||||
static int ftrace_mod_jmp(unsigned long ip, void *func)
|
||||
{
|
||||
unsigned char *new;
|
||||
|
||||
if (do_ftrace_mod_code(ip, &code))
|
||||
return -EPERM;
|
||||
new = ftrace_jmp_replace(ip, (unsigned long)func);
|
||||
|
||||
return 0;
|
||||
return update_ftrace_func(ip, new);
|
||||
}
|
||||
|
||||
int ftrace_enable_ftrace_graph_caller(void)
|
||||
{
|
||||
unsigned long ip = (unsigned long)(&ftrace_graph_call);
|
||||
int old_offset, new_offset;
|
||||
|
||||
old_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE);
|
||||
new_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE);
|
||||
|
||||
return ftrace_mod_jmp(ip, old_offset, new_offset);
|
||||
return ftrace_mod_jmp(ip, &ftrace_graph_caller);
|
||||
}
|
||||
|
||||
int ftrace_disable_ftrace_graph_caller(void)
|
||||
{
|
||||
unsigned long ip = (unsigned long)(&ftrace_graph_call);
|
||||
int old_offset, new_offset;
|
||||
|
||||
old_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE);
|
||||
new_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE);
|
||||
|
||||
return ftrace_mod_jmp(ip, old_offset, new_offset);
|
||||
return ftrace_mod_jmp(ip, &ftrace_stub);
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_DYNAMIC_FTRACE */
|
||||
|
|
|
@ -2397,6 +2397,13 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
|
|||
write &= RB_WRITE_MASK;
|
||||
tail = write - length;
|
||||
|
||||
/*
|
||||
* If this is the first commit on the page, then it has the same
|
||||
* timestamp as the page itself.
|
||||
*/
|
||||
if (!tail)
|
||||
delta = 0;
|
||||
|
||||
/* See if we shot pass the end of this buffer page */
|
||||
if (unlikely(write > BUF_PAGE_SIZE))
|
||||
return rb_move_tail(cpu_buffer, length, tail,
|
||||
|
|
Loading…
Reference in a new issue