Skip to content

Commit c91c5a8

Browse files
rnavmpe
authored andcommitted
powerpc/ftrace: Implement ftrace_replace_code()
Implement ftrace_replace_code() to consolidate logic from the different ftrace patching routines: ftrace_make_nop(), ftrace_make_call() and ftrace_modify_call(). Note that ftrace_make_call() is still required primarily to handle patching modules during their load time. The other two routines should no longer be called. This lays the groundwork to enable better control in patching ftrace locations, including the ability to nop-out preceding profiling instructions when ftrace is disabled. Signed-off-by: Naveen N Rao <[email protected]> Reviewed-by: Christophe Leroy <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://msgid.link/c28f852225646b0561bbf3c1d22d03f041ace8e0.1687166935.git.naveen@kernel.org
1 parent a26ce42 commit c91c5a8

File tree

1 file changed

+92
-73
lines changed

1 file changed

+92
-73
lines changed

arch/powerpc/kernel/trace/ftrace.c

Lines changed: 92 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -94,104 +94,123 @@ static unsigned long find_ftrace_tramp(unsigned long ip)
9494
return 0;
9595
}
9696

97-
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
98-
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr)
97+
static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_inst_t *call_inst)
9998
{
100-
unsigned long tramp, tramp_old, ip = rec->ip;
101-
ppc_inst_t old, new;
102-
struct module *mod;
99+
unsigned long ip = rec->ip;
100+
unsigned long stub;
103101

104-
if (is_offset_in_branch_range(old_addr - ip) && is_offset_in_branch_range(addr - ip)) {
102+
if (is_offset_in_branch_range(addr - ip)) {
105103
/* Within range */
106-
old = ftrace_create_branch_inst(ip, old_addr, 1);
107-
new = ftrace_create_branch_inst(ip, addr, 1);
108-
return ftrace_modify_code(ip, old, new);
109-
} else if (core_kernel_text(ip)) {
110-
/*
111-
* We always patch out of range locations to go to the regs
112-
* variant, so there is nothing to do here
113-
*/
114-
return 0;
115-
} else if (IS_ENABLED(CONFIG_MODULES)) {
104+
stub = addr;
105+
#ifdef CONFIG_MODULES
106+
} else if (rec->arch.mod) {
116107
/* Module code would be going to one of the module stubs */
117-
mod = rec->arch.mod;
118-
if (addr == (unsigned long)ftrace_caller) {
119-
tramp_old = mod->arch.tramp_regs;
120-
tramp = mod->arch.tramp;
121-
} else {
122-
tramp_old = mod->arch.tramp;
123-
tramp = mod->arch.tramp_regs;
108+
stub = (addr == (unsigned long)ftrace_caller ? rec->arch.mod->arch.tramp :
109+
rec->arch.mod->arch.tramp_regs);
110+
#endif
111+
} else if (core_kernel_text(ip)) {
112+
/* We would be branching to one of our ftrace stubs */
113+
stub = find_ftrace_tramp(ip);
114+
if (!stub) {
115+
pr_err("0x%lx: No ftrace stubs reachable\n", ip);
116+
return -EINVAL;
124117
}
125-
old = ftrace_create_branch_inst(ip, tramp_old, 1);
126-
new = ftrace_create_branch_inst(ip, tramp, 1);
127-
return ftrace_modify_code(ip, old, new);
118+
} else {
119+
return -EINVAL;
128120
}
129121

122+
*call_inst = ftrace_create_branch_inst(ip, stub, 1);
123+
return 0;
124+
}
125+
126+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
127+
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr)
128+
{
129+
/* This should never be called since we override ftrace_replace_code() */
130+
WARN_ON(1);
130131
return -EINVAL;
131132
}
132133
#endif
133134

134135
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
135136
{
136-
unsigned long tramp, ip = rec->ip;
137137
ppc_inst_t old, new;
138-
struct module *mod;
138+
int ret;
139+
140+
/* This can only ever be called during module load */
141+
if (WARN_ON(!IS_ENABLED(CONFIG_MODULES) || core_kernel_text(rec->ip)))
142+
return -EINVAL;
139143

140144
old = ppc_inst(PPC_RAW_NOP());
141-
if (is_offset_in_branch_range(addr - ip)) {
142-
/* Within range */
143-
new = ftrace_create_branch_inst(ip, addr, 1);
144-
return ftrace_modify_code(ip, old, new);
145-
} else if (core_kernel_text(ip)) {
146-
/* We would be branching to one of our ftrace tramps */
147-
tramp = find_ftrace_tramp(ip);
148-
if (!tramp) {
149-
pr_err("0x%lx: No ftrace trampolines reachable\n", ip);
150-
return -EINVAL;
151-
}
152-
new = ftrace_create_branch_inst(ip, tramp, 1);
153-
return ftrace_modify_code(ip, old, new);
154-
} else if (IS_ENABLED(CONFIG_MODULES)) {
155-
/* Module code would be going to one of the module stubs */
156-
mod = rec->arch.mod;
157-
tramp = (addr == (unsigned long)ftrace_caller ? mod->arch.tramp : mod->arch.tramp_regs);
158-
new = ftrace_create_branch_inst(ip, tramp, 1);
159-
return ftrace_modify_code(ip, old, new);
160-
}
145+
ret = ftrace_get_call_inst(rec, addr, &new);
146+
if (ret)
147+
return ret;
161148

162-
return -EINVAL;
149+
return ftrace_modify_code(rec->ip, old, new);
163150
}
164151

165152
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr)
166153
{
167-
unsigned long tramp, ip = rec->ip;
168-
ppc_inst_t old, new;
154+
/*
155+
* This should never be called since we override ftrace_replace_code(),
156+
* as well as ftrace_init_nop()
157+
*/
158+
WARN_ON(1);
159+
return -EINVAL;
160+
}
169161

