Skip to contentSkip to conversation

paravirt: clean up lazy mode handling

RFC PATCH In review Jeremy Fitzhardinge 3 participants 3 messages Oct 12, 2007 – Oct 16, 2007 PARAVIRT_OPS interface

lore
Share & tools
lore
#0 lore raw

RFC PATCH 2/2 paravirt: clean up lazy mode handling

[ Changes since last post: fixed up lguest ]

Currently, the set_lazy_mode pv_op is overloaded with 5 functions:
1. enter lazy cpu mode
2. leave lazy cpu mode
3. enter lazy mmu mode
4. leave lazy mmu mode
5. flush pending batched operations

This complicates each paravirt backend, since it needs to deal with all the possible state transitions, handling flushing, etc. In particular, flushing is quite distinct from the other 4 functions, and seems to just cause complication.

This patch removes the set_lazy_mode operation, and adds "enter" and "leave" lazy mode operations on mmu_ops and cpu_ops. All the logic associated with enter and leaving lazy states is now in common code (basically BUG_ONs to make sure that no mode is current when entering a lazy mode, and make sure that the mode is current when leaving). Also, flush is handled in a common way, by simply leaving and re-entering the lazy mode.

The result is that the Xen, lguest and VMI lazy mode implementations are much simpler.

Cc Andi Kleen <[email protected]>
Cc Zach Amsden <[email protected]>
Cc Rusty Russell <[email protected]>
Cc Avi Kivity <[email protected]>
Cc Anthony Liguory <[email protected]>
Cc "Glauber de Oliveira Costa" <[email protected]>
Cc "Nakajima, Jun" <[email protected]>
---
 arch/i386/kernel/paravirt.c |   58 +++++++++++++++++++++++++++++++++++++++----
 arch/i386/kernel/vmi.c      |   45 +++++++++++++++++++--------------
 arch/i386/xen/enlighten.c   |   44 ++++++++++----------------------
 arch/i386/xen/mmu.c         |    2 -
 arch/i386/xen/multicalls.h  |    2 -
 arch/i386/xen/xen-ops.h     |    7 -----
 drivers/lguest/lguest.c     |   34 ++++++++-----------------
 include/asm-i386/paravirt.h |   52 ++++++++++++++++++++++++--------------
 8 files changed, 140 insertions(+), 104 deletions(-)

===================================================================

arch/i386/kernel/paravirt.c +53 -5
@@ -266,6 +266,49 @@ int paravirt_disable_iospace(void)
 	}
 
 	return ret;
