linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: Benjamin Gray <bgray@linux.ibm.com>
To: linuxppc-dev@lists.ozlabs.org
Cc: Benjamin Gray <bgray@linux.ibm.com>
Subject: [PATCH v2 1/3] powerpc/code-patching: Introduce open_patch_window()/close_patch_window()
Date: Tue, 26 Mar 2024 09:48:46 +1100	[thread overview]
Message-ID: <20240325224848.20987-1-bgray@linux.ibm.com> (raw)

The code patching capabilities have grown, and the specific quirks for
setting up and tearing down the patching window are getting duplicated.

This commit introduces an abstraction for working with this patching
window. It defines open_patch_window() to set up the writable alias
page, and close_patch_window() to remove it and flush the TLB. The
actual mechanism for providing this patching window is an implementation
detail that consumers don't need to worry about. Consumers are still
responsible for flushing/syncing any changes they make through this
alias page though.

Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>

---

v1: https://lore.kernel.org/all/20240315025937.407590-1-bgray@linux.ibm.com/

This design can be readily extended to remap the writable page to
another physical page without incurring all of the entry and exit
overhead. But that might have problems with spending too long in
an interrupts disabled context, so I've left it out for now.
---
 arch/powerpc/lib/code-patching.c | 113 +++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
index df64343b9214..7c193e19e297 100644
--- a/arch/powerpc/lib/code-patching.c
+++ b/arch/powerpc/lib/code-patching.c
@@ -276,6 +276,119 @@ static void unmap_patch_area(unsigned long addr)
 	flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
 }
 
+/*
+ * Represents an active patching window.
+ */
+struct patch_window {
+	/*
+	 * Page aligned patching window address. The page is a writable alias of
+	 * the configured target page.
+	 */
+	unsigned long text_poke_addr;
+	/*
+	 * Pointer to the PTE for the patching window page.
+	 */
+	pte_t *ptep;
+	/*
+	 * (Temporary MM patching only) The original MM before creating the
+	 * patching window.
+	 */
+	struct mm_struct *orig_mm;
+	/*
+	 * (Temporary MM patching only) The patching window MM.
+	 */
+	struct mm_struct *patch_mm;
+	/*
+	 * (Temporary MM patching only) Lock for the patching window PTE.
+	 */
+	spinlock_t *ptl;
+};
+
+/*
+ * Calling this function opens a 'patching window' that maps a
+ * page starting at ctx->text_poke_addr (which is set by this function)
+ * as a writable alias to the page starting at addr.
+ *
+ * Upon success, callers must invoke the corresponding close_patch_window()
+ * function to return to the original state. Callers are also responsible
+ * for syncing any changes they make inside the window.
+ *
+ * Interrupts must be disabled for the entire duration of the patching. The PIDR
+ * is potentially changed during this time.
+ */
+static int open_patch_window(void *addr, struct patch_window *ctx)
+{
+	unsigned long pfn = get_patch_pfn(addr);
+
+	lockdep_assert_irqs_disabled();
+
+	ctx->text_poke_addr = (unsigned long)__this_cpu_read(cpu_patching_context.addr);
+
+	if (!mm_patch_enabled()) {
+		ctx->ptep = __this_cpu_read(cpu_patching_context.pte);
+		__set_pte_at(&init_mm, ctx->text_poke_addr,
+			     ctx->ptep, pfn_pte(pfn, PAGE_KERNEL), 0);
+
+		/* See ptesync comment in radix__set_pte_at() */
+		if (radix_enabled())
+			asm volatile("ptesync" ::: "memory");
+
+		return 0;
+	}
+
+	ctx->orig_mm = current->active_mm;
+	ctx->patch_mm = __this_cpu_read(cpu_patching_context.mm);
+
+	ctx->ptep = get_locked_pte(ctx->patch_mm, ctx->text_poke_addr, &ctx->ptl);
+	if (!ctx->ptep)
+		return -ENOMEM;
+
+	__set_pte_at(ctx->patch_mm, ctx->text_poke_addr,
+		     ctx->ptep, pfn_pte(pfn, PAGE_KERNEL), 0);
+
+	/* order PTE update before use, also serves as the hwsync */
+	asm volatile("ptesync" ::: "memory");
+
+	/*
+	 * Changing mm requires context synchronising instructions on both sides of
+	 * the context switch, as well as a hwsync between the last instruction for
+	 * which the address of an associated storage access was translated using
+	 * the current context. switch_mm_irqs_off() performs an isync after the
+	 * context switch.
+	 */
+	isync();
+	switch_mm_irqs_off(ctx->orig_mm, ctx->patch_mm, current);
+
+	WARN_ON(!mm_is_thread_local(ctx->patch_mm));
+
+	suspend_breakpoints();
+	return 0;
+}
+
+static void close_patch_window(struct patch_window *ctx)
+{
+	lockdep_assert_irqs_disabled();
+
+	if (!mm_patch_enabled()) {
+		pte_clear(&init_mm, ctx->text_poke_addr, ctx->ptep);
+		flush_tlb_kernel_range(ctx->text_poke_addr, ctx->text_poke_addr + PAGE_SIZE);
+		return;
+	}
+
+	isync();
+	switch_mm_irqs_off(ctx->patch_mm, ctx->orig_mm, current);
+	restore_breakpoints();
+
+	pte_clear(ctx->patch_mm, ctx->text_poke_addr, ctx->ptep);
+	/*
+	 * ptesync to order PTE update before TLB invalidation done
+	 * by radix__local_flush_tlb_page_psize (in _tlbiel_va)
+	 */
+	local_flush_tlb_page_psize(ctx->patch_mm, ctx->text_poke_addr, mmu_virtual_psize);
+
+	pte_unmap_unlock(ctx->ptep, ctx->ptl);
+}
+
 static int __do_patch_instruction_mm(u32 *addr, ppc_inst_t instr)
 {
 	int err;
-- 
2.44.0


             reply	other threads:[~2024-03-25 22:50 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-25 22:48 Benjamin Gray [this message]
2024-03-25 22:48 ` [PATCH v2 2/3] powerpc/code-patching: Convert to open_patch_window()/close_patch_window() Benjamin Gray
2024-03-25 22:48 ` [PATCH v2 3/3] powerpc/code-patching: Restore 32-bit patching performance Benjamin Gray
2024-03-26  7:15   ` Christophe Leroy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240325224848.20987-1-bgray@linux.ibm.com \
    --to=bgray@linux.ibm.com \
    --cc=linuxppc-dev@lists.ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).