Skip to content

Commit de5012b

Browse files
iii-ihcahca
authored andcommitted
s390/ftrace: implement hotpatching
s390 allows hotpatching the mask of a conditional jump instruction. Make use of this feature in order to avoid the expensive stop_machine() call. The new trampolines are split in 3 stages: - A first stage is a 6-byte relative conditional long branch located at each function's entry point. Its offset always points to the second stage for the corresponding function, and its mask is either all 0s (ftrace off) or all 1s (ftrace on). The code for flipping the mask is borrowed from ftrace_{enable,disable}_ftrace_graph_caller. After flipping, ftrace_arch_code_modify_post_process() syncs with all the other CPUs by sending SIGPs. - Second stages for vmlinux are stored in a separate part of the .text section reserved by the linker script, and in dynamically allocated memory for modules. This prevents the icache pollution. The total size of second stages is about 1.5% of that of the kernel image. Putting second stages in the .bss section is possible and decreases the size of the non-compressed vmlinux, but splits the kernel 1:1 mapping, which is a bad tradeoff. Each second stage contains a call to the third stage, a pointer to the part of the intercepted function right after the first stage, and a pointer to an interceptor function (e.g. ftrace_caller). Second stages are 8-byte aligned for the future direct calls implementation. - There are only two copies of the third stage: in the .text section for vmlinux and in dynamically allocated memory for modules. It can be an expoline, which is relatively large, so inlining it into each second stage is prohibitively expensive. As a result of this organization, phoronix-test-suite with ftrace off does not show any performance degradation. Suggested-by: Sven Schnelle <[email protected]> Suggested-by: Vasily Gorbik <[email protected]> Co-developed-by: Heiko Carstens <[email protected]> Signed-off-by: Ilya Leoshkevich <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Heiko Carstens <[email protected]>
1 parent 67ccddf commit de5012b

File tree

7 files changed

+313
-57
lines changed

7 files changed

+313
-57
lines changed

arch/s390/include/asm/ftrace.h

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
void ftrace_caller(void);
1919

2020
extern char ftrace_graph_caller_end;
21-
extern unsigned long ftrace_plt;
2221
extern void *ftrace_func;
2322

2423
struct dyn_arch_ftrace { };
@@ -31,53 +30,18 @@ struct dyn_arch_ftrace { };
3130

3231
struct module;
3332
struct dyn_ftrace;
34-
/*
35-
* Either -mhotpatch or -mnop-mcount is used - no explicit init is required
36-
*/
37-
static inline int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec) { return 0; }
33+
34+
bool ftrace_need_init_nop(void);
35+
#define ftrace_need_init_nop ftrace_need_init_nop
36+
37+
int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
3838
#define ftrace_init_nop ftrace_init_nop
3939

4040
static inline unsigned long ftrace_call_adjust(unsigned long addr)
4141
{
4242
return addr;
4343
}
4444

