Commit 49f19710 authored by Zachary Amsden's avatar Zachary Amsden Committed by Linus Torvalds

[PATCH] Proper fix for highmem kmap_atomic functions for VMI for 2.6.21

Since lazy MMU batching mode still allows interrupts to enter, it is
possible for interrupt handlers to try to use kmap_atomic, which fails when
lazy mode is active, since the PTE update to highmem will be delayed.  The
best workaround is to issue an explicit flush in kmap_atomic_functions
case; this is the only way nested PTE updates can happen in the interrupt
handler.

Thanks to Jeremy Fitzhardinge for noting the bug and suggestions on a fix.

This patch gets reverted again when we start 2.6.22 and the bug gets fixed
differently.
Signed-off-by: default avatarZachary Amsden <zach@vmware.com>
Cc: Andi Kleen <ak@muc.de>
Cc: Jeremy Fitzhardinge <jeremy@goop.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent a5bfffac
...@@ -69,6 +69,7 @@ struct { ...@@ -69,6 +69,7 @@ struct {
void (*flush_tlb)(int); void (*flush_tlb)(int);
void (*set_initial_ap_state)(int, int); void (*set_initial_ap_state)(int, int);
void (*halt)(void); void (*halt)(void);
void (*set_lazy_mode)(int mode);
} vmi_ops; } vmi_ops;
/* XXX move this to alternative.h */ /* XXX move this to alternative.h */
...@@ -574,6 +575,26 @@ vmi_startup_ipi_hook(int phys_apicid, unsigned long start_eip, ...@@ -574,6 +575,26 @@ vmi_startup_ipi_hook(int phys_apicid, unsigned long start_eip,
} }
#endif #endif
static void vmi_set_lazy_mode(int mode)
{
static DEFINE_PER_CPU(int, lazy_mode);
if (!vmi_ops.set_lazy_mode)
return;
/* Modes should never nest or overlap */
BUG_ON(__get_cpu_var(lazy_mode) && !(mode == PARAVIRT_LAZY_NONE ||
mode == PARAVIRT_LAZY_FLUSH));
if (mode == PARAVIRT_LAZY_FLUSH) {
vmi_ops.set_lazy_mode(0);
vmi_ops.set_lazy_mode(__get_cpu_var(lazy_mode));
} else {
vmi_ops.set_lazy_mode(mode);
__get_cpu_var(lazy_mode) = mode;
}
}
static inline int __init check_vmi_rom(struct vrom_header *rom) static inline int __init check_vmi_rom(struct vrom_header *rom)
{ {
struct pci_header *pci; struct pci_header *pci;
...@@ -804,7 +825,7 @@ static inline int __init activate_vmi(void) ...@@ -804,7 +825,7 @@ static inline int __init activate_vmi(void)
para_wrap(load_esp0, vmi_load_esp0, set_kernel_stack, UpdateKernelStack); para_wrap(load_esp0, vmi_load_esp0, set_kernel_stack, UpdateKernelStack);
para_fill(set_iopl_mask, SetIOPLMask); para_fill(set_iopl_mask, SetIOPLMask);
para_fill(io_delay, IODelay); para_fill(io_delay, IODelay);
para_fill(set_lazy_mode, SetLazyMode); para_wrap(set_lazy_mode, vmi_set_lazy_mode, set_lazy_mode, SetLazyMode);
/* user and kernel flush are just handled with different flags to FlushTLB */ /* user and kernel flush are just handled with different flags to FlushTLB */
para_wrap(flush_tlb_user, vmi_flush_tlb_user, flush_tlb, FlushTLB); para_wrap(flush_tlb_user, vmi_flush_tlb_user, flush_tlb, FlushTLB);
......
...@@ -42,6 +42,7 @@ void *kmap_atomic(struct page *page, enum km_type type) ...@@ -42,6 +42,7 @@ void *kmap_atomic(struct page *page, enum km_type type)
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
set_pte(kmap_pte-idx, mk_pte(page, kmap_prot)); set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));
arch_flush_lazy_mmu_mode();
return (void*) vaddr; return (void*) vaddr;
} }
...@@ -82,6 +83,7 @@ void *kmap_atomic_pfn(unsigned long pfn, enum km_type type) ...@@ -82,6 +83,7 @@ void *kmap_atomic_pfn(unsigned long pfn, enum km_type type)
idx = type + KM_TYPE_NR*smp_processor_id(); idx = type + KM_TYPE_NR*smp_processor_id();
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
set_pte(kmap_pte-idx, pfn_pte(pfn, kmap_prot)); set_pte(kmap_pte-idx, pfn_pte(pfn, kmap_prot));
arch_flush_lazy_mmu_mode();
return (void*) vaddr; return (void*) vaddr;
} }
......
...@@ -180,6 +180,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres ...@@ -180,6 +180,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres
#ifndef __HAVE_ARCH_ENTER_LAZY_MMU_MODE #ifndef __HAVE_ARCH_ENTER_LAZY_MMU_MODE
#define arch_enter_lazy_mmu_mode() do {} while (0) #define arch_enter_lazy_mmu_mode() do {} while (0)
#define arch_leave_lazy_mmu_mode() do {} while (0) #define arch_leave_lazy_mmu_mode() do {} while (0)
#define arch_flush_lazy_mmu_mode() do {} while (0)
#endif #endif
/* /*
...@@ -193,6 +194,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres ...@@ -193,6 +194,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres
#ifndef __HAVE_ARCH_ENTER_LAZY_CPU_MODE #ifndef __HAVE_ARCH_ENTER_LAZY_CPU_MODE
#define arch_enter_lazy_cpu_mode() do {} while (0) #define arch_enter_lazy_cpu_mode() do {} while (0)
#define arch_leave_lazy_cpu_mode() do {} while (0) #define arch_leave_lazy_cpu_mode() do {} while (0)
#define arch_flush_lazy_cpu_mode() do {} while (0)
#endif #endif
/* /*
......
...@@ -421,14 +421,17 @@ static inline void pmd_clear(pmd_t *pmdp) ...@@ -421,14 +421,17 @@ static inline void pmd_clear(pmd_t *pmdp)
#define PARAVIRT_LAZY_NONE 0 #define PARAVIRT_LAZY_NONE 0
#define PARAVIRT_LAZY_MMU 1 #define PARAVIRT_LAZY_MMU 1
#define PARAVIRT_LAZY_CPU 2 #define PARAVIRT_LAZY_CPU 2
#define PARAVIRT_LAZY_FLUSH 3
#define __HAVE_ARCH_ENTER_LAZY_CPU_MODE #define __HAVE_ARCH_ENTER_LAZY_CPU_MODE
#define arch_enter_lazy_cpu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_CPU) #define arch_enter_lazy_cpu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_CPU)
#define arch_leave_lazy_cpu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE) #define arch_leave_lazy_cpu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE)
#define arch_flush_lazy_cpu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_FLUSH)
#define __HAVE_ARCH_ENTER_LAZY_MMU_MODE #define __HAVE_ARCH_ENTER_LAZY_MMU_MODE
#define arch_enter_lazy_mmu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_MMU) #define arch_enter_lazy_mmu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_MMU)
#define arch_leave_lazy_mmu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE) #define arch_leave_lazy_mmu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_NONE)
#define arch_flush_lazy_mmu_mode() paravirt_ops.set_lazy_mode(PARAVIRT_LAZY_FLUSH)
/* These all sit in the .parainstructions section to tell us what to patch. */ /* These all sit in the .parainstructions section to tell us what to patch. */
struct paravirt_patch { struct paravirt_patch {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment