[PATCH] i386: PARAVIRT: add kmap_atomic_pte for mapping highpte pages
Xen and VMI both have special requirements when mapping a highmem pte page into the kernel address space. These can be dealt with by adding a new kmap_atomic_pte() function for mapping highptes, and hooking it into the paravirt_ops infrastructure. Xen specifically wants to map the pte page RO, so this patch exposes a helper function, kmap_atomic_prot, which maps the page with the specified page protections. This also adds a kmap_flush_unused() function to clear out the cached kmap mappings. Xen needs this to clear out any potential stray RW mappings of pages which will become part of a pagetable. [ Zach - vmi.c will need some attention after this patch. It wasn't immediately obvious to me what needs to be done. ] Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com> Signed-off-by: Andi Kleen <ak@suse.de> Cc: Zachary Amsden <zach@vmware.com>
This commit is contained in:
parent
a27fe809b8
commit
ce6234b529
7 changed files with 50 additions and 4 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <linux/bcd.h>
|
#include <linux/bcd.h>
|
||||||
#include <linux/start_kernel.h>
|
#include <linux/start_kernel.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
|
|
||||||
#include <asm/bug.h>
|
#include <asm/bug.h>
|
||||||
#include <asm/paravirt.h>
|
#include <asm/paravirt.h>
|
||||||
|
@ -316,6 +317,10 @@ struct paravirt_ops paravirt_ops = {
|
||||||
|
|
||||||
.ptep_get_and_clear = native_ptep_get_and_clear,
|
.ptep_get_and_clear = native_ptep_get_and_clear,
|
||||||
|
|
||||||
|
#ifdef CONFIG_HIGHPTE
|
||||||
|
.kmap_atomic_pte = kmap_atomic,
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_X86_PAE
|
#ifdef CONFIG_X86_PAE
|
||||||
.set_pte_atomic = native_set_pte_atomic,
|
.set_pte_atomic = native_set_pte_atomic,
|
||||||
.set_pte_present = native_set_pte_present,
|
.set_pte_present = native_set_pte_present,
|
||||||
|
|
|
@ -26,7 +26,7 @@ void kunmap(struct page *page)
|
||||||
* However when holding an atomic kmap is is not legal to sleep, so atomic
|
* However when holding an atomic kmap is is not legal to sleep, so atomic
|
||||||
* kmaps are appropriate for short, tight code paths only.
|
* kmaps are appropriate for short, tight code paths only.
|
||||||
*/
|
*/
|
||||||
void *kmap_atomic(struct page *page, enum km_type type)
|
void *kmap_atomic_prot(struct page *page, enum km_type type, pgprot_t prot)
|
||||||
{
|
{
|
||||||
enum fixed_addresses idx;
|
enum fixed_addresses idx;
|
||||||
unsigned long vaddr;
|
unsigned long vaddr;
|
||||||
|
@ -41,12 +41,17 @@ void *kmap_atomic(struct page *page, enum km_type type)
|
||||||
return page_address(page);
|
return page_address(page);
|
||||||
|
|
||||||
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, prot));
|
||||||
arch_flush_lazy_mmu_mode();
|
arch_flush_lazy_mmu_mode();
|
||||||
|
|
||||||
return (void*) vaddr;
|
return (void*) vaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *kmap_atomic(struct page *page, enum km_type type)
|
||||||
|
{
|
||||||
|
return kmap_atomic_prot(page, type, kmap_prot);
|
||||||
|
}
|
||||||
|
|
||||||
void kunmap_atomic(void *kvaddr, enum km_type type)
|
void kunmap_atomic(void *kvaddr, enum km_type type)
|
||||||
{
|
{
|
||||||
unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
|
unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/threads.h>
|
#include <linux/threads.h>
|
||||||
#include <asm/kmap_types.h>
|
#include <asm/kmap_types.h>
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
|
#include <asm/paravirt.h>
|
||||||
|
|
||||||
/* declarations for highmem.c */
|
/* declarations for highmem.c */
|
||||||
extern unsigned long highstart_pfn, highend_pfn;
|
extern unsigned long highstart_pfn, highend_pfn;
|
||||||
|
@ -67,11 +68,16 @@ extern void FASTCALL(kunmap_high(struct page *page));
|
||||||
|
|
||||||
void *kmap(struct page *page);
|
void *kmap(struct page *page);
|
||||||
void kunmap(struct page *page);
|
void kunmap(struct page *page);
|
||||||
|
void *kmap_atomic_prot(struct page *page, enum km_type type, pgprot_t prot);
|
||||||
void *kmap_atomic(struct page *page, enum km_type type);
|
void *kmap_atomic(struct page *page, enum km_type type);
|
||||||
void kunmap_atomic(void *kvaddr, enum km_type type);
|
void kunmap_atomic(void *kvaddr, enum km_type type);
|
||||||
void *kmap_atomic_pfn(unsigned long pfn, enum km_type type);
|
void *kmap_atomic_pfn(unsigned long pfn, enum km_type type);
|
||||||
struct page *kmap_atomic_to_page(void *ptr);
|
struct page *kmap_atomic_to_page(void *ptr);
|
||||||
|
|
||||||
|
#ifndef CONFIG_PARAVIRT
|
||||||
|
#define kmap_atomic_pte(page, type) kmap_atomic(page, type)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define flush_cache_kmaps() do { } while (0)
|
#define flush_cache_kmaps() do { } while (0)
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
|
#include <asm/kmap_types.h>
|
||||||
|
|
||||||
|
struct page;
|
||||||
struct thread_struct;
|
struct thread_struct;
|
||||||
struct Xgt_desc_struct;
|
struct Xgt_desc_struct;
|
||||||
struct tss_struct;
|
struct tss_struct;
|
||||||
|
@ -187,6 +189,10 @@ struct paravirt_ops
|
||||||
|
|
||||||
pte_t (*ptep_get_and_clear)(pte_t *ptep);
|
pte_t (*ptep_get_and_clear)(pte_t *ptep);
|
||||||
|
|
||||||
|
#ifdef CONFIG_HIGHPTE
|
||||||
|
void *(*kmap_atomic_pte)(struct page *page, enum km_type type);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_X86_PAE
|
#ifdef CONFIG_X86_PAE
|
||||||
void (*set_pte_atomic)(pte_t *ptep, pte_t pteval);
|
void (*set_pte_atomic)(pte_t *ptep, pte_t pteval);
|
||||||
void (*set_pte_present)(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte);
|
void (*set_pte_present)(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte);
|
||||||
|
@ -884,6 +890,15 @@ static inline void paravirt_release_pd(unsigned pfn)
|
||||||
PVOP_VCALL1(release_pd, pfn);
|
PVOP_VCALL1(release_pd, pfn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_HIGHPTE
|
||||||
|
static inline void *kmap_atomic_pte(struct page *page, enum km_type type)
|
||||||
|
{
|
||||||
|
unsigned long ret;
|
||||||
|
ret = PVOP_CALL2(unsigned long, kmap_atomic_pte, page, type);
|
||||||
|
return (void *)ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline void pte_update(struct mm_struct *mm, unsigned long addr,
|
static inline void pte_update(struct mm_struct *mm, unsigned long addr,
|
||||||
pte_t *ptep)
|
pte_t *ptep)
|
||||||
{
|
{
|
||||||
|
|
|
@ -476,9 +476,9 @@ extern pte_t *lookup_address(unsigned long address);
|
||||||
|
|
||||||
#if defined(CONFIG_HIGHPTE)
|
#if defined(CONFIG_HIGHPTE)
|
||||||
#define pte_offset_map(dir, address) \
|
#define pte_offset_map(dir, address) \
|
||||||
((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + pte_index(address))
|
((pte_t *)kmap_atomic_pte(pmd_page(*(dir)),KM_PTE0) + pte_index(address))
|
||||||
#define pte_offset_map_nested(dir, address) \
|
#define pte_offset_map_nested(dir, address) \
|
||||||
((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + pte_index(address))
|
((pte_t *)kmap_atomic_pte(pmd_page(*(dir)),KM_PTE1) + pte_index(address))
|
||||||
#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0)
|
#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0)
|
||||||
#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1)
|
#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1)
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -27,6 +27,8 @@ static inline void flush_kernel_dcache_page(struct page *page)
|
||||||
unsigned int nr_free_highpages(void);
|
unsigned int nr_free_highpages(void);
|
||||||
extern unsigned long totalhigh_pages;
|
extern unsigned long totalhigh_pages;
|
||||||
|
|
||||||
|
void kmap_flush_unused(void);
|
||||||
|
|
||||||
#else /* CONFIG_HIGHMEM */
|
#else /* CONFIG_HIGHMEM */
|
||||||
|
|
||||||
static inline unsigned int nr_free_highpages(void) { return 0; }
|
static inline unsigned int nr_free_highpages(void) { return 0; }
|
||||||
|
@ -44,9 +46,13 @@ static inline void *kmap(struct page *page)
|
||||||
|
|
||||||
#define kmap_atomic(page, idx) \
|
#define kmap_atomic(page, idx) \
|
||||||
({ pagefault_disable(); page_address(page); })
|
({ pagefault_disable(); page_address(page); })
|
||||||
|
#define kmap_atomic_prot(page, idx, prot) kmap_atomic(page, idx)
|
||||||
|
|
||||||
#define kunmap_atomic(addr, idx) do { pagefault_enable(); } while (0)
|
#define kunmap_atomic(addr, idx) do { pagefault_enable(); } while (0)
|
||||||
#define kmap_atomic_pfn(pfn, idx) kmap_atomic(pfn_to_page(pfn), (idx))
|
#define kmap_atomic_pfn(pfn, idx) kmap_atomic(pfn_to_page(pfn), (idx))
|
||||||
#define kmap_atomic_to_page(ptr) virt_to_page(ptr)
|
#define kmap_atomic_to_page(ptr) virt_to_page(ptr)
|
||||||
|
|
||||||
|
#define kmap_flush_unused() do {} while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* CONFIG_HIGHMEM */
|
#endif /* CONFIG_HIGHMEM */
|
||||||
|
|
|
@ -99,6 +99,15 @@ static void flush_all_zero_pkmaps(void)
|
||||||
flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
|
flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Flush all unused kmap mappings in order to remove stray
|
||||||
|
mappings. */
|
||||||
|
void kmap_flush_unused(void)
|
||||||
|
{
|
||||||
|
spin_lock(&kmap_lock);
|
||||||
|
flush_all_zero_pkmaps();
|
||||||
|
spin_unlock(&kmap_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static inline unsigned long map_new_virtual(struct page *page)
|
static inline unsigned long map_new_virtual(struct page *page)
|
||||||
{
|
{
|
||||||
unsigned long vaddr;
|
unsigned long vaddr;
|
||||||
|
|
Loading…
Reference in a new issue