Skip to content

Commit afc76b8

Browse files
guoren83palmer-dabbelt
authored andcommitted
riscv: Using PATCHABLE_FUNCTION_ENTRY instead of MCOUNT
This patch changes the current detour mechanism of dynamic ftrace which has been discussed during LPC 2020 RISCV-MC [1]. Before the patch, we used mcount for detour: <funca>: addi sp,sp,-16 sd ra,8(sp) sd s0,0(sp) addi s0,sp,16 mv a5,ra mv a0,a5 auipc ra,0x0 -> nop jalr -296(ra) <_mcount@plt> ->nop ... After the patch, we use nop call site area for detour: <funca>: nop -> REG_S ra, -SZREG(sp) nop -> auipc ra, 0x? nop -> jalr ?(ra) nop -> REG_L ra, -SZREG(sp) ... The mcount mechanism is mixed with gcc function prologue which is not very clear. The patchable function entry just put 16 bytes nop before the front of the function prologue which could be filled with a separated detour mechanism. [1] https://www.linuxplumbersconf.org/event/7/contributions/807/ Signed-off-by: Guo Ren <[email protected]> Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 5ad84ad commit afc76b8

File tree

3 files changed

+204
-235
lines changed

3 files changed

+204
-235
lines changed

arch/riscv/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ OBJCOPYFLAGS := -O binary
1212
LDFLAGS_vmlinux :=
1313
ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
1414
LDFLAGS_vmlinux := --no-relax
15+
KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
16+
CC_FLAGS_FTRACE := -fpatchable-function-entry=8
1517
endif
1618

1719
ifeq ($(CONFIG_64BIT)$(CONFIG_CMODEL_MEDLOW),yy)

arch/riscv/kernel/ftrace.c

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -72,29 +72,56 @@ static int __ftrace_modify_call(unsigned long hook_pos, unsigned long target,
7272
return 0;
7373
}
7474

75+
/*
76+
* Put 5 instructions with 16 bytes at the front of function within
77+
* patchable function entry nops' area.
78+
*
79+
* 0: REG_S ra, -SZREG(sp)
80+
* 1: auipc ra, 0x?
81+
* 2: jalr -?(ra)
82+
* 3: REG_L ra, -SZREG(sp)
83+
*
84+
* So the opcodes is:
85+
* 0: 0xfe113c23 (sd)/0xfe112e23 (sw)
86+
* 1: 0x???????? -> auipc
87+
* 2: 0x???????? -> jalr
88+
* 3: 0xff813083 (ld)/0xffc12083 (lw)
89+
*/
90+
#if __riscv_xlen == 64
91+
#define INSN0 0xfe113c23
92+
#define INSN3 0xff813083
93+
#elif __riscv_xlen == 32
94+
#define INSN0 0xfe112e23
95+
#define INSN3 0xffc12083
96+
#endif
97+
98+
#define FUNC_ENTRY_SIZE 16
99+
#define FUNC_ENTRY_JMP 4
100+
75101
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
76102
{
77-
int ret = ftrace_check_current_call(rec->ip, NULL);
103+
unsigned int call[4] = {INSN0, 0, 0, INSN3};
104+
unsigned long target = addr;
105+
unsigned long caller = rec->ip + FUNC_ENTRY_JMP;
78106

79-
if (ret)
80-
return ret;
107+
call[1] = to_auipc_insn((unsigned int)(target - caller));
108+
call[2] = to_jalr_insn((unsigned int)(target - caller));
81109

82-
return __ftrace_modify_call(rec->ip, addr, true);
110+
if (patch_text_nosync((void *)rec->ip, call, FUNC_ENTRY_SIZE))
111+
return -EPERM;
112+
113+
return 0;
83114
}
84115

85116
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
86117
unsigned long addr)
87118
{
88-
unsigned int call[2];
89-
int ret;
119+
unsigned int nops[4] = {NOP4, NOP4, NOP4, NOP4};
90120

91-
make_call(rec->ip, addr, call);
92-
ret = ftrace_check_current_call(rec->ip, call);
93-
94-
if (ret)
95-
return ret;
121+
if (patch_text_nosync((void *)rec->ip, nops, FUNC_ENTRY_SIZE))
122+
return -EPERM;
96123

97-
return __ftrace_modify_call(rec->ip, addr, false);
124+
return 0;
98125
}
99126

100127

@@ -139,15 +166,16 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
139166
unsigned long addr)
140167
{
141168
unsigned int call[2];
169+
unsigned long caller = rec->ip + FUNC_ENTRY_JMP;
142170
int ret;
143171

144-
make_call(rec->ip, old_addr, call);
145-
ret = ftrace_check_current_call(rec->ip, call);
172+
make_call(caller, old_addr, call);
173+
ret = ftrace_check_current_call(caller, call);
146174

147175
if (ret)
148176
return ret;
149177

150-
return __ftrace_modify_call(rec->ip, addr, true);
178+
return __ftrace_modify_call(caller, addr, true);
151179
}
152180
#endif
153181

@@ -176,53 +204,30 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
176204

177205
#ifdef CONFIG_DYNAMIC_FTRACE
178206
extern void ftrace_graph_call(void);
207+
extern void ftrace_graph_regs_call(void);
179208
int ftrace_enable_ftrace_graph_caller(void)
180209
{
181-
unsigned int call[2];
182-
static int init_graph = 1;
183210
int ret;
184211

185-
make_call(&ftrace_graph_call, &ftrace_stub, call);
186-
187-
/*
188-
* When enabling graph tracer for the first time, ftrace_graph_call
189-
* should contains a call to ftrace_stub. Once it has been disabled,
190-
* the 8-bytes at the position becomes NOPs.
191-
*/
192-
if (init_graph) {
193-
ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
194-
call);
195-
init_graph = 0;
196-
} else {
197-
ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
198-
NULL);
199-
}
200-
212+
ret = __ftrace_modify_call((unsigned long)&ftrace_graph_call,
213+
(unsigned long)&prepare_ftrace_return, true);
201214
if (ret)
202215
return ret;
203216

204-
return __ftrace_modify_call((unsigned long)&ftrace_graph_call,
217+
return __ftrace_modify_call((unsigned long)&ftrace_graph_regs_call,
205218
(unsigned long)&prepare_ftrace_return, true);
206219
}
207220

208221
int ftrace_disable_ftrace_graph_caller(void)
209222
{
210-
unsigned int call[2];
211223
int ret;
212224

213-
make_call(&ftrace_graph_call, &prepare_ftrace_return, call);
214-
215-
/*
216-
* This is to make sure that ftrace_enable_ftrace_graph_caller
217-
* did the right thing.
218-
*/
219-
ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
220-
call);
221-
225+
ret = __ftrace_modify_call((unsigned long)&ftrace_graph_call,
226+
(unsigned long)&prepare_ftrace_return, false);
222227
if (ret)
223228
return ret;
224229

225-
return __ftrace_modify_call((unsigned long)&ftrace_graph_call,
230+
return __ftrace_modify_call((unsigned long)&ftrace_graph_regs_call,
226231
(unsigned long)&prepare_ftrace_return, false);
227232
}
228233
#endif /* CONFIG_DYNAMIC_FTRACE */

0 commit comments

Comments
 (0)