Skip to contentSkip to conversation

paravirt_ops: refactor struct paravirt_ops into smaller pv_*_ops

RFC PATCH In review Jeremy Fitzhardinge 3 participants 10 messages Sep 28, 2007 – Sep 29, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 paravirt_ops: refactor struct paravirt_ops into smaller pv_*_ops

This patch refactors the paravirt_ops structure into groups of functionally related ops:

pv_info - random info, rather than function entrypoints pv_init_ops - functions used at boot time (some for module_init too) pv_misc_ops - lazy mode, which didn't fit well anywhere else pv_time_ops - time-related functions
pv_cpu_ops - various privileged instruction ops pv_irq_ops - operations for managing interrupt state pv_apic_ops - APIC operations
pv_mmu_ops - operations for managing pagetables

There are several motivations for this:

1. Some of these ops will be general to all x86, and some will be i386/x86-64 specific. This makes it easier to share common stuff while allowing separate implementations where needed.

2. At the moment we must export all of paravirt_ops, but modules only need selected parts of it. This allows us to export on a case by case basis (and also choose which export license we want to apply).

3. Functional groupings make things a bit more readable.

struct paravirt_ops still exists as a container for the pv_* structures; this is mainly so that each function pointer has a unique offset for use as a patch-site identifier.

This still needs to be reconciled with the x86 merge and glommer's paravirt_ops unification work.

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]>
---
 arch/i386/kernel/alternative.c         |    4
 arch/i386/kernel/asm-offsets.c         |   12
 arch/i386/kernel/entry.S               |    2
 arch/i386/kernel/paravirt.c            |  352 +++++++++--------
 arch/i386/kernel/vmi.c                 |  168 ++++----
 arch/i386/xen/enlighten.c              |  152 ++++---
 include/asm-i386/paravirt.h            |  632 ++++++++++++++++----------------
 include/asm-i386/pgtable-3level-defs.h |    2
 8 files changed, 712 insertions(+), 612 deletions(-)