45-
struct ftrace_insn {
46-
u16 opc;
47-
s32 disp;
48-
} __packed;
49-
50-
static inline void ftrace_generate_nop_insn(struct ftrace_insn *insn)
51-
{
52-
#ifdef CONFIG_FUNCTION_TRACER
53-
/* brcl 0,0 */
54-
insn->opc = 0xc004;
55-
insn->disp = 0;
56-
#endif
57-
}
58-
59-
static inline int is_ftrace_nop(struct ftrace_insn *insn)
60-
{
61-
#ifdef CONFIG_FUNCTION_TRACER
62-
if (insn->disp == 0)
63-
return 1;
64-
#endif
65-
return 0;
66-
}
67-
68-
static inline void ftrace_generate_call_insn(struct ftrace_insn *insn,
69-
unsigned long ip)
70-
{
71-
#ifdef CONFIG_FUNCTION_TRACER
72-
unsigned long target;
73-
74-
/* brasl r0,ftrace_caller */
75-
target = is_module_addr((void *) ip) ? ftrace_plt : FTRACE_ADDR;
76-
insn->opc = 0xc005;
77-
insn->disp = (target - ip) / 2;
78-
#endif
79-
}
80-
8145
/*
8246
* Even though the system call numbers are identical for s390/s390x a
8347
* different system call table is used for compat tasks. This may lead

arch/s390/include/asm/ftrace.lds.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef DIV_ROUND_UP
3+
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
4+
#endif
5+
6+
#define SIZEOF_MCOUNT_LOC_ENTRY 8
7+
#define SIZEOF_FTRACE_HOTPATCH_TRAMPOLINE 24
8+
#define FTRACE_HOTPATCH_TRAMPOLINES_SIZE(n) \
9+
DIV_ROUND_UP(SIZEOF_FTRACE_HOTPATCH_TRAMPOLINE * (n), \
10+
SIZEOF_MCOUNT_LOC_ENTRY)
11+
12+
#ifdef CONFIG_FUNCTION_TRACER
13+
#define FTRACE_HOTPATCH_TRAMPOLINES_TEXT \
14+
. = ALIGN(8); \
15+
__ftrace_hotpatch_trampolines_start = .; \
16+
. = . + FTRACE_HOTPATCH_TRAMPOLINES_SIZE(__stop_mcount_loc - \
17+
__start_mcount_loc); \
18+
__ftrace_hotpatch_trampolines_end = .;
19+
#else
20+
#define FTRACE_HOTPATCH_TRAMPOLINES_TEXT
21+
#endif

arch/s390/include/asm/module.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ struct mod_arch_specific {
2828
int nsyms;
2929
/* Additional symbol information (got and plt offsets). */
3030
struct mod_arch_syminfo *syminfo;
31+
#ifdef CONFIG_FUNCTION_TRACER
32+
/* Start of memory reserved for ftrace hotpatch trampolines. */
33+
struct ftrace_hotpatch_trampoline *trampolines_start;
34+
/* End of memory reserved for ftrace hotpatch trampolines. */
35+
struct ftrace_hotpatch_trampoline *trampolines_end;
36+
/* Next unused ftrace hotpatch trampoline slot. */
37+
struct ftrace_hotpatch_trampoline *next_trampoline;
38+
#endif /* CONFIG_FUNCTION_TRACER */
3139
};
3240

3341
#endif /* _ASM_S390_MODULE_H */

arch/s390/kernel/ftrace.c

Lines changed: 206 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
#include <trace/syscall.h>
1919
#include <asm/asm-offsets.h>
2020
#include <asm/cacheflush.h>
21+
#include <asm/ftrace.lds.h>
22+
#include <asm/nospec-branch.h>
2123
#include <asm/set_memory.h>
2224
#include "entry.h"
25+
#include "ftrace.h"
2326

2427
/*
2528
* To generate function prologue either gcc's hotpatch feature (since gcc 4.8)
@@ -41,19 +44,176 @@
4144
*/
4245

