diff options
Diffstat (limited to 'arch/arm/mm/pgd.c')
-rw-r--r-- | arch/arm/mm/pgd.c | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/arch/arm/mm/pgd.c b/arch/arm/mm/pgd.c index ad249e98a942..3e9503bb7bf5 100644 --- a/arch/arm/mm/pgd.c +++ b/arch/arm/mm/pgd.c @@ -17,6 +17,23 @@ #include "mm.h" +DEFINE_SPINLOCK(pgd_lock); +LIST_HEAD(pgd_list); + +static inline void pgd_list_add(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + + list_add(&page->lru, &pgd_list); +} + +static inline void pgd_list_del(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + + list_del(&page->lru); +} + /* * need to get a 16k page for level 1 */ @@ -26,6 +43,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm) pud_t *new_pud, *init_pud; pmd_t *new_pmd, *init_pmd; pte_t *new_pte, *init_pte; + unsigned long flags; new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2); if (!new_pgd) @@ -33,6 +51,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm) memset(new_pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); + spin_lock_irqsave(&pgd_lock, flags); /* * Copy over the kernel and IO PGD entries */ @@ -43,6 +62,10 @@ pgd_t *pgd_alloc(struct mm_struct *mm) #if !defined(CONFIG_CPU_CACHE_V7) || !defined(CONFIG_SMP) clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t)); #endif + + pgd_list_add(new_pgd); + spin_unlock_irqrestore(&pgd_lock, flags); + if (!vectors_high()) { /* * On ARM, first page must always be allocated since it @@ -75,6 +98,9 @@ no_pte: no_pmd: pud_free(mm, new_pud); no_pud: + spin_lock_irqsave(&pgd_lock, flags); + pgd_list_del(new_pgd); + spin_unlock_irqrestore(&pgd_lock, flags); free_pages((unsigned long)new_pgd, 2); no_pgd: return NULL; @@ -86,10 +112,15 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd_base) pud_t *pud; pmd_t *pmd; pgtable_t pte; + unsigned long flags; if (!pgd_base) return; + spin_lock_irqsave(&pgd_lock, flags); + pgd_list_del(pgd_base); + spin_unlock_irqrestore(&pgd_lock, flags); + pgd = pgd_base + pgd_index(0); if (pgd_none_or_clear_bad(pgd)) goto no_pgd; |