Skip to content

Commit d4b539a

Browse files
Merge patch series "riscv: Various text patching improvements"
Samuel Holland <[email protected]> says: Here are a few changes to minimize calls to stop_machine() and flush_icache_*() in the various text patching functions, as well as to simplify the code. * b4-shazam-merge: riscv: Remove extra variable in patch_text_nosync() riscv: Use offset_in_page() in text patching functions riscv: Pass patch_text() the length in bytes riscv: Simplify text patching loops riscv: kprobes: Use patch_text_nosync() for insn slots riscv: jump_label: Simplify assembly syntax riscv: jump_label: Batch icache maintenance Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
2 parents c74f037 + 4774248 commit d4b539a

File tree

6 files changed

+63
-54
lines changed

6 files changed

+63
-54
lines changed

arch/riscv/include/asm/jump_label.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <linux/types.h>
1313
#include <asm/asm.h>
1414

15+
#define HAVE_JUMP_LABEL_BATCH
16+
1517
#define JUMP_LABEL_NOP_SIZE 4
1618

1719
static __always_inline bool arch_static_branch(struct static_key * const key,
@@ -44,7 +46,7 @@ static __always_inline bool arch_static_branch_jump(struct static_key * const ke
4446
" .option push \n\t"
4547
" .option norelax \n\t"
4648
" .option norvc \n\t"
47-
"1: jal zero, %l[label] \n\t"
49+
"1: j %l[label] \n\t"
4850
" .option pop \n\t"
4951
" .pushsection __jump_table, \"aw\" \n\t"
5052
" .align " RISCV_LGPTR " \n\t"

arch/riscv/include/asm/patch.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
int patch_insn_write(void *addr, const void *insn, size_t len);
1010
int patch_text_nosync(void *addr, const void *insns, size_t len);
1111
int patch_text_set_nosync(void *addr, u8 c, size_t len);
12-
int patch_text(void *addr, u32 *insns, int ninsns);
12+
int patch_text(void *addr, u32 *insns, size_t len);
1313

1414
extern int riscv_patch_in_stop_machine;
1515

arch/riscv/kernel/jump_label.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99
#include <linux/memory.h>
1010
#include <linux/mutex.h>
1111
#include <asm/bug.h>
12+
#include <asm/cacheflush.h>
1213
#include <asm/patch.h>
1314

1415
#define RISCV_INSN_NOP 0x00000013U
1516
#define RISCV_INSN_JAL 0x0000006fU
1617

17-
void arch_jump_label_transform(struct jump_entry *entry,
18-
enum jump_label_type type)
18+
bool arch_jump_label_transform_queue(struct jump_entry *entry,
19+
enum jump_label_type type)
1920
{
2021
void *addr = (void *)jump_entry_code(entry);
2122
u32 insn;
@@ -24,7 +25,7 @@ void arch_jump_label_transform(struct jump_entry *entry,
2425
long offset = jump_entry_target(entry) - jump_entry_code(entry);
2526

2627
if (WARN_ON(offset & 1 || offset < -524288 || offset >= 524288))
27-
return;
28+
return true;
2829

2930
insn = RISCV_INSN_JAL |
3031
(((u32)offset & GENMASK(19, 12)) << (12 - 12)) |
@@ -36,6 +37,13 @@ void arch_jump_label_transform(struct jump_entry *entry,
3637
}
3738

3839
mutex_lock(&text_mutex);
39-
patch_text_nosync(addr, &insn, sizeof(insn));
40+
patch_insn_write(addr, &insn, sizeof(insn));
4041
mutex_unlock(&text_mutex);
42+
43+
return true;
44+
}
45+
46+
void arch_jump_label_transform_apply(void)
47+
{
48+
flush_icache_all();
4149
}

arch/riscv/kernel/patch.c

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
struct patch_insn {
2020
void *addr;
2121
u32 *insns;
22-
int ninsns;
22+
size_t len;
2323
atomic_t cpu_count;
2424
};
2525

@@ -54,7 +54,7 @@ static __always_inline void *patch_map(void *addr, const unsigned int fixmap)
5454
BUG_ON(!page);
5555

5656
return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
57-
(uintaddr & ~PAGE_MASK));
57+
offset_in_page(addr));
5858
}
5959

6060
static void patch_unmap(int fixmap)
@@ -65,8 +65,8 @@ NOKPROBE_SYMBOL(patch_unmap);
6565