4346
void *ftrace_func __read_mostly = ftrace_stub;
44-
unsigned long ftrace_plt;
47+
struct ftrace_insn {
48+
u16 opc;
49+
s32 disp;
50+
} __packed;
51+
52+
asm(
53+
" .align 16\n"
54+
"ftrace_shared_hotpatch_trampoline_br:\n"
55+
" lmg %r0,%r1,2(%r1)\n"
56+
" br %r1\n"
57+
"ftrace_shared_hotpatch_trampoline_br_end:\n"
58+
);
59+
60+
#ifdef CONFIG_EXPOLINE
61+
asm(
62+
" .align 16\n"
63+
"ftrace_shared_hotpatch_trampoline_ex:\n"
64+
" lmg %r0,%r1,2(%r1)\n"
65+
" ex %r0," __stringify(__LC_BR_R1) "(%r0)\n"
66+
" j .\n"
67+
"ftrace_shared_hotpatch_trampoline_ex_end:\n"
68+
);
69+
70+
asm(
71+
" .align 16\n"
72+
"ftrace_shared_hotpatch_trampoline_exrl:\n"
73+
" lmg %r0,%r1,2(%r1)\n"
74+
" .insn ril,0xc60000000000,%r0,0f\n" /* exrl */
75+
" j .\n"
76+
"0: br %r1\n"
77+
"ftrace_shared_hotpatch_trampoline_exrl_end:\n"
78+
);
79+
#endif /* CONFIG_EXPOLINE */
80+
81+
#ifdef CONFIG_MODULES
82+
static char *ftrace_plt;
83+
84+
asm(
85+
" .data\n"
86+
"ftrace_plt_template:\n"
87+
" basr %r1,%r0\n"
88+
" lg %r1,0f-.(%r1)\n"
89+
" br %r1\n"
90+
"0: .quad ftrace_caller\n"
91+
"ftrace_plt_template_end:\n"
92+
" .previous\n"
93+
);
94+
#endif /* CONFIG_MODULES */
95+
96+
static const char *ftrace_shared_hotpatch_trampoline(const char **end)
97+
{
98+
const char *tstart, *tend;
99+
100+
tstart = ftrace_shared_hotpatch_trampoline_br;
101+
tend = ftrace_shared_hotpatch_trampoline_br_end;
102+
#ifdef CONFIG_EXPOLINE
103+
if (!nospec_disable) {
104+
tstart = ftrace_shared_hotpatch_trampoline_ex;
105+
tend = ftrace_shared_hotpatch_trampoline_ex_end;
106+
if (test_facility(35)) { /* exrl */
107+
tstart = ftrace_shared_hotpatch_trampoline_exrl;
108+
tend = ftrace_shared_hotpatch_trampoline_exrl_end;
109+
}
110+
}
111+
#endif /* CONFIG_EXPOLINE */
112+
if (end)
113+
*end = tend;
114+
return tstart;
115+
}
116+
117+
bool ftrace_need_init_nop(void)
118+
{
119+
return ftrace_shared_hotpatch_trampoline(NULL);
120+
}
121+
122+
int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
123+
{
124+
static struct ftrace_hotpatch_trampoline *next_vmlinux_trampoline =
125+
__ftrace_hotpatch_trampolines_start;
126+
static const char orig[6] = { 0xc0, 0x04, 0x00, 0x00, 0x00, 0x00 };
127+
static struct ftrace_hotpatch_trampoline *trampoline;
128+
struct ftrace_hotpatch_trampoline **next_trampoline;
129+
struct ftrace_hotpatch_trampoline *trampolines_end;
130+
struct ftrace_hotpatch_trampoline tmp;
131+
struct ftrace_insn *insn;
132+
const char *shared;
133+
s32 disp;
134+
135+
BUILD_BUG_ON(sizeof(struct ftrace_hotpatch_trampoline) !=
136+
SIZEOF_FTRACE_HOTPATCH_TRAMPOLINE);
137+
138+
next_trampoline = &next_vmlinux_trampoline;
139+
trampolines_end = __ftrace_hotpatch_trampolines_end;
140+
shared = ftrace_shared_hotpatch_trampoline(NULL);
141+
#ifdef CONFIG_MODULES
142+
if (mod) {
143+
next_trampoline = &mod->arch.next_trampoline;
144+
trampolines_end = mod->arch.trampolines_end;
145+
shared = ftrace_plt;
146+
}
147+
#endif
148+
149+
if (WARN_ON_ONCE(*next_trampoline >= trampolines_end))
150+
return -ENOMEM;
151+
trampoline = (*next_trampoline)++;
152+
153+
/* Check for the compiler-generated fentry nop (brcl 0, .). */
154+
if (WARN_ON_ONCE(memcmp((const void *)rec->ip, &orig, sizeof(orig))))
155+
return -EINVAL;
156+
157+
/* Generate the trampoline. */
158+
tmp.brasl_opc = 0xc015; /* brasl %r1, shared */
159+
tmp.brasl_disp = (shared - (const char *)&trampoline->brasl_opc) / 2;
160+
tmp.interceptor = FTRACE_ADDR;
161+
tmp.rest_of_intercepted_function = rec->ip + sizeof(struct ftrace_insn);
162+
s390_kernel_write(trampoline, &tmp, sizeof(tmp));
163+
164+
/* Generate a jump to the trampoline. */
165+
disp = ((char *)trampoline - (char *)rec->ip) / 2;
166+
insn = (struct ftrace_insn *)rec->ip;
167+
s390_kernel_write(&insn->disp, &disp, sizeof(disp));
168+
169+
return 0;
170+
}
45171

46172
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
47173
unsigned long addr)
48174
{
49175
return 0;
50176
}
51177

