Skip to content

Commit 08a2117

Browse files
Alexandre Ghitigregkh
authored andcommitted
riscv: Fix text patching when IPI are used
[ Upstream commit c97bf62 ] 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 <bjorn@rivosinc.com> Signed-off-by: Björn Töpel <bjorn@rivosinc.com> Signed-off-by: Alexandre Ghiti <alexghiti@rivosinc.com> Reviewed-by: Andrea Parri <parri.andrea@gmail.com> Link: https://lore.kernel.org/r/20240229121056.203419-3-alexghiti@rivosinc.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com> Stable-dep-of: 13134cc ("riscv: kprobes: Fix incorrect address calculation") Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 56b2744 commit 08a2117

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;
@@ -137,6 +137,42 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
137137

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

142178
#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
@@ -196,7 +196,7 @@ int patch_text_set_nosync(void *addr, u8 c, size_t len)
196196
}
197197
NOKPROBE_SYMBOL(patch_text_set_nosync);
198198

199-
static int patch_insn_write(void *addr, const void *insn, size_t len)
199+
int patch_insn_write(void *addr, const void *insn, size_t len)
200200
{
201201
size_t patched = 0;
202202
size_t size;
@@ -240,16 +240,24 @@ static int patch_text_cb(void *data)
240240
if (atomic_inc_return(&patch->cpu_count) == num_online_cpus()) {
241241
for (i = 0; ret == 0 && i < patch->ninsns; i++) {
242242
len = GET_INSN_LENGTH(patch->insns[i]);
243-
ret = patch_text_nosync(patch->addr + i * len,
244-
&patch->insns[i], len);
243+
ret = patch_insn_write(patch->addr + i * len, &patch->insns[i], len);
245244
}
246-
atomic_inc(&patch->cpu_count);
245+
/*
246+
* Make sure the patching store is effective *before* we
247+
* increment the counter which releases all waiting CPUs
248+
* by using the release variant of atomic increment. The
249+
* release pairs with the call to local_flush_icache_all()
250+
* on the waiting CPU.
251+
*/
252+
atomic_inc_return_release(&patch->cpu_count);
247253
} else {
248254
while (atomic_read(&patch->cpu_count) <= num_online_cpus())
249255
cpu_relax();
250256
smp_mb();
251257
}
252258

259+
local_flush_icache_all();
260+
253261
return ret;
254262
}
255263
NOKPROBE_SYMBOL(patch_text_cb);

0 commit comments

Comments
 (0)