Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a56c1e4 authored by Russell King's avatar Russell King Committed by Russell King
Browse files

[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: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent d5c98176
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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);
+61 −69
Original line number Diff line number Diff line
@@ -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
@@ -469,26 +512,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);
		create_36bit_mapping(md, type);
		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);
	}

	virt   = md->virtual;
	off   -= virt;
@@ -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.
	 */