6666
static int __patch_insn_set(void *addr, u8 c, size_t len)
6767
{
68+
bool across_pages = (offset_in_page(addr) + len) > PAGE_SIZE;
6869
void *waddr = addr;
69-
bool across_pages = (((uintptr_t)addr & ~PAGE_MASK) + len) > PAGE_SIZE;
7070

7171
/*
7272
* Only two pages can be mapped at a time for writing.
@@ -102,8 +102,8 @@ NOKPROBE_SYMBOL(__patch_insn_set);
102102

103103
static int __patch_insn_write(void *addr, const void *insn, size_t len)
104104
{
105+
bool across_pages = (offset_in_page(addr) + len) > PAGE_SIZE;
105106
void *waddr = addr;
106-
bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
107107
int ret;
108108

109109
/*
@@ -163,69 +163,70 @@ NOKPROBE_SYMBOL(__patch_insn_write);
163163

164164
static int patch_insn_set(void *addr, u8 c, size_t len)
165165
{
166-
size_t patched = 0;
167166
size_t size;
168-
int ret = 0;
167+
int ret;
169168

170169
/*
171170
* __patch_insn_set() can only work on 2 pages at a time so call it in a
172171
* loop with len <= 2 * PAGE_SIZE.
173172
*/
174-
while (patched < len && !ret) {
175-
size = min_t(size_t, PAGE_SIZE * 2 - offset_in_page(addr + patched), len - patched);
176-
ret = __patch_insn_set(addr + patched, c, size);
177-
178-
patched += size;
173+
while (len) {
174+
size = min(len, PAGE_SIZE * 2 - offset_in_page(addr));
175+
ret = __patch_insn_set(addr, c, size);
176+
if (ret)
177+
return ret;
178+
179+
addr += size;
180+
len -= size;
179181
}
180182

181-
return ret;
183+
return 0;
182184
}
183185
NOKPROBE_SYMBOL(patch_insn_set);
184186

185187
int patch_text_set_nosync(void *addr, u8 c, size_t len)
186188
{
187-
u32 *tp = addr;
188189
int ret;
189190

190-
ret = patch_insn_set(tp, c, len);
191-
191+
ret = patch_insn_set(addr, c, len);
192192
if (!ret)
193-
flush_icache_range((uintptr_t)tp, (uintptr_t)tp + len);
193+
flush_icache_range((uintptr_t)addr, (uintptr_t)addr + len);
194194

195195
return ret;
196196
}
197197
NOKPROBE_SYMBOL(patch_text_set_nosync);
198198

199199
int patch_insn_write(void *addr, const void *insn, size_t len)
200200
{
201-
size_t patched = 0;
202201
size_t size;
203-
int ret = 0;
202+
int ret;
204203

205204
/*
206205
* Copy the instructions to the destination address, two pages at a time
207206
* because __patch_insn_write() can only handle len <= 2 * PAGE_SIZE.
208207
*/
209-
while (patched < len && !ret) {
210-
size = min_t(size_t, PAGE_SIZE * 2 - offset_in_page(addr + patched), len - patched);
211-
ret = __patch_insn_write(addr + patched, insn + patched, size);
212-
213-
patched += size;
208+
while (len) {
209+
size = min(len, PAGE_SIZE * 2 - offset_in_page(addr));
210+
ret = __patch_insn_write(addr, insn, size);
211+
if (ret)
212+
return ret;
213+
214+
addr += size;
215+
insn += size;
216+
len -= size;
214217
}
215218

216-
return ret;
219+
return 0;
217220
}
218221
NOKPROBE_SYMBOL(patch_insn_write);
219222

220223
int patch_text_nosync(void *addr, const void *insns, size_t len)
221224
{
222-
u32 *tp = addr;
223225
int ret;
224226

225-
ret = patch_insn_write(tp, insns, len);
226-
227+
ret = patch_insn_write(addr, insns, len);
227228
if (!ret)
228-
flush_icache_range((uintptr_t) tp, (uintptr_t) tp + len);
229+
flush_icache_range((uintptr_t)addr, (uintptr_t)addr + len);
229230

230231
return ret;
231232
}
@@ -234,14 +235,10 @@ NOKPROBE_SYMBOL(patch_text_nosync);
234235
static int patch_text_cb(void *data)
235236
{
236237
struct patch_insn *patch = data;
237-
unsigned long len;
238-
int i, ret = 0;
238+
int ret = 0;
239239

240240
if (atomic_inc_return(&patch->cpu_count) == num_online_cpus()) {
241-
for (i = 0; ret == 0 && i < patch->ninsns; i++) {
242-
len = GET_INSN_LENGTH(patch->insns[i]);
243-
ret = patch_insn_write(patch->addr + i * len, &patch->insns[i], len);
244-
}
241+
ret = patch_insn_write(patch->addr, patch->insns, patch->len);
245242
/*
246243
* Make sure the patching store is effective *before* we
247244
* increment the counter which releases all waiting CPUs
@@ -261,13 +258,13 @@ static int patch_text_cb(void *data)
261258
}
262259
NOKPROBE_SYMBOL(patch_text_cb);
263260

264-
int patch_text(void *addr, u32 *insns, int ninsns)
261+
int patch_text(void *addr, u32 *insns, size_t len)
265262
{
266263
int ret;
267264
struct patch_insn patch = {
268265
.addr = addr,
269266
.insns = insns,
270-
.ninsns = ninsns,
267+
.len = len,
271268
.cpu_count = ATOMIC_INIT(0),
272269
};
273270

arch/riscv/kernel/probes/kprobes.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,13 @@ post_kprobe_handler(struct kprobe *, struct kprobe_ctlblk *, struct pt_regs *);
2424

2525
static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
2626
{
27+
size_t len = GET_INSN_LENGTH(p->opcode);
2728
u32 insn = __BUG_INSN_32;
28-
unsigned long offset = GET_INSN_LENGTH(p->opcode);
2929

30-
p->ainsn.api.restore = (unsigned long)p->addr + offset;
30+
p->ainsn.api.restore = (unsigned long)p->addr + len;
3131

32-
patch_text(p->ainsn.api.insn, &p->opcode, 1);
33-
patch_text((void *)((unsigned long)(p->ainsn.api.insn) + offset),
34-
&insn, 1);
32+
patch_text_nosync(p->ainsn.api.insn, &p->opcode, len);
33+
patch_text_nosync(p->ainsn.api.insn + len, &insn, GET_INSN_LENGTH(insn));
3534
}
3635

3736
static void __kprobes arch_prepare_simulate(struct kprobe *p)
@@ -108,16 +107,18 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
108107
/* install breakpoint in text */
109108
void __kprobes arch_arm_kprobe(struct kprobe *p)
110109
{
111-
u32 insn = (p->opcode & __INSN_LENGTH_MASK) == __INSN_LENGTH_32 ?
112-
__BUG_INSN_32 : __BUG_INSN_16;
110+
size_t len = GET_INSN_LENGTH(p->opcode);
111+
u32 insn = len == 4 ? __BUG_INSN_32 : __BUG_INSN_16;
113112

114-
patch_text(p->addr, &insn, 1);
113+
patch_text(p->addr, &insn, len);
115114
}
116115

117116
/* remove breakpoint from text */
118117
void __kprobes arch_disarm_kprobe(struct kprobe *p)
119118
{
120-
patch_text(p->addr, &p->opcode, 1);
119+
size_t len = GET_INSN_LENGTH(p->opcode);
120+
121+
patch_text(p->addr, &p->opcode, len);
121122
}
122123

123124
void __kprobes arch_remove_kprobe(struct kprobe *p)

arch/riscv/net/bpf_jit_comp64.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "bpf_jit.h"
1717

1818
#define RV_FENTRY_NINSNS 2
19+
#define RV_FENTRY_NBYTES (RV_FENTRY_NINSNS * 4)
1920

2021
#define RV_REG_TCC RV_REG_A6
2122
#define RV_REG_TCC_SAVED RV_REG_S6 /* Store A6 in S6 if program do calls */
@@ -672,7 +673,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
672673
if (ret)
673674
return ret;
674675

675-
if (memcmp(ip, old_insns, RV_FENTRY_NINSNS * 4))
676+
if (memcmp(ip, old_insns, RV_FENTRY_NBYTES))
676677
return -EFAULT;
677678

678679
ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call);
@@ -681,8 +682,8 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
681682

682683
cpus_read_lock();
683684
mutex_lock(&text_mutex);
684-
if (memcmp(ip, new_insns, RV_FENTRY_NINSNS * 4))
685-
ret = patch_text(ip, new_insns, RV_FENTRY_NINSNS);
685+
if (memcmp(ip, new_insns, RV_FENTRY_NBYTES))
686+
ret = patch_text(ip, new_insns, RV_FENTRY_NBYTES);
686687
mutex_unlock(&text_mutex);
687688
cpus_read_unlock();
688689

0 commit comments

Comments
 (0)