[ARM] mm 3: separate out supersection mappings, avoid for <4GB
Catalin Marinas at ARM Ltd says: > The CPU architects in ARM intended supersections only as a way to map > addresses >= 4GB. Supersections are not mandated by the architecture > and there is no easy way to detect their hardware support at run-time > (other than checking for a specific core). From the analysis done in > ARM, there wasn't a clear performance gain by using supersections > rather than sections (no significant improvement in the TLB misses). Therefore, we should avoid using supersections unless there's a real need (iow, we're mapping addresses >= 4GB). This means that we can simplify create_mapping() a bit since we will only use supersection mappings for addresses >= 4GB, which means that the physical, virtual and length must be multiples of the supersection mapping size. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
d5c98176ef
commit
4a56c1e41f
|
@ -302,7 +302,7 @@ __ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size,
|
|||
#ifndef CONFIG_SMP
|
||||
if (DOMAIN_IO == 0 &&
|
||||
(((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
|
||||
cpu_is_xsc3()) &&
|
||||
cpu_is_xsc3()) && pfn >= 0x100000 &&
|
||||
!((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {
|
||||
area->flags |= VM_ARM_SECTION_MAPPING;
|
||||
err = remap_area_supersections(addr, pfn, size, flags);
|
||||
|
|
|
@ -397,21 +397,6 @@ alloc_init_section(unsigned long virt, unsigned long phys, int prot)
|
|||
flush_pmd_entry(pmdp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a SUPER SECTION PGD between VIRT and PHYS with protection PROT
|
||||
*/
|
||||
static inline void
|
||||
alloc_init_supersection(unsigned long virt, unsigned long phys, int prot)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
alloc_init_section(virt, phys, prot | PMD_SECT_SUPER);
|
||||
|
||||
virt += (PGDIR_SIZE / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a PAGE mapping between VIRT and PHYS in domain
|
||||
* DOMAIN with protection PROT. Note that due to the
|
||||
|
@ -436,6 +421,64 @@ alloc_init_page(unsigned long virt, unsigned long phys, const struct mem_type *t
|
|||
set_pte_ext(ptep, pfn_pte(phys >> PAGE_SHIFT, __pgprot(type->prot_pte)), 0);
|
||||
}
|
||||
|
||||
static void __init create_36bit_mapping(struct map_desc *md,
|
||||
const struct mem_type *type)
|
||||
{
|
||||
unsigned long phys, addr, length, end;
|
||||
pgd_t *pgd;
|
||||
|
||||
addr = md->virtual;
|
||||
phys = (unsigned long)__pfn_to_phys(md->pfn);
|
||||
length = PAGE_ALIGN(md->length);
|
||||
|
||||
if (!(cpu_architecture() >= CPU_ARCH_ARMv6 || cpu_is_xsc3())) {
|
||||
printk(KERN_ERR "MM: CPU does not support supersection "
|
||||
"mapping for 0x%08llx at 0x%08lx\n",
|
||||
__pfn_to_phys((u64)md->pfn), addr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* N.B. ARMv6 supersections are only defined to work with domain 0.
|
||||
* Since domain assignments can in fact be arbitrary, the
|
||||
* 'domain == 0' check below is required to insure that ARMv6
|
||||
* supersections are only allocated for domain 0 regardless
|
||||
* of the actual domain assignments in use.
|
||||
*/
|
||||
if (type->domain) {
|
||||
printk(KERN_ERR "MM: invalid domain in supersection "
|
||||
"mapping for 0x%08llx at 0x%08lx\n",
|
||||
__pfn_to_phys((u64)md->pfn), addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((addr | length | __pfn_to_phys(md->pfn)) & ~SUPERSECTION_MASK) {
|
||||
printk(KERN_ERR "MM: cannot create mapping for "
|
||||
"0x%08llx at 0x%08lx invalid alignment\n",
|
||||
__pfn_to_phys((u64)md->pfn), addr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shift bits [35:32] of address into bits [23:20] of PMD
|
||||
* (See ARMv6 spec).
|
||||
*/
|
||||
phys |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20);
|
||||
|
||||
pgd = pgd_offset_k(addr);
|
||||
end = addr + length;
|
||||
do {
|
||||
pmd_t *pmd = pmd_offset(pgd, addr);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
*pmd++ = __pmd(phys | type->prot_sect | PMD_SECT_SUPER);
|
||||
|
||||
addr += SUPERSECTION_SIZE;
|
||||
phys += SUPERSECTION_SIZE;
|
||||
pgd += SUPERSECTION_SIZE >> PGDIR_SHIFT;
|
||||
} while (addr != end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the page directory entries and any necessary
|
||||
* page tables for the mapping specified by `md'. We
|
||||
|
@ -468,26 +511,9 @@ void __init create_mapping(struct map_desc *md)
|
|||
/*
|
||||
* Catch 36-bit addresses
|
||||
*/
|
||||
if(md->pfn >= 0x100000) {
|
||||
if (type->domain) {
|
||||
printk(KERN_ERR "MM: invalid domain in supersection "
|
||||
"mapping for 0x%08llx at 0x%08lx\n",
|
||||
__pfn_to_phys((u64)md->pfn), md->virtual);
|
||||
return;
|
||||
}
|
||||
if((md->virtual | md->length | __pfn_to_phys(md->pfn))
|
||||
& ~SUPERSECTION_MASK) {
|
||||
printk(KERN_ERR "MM: cannot create mapping for "
|
||||
"0x%08llx at 0x%08lx invalid alignment\n",
|
||||
__pfn_to_phys((u64)md->pfn), md->virtual);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shift bits [35:32] of address into bits [23:20] of PMD
|
||||
* (See ARMv6 spec).
|
||||
*/
|
||||
off |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20);
|
||||
if (md->pfn >= 0x100000) {
|
||||
create_36bit_mapping(md, type);
|
||||
return;
|
||||
}
|
||||
|
||||
virt = md->virtual;
|
||||
|
@ -509,40 +535,6 @@ void __init create_mapping(struct map_desc *md)
|
|||
length -= PAGE_SIZE;
|
||||
}
|
||||
|
||||
/* N.B. ARMv6 supersections are only defined to work with domain 0.
|
||||
* Since domain assignments can in fact be arbitrary, the
|
||||
* 'domain == 0' check below is required to insure that ARMv6
|
||||
* supersections are only allocated for domain 0 regardless
|
||||
* of the actual domain assignments in use.
|
||||
*/
|
||||
if ((cpu_architecture() >= CPU_ARCH_ARMv6 || cpu_is_xsc3())
|
||||
&& type->domain == 0) {
|
||||
/*
|
||||
* Align to supersection boundary if !high pages.
|
||||
* High pages have already been checked for proper
|
||||
* alignment above and they will fail the SUPSERSECTION_MASK
|
||||
* check because of the way the address is encoded into
|
||||
* offset.
|
||||
*/
|
||||
if (md->pfn <= 0x100000) {
|
||||
while ((virt & ~SUPERSECTION_MASK ||
|
||||
(virt + off) & ~SUPERSECTION_MASK) &&
|
||||
length >= (PGDIR_SIZE / 2)) {
|
||||
alloc_init_section(virt, virt + off, type->prot_sect);
|
||||
|
||||
virt += (PGDIR_SIZE / 2);
|
||||
length -= (PGDIR_SIZE / 2);
|
||||
}
|
||||
}
|
||||
|
||||
while (length >= SUPERSECTION_SIZE) {
|
||||
alloc_init_supersection(virt, virt + off, type->prot_sect);
|
||||
|
||||
virt += SUPERSECTION_SIZE;
|
||||
length -= SUPERSECTION_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A section mapping covers half a "pgdir" entry.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue