[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:
Russell King 2007-04-21 10:16:48 +01:00 committed by Russell King
parent d5c98176ef
commit 4a56c1e41f
2 changed files with 62 additions and 70 deletions

View File

@ -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);

View File

@ -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.
*/