arch/i386/kernel/alternative.c +2 -2
diff -r f1e210bdae95 arch/i386/kernel/alternative.c
@@ -368,8 +368,8 @@ void apply_paravirt(struct paravirt_patc
 		BUG_ON(p->len > MAX_PATCH_LEN);
 		/* prep the buffer with the original instructions */
 		memcpy(insnbuf, p->instr, p->len);
-		used = paravirt_ops.patch(p->instrtype, p->clobbers, insnbuf,
-					  (unsigned long)p->instr, p->len);
+		used = pv_init_ops.patch(p->instrtype, p->clobbers, insnbuf,
+					 (unsigned long)p->instr, p->len);
 
 		BUG_ON(used > p->len);
 
arch/i386/kernel/asm-offsets.c +6 -6
diff -r f1e210bdae95 arch/i386/kernel/asm-offsets.c
@@ -116,12 +116,12 @@ void foo(void)
 
 #ifdef CONFIG_PARAVIRT
 	BLANK();
-	OFFSET(PARAVIRT_enabled, paravirt_ops, paravirt_enabled);
-	OFFSET(PARAVIRT_irq_disable, paravirt_ops, irq_disable);
-	OFFSET(PARAVIRT_irq_enable, paravirt_ops, irq_enable);
-	OFFSET(PARAVIRT_irq_enable_sysexit, paravirt_ops, irq_enable_sysexit);
-	OFFSET(PARAVIRT_iret, paravirt_ops, iret);
-	OFFSET(PARAVIRT_read_cr0, paravirt_ops, read_cr0);
+	OFFSET(PARAVIRT_enabled, pv_info, paravirt_enabled);
+	OFFSET(PARAVIRT_irq_disable, paravirt_ops, pv_irq_ops.irq_disable);
+	OFFSET(PARAVIRT_irq_enable, paravirt_ops, pv_irq_ops.irq_enable);
+	OFFSET(PARAVIRT_irq_enable_sysexit, paravirt_ops, pv_cpu_ops.irq_enable_sysexit);
+	OFFSET(PARAVIRT_iret, paravirt_ops, pv_cpu_ops.iret);
+	OFFSET(PARAVIRT_read_cr0, paravirt_ops, pv_cpu_ops.read_cr0);
 #endif
 
 #ifdef CONFIG_XEN
arch/i386/kernel/entry.S +1 -1
diff -r f1e210bdae95 arch/i386/kernel/entry.S
@@ -434,7 +434,7 @@ ldt_ss:
 	 * is still available to implement the setting of the high
 	 * 16-bits in the INTERRUPT_RETURN paravirt-op.
 	 */
-	cmpl $0, paravirt_ops+PARAVIRT_enabled
+	cmpl $0, pv_info+PARAVIRT_enabled
 	jne restore_nocheck
 #endif
 
arch/i386/kernel/paravirt.c +196 -156

423 lines not on this page · raw message

arch/i386/kernel/vmi.c +84 -84

267 lines not on this page · raw message

arch/i386/xen/enlighten.c +88 -64

255 lines not on this page · raw message

include/asm-i386/paravirt.h +237 -191

936 lines not on this page · raw message

include/asm-i386/pgtable-3level-defs.h +1 -1
diff -r f1e210bdae95 include/asm-i386/pgtable-3level-defs.h
@@ -2,7 +2,7 @@
 #define _I386_PGTABLE_3LEVEL_DEFS_H
 
 #ifdef CONFIG_PARAVIRT
-#define SHARED_KERNEL_PMD	(paravirt_ops.shared_kernel_pmd)
+#define SHARED_KERNEL_PMD	(pv_info.shared_kernel_pmd)
 #else
 #define SHARED_KERNEL_PMD	1
 #endif
#1 lore raw
Quoted 14 lines · Jeremy Fitzhardinge

This patch refactors the paravirt_ops structure into groups of functionally related ops:

pv_info - random info, rather than function entrypoints pv_init_ops - functions used at boot time (some for module_init too) pv_misc_ops - lazy mode, which didn't fit well anywhere else pv_time_ops - time-related functions
pv_cpu_ops - various privileged instruction ops pv_irq_ops - operations for managing interrupt state pv_apic_ops - APIC operations
pv_mmu_ops - operations for managing pagetables

There are several motivations for this:

Quoted 7 lines

1. Some of these ops will be general to all x86, and some will be i386/x86-64 specific. This makes it easier to share common stuff while allowing separate implementations where needed.

2. At the moment we must export all of paravirt_ops, but modules only need selected parts of it. This allows us to export on a case by case basis (and also choose which export license we want to apply).

We shouldn't need to export pv_init_ops. It is debatable whether CR2/CR3 should be part of CPU or MMU ops. Also, can we drop write_cr2? It isn't used anywhere, so the only reason to keep it is symmetry. Which was a fine argument when it was an inline, but now it just adds unused junk to the code.

Quoted 2 lines

This still needs to be reconciled with the x86 merge and glommer's paravirt_ops unification work.

Those issues aside, this looks great. I'll give it a whirl today.

Zach

#2 lore raw
Quoted 20 lines · Zachary Amsden
Quoted 18 lines · Jeremy Fitzhardinge

This patch refactors the paravirt_ops structure into groups of functionally related ops:

pv_info - random info, rather than function entrypoints pv_init_ops - functions used at boot time (some for module_init too) pv_misc_ops - lazy mode, which didn't fit well anywhere else pv_time_ops - time-related functions
pv_cpu_ops - various privileged instruction ops pv_irq_ops - operations for managing interrupt state pv_apic_ops - APIC operations
pv_mmu_ops - operations for managing pagetables

There are several motivations for this:

1. Some of these ops will be general to all x86, and some will be i386/x86-64 specific. This makes it easier to share common stuff while allowing separate implementations where needed.

Quoted 6 lines
Quoted 4 lines

2. At the moment we must export all of paravirt_ops, but modules only need selected parts of it. This allows us to export on a case by case basis (and also choose which export license we want to apply).

We shouldn't need to export pv_init_ops.

No. The only ones I export are:

EXPORT_SYMBOL_GPL(pv_time_ops);
EXPORT_SYMBOL_GPL(pv_cpu_ops);
EXPORT_SYMBOL_GPL(pv_mmu_ops);
EXPORT_SYMBOL_GPL(pv_apic_ops);
EXPORT_SYMBOL (pv_irq_ops);

Quoted 2 lines

It is debatable whether
CR2/CR3 should be part of CPU or MMU ops.

Yeah, I was in two minds. CR3, at least, should be grouped with the other tlb operations, wherever they go. And while they're privileged CPU instructions (cpu_ops), they're more logically related to the rest of the mmu state. On the other hand, we could have an ops structure specifically dedicated to pagetable manipulations, and put the cr3/tlb ops elsewhere.

Quoted 5 lines

Also, can we drop write_cr2?
It isn't used anywhere, so the only reason to keep it is symmetry. Which was a fine argument when it was an inline, but now it just adds unused junk to the code.

I think its used in some cpu state save/restore code, but its not relevant to pv-ops.

J

#3 lore raw
Quoted 9 lines · Jeremy Fitzhardinge
Quoted 1 line

We shouldn't need to export pv_init_ops.

No. The only ones I export are:

EXPORT_SYMBOL_GPL(pv_time_ops);
EXPORT_SYMBOL_GPL(pv_cpu_ops);
EXPORT_SYMBOL_GPL(pv_mmu_ops);
EXPORT_SYMBOL_GPL(pv_apic_ops);
EXPORT_SYMBOL (pv_irq_ops);

Nicely done. I know of some out of tree modules which use part of the pv_cpu_ops and pv_mmu_ops, but we should not worry about such things, and it turns out those modules don't need to be virtualized anyway.

Quoted 11 lines
Quoted 4 lines

It is debatable whether
CR2/CR3 should be part of CPU or MMU ops.

Yeah, I was in two minds. CR3, at least, should be grouped with the other tlb operations, wherever they go. And while they're privileged CPU instructions (cpu_ops), they're more logically related to the rest of the mmu state. On the other hand, we could have an ops structure specifically dedicated to pagetable manipulations, and put the cr3/tlb ops elsewhere.

I'm not against either approach. I think the way you did it is fine. If it were up to me, I would probably have driven myself crazy splitting hairs on it until I was bald.

Quoted 8 lines
Quoted 5 lines

Also, can we drop write_cr2?
It isn't used anywhere, so the only reason to keep it is symmetry. Which was a fine argument when it was an inline, but now it just adds unused junk to the code.

I think its used in some cpu state save/restore code, but its not relevant to pv-ops.

Ah yes, it is used there. We actually exercise some of those paths, but they don't need to be strictly virtualized.

Zach

#4 lore raw
In reply to Jeremy Fitzhardinge
Quoted 12 lines · Jeremy Fitzhardinge

This patch refactors the paravirt_ops structure into groups of functionally related ops:

pv_info - random info, rather than function entrypoints pv_init_ops - functions used at boot time (some for module_init too) pv_misc_ops - lazy mode, which didn't fit well anywhere else pv_time_ops - time-related functions
pv_cpu_ops - various privileged instruction ops pv_irq_ops - operations for managing interrupt state pv_apic_ops - APIC operations
pv_mmu_ops - operations for managing pagetables

Good. These make sense to me.

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

Quoted 9 lines
arch/i386/kernel/paravirt.c +9 -0
+	.pv_irq_ops = {
+		 .init_IRQ = native_init_IRQ,
+		 .save_fl = native_save_fl,
+		 .restore_fl = native_restore_fl,
+		 .irq_disable = native_irq_disable,
+		 .irq_enable = native_irq_enable,
+		 .safe_halt = native_safe_halt,
+		 .halt = native_halt,
+	 },

I think the halt stuff should be moved to pv_cpu_ops?

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

Quoted 3 lines
arch/i386/kernel/paravirt.c +3 -0
+	.pv_misc_ops = {
+		 .set_lazy_mode = paravirt_nop,
+	 },

Or you can split it to pv_cpu_ops and pv_mmu_ops, assuming that they don't need to interact with each other in terms of the lazy handling.

Jun
---
Intel Open Source Technology Center

#5 lore raw
Quoted 19 lines · Jun
Quoted 13 lines · Jeremy Fitzhardinge

This patch refactors the paravirt_ops structure into groups of functionally related ops:

pv_info - random info, rather than function entrypoints pv_init_ops - functions used at boot time (some for module_init too) pv_misc_ops - lazy mode, which didn't fit well anywhere else pv_time_ops - time-related functions
pv_cpu_ops - various privileged instruction ops pv_irq_ops - operations for managing interrupt state pv_apic_ops - APIC operations
pv_mmu_ops - operations for managing pagetables

Good. These make sense to me.

Quoted 13 lines

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

Quoted 10 lines
arch/i386/kernel/paravirt.c +9 -0
+	.pv_irq_ops = {
+		 .init_IRQ = native_init_IRQ,
+		 .save_fl = native_save_fl,
+		 .restore_fl = native_restore_fl,
+		 .irq_disable = native_irq_disable,
+		 .irq_enable = native_irq_enable,
+		 .safe_halt = native_safe_halt,
+		 .halt = native_halt,
+	 },

I think the halt stuff should be moved to pv_cpu_ops?

You mean halt's alternate "shutdown vcpu" meaning if you call it with interrupts disabled? Yeah, I'd be happy to have an explicit op for that, rather than making it a secondary overloaded meaning. And use "safe_halt" for all uses of "wait for next interrupt".

Quoted 8 lines

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

Quoted 4 lines
arch/i386/kernel/paravirt.c +3 -0
+	.pv_misc_ops = {
+		 .set_lazy_mode = paravirt_nop,
+	 },

Or you can split it to pv_cpu_ops and pv_mmu_ops, assuming that they don't need to interact with each other in terms of the lazy handling.

You mean have separate lazy_mmu and lazy_cpu (lazy_context_switch) ops? Possible, but they're still exclusive. (I think VMI, at least, assumes that you can't have lazy_mmu and lazy_cpu active at the same time, and its nice to enforce this in the interface.)

