[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC PATCH 16/26] x86/alternatives: Add paravirt patching at runtime



Add paravirt_patch_runtime() which uses text_poke_late() to patch
paravirt sites.

Also add paravirt_worker() which does the actual insn generation
generate_paravirt() (which uses runtime_patch() to generate the
appropriate native or paravirt insn sequences) and then calls
text_poke_site() to do the actual poking.

 CPU0                                CPUx
 ----                                ----

 patch_worker()                      patch_worker()

   /* Traversal, insn-gen */           text_poke_sync_finish()
   tps.patch_worker()
     /* = paravirt_worker() */         /*
                                        * wait until:
     /* for each patch-site */          *  tps->state == PATCH_DONE
     generate_paravirt()                */
       runtime_patch()
     text_poke_site()
       poke_sync()

           ...                                 ...

   smp_store_release(&tps->state, PATCH_DONE)

Signed-off-by: Ankur Arora <ankur.a.arora@xxxxxxxxxx>
---
 arch/x86/include/asm/text-patching.h |  2 +
 arch/x86/kernel/alternative.c        | 98 +++++++++++++++++++++++++++-
 2 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/text-patching.h 
b/arch/x86/include/asm/text-patching.h
index c4b2814f2f9d..e86709a8287e 100644
--- a/arch/x86/include/asm/text-patching.h
+++ b/arch/x86/include/asm/text-patching.h
@@ -21,6 +21,8 @@ static inline void apply_paravirt(struct paravirt_patch_site 
*start,
 #ifndef CONFIG_PARAVIRT_RUNTIME
 #define __parainstructions_runtime     NULL
 #define __parainstructions_runtime_end NULL
+#else
+int paravirt_runtime_patch(void);
 #endif
 
 /*
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 452d4081eded..1c5acdc4f349 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -1463,7 +1463,9 @@ static void poke_sync(struct text_poke_state *tps, int 
state, int offset,
 /**
  * text_poke_site() - called on the primary to patch a single call site.
  *
- * Returns after switching tps->state to PATCH_SYNC_DONE.
+ * Called in thread context with tps->state == PATCH_SYNC_DONE where it
+ * takes tps->state through different PATCH_SYNC_* states, returning
+ * after having switched the tps->state back to PATCH_SYNC_DONE.
  */
 static void __maybe_unused text_poke_site(struct text_poke_state *tps,
                                          struct text_poke_loc *tp)
@@ -1598,6 +1600,16 @@ static int __maybe_unused text_poke_late(patch_worker_t 
worker, void *stage)
        return ret;
 }
 
+/*
+ * Check if this address is still in scope of this module's .text section.
+ */
+static bool __maybe_unused stale_address(struct alt_module *am, u8 *p)
+{
+       if (p < am->text || p >= am->text_end)
+               return true;
+       return false;
+}
+
 #ifdef CONFIG_PARAVIRT_RUNTIME
 struct paravirt_stage_entry {
        void *dest;     /* pv_op destination */
@@ -1654,4 +1666,88 @@ void text_poke_pv_stage_zero(void)
        lockdep_assert_held(&text_mutex);
        pv_stage.count = 0;
 }
+
+/**
+ * generate_paravirt - fill up the insn sequence for a pv-op.
+ *
+ * @tp - address of struct text_poke_loc
+ * @op - the pv-op entry for this location
+ * @site - patch site (kernel or module text)
+ */
+static void generate_paravirt(struct text_poke_loc *tp,
+                             struct paravirt_stage_entry *op,
+                             struct paravirt_patch_site *site)
+{
+       unsigned int used;
+
+       BUG_ON(site->len > POKE_MAX_OPCODE_SIZE);
+
+       text_poke_loc_init(tp, site->instr, site->instr, site->len, NULL, true);
+
+       /*
+        * Paravirt patches can patch calls (ex. mmu.tlb_flush),
+        * callee_saves(ex. queued_spin_unlock).
+        *
+        * runtime_patch() calls native_patch(), or paravirt_patch()
+        * based on the destination.
+        */
+       used = runtime_patch(site->type, (void *)tp->text, op->dest,
+                            (unsigned long)site->instr, site->len);
+
+       /* No good way to recover. */
+       BUG_ON(used < 0);
+
+       /* Pad the rest with nops */
+       add_nops((void *)tp->text + used, site->len - used);
+}
+
+/**
+ * paravirt_worker - generate the paravirt patching
+ * insns and calls text_poke_site() to do the actual patching.
+ */
+static void paravirt_worker(struct text_poke_state *tps)
+{
+       struct paravirt_patch_site *site;
+       struct paravirt_stage *stage = tps->stage;
+       struct paravirt_stage_entry *op = &stage->ops[0];
+       struct alt_module *am;
+       struct text_poke_loc tp;
+       int i;
+
+       list_for_each_entry(am, tps->head, next) {
+               for (site = am->para; site < am->para_end; site++) {
+                       if (stale_address(am, site->instr))
+                               continue;
+
+                       for (i = 0;  i < stage->count; i++) {
+                               if (op[i].type != site->type)
+                                       continue;
+
+                               generate_paravirt(&tp, &op[i], site);
+
+                               text_poke_site(tps, &tp);
+                       }
+               }
+       }
+}
+
+/**
+ * paravirt_runtime_patch() -- patch pv-ops, including paired ops.
+ *
+ * Called holding the text_mutex.
+ *
+ * Modify possibly multiple mutually-dependent pv-op callsites
+ * (ex. pv_lock_ops) using stop_machine().
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int paravirt_runtime_patch(void)
+{
+       lockdep_assert_held(&text_mutex);
+
+       if (!pv_stage.count)
+               return -EINVAL;
+
+       return text_poke_late(paravirt_worker, &pv_stage);
+}
 #endif /* CONFIG_PARAVIRT_RUNTIME */
-- 
2.20.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.