x86/alternative: Introduce text_poke_copy
authorSong Liu <song@kernel.org>
Fri, 4 Feb 2022 18:57:38 +0000 (10:57 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 8 Feb 2022 02:13:01 +0000 (18:13 -0800)
This will be used by BPF jit compiler to dump JITed binary to a RX huge
page, and thus allow multiple BPF programs sharing the a huge (2MB) page.

Signed-off-by: Song Liu <song@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/bpf/20220204185742.271030-6-song@kernel.org
arch/x86/include/asm/text-patching.h
arch/x86/kernel/alternative.c

index b742178..4cc18ba 100644 (file)
@@ -44,6 +44,7 @@ extern void text_poke_early(void *addr, const void *opcode, size_t len);
 extern void *text_poke(void *addr, const void *opcode, size_t len);
 extern void text_poke_sync(void);
 extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
+extern void *text_poke_copy(void *addr, const void *opcode, size_t len);
 extern int poke_int3_handler(struct pt_regs *regs);
 extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate);
 
index 5007c3f..018b61f 100644 (file)
@@ -1102,6 +1102,40 @@ void *text_poke_kgdb(void *addr, const void *opcode, size_t len)
        return __text_poke(addr, opcode, len);
 }
 
+/**
+ * text_poke_copy - Copy instructions into (an unused part of) RX memory
+ * @addr: address to modify
+ * @opcode: source of the copy
+ * @len: length to copy, could be more than 2x PAGE_SIZE
+ *
+ * Not safe against concurrent execution; useful for JITs to dump
+ * new code blocks into unused regions of RX memory. Can be used in
+ * conjunction with synchronize_rcu_tasks() to wait for existing
+ * execution to quiesce after having made sure no existing functions
+ * pointers are live.
+ */
+void *text_poke_copy(void *addr, const void *opcode, size_t len)
+{
+       unsigned long start = (unsigned long)addr;
+       size_t patched = 0;
+
+       if (WARN_ON_ONCE(core_kernel_text(start)))
+               return NULL;
+
+       mutex_lock(&text_mutex);
+       while (patched < len) {
+               unsigned long ptr = start + patched;
+               size_t s;
+
+               s = min_t(size_t, PAGE_SIZE * 2 - offset_in_page(ptr), len - patched);
+
+               __text_poke((void *)ptr, opcode + patched, s);
+               patched += s;
+       }
+       mutex_unlock(&text_mutex);
+       return addr;
+}
+
 static void do_sync_core(void *info)
 {
        sync_core();