But having a whole misc structure for this interface is pretty warty, I admit.

J

#6 lore raw
Quoted 17 lines · Jeremy Fitzhardinge
Quoted 16 lines · Jun

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

Quoted 10 lines · Jeremy Fitzhardinge
arch/i386/kernel/paravirt.c +9 -0
+	.pv_irq_ops = {
+		 .init_IRQ = native_init_IRQ,
+		 .save_fl = native_save_fl,
+		 .restore_fl = native_restore_fl,
+		 .irq_disable = native_irq_disable,
+		 .irq_enable = native_irq_enable,
+		 .safe_halt = native_safe_halt,
+		 .halt = native_halt,
+	 },

I think the halt stuff should be moved to pv_cpu_ops?

Quoted 4 lines

You mean halt's alternate "shutdown vcpu" meaning if you call it with interrupts disabled? Yeah, I'd be happy to have an explicit op for that, rather than making it a secondary overloaded meaning. And use "safe_halt" for all uses of "wait for next interrupt".

Yes. For the native, "safe_halt" is "sti; hlt". The "native_halt" is just "hlt". So the para_virt part of "hlt" could be moved to pv_cpu_ops, and the "sti" part stays in pv_irq_ops.

Quoted 8 lines
Quoted 8 lines

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

Quoted 5 lines
arch/i386/kernel/paravirt.c +3 -0
+	.pv_misc_ops = {
+		 .set_lazy_mode = paravirt_nop,
+	 },

