forked from Minki/linux
a866374aec
Introduce pagefault_{disable,enable}() and use these where previously we did manual preempt increments/decrements to make the pagefault handler do the atomic thing. Currently they still rely on the increased preempt count, but do not rely on the disabled preemption, this might go away in the future. (NOTE: the extra barrier() in pagefault_disable might fix some holes on machines which have too many registers for their own good) [heiko.carstens@de.ibm.com: s390 fix] Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Acked-by: Nick Piggin <npiggin@suse.de> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
119 lines
3.0 KiB
C
119 lines
3.0 KiB
C
/*
|
|
* highmem.c: virtual kernel memory mappings for high memory
|
|
*
|
|
* Provides kernel-static versions of atomic kmap functions originally
|
|
* found as inlines in include/asm-sparc/highmem.h. These became
|
|
* needed as kmap_atomic() and kunmap_atomic() started getting
|
|
* called from within modules.
|
|
* -- Tomas Szepe <szepe@pinerecords.com>, September 2002
|
|
*
|
|
* But kmap_atomic() and kunmap_atomic() cannot be inlined in
|
|
* modules because they are loaded with btfixup-ped functions.
|
|
*/
|
|
|
|
/*
|
|
* The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
|
|
* gives a more generic (and caching) interface. But kmap_atomic can
|
|
* be used in IRQ contexts, so in some (very limited) cases we need it.
|
|
*
|
|
* XXX This is an old text. Actually, it's good to use atomic kmaps,
|
|
* provided you remember that they are atomic and not try to sleep
|
|
* with a kmap taken, much like a spinlock. Non-atomic kmaps are
|
|
* shared by CPUs, and so precious, and establishing them requires IPI.
|
|
* Atomic kmaps are lightweight and we may have NCPUS more of them.
|
|
*/
|
|
#include <linux/mm.h>
|
|
#include <linux/highmem.h>
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/fixmap.h>
|
|
|
|
void *kmap_atomic(struct page *page, enum km_type type)
|
|
{
|
|
unsigned long idx;
|
|
unsigned long vaddr;
|
|
|
|
/* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
|
|
pagefault_disable();
|
|
if (!PageHighMem(page))
|
|
return page_address(page);
|
|
|
|
idx = type + KM_TYPE_NR*smp_processor_id();
|
|
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
|
|
|
|
/* XXX Fix - Anton */
|
|
#if 0
|
|
__flush_cache_one(vaddr);
|
|
#else
|
|
flush_cache_all();
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_HIGHMEM
|
|
BUG_ON(!pte_none(*(kmap_pte-idx)));
|
|
#endif
|
|
set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));
|
|
/* XXX Fix - Anton */
|
|
#if 0
|
|
__flush_tlb_one(vaddr);
|
|
#else
|
|
flush_tlb_all();
|
|
#endif
|
|
|
|
return (void*) vaddr;
|
|
}
|
|
|
|
void kunmap_atomic(void *kvaddr, enum km_type type)
|
|
{
|
|
#ifdef CONFIG_DEBUG_HIGHMEM
|
|
unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
|
|
unsigned long idx = type + KM_TYPE_NR*smp_processor_id();
|
|
|
|
if (vaddr < FIXADDR_START) { // FIXME
|
|
pagefault_enable();
|
|
return;
|
|
}
|
|
|
|
BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx));
|
|
|
|
/* XXX Fix - Anton */
|
|
#if 0
|
|
__flush_cache_one(vaddr);
|
|
#else
|
|
flush_cache_all();
|
|
#endif
|
|
|
|
/*
|
|
* force other mappings to Oops if they'll try to access
|
|
* this pte without first remap it
|
|
*/
|
|
pte_clear(&init_mm, vaddr, kmap_pte-idx);
|
|
/* XXX Fix - Anton */
|
|
#if 0
|
|
__flush_tlb_one(vaddr);
|
|
#else
|
|
flush_tlb_all();
|
|
#endif
|
|
#endif
|
|
|
|
pagefault_enable();
|
|
}
|
|
|
|
/* We may be fed a pagetable here by ptep_to_xxx and others. */
|
|
struct page *kmap_atomic_to_page(void *ptr)
|
|
{
|
|
unsigned long idx, vaddr = (unsigned long)ptr;
|
|
pte_t *pte;
|
|
|
|
if (vaddr < SRMMU_NOCACHE_VADDR)
|
|
return virt_to_page(ptr);
|
|
if (vaddr < PKMAP_BASE)
|
|
return pfn_to_page(__nocache_pa(vaddr) >> PAGE_SHIFT);
|
|
BUG_ON(vaddr < FIXADDR_START);
|
|
BUG_ON(vaddr > FIXADDR_TOP);
|
|
|
|
idx = virt_to_fix(vaddr);
|
|
pte = kmap_pte - (idx - FIX_KMAP_BEGIN);
|
|
return pte_page(*pte);
|
|
}
|