Merge branch 'linus' into tracing/ftrace
[deliverable/linux.git] / arch / x86 / kernel / ftrace.c
CommitLineData
3d083395
SR
1/*
2 * Code for replacing ftrace calls with jumps.
3 *
4 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
5 *
6 * Thanks goes to Ingo Molnar, for suggesting the idea.
7 * Mathieu Desnoyers, for suggesting postponing the modifications.
8 * Arjan van de Ven, for keeping me straight, and explaining to me
9 * the dangers of modifying code on the run.
10 */
11
12#include <linux/spinlock.h>
13#include <linux/hardirq.h>
14#include <linux/ftrace.h>
15#include <linux/percpu.h>
16#include <linux/init.h>
17#include <linux/list.h>
18
dfa60aba 19#include <asm/alternative.h>
3d083395 20
dfa60aba 21#define CALL_BACK 5
3d083395 22
dfa60aba
SR
23/* Long is fine, even if it is only 4 bytes ;-) */
24static long *ftrace_nop;
3d083395 25
3d083395
SR
26union ftrace_code_union {
27 char code[5];
28 struct {
29 char e8;
30 int offset;
31 } __attribute__((packed));
32};
33
3c1720f0
SR
34static int notrace ftrace_calc_offset(long ip, long addr)
35{
36 return (int)(addr - ip);
37}
3d083395 38
3c1720f0
SR
39notrace unsigned char *ftrace_nop_replace(void)
40{
41 return (char *)ftrace_nop;
42}
43
44notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
45{
46 static union ftrace_code_union calc;
3d083395 47
3c1720f0
SR
48 calc.e8 = 0xe8;
49 calc.offset = ftrace_calc_offset(ip, addr);
50
51 /*
52 * No locking needed, this must be called via kstop_machine
53 * which in essence is like running on a uniprocessor machine.
54 */
55 return calc.code;
3d083395
SR
56}
57
3c1720f0 58notrace int
3d083395
SR
59ftrace_modify_code(unsigned long ip, unsigned char *old_code,
60 unsigned char *new_code)
61{
dfa60aba
SR
62 unsigned replaced;
63 unsigned old = *(unsigned *)old_code; /* 4 bytes */
64 unsigned new = *(unsigned *)new_code; /* 4 bytes */
65 unsigned char newch = new_code[4];
3d083395
SR
66 int faulted = 0;
67
3c1720f0
SR
68 /* move the IP back to the start of the call */
69 ip -= CALL_BACK;
70
3d083395
SR
71 /*
72 * Note: Due to modules and __init, code can
73 * disappear and change, we need to protect against faulting
74 * as well as code changing.
75 *
76 * No real locking needed, this code is run through
77 * kstop_machine.
78 */
79 asm volatile (
80 "1: lock\n"
dfa60aba
SR
81 " cmpxchg %3, (%2)\n"
82 " jnz 2f\n"
83 " movb %b4, 4(%2)\n"
3d083395
SR
84 "2:\n"
85 ".section .fixup, \"ax\"\n"
a56be3fe
SR
86 "3: movl $1, %0\n"
87 " jmp 2b\n"
3d083395
SR
88 ".previous\n"
89 _ASM_EXTABLE(1b, 3b)
90 : "=r"(faulted), "=a"(replaced)
ee4311ad 91 : "r"(ip), "r"(new), "c"(newch),
dfa60aba 92 "0"(faulted), "a"(old)
3d083395
SR
93 : "memory");
94 sync_core();
95
dfa60aba 96 if (replaced != old && replaced != new)
3d083395
SR
97 faulted = 2;
98
99 return faulted;
100}
101
d61f82d0
SR
102notrace int ftrace_update_ftrace_func(ftrace_func_t func)
103{
104 unsigned long ip = (unsigned long)(&ftrace_call);
105 unsigned char old[5], *new;
106 int ret;
107
108 ip += CALL_BACK;
109
110 memcpy(old, &ftrace_call, 5);
111 new = ftrace_call_replace(ip, (unsigned long)func);
112 ret = ftrace_modify_code(ip, old, new);
113
114 return ret;
115}
116
117notrace int ftrace_mcount_set(unsigned long *data)
118{
119 unsigned long ip = (long)(&mcount_call);
120 unsigned long *addr = data;
121 unsigned char old[5], *new;
122
123 /* ip is at the location, but modify code will subtact this */
124 ip += CALL_BACK;
125
126 /*
127 * Replace the mcount stub with a pointer to the
128 * ip recorder function.
129 */
130 memcpy(old, &mcount_call, 5);
131 new = ftrace_call_replace(ip, *addr);
132 *addr = ftrace_modify_code(ip, old, new);
133
134 return 0;
135}
136
137int __init ftrace_dyn_arch_init(void *data)
3d083395 138{
dfa60aba 139 const unsigned char *const *noptable = find_nop_table();
3d083395 140
d61f82d0
SR
141 /* This is running in kstop_machine */
142
143 ftrace_mcount_set(data);
144
dfa60aba
SR
145 ftrace_nop = (unsigned long *)noptable[CALL_BACK];
146
3d083395
SR
147 return 0;
148}
3c1720f0 149
This page took 0.039105 seconds and 5 git commands to generate.