Or you can split it to pv_cpu_ops and pv_mmu_ops, assuming that they don't need to interact with each other in terms of the lazy

handling.

Quoted 3 lines
Quoted 1 line

You mean have separate lazy_mmu and lazy_cpu (lazy_context_switch)

ops?

Quoted 1 line

Possible, but they're still exclusive. (I think VMI, at least,

assumes

Quoted 2 lines

that you can't have lazy_mmu and lazy_cpu active at the same time, and its nice to enforce this in the interface.)

Okay I understand what you are saying.

Quoted 2 lines

But having a whole misc structure for this interface is pretty warty,

I

Quoted 3 lines

admit.

J

Actually my concern was that such misc ops might grow to include the things don't fit well anywhere else. To me, then pv_lazy_ops (with just .set_mode) might be better.

Jun
---
Intel Open Source Technology Center

#7 lore raw
Quoted 4 lines · Jun

Yes. For the native, "safe_halt" is "sti; hlt". The "native_halt" is just "hlt". So the para_virt part of "hlt" could be moved to pv_cpu_ops, and the "sti" part stays in pv_irq_ops.

By "sti part", you mean the full "sti; hlt" sequence of safe_halt, right? Since it needs to be an atomic sequence to avoid race conditions, so the native sequence has to be precisely "sti; hlt" to take advantage of the sti shadow, and other pv-backends will need their own way to guarantee this atomicity.

