Skip to content

Commit c97bf62

Browse files
Alexandre Ghitipalmer-dabbelt
authored andcommitted
riscv: Fix text patching when IPI are used
For now, we use stop_machine() to patch the text and when we use IPIs for remote icache flushes (which is emitted in patch_text_nosync()), the system hangs. So instead, make sure every CPU executes the stop_machine() patching function and emit a local icache flush there. Co-developed-by: Björn Töpel <[email protected]> Signed-off-by: Björn Töpel <[email protected]> Signed-off-by: Alexandre Ghiti <[email protected]> Reviewed-by: Andrea Parri <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 29cee75 commit c97bf62

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

arch/riscv/include/asm/patch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef _ASM_RISCV_PATCH_H
77
#define _ASM_RISCV_PATCH_H
88

9+
int patch_insn_write(void *addr, const void *insn, size_t len);
910
int patch_text_nosync(void *addr, const void *insns, size_t len);
1011
int patch_text_set_nosync(void *addr, u8 c, size_t len);
1112
int patch_text(void *addr, u32 *insns, int ninsns);

arch/riscv/kernel/ftrace.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/ftrace.h>
99
#include <linux/uaccess.h>
1010
#include <linux/memory.h>
11+
#include <linux/stop_machine.h>
1112
#include <asm/cacheflush.h>
1213
#include <asm/patch.h>
1314

@@ -75,8 +76,7 @@ static int __ftrace_modify_call(unsigned long hook_pos, unsigned long target,
7576
make_call_t0(hook_pos, target, call);
7677

7778
/* Replace the auipc-jalr pair at once. Return -EPERM on write error. */
78-
if (patch_text_nosync
79-
((void *)hook_pos, enable ? call : nops, MCOUNT_INSN_SIZE))
79+
if (patch_insn_write((void *)hook_pos, enable ? call : nops, MCOUNT_INSN_SIZE))
8080
return -EPERM;
8181

8282
return 0;
@@ -88,7 +88,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
8888

8989
make_call_t0(rec->ip, addr, call);
9090

91-
if (patch_text_nosync((void *)rec->ip, call, MCOUNT_INSN_SIZE))
91+
if (patch_insn_write((void *)rec->ip, call, MCOUNT_INSN_SIZE))
9292
return -EPERM;
9393

9494
return 0;
@@ -99,7 +99,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
9999
{
100100
unsigned int nops[2] = {NOP4, NOP4};
101101

102-
if (patch_text_nosync((void *)rec->ip, nops, MCOUNT_INSN_SIZE))
102+
if (patch_insn_write((void *)rec->ip, nops, MCOUNT_INSN_SIZE))
103103
return -EPERM;
104104

105105
return 0;
@@ -134,6 +134,42 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
134134

135135
return ret;
136136
}
137+
138+
struct ftrace_modify_param {
139+
int command;
140+
atomic_t cpu_count;
141+
};
142+
143+
static int __ftrace_modify_code(void *data)
144+
{
145+
struct ftrace_modify_param *param = data;
146+
147+
if (atomic_inc_return(&param->cpu_count) == num_online_cpus()) {
148+
ftrace_modify_all_code(param->command);
149+
/*
150+
* Make sure the patching store is effective *before* we
151+
* increment the counter which releases all waiting CPUs
152+
* by using the release variant of atomic increment. The
153+
* release pairs with the call to local_flush_icache_all()
154+
* on the waiting CPU.
155+
*/
156+
atomic_inc_return_release(&param->cpu_count);
157+
} else {
158+
while (atomic_read(&param->cpu_count) <= num_online_cpus())
159+
cpu_relax();
160+
}
161+
162+
local_flush_icache_all();
163+
164+
return 0;
165+
}
166+
167+
void arch_ftrace_update_code(int command)
168+
{
169+
struct ftrace_modify_param param = { command, ATOMIC_INIT(0) };
170+
171+
stop_machine(__ftrace_modify_code, &param, cpu_online_mask);
172+
}
137173
#endif
138174

139175
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS

arch/riscv/kernel/patch.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ int patch_text_set_nosync(void *addr, u8 c, size_t len)
188188
}
189189
NOKPROBE_SYMBOL(patch_text_set_nosync);
190190

191-
static int patch_insn_write(void *addr, const void *insn, size_t len)
191+
int patch_insn_write(void *addr, const void *insn, size_t len)
192192
{
193193
size_t patched = 0;
194194
size_t size;
@@ -232,15 +232,23 @@ static int patch_text_cb(void *data)
232232
if (atomic_inc_return(&patch->cpu_count) == num_online_cpus()) {
233233
for (i = 0; ret == 0 && i < patch->ninsns; i++) {
234234
len = GET_INSN_LENGTH(patch->insns[i]);
235-
ret = patch_text_nosync(patch->addr + i * len,
236-
&patch->insns[i], len);
235+
ret = patch_insn_write(patch->addr + i * len, &patch->insns[i], len);
237236
}
238-
atomic_inc(&patch->cpu_count);
237+
/*
238+
* Make sure the patching store is effective *before* we
239+
* increment the counter which releases all waiting CPUs
240+
* by using the release variant of atomic increment. The
241+
* release pairs with the call to local_flush_icache_all()
242+
* on the waiting CPU.
243+
*/
244+
atomic_inc_return_release(&patch->cpu_count);
239245
} else {
240246
while (atomic_read(&patch->cpu_count) <= num_online_cpus())
241247
cpu_relax();
242248
}
243249

250+
local_flush_icache_all();
251+
244252
return ret;
245253
}
246254
NOKPROBE_SYMBOL(patch_text_cb);

0 commit comments

Comments
 (0)