paravirt_ops: refactor struct paravirt_ops into smaller pv_*_ops
Subsystems (2)
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 functionspv_cpu_ops - various privileged instruction ops pv_irq_ops - operations for managing interrupt state pv_apic_ops - APIC operationspv_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.
--- 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
@@ -368,8 +368,8 @@ void apply_paravirt(struct paravirt_patcBUG_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
@@ -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
@@ -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
@@ -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
Quoted 14 lines · Jeremy Fitzhardinge
This patch refactors the
paravirt_opsstructure into groups of functionally related ops:
pv_info- random info, rather than function entrypointspv_init_ops- functions used at boot time (some formodule_inittoo)pv_misc_ops- lazy mode, which didn't fit well anywhere elsepv_time_ops- time-related functionspv_cpu_ops- various privileged instruction opspv_irq_ops- operations for managing interrupt statepv_apic_ops- APIC operationspv_mmu_ops- operations for managing pagetablesThere 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_opsunification work.
Those issues aside, this looks great. I'll give it a whirl today.
Zach
Quoted 20 lines · Zachary Amsden
Quoted 18 lines · Jeremy Fitzhardinge
This patch refactors the
paravirt_opsstructure into groups of functionally related ops:
pv_info- random info, rather than function entrypointspv_init_ops- functions used at boot time (some formodule_inittoo)pv_misc_ops- lazy mode, which didn't fit well anywhere elsepv_time_ops- time-related functionspv_cpu_ops- various privileged instruction opspv_irq_ops- operations for managing interrupt statepv_apic_ops- APIC operationspv_mmu_ops- operations for managing pagetablesThere 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
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
Quoted 12 lines · Jeremy Fitzhardinge
This patch refactors the
paravirt_opsstructure into groups of functionally related ops:
pv_info- random info, rather than function entrypointspv_init_ops- functions used at boot time (some formodule_inittoo)pv_misc_ops- lazy mode, which didn't fit well anywhere elsepv_time_ops- time-related functionspv_cpu_ops- various privileged instruction opspv_irq_ops- operations for managing interrupt statepv_apic_ops- APIC operationspv_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
Quoted 19 lines · Jun
Quoted 13 lines · Jeremy Fitzhardinge
This patch refactors the
paravirt_opsstructure into groups of functionally related ops:
pv_info- random info, rather than function entrypointspv_init_ops- functions used at boot time (some formodule_inittoo)pv_misc_ops- lazy mode, which didn't fit well anywhere elsepv_time_ops- time-related functionspv_cpu_ops- various privileged instruction opspv_irq_ops- operations for managing interrupt statepv_apic_ops- APIC operationspv_mmu_ops- operations for managing pagetablesGood. 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_opsandpv_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
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_opsandpv_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_mmuandlazy_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_mmuandlazy_cpuactive 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
Quoted 4 lines · Jun
Yes. For the native, "
safe_halt" is "sti; hlt". The "native_halt" is just "hlt". So thepara_virtpart of "hlt" could be moved topv_cpu_ops, and the "sti" part stays inpv_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
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 thepara_virtpart 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
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 asSTI("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