170-
/* Nop-out the ftrace location */
171-
new = ppc_inst(PPC_RAW_NOP());
172-
if (is_offset_in_branch_range(addr - ip)) {
173-
/* Within range */
174-
old = ftrace_create_branch_inst(ip, addr, 1);
175-
return ftrace_modify_code(ip, old, new);
176-
} else if (core_kernel_text(ip)) {
177-
/* We would be branching to one of our ftrace tramps */
178-
tramp = find_ftrace_tramp(ip);
179-
if (!tramp) {
180-
pr_err("0x%lx: No ftrace trampolines reachable\n", ip);
181-
return -EINVAL;
162+
void ftrace_replace_code(int enable)
163+
{
164+
ppc_inst_t old, new, call_inst, new_call_inst;
165+
ppc_inst_t nop_inst = ppc_inst(PPC_RAW_NOP());
166+
unsigned long ip, new_addr, addr;
167+
struct ftrace_rec_iter *iter;
168+
struct dyn_ftrace *rec;
169+
int ret = 0, update;
170+
171+
for_ftrace_rec_iter(iter) {
172+
rec = ftrace_rec_iter_record(iter);
173+
ip = rec->ip;
174+
175+
if (rec->flags & FTRACE_FL_DISABLED && !(rec->flags & FTRACE_FL_ENABLED))
176+
continue;
177+
178+
addr = ftrace_get_addr_curr(rec);
179+
new_addr = ftrace_get_addr_new(rec);
180+
update = ftrace_update_record(rec, enable);
181+
182+
switch (update) {
183+
case FTRACE_UPDATE_IGNORE:
184+
default:
185+
continue;
186+
case FTRACE_UPDATE_MODIFY_CALL:
187+
ret = ftrace_get_call_inst(rec, new_addr, &new_call_inst);
188+
ret |= ftrace_get_call_inst(rec, addr, &call_inst);
189+
old = call_inst;
190+
new = new_call_inst;
191+
break;
192+
case FTRACE_UPDATE_MAKE_NOP:
193+
ret = ftrace_get_call_inst(rec, addr, &call_inst);
194+
old = call_inst;
195+
new = nop_inst;
196+
break;
197+
case FTRACE_UPDATE_MAKE_CALL:
198+
ret = ftrace_get_call_inst(rec, new_addr, &call_inst);
199+
old = nop_inst;
200+
new = call_inst;
201+
break;
182202
}
183-
old = ftrace_create_branch_inst(ip, tramp, 1);
184-
return ftrace_modify_code(ip, old, new);
185-
} else if (IS_ENABLED(CONFIG_MODULES)) {
186-
/* Module code would be going to one of the module stubs */
187-
if (!mod)
188-
mod = rec->arch.mod;
189-
tramp = (addr == (unsigned long)ftrace_caller ? mod->arch.tramp : mod->arch.tramp_regs);
190-
old = ftrace_create_branch_inst(ip, tramp, 1);
191-
return ftrace_modify_code(ip, old, new);
203+
204+
if (!ret)
205+
ret = ftrace_modify_code(ip, old, new);
206+
if (ret)
207+
goto out;
192208
}
193209

194-
return -EINVAL;
210+
out:
211+
if (ret)
212+
ftrace_bug(ret, rec);
213+
return;
195214
}
196215

197216
int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)

0 commit comments

Comments
 (0)