+}
+
+static DEFINE_PER_CPU(enum paravirt_lazy_mode, paravirt_lazy_mode) = PARAVIRT_LAZY_NONE;
+
+static inline void enter_lazy(enum paravirt_lazy_mode mode)
+{
+	BUG_ON(x86_read_percpu(paravirt_lazy_mode) != PARAVIRT_LAZY_NONE);
+	BUG_ON(preemptible());
+
+	x86_write_percpu(paravirt_lazy_mode, mode);
+}
+
+void paravirt_leave_lazy(enum paravirt_lazy_mode mode)
+{
+	BUG_ON(x86_read_percpu(paravirt_lazy_mode) != mode);
+	BUG_ON(preemptible());
+
+	x86_write_percpu(paravirt_lazy_mode, PARAVIRT_LAZY_NONE);
+}
+
+void paravirt_enter_lazy_mmu(void)
+{
+	enter_lazy(PARAVIRT_LAZY_MMU);
+}
+
+void paravirt_leave_lazy_mmu(void)
+{
+	paravirt_leave_lazy(PARAVIRT_LAZY_MMU);
+}
+
+void paravirt_enter_lazy_cpu(void)
+{
+	enter_lazy(PARAVIRT_LAZY_CPU);
+}
+
+void paravirt_leave_lazy_cpu(void)
+{
+	paravirt_leave_lazy(PARAVIRT_LAZY_CPU);
+}
+
+enum paravirt_lazy_mode paravirt_get_lazy_mode(void)
+{
+	return x86_read_percpu(paravirt_lazy_mode);
 }
 
 struct pv_info pv_info = {
@@ -334,6 +377,11 @@ struct paravirt_ops paravirt_ops = {
 
 		 .set_iopl_mask = native_set_iopl_mask,
 		 .io_delay = native_io_delay,
+
+		 .lazy_mode = {
+			 .enter = paravirt_nop,
+			 .leave = paravirt_nop,
+		 },
 	 },
 
 	.pv_apic_ops = {
@@ -347,10 +395,6 @@ struct paravirt_ops paravirt_ops = {
 #endif
 	 },
 
-	.pv_misc_ops = {
-		 .set_lazy_mode = paravirt_nop,
-	 },
-
 	.pv_mmu_ops = {
 		 .pagetable_setup_start = native_pagetable_setup_start,
 		 .pagetable_setup_done = native_pagetable_setup_done,
@@ -401,6 +445,11 @@ struct paravirt_ops paravirt_ops = {
 		 .dup_mmap = paravirt_nop,
 		 .exit_mmap = paravirt_nop,
 		 .activate_mm = paravirt_nop,
+
+		 .lazy_mode = {
+			 .enter = paravirt_nop,
+			 .leave = paravirt_nop,
+		 },
 	 },
 };
 
@@ -417,7 +466,6 @@ static void __init __used pv_aliases(voi
 		     : : "i" (offsetof(struct paravirt_ops, inner)))
 
 	substructure(pv_init_ops);
-	substructure(pv_misc_ops);
 	substructure(pv_time_ops);
 	substructure(pv_cpu_ops);
 	substructure(pv_irq_ops);

===================================================================

arch/i386/kernel/vmi.c +26 -19

57 lines not on this page · raw message

===================================================================

arch/i386/xen/enlighten.c +14 -30

81 lines not on this page · raw message

===================================================================

arch/i386/xen/mmu.c +1 -1
@@ -154,7 +154,7 @@ void xen_set_pte_at(struct mm_struct *mm
 		    pte_t *ptep, pte_t pteval)
 {
 	if (mm == current->mm || mm == &init_mm) {
-		if (xen_get_lazy_mode() == PARAVIRT_LAZY_MMU) {
+		if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) {
 			struct multicall_space mcs;
 			mcs = xen_mc_entry(0);
 

===================================================================

arch/i386/xen/multicalls.h +1 -1
@@ -35,7 +35,7 @@ void xen_mc_flush(void);
 /* Issue a multicall if we're not in a lazy mode */
 static inline void xen_mc_issue(unsigned mode)
 {
-	if ((xen_get_lazy_mode() & mode) == 0)
+	if ((paravirt_get_lazy_mode() & mode) == 0)
 		xen_mc_flush();
 
 	/* restore flags saved in xen_mc_batch */

===================================================================

arch/i386/xen/xen-ops.h +0 -7
@@ -32,13 +32,6 @@ bool xen_vcpu_stolen(int vcpu);
 
 void xen_mark_init_mm_pinned(void);
 
-DECLARE_PER_CPU(enum paravirt_lazy_mode, xen_lazy_mode);
-
-static inline unsigned xen_get_lazy_mode(void)
-{
-	return x86_read_percpu(xen_lazy_mode);
-}
-
 void __init xen_fill_possible_map(void);
 
 void __init xen_setup_vcpu_info_placement(void);

===================================================================

drivers/lguest/lguest.c +12 -22
@@ -97,29 +97,17 @@ static cycle_t clock_base;
  * them as a batch when lazy_mode is eventually turned off.  Because hypercalls
  * are reasonably expensive, batching them up makes sense.  For example, a
  * large mmap might update dozens of page table entries: that code calls
- * lguest_lazy_mode(PARAVIRT_LAZY_MMU), does the dozen updates, then calls
- * lguest_lazy_mode(PARAVIRT_LAZY_NONE).
+ * paravirt_enter_lazy_mmu(), does the dozen updates, then calls
+ * lguest_leave_lazy_mode().
  *
  * So, when we're in lazy mode, we call async_hypercall() to store the call for
  * future processing.  When lazy mode is turned off we issue a hypercall to
  * flush the stored calls.
- *
- * There's also a hack where "mode" is set to "PARAVIRT_LAZY_FLUSH" which
- * indicates we're to flush any outstanding calls immediately.  This is used
- * when an interrupt handler does a kmap_atomic(): the page table changes must
- * happen immediately even if we're in the middle of a batch.  Usually we're
- * not, though, so there's nothing to do. */
-static enum paravirt_lazy_mode lazy_mode; /* Note: not SMP-safe! */
-static void lguest_lazy_mode(enum paravirt_lazy_mode mode)
-{
-	if (mode == PARAVIRT_LAZY_FLUSH) {
-		if (unlikely(lazy_mode != PARAVIRT_LAZY_NONE))
-			hcall(LHCALL_FLUSH_ASYNC, 0, 0, 0);
-	} else {
-		lazy_mode = mode;
-		if (mode == PARAVIRT_LAZY_NONE)
-			hcall(LHCALL_FLUSH_ASYNC, 0, 0, 0);
-	}
+ */
+static void lguest_leave_lazy_mode(void)
+{
+	hcall(LHCALL_FLUSH_ASYNC, 0, 0, 0);
+	paravirt_leave_lazy(paravirt_get_lazy_mode());
 }
 
 static void lazy_hcall(unsigned long call,
@@ -127,7 +115,7 @@ static void lazy_hcall(unsigned long cal
 		       unsigned long arg2,
 		       unsigned long arg3)
 {
-	if (lazy_mode == PARAVIRT_LAZY_NONE)
+	if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE)
 		hcall(call, arg1, arg2, arg3);
 	else
 		async_hcall(call, arg1, arg2, arg3);
@@ -1011,6 +999,8 @@ __init void lguest_init(void *boot)
 	pv_cpu_ops.write_gdt_entry = lguest_write_gdt_entry;
 	pv_cpu_ops.write_idt_entry = lguest_write_idt_entry;
 	pv_cpu_ops.wbinvd = lguest_wbinvd;
+	pv_cpu_ops.lazy_mode.enter = paravirt_enter_lazy_cpu;
+	pv_cpu_ops.lazy_mode.leave = lguest_leave_lazy_mode;
 
 	/* pagetable management */
 	pv_mmu_ops.write_cr3 = lguest_write_cr3;
@@ -1022,6 +1012,8 @@ __init void lguest_init(void *boot)
 	pv_mmu_ops.set_pmd = lguest_set_pmd;
 	pv_mmu_ops.read_cr2 = lguest_read_cr2;
 	pv_mmu_ops.read_cr3 = lguest_read_cr3;
+	pv_mmu_ops.lazy_mode.enter = paravirt_enter_lazy_mmu;
+	pv_mmu_ops.lazy_mode.leave = lguest_leave_lazy_mode;
 
 #ifdef CONFIG_X86_LOCAL_APIC
 	/* apic read/write intercepts */
@@ -1033,8 +1025,6 @@ __init void lguest_init(void *boot)
 	/* time operations */
 	pv_time_ops.get_wallclock = lguest_get_wallclock;
 	pv_time_ops.time_init = lguest_time_init;
-
-	pv_misc_ops.set_lazy_mode = lguest_lazy_mode;
 
 	/* Now is a good time to look at the implementations of these functions
 	 * before returning to the rest of lguest_init(). */

===================================================================

include/asm-i386/paravirt.h +33 -19

119 lines not on this page · raw message

Acked-by on v1 2/2
#1 lore raw
Quoted 1 line · On Saturday 13 October 2007 06:40:36 Jeremy Fitzhardinge wrote:

[ Changes since last post: fixed up lguest ]

This is really nice. Thanks Jeremy!

This will conflict a little with my own churn (file movement), but no great drama if it goes in soon.

#2 lore raw
In reply to Jeremy Fitzhardinge
Quoted 50 lines · Jeremy Fitzhardinge

[ Changes since last post: fixed up lguest ]

Currently, the set_lazy_mode pv_op is overloaded with 5 functions:
1. enter lazy cpu mode
2. leave lazy cpu mode
3. enter lazy mmu mode
4. leave lazy mmu mode
5. flush pending batched operations

This complicates each paravirt backend, since it needs to deal with all the possible state transitions, handling flushing, etc. In particular, flushing is quite distinct from the other 4 functions, and seems to just cause complication.

This patch removes the set_lazy_mode operation, and adds "enter" and "leave" lazy mode operations on mmu_ops and cpu_ops. All the logic associated with enter and leaving lazy states is now in common code (basically BUG_ONs to make sure that no mode is current when entering a lazy mode, and make sure that the mode is current when leaving). Also, flush is handled in a common way, by simply leaving and re-entering the lazy mode.

The result is that the Xen, lguest and VMI lazy mode implementations are much simpler.

Cc Andi Kleen <[email protected]>
Cc Zach Amsden <[email protected]>
Cc Rusty Russell <[email protected]>
Cc Avi Kivity <[email protected]>
Cc Anthony Liguory <[email protected]>
Cc "Glauber de Oliveira Costa" <[email protected]>
Cc "Nakajima, Jun" <[email protected]>
---
 arch/i386/kernel/paravirt.c |   58 +++++++++++++++++++++++++++++++++++++++----
 arch/i386/kernel/vmi.c      |   45 +++++++++++++++++++--------------
 arch/i386/xen/enlighten.c   |   44 ++++++++++----------------------
 arch/i386/xen/mmu.c         |    2 -
 arch/i386/xen/multicalls.h  |    2 -
 arch/i386/xen/xen-ops.h     |    7 -----
 drivers/lguest/lguest.c     |   34 ++++++++-----------------
 include/asm-i386/paravirt.h |   52 ++++++++++++++++++++++++--------------
 8 files changed, 140 insertions(+), 104 deletions(-)

===================================================================

arch/i386/kernel/paravirt.c +0 -0
@@ -266,6 +266,49 @@ int paravirt_disable_iospace(void)
        }

arch/i386/kernel/paravirt.c int paravirt_disable_iospace(void) · line 276

Quoted 10 lines
arch/i386/kernel/paravirt.c +8 -0
 
        return ret;
+}
+
+static DEFINE_PER_CPU(enum paravirt_lazy_mode, paravirt_lazy_mode) = PARAVIRT_LAZY_NONE;
+
+static inline void enter_lazy(enum paravirt_lazy_mode mode)
+{
+       BUG_ON(x86_read_percpu(paravirt_lazy_mode) != PARAVIRT_LAZY_NONE);
+       BUG_ON(preemptible());

Wouldn't it be better to WARN_ON, and simply not entering lazy mode? It does not sound like a fatal condition.

arch/i386/kernel/paravirt.c int paravirt_disable_iospace(void) · line 284

Quoted 4 lines
arch/i386/kernel/paravirt.c +4 -0
+void paravirt_leave_lazy(enum paravirt_lazy_mode mode)
+{
+       BUG_ON(x86_read_percpu(paravirt_lazy_mode) != mode);
+       BUG_ON(preemptible());

Although this one seems like a fatal condition ;-)

Quoted 14 lines
arch/i386/kernel/paravirt.c +14 -0
+void paravirt_enter_lazy_mmu(void)
+{
+       enter_lazy(PARAVIRT_LAZY_MMU);
+}
+
+void paravirt_leave_lazy_mmu(void)
+{
+       paravirt_leave_lazy(PARAVIRT_LAZY_MMU);
+}
+
+void paravirt_enter_lazy_cpu(void)
+{
+       enter_lazy(PARAVIRT_LAZY_CPU);
+}

arch/i386/kernel/paravirt.c int paravirt_disable_iospace(void) · line 312

Quoted 10 lines
arch/i386/kernel/paravirt.c +9 -0
+
+void paravirt_leave_lazy_cpu(void)
+{
+       paravirt_leave_lazy(PARAVIRT_LAZY_CPU);
+}
+
+enum paravirt_lazy_mode paravirt_get_lazy_mode(void)
+{
+       return x86_read_percpu(paravirt_lazy_mode);
 }

I am concerned that this is 32-bit specific.
But hey: We could wrap it here, but the best solution may be just to define this macro for 64-bit, and make it everyone benefits. So yeah, this is a concern here, but I don't think anything should be changed in this patch to address it so... so... ok ;-)

Signature · 5 lines
Glauber de Oliveira Costa.
"Free as in Freedom"
http://glommer.net

"The less confident you are, the more serious you have to act."