forked from Minki/linux
ba9c231f74
Move initialization of all memory end variables to as early as possible, so that dependent code doesn't need to check whether these variables have already been set. Change the range check in kunmap_atomic to actually make use of this so that the no-mapping-estabished path (under CONFIG_DEBUG_HIGHMEM) gets used only when the address is inside the lowmem area (and BUG() otherwise). Signed-off-by: Jan Beulich <jbeulich@novell.com> Signed-off-by: Andi Kleen <ak@suse.de>
114 lines
2.6 KiB
C
114 lines
2.6 KiB
C
#include <linux/highmem.h>
|
|
#include <linux/module.h>
|
|
|
|
void *kmap(struct page *page)
|
|
{
|
|
might_sleep();
|
|
if (!PageHighMem(page))
|
|
return page_address(page);
|
|
return kmap_high(page);
|
|
}
|
|
|
|
void kunmap(struct page *page)
|
|
{
|
|
if (in_interrupt())
|
|
BUG();
|
|
if (!PageHighMem(page))
|
|
return;
|
|
kunmap_high(page);
|
|
}
|
|
|
|
/*
|
|
* kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because
|
|
* no global lock is needed and because the kmap code must perform a global TLB
|
|
* invalidation when the kmap pool wraps.
|
|
*
|
|
* However when holding an atomic kmap is is not legal to sleep, so atomic
|
|
* kmaps are appropriate for short, tight code paths only.
|
|
*/
|
|
void *kmap_atomic(struct page *page, enum km_type type)
|
|
{
|
|
enum fixed_addresses idx;
|
|
unsigned long vaddr;
|
|
|
|
/* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
|
|
inc_preempt_count();
|
|
if (!PageHighMem(page))
|
|
return page_address(page);
|
|
|
|
idx = type + KM_TYPE_NR*smp_processor_id();
|
|
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
|
|
#ifdef CONFIG_DEBUG_HIGHMEM
|
|
if (!pte_none(*(kmap_pte-idx)))
|
|
BUG();
|
|
#endif
|
|
set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));
|
|
__flush_tlb_one(vaddr);
|
|
|
|
return (void*) vaddr;
|
|
}
|
|
|
|
void kunmap_atomic(void *kvaddr, enum km_type type)
|
|
{
|
|
#ifdef CONFIG_DEBUG_HIGHMEM
|
|
unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
|
|
enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
|
|
|
|
if (vaddr >= PAGE_OFFSET && vaddr < (unsigned long)high_memory) {
|
|
dec_preempt_count();
|
|
preempt_check_resched();
|
|
return;
|
|
}
|
|
|
|
if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx))
|
|
BUG();
|
|
|
|
/*
|
|
* 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);
|
|
__flush_tlb_one(vaddr);
|
|
#endif
|
|
|
|
dec_preempt_count();
|
|
preempt_check_resched();
|
|
}
|
|
|
|
/* This is the same as kmap_atomic() but can map memory that doesn't
|
|
* have a struct page associated with it.
|
|
*/
|
|
void *kmap_atomic_pfn(unsigned long pfn, enum km_type type)
|
|
{
|
|
enum fixed_addresses idx;
|
|
unsigned long vaddr;
|
|
|
|
inc_preempt_count();
|
|
|
|
idx = type + KM_TYPE_NR*smp_processor_id();
|
|
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
|
|
set_pte(kmap_pte-idx, pfn_pte(pfn, kmap_prot));
|
|
__flush_tlb_one(vaddr);
|
|
|
|
return (void*) vaddr;
|
|
}
|
|
|
|
struct page *kmap_atomic_to_page(void *ptr)
|
|
{
|
|
unsigned long idx, vaddr = (unsigned long)ptr;
|
|
pte_t *pte;
|
|
|
|
if (vaddr < FIXADDR_START)
|
|
return virt_to_page(ptr);
|
|
|
|
idx = virt_to_fix(vaddr);
|
|
pte = kmap_pte - (idx - FIX_KMAP_BEGIN);
|
|
return pte_page(*pte);
|
|
}
|
|
|
|
EXPORT_SYMBOL(kmap);
|
|
EXPORT_SYMBOL(kunmap);
|
|
EXPORT_SYMBOL(kmap_atomic);
|
|
EXPORT_SYMBOL(kunmap_atomic);
|
|
EXPORT_SYMBOL(kmap_atomic_to_page);
|