178+
static void ftrace_generate_nop_insn(struct ftrace_insn *insn)
179+
{
180+
/* brcl 0,0 */
181+
insn->opc = 0xc004;
182+
insn->disp = 0;
183+
}
184+
185+
static void ftrace_generate_call_insn(struct ftrace_insn *insn,
186+
unsigned long ip)
187+
{
188+
unsigned long target;
189+
190+
/* brasl r0,ftrace_caller */
191+
target = FTRACE_ADDR;
192+
#ifdef CONFIG_MODULES
193+
if (is_module_addr((void *)ip))
194+
target = (unsigned long)ftrace_plt;
195+
#endif /* CONFIG_MODULES */
196+
insn->opc = 0xc005;
197+
insn->disp = (target - ip) / 2;
198+
}
199+
200+
static void brcl_disable(void *brcl)
201+
{
202+
u8 op = 0x04; /* set mask field to zero */
203+
204+
s390_kernel_write((char *)brcl + 1, &op, sizeof(op));
205+
}
206+
52207
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
53208
unsigned long addr)
54209
{
55210
struct ftrace_insn orig, new, old;
56211

212+
if (ftrace_shared_hotpatch_trampoline(NULL)) {
213+
brcl_disable((void *)rec->ip);
214+
return 0;
215+
}
216+
57217
if (copy_from_kernel_nofault(&old, (void *) rec->ip, sizeof(old)))
58218
return -EFAULT;
59219
/* Replace ftrace call with a nop. */
@@ -67,10 +227,22 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
67227
return 0;
68228
}
69229

230+
static void brcl_enable(void *brcl)
231+
{
232+
u8 op = 0xf4; /* set mask field to all ones */
233+
234+
s390_kernel_write((char *)brcl + 1, &op, sizeof(op));
235+
}
236+
70237
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
71238
{
72239
struct ftrace_insn orig, new, old;
73240

241+
if (ftrace_shared_hotpatch_trampoline(NULL)) {
242+
brcl_enable((void *)rec->ip);
243+
return 0;
244+
}
245+
74246
if (copy_from_kernel_nofault(&old, (void *) rec->ip, sizeof(old)))
75247
return -EFAULT;
76248
/* Replace nop with an ftrace call. */
@@ -95,22 +267,44 @@ int __init ftrace_dyn_arch_init(void)
95267
return 0;
96268
}
97269

270+
void arch_ftrace_update_code(int command)
271+
{
272+
if (ftrace_shared_hotpatch_trampoline(NULL))
273+
ftrace_modify_all_code(command);
274+
else
275+
ftrace_run_stop_machine(command);
276+
}
277+
278+
static void __ftrace_sync(void *dummy)
279+
{
280+
}
281+
282+
int ftrace_arch_code_modify_post_process(void)
283+
{
284+
if (ftrace_shared_hotpatch_trampoline(NULL)) {
285+
/* Send SIGP to the other CPUs, so they see the new code. */
286+
smp_call_function(__ftrace_sync, NULL, 1);
287+
}
288+
return 0;
289+
}
290+
98291
#ifdef CONFIG_MODULES
99292

100293
static int __init ftrace_plt_init(void)
101294
{
102-
unsigned int *ip;
295+
const char *start, *end;
103296

104-
ftrace_plt = (unsigned long) module_alloc(PAGE_SIZE);
297+
ftrace_plt = module_alloc(PAGE_SIZE);
105298
if (!ftrace_plt)
106299
panic("cannot allocate ftrace plt\n");
107-
ip = (unsigned int *) ftrace_plt;
108-
ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */
109-
ip[1] = 0x100a0004;
110-
ip[2] = 0x07f10000;
111-
ip[3] = FTRACE_ADDR >> 32;
112-
ip[4] = FTRACE_ADDR & 0xffffffff;
113-
set_memory_ro(ftrace_plt, 1);
300+
301+
start = ftrace_shared_hotpatch_trampoline(&end);
302+
if (!start) {
303+
start = ftrace_plt_template;
304+
end = ftrace_plt_template_end;
305+
}
306+
memcpy(ftrace_plt, start, end - start);
307+
set_memory_ro((unsigned long)ftrace_plt, 1);
114308
return 0;
115309
}
116310
device_initcall(ftrace_plt_init);
@@ -147,17 +341,13 @@ NOKPROBE_SYMBOL(prepare_ftrace_return);
147341
*/
148342
int ftrace_enable_ftrace_graph_caller(void)
149343
{
150-
u8 op = 0x04; /* set mask field to zero */
151-
152-
s390_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op));
344+
brcl_disable(__va(ftrace_graph_caller));
153345
return 0;
154346
}
155347

156348
int ftrace_disable_ftrace_graph_caller(void)
157349
{
158-
u8 op = 0xf4; /* set mask field to all ones */
159-
160-
s390_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op));
350+
brcl_enable(__va(ftrace_graph_caller));
161351
return 0;
162352
}
163353

0 commit comments

Comments
 (0)