But I'm quite happy to put plain "hlt" into cpu_ops as halt_cpu() or something (and perhaps rename safe_halt to something a bit more descriptive).

Quoted 4 lines

Actually my concern was that such misc ops might grow to include the things don't fit well anywhere else. To me, then pv_lazy_ops (with just .set_mode) might be better.

The lazy interface has needed a rethink anyway.

J

#8 lore raw
Quoted 3 lines · Jeremy Fitzhardinge
Quoted 2 lines · Jun

Yes. For the native, "safe_halt" is "sti; hlt". The "native_halt" is just "hlt". So the para_virt part of "hlt" could be moved to

pv_cpu_ops,

Quoted 7 lines
Quoted 2 lines

and the "sti" part stays in pv_irq_ops.

By "sti part", you mean the full "sti; hlt" sequence of safe_halt, right? Since it needs to be an atomic sequence to avoid race conditions, so the native sequence has to be precisely "sti; hlt" to take advantage of the sti shadow, and other pv-backends will need

their

Quoted 1 line

own way to guarantee this atomicity.

To me such atomicity is provided by the "sti" instruction (i.e. the processor begins responding to external, maskable interrupts _after_ the next instruction is executed), and there is nothing special with that combination "sti; hlt" (you can also have like "sti; ret", for example). So if you define a PV ops like STI(next_instruction), "safe_halt" for the native should be defined as STI("hlt"), and inlined as "sti; hlt".

If it's hard or we don't need to expose the semantics of "sti" other than that, I think it's okay to have a PV operation for safe_halt.

Jun
---
Intel Open Source Technology Center

#9 lore raw
Quoted 5 lines · Jun

To me such atomicity is provided by the "sti" instruction (i.e. the processor begins responding to external, maskable interrupts _after_ the next instruction is executed), and there is nothing special with that combination "sti; hlt" (you can also have like "sti; ret", for example).

Sure, but there's no particular value in "sti; ret". While the sti mask window works everywhere, its only cases like "sti; hlt" where it's needed to avoid a race condition.

Quoted 3 lines

So if you define a PV ops like STI(next_instruction), "safe_halt" for the native should be defined as STI("hlt"), and inlined as "sti; hlt".

That's only meaningful if the pv_op is implemented directly in x86 instructions - ie, the native (or almost native) case.

Quoted 3 lines

If it's hard or we don't need to expose the semantics of "sti" other than that, I think it's okay to have a PV operation for safe_halt.

Yeah, the general form would be hard to support for a hypervisor. Xen, for example, has an "atomically enable events and block" operation, but no other "atomically enable events and do X" operations.

J