[PATCH v2 07/20] ARM: LPAE: Page table maintenance for the 3-level format
Russell King - ARM Linux
linux at arm.linux.org.uk
Mon Nov 22 07:58:13 EST 2010
On Fri, Nov 12, 2010 at 06:00:27PM +0000, Catalin Marinas wrote:
> diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
> index 97a5de3..41236f0 100644
> --- a/arch/arm/include/asm/pgtable.h
> +++ b/arch/arm/include/asm/pgtable.h
> @@ -124,7 +124,12 @@ extern pgprot_t pgprot_kernel;
> extern struct page *empty_zero_page;
> #define ZERO_PAGE(vaddr) (empty_zero_page)
>
> +#ifdef CONFIG_ARM_LPAE
> +#define pte_pfn(pte) ((pte_val(pte) & PTE_PFN_MASK) >> PAGE_SHIFT)
> +#else
> #define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT)
> +#endif
Just make LPAE and non-LPAE both provide PTE_PFN_MASK - for non-LPAE
this can be defined as ~0UL to optimize it away. However, PTE_PFN_MASK
is the wrong name for this - you're not masking out the PFN, but the
physical address. It only becomes a PFN when you shift.
This is important because...
> +static inline pte_t *pmd_page_vaddr(pmd_t pmd)
> +{
> + return __va(pmd_val(pmd) & PTE_PFN_MASK);
... here it becomes much more confusing - it suggests that
"pmd_val(pmd) & PTE_PFN_MASK" gives you a PFN, which you then pass to
a function which takes a physical address.
Also, pmd_page_vaddr() in my patches ends up as:
static inline pte_t *pmd_page_vaddr(pmd_t pmd)
{
+ return __va(pmd_val(pmd) & PAGE_MASK);
}
which is almost the same. I'd suggest that this becomes for both:
static inline pte_t *pmd_page_vaddr(pmd_t pmd)
{
return __va(pmd_val(pmd) & PTE_PFN_MASK & PAGE_MASK);
}
but with PTE_PFN_MASK more appropriately named.
> +}
> +
> +#else /* !CONFIG_ARM_LPAE */
> +
> #define pmd_bad(pmd) (pmd_val(pmd) & 2)
>
> #define copy_pmd(pmdpd,pmdps) \
> @@ -252,7 +285,13 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
> return __va(ptr);
> }
>
> +#endif /* CONFIG_ARM_LPAE */
> +
> +#ifdef CONFIG_ARM_LPAE
> +#define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PTE_PFN_MASK))
> +#else
> #define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd)))
> +#endif
Ditto.
> diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h
> index 8fdae9b..f00ae99 100644
> --- a/arch/arm/include/asm/proc-fns.h
> +++ b/arch/arm/include/asm/proc-fns.h
> @@ -263,6 +263,18 @@
>
> #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
>
> +#ifdef CONFIG_ARM_LPAE
> +#define cpu_get_pgd() \
> + ({ \
> + unsigned long pg, pg2; \
> + __asm__("mrrc p15, 0, %0, %1, c2" \
> + : "=r" (pg), "=r" (pg2) \
> + : \
> + : "cc"); \
> + pg &= ~(PTRS_PER_PGD*sizeof(pgd_t)-1); \
> + (pgd_t *)phys_to_virt(pg); \
> + })
> +#else
> #define cpu_get_pgd() \
> ({ \
> unsigned long pg; \
> @@ -271,6 +283,7 @@
> pg &= ~0x3fff; \
I think this wants updating to use similar math to the one above.
> @@ -81,7 +90,8 @@ void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd)
> if (!pgd)
> return;
>
> - /* pgd is always present and good */
> + if (pgd_none(*pgd))
> + goto free;
This actually wants to become something more like:
+ pgd = pgd_base + pgd_index(0);
+ if (pgd_none_or_clear_bad(pgd))
+ goto no_pgd;
+ pmd = pmd_offset(pgd, 0);
+ if (pmd_none_or_clear_bad(pmd))
+ goto no_pmd;
pte = pmd_pgtable(*pmd);
pmd_clear(pmd);
pte_free(mm, pte);
+no_pmd:
+ pgd_clear(pgd);
pmd_free(mm, pmd);
+no_pgd:
free_pgd(pgd_base);
More information about the linux-arm-kernel
mailing list