diff options
author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2015-02-11 18:26:53 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-11 20:06:04 -0500 |
commit | b30fe6c7ced70f62862c3d09357e7e8084e98d9f (patch) | |
tree | 317c0298b2111b342e1ff031b5da5aad2b894c00 | |
parent | dc6c9a35b66b520cf67e05d8ca60ebecad3b0479 (diff) |
mm: fix false-positive warning on exit due mm_nr_pmds(mm)
The problem is that we check nr_ptes/nr_pmds in exit_mmap() which happens
*before* pgd_free(). And if an arch does pte/pmd allocation in
pgd_alloc() and frees them in pgd_free() we see offset in counters by the
time of the checks.
We tried to workaround this by offsetting expected counter value according
to FIRST_USER_ADDRESS for both nr_pte and nr_pmd in exit_mmap(). But it
doesn't work in some cases:
1. ARM with LPAE enabled also has non-zero USER_PGTABLES_CEILING, but
upper addresses occupied with huge pmd entries, so the trick with
offsetting expected counter value will get really ugly: we will have
to apply it nr_pmds, but not nr_ptes.
2. Metag has non-zero FIRST_USER_ADDRESS, but doesn't do allocation
pte/pmd page tables allocation in pgd_alloc(), just setup a pgd entry
which is allocated at boot and shared accross all processes.
The proposal is to move the check to check_mm() which happens *after*
pgd_free() and do proper accounting during pgd_alloc() and pgd_free()
which would bring counters to zero if nothing leaked.
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reported-by: Tyler Baker <tyler.baker@linaro.org>
Tested-by: Tyler Baker <tyler.baker@linaro.org>
Tested-by: Nishanth Menon <nm@ti.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: James Hogan <james.hogan@imgtec.com>
Cc: Guan Xuetao <gxt@mprc.pku.edu.cn>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/arm/mm/pgd.c | 4 | ||||
-rw-r--r-- | arch/unicore32/mm/pgd.c | 3 | ||||
-rw-r--r-- | kernel/fork.c | 8 | ||||
-rw-r--r-- | mm/mmap.c | 5 |
4 files changed, 15 insertions, 5 deletions
diff --git a/arch/arm/mm/pgd.c b/arch/arm/mm/pgd.c index 249379535be2..a3681f11dd9f 100644 --- a/arch/arm/mm/pgd.c +++ b/arch/arm/mm/pgd.c | |||
@@ -97,6 +97,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm) | |||
97 | 97 | ||
98 | no_pte: | 98 | no_pte: |
99 | pmd_free(mm, new_pmd); | 99 | pmd_free(mm, new_pmd); |
100 | mm_dec_nr_pmds(mm); | ||
100 | no_pmd: | 101 | no_pmd: |
101 | pud_free(mm, new_pud); | 102 | pud_free(mm, new_pud); |
102 | no_pud: | 103 | no_pud: |
@@ -130,9 +131,11 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd_base) | |||
130 | pte = pmd_pgtable(*pmd); | 131 | pte = pmd_pgtable(*pmd); |
131 | pmd_clear(pmd); | 132 | pmd_clear(pmd); |
132 | pte_free(mm, pte); | 133 | pte_free(mm, pte); |
134 | atomic_long_dec(&mm->nr_ptes); | ||
133 | no_pmd: | 135 | no_pmd: |
134 | pud_clear(pud); | 136 | pud_clear(pud); |
135 | pmd_free(mm, pmd); | 137 | pmd_free(mm, pmd); |
138 | mm_dec_nr_pmds(mm); | ||
136 | no_pud: | 139 | no_pud: |
137 | pgd_clear(pgd); | 140 | pgd_clear(pgd); |
138 | pud_free(mm, pud); | 141 | pud_free(mm, pud); |
@@ -152,6 +155,7 @@ no_pgd: | |||
152 | pmd = pmd_offset(pud, 0); | 155 | pmd = pmd_offset(pud, 0); |
153 | pud_clear(pud); | 156 | pud_clear(pud); |
154 | pmd_free(mm, pmd); | 157 | pmd_free(mm, pmd); |
158 | mm_dec_nr_pmds(mm); | ||
155 | pgd_clear(pgd); | 159 | pgd_clear(pgd); |
156 | pud_free(mm, pud); | 160 | pud_free(mm, pud); |
157 | } | 161 | } |
diff --git a/arch/unicore32/mm/pgd.c b/arch/unicore32/mm/pgd.c index 08b8d4295e70..2ade20d8eab3 100644 --- a/arch/unicore32/mm/pgd.c +++ b/arch/unicore32/mm/pgd.c | |||
@@ -69,6 +69,7 @@ pgd_t *get_pgd_slow(struct mm_struct *mm) | |||
69 | 69 | ||
70 | no_pte: | 70 | no_pte: |
71 | pmd_free(mm, new_pmd); | 71 | pmd_free(mm, new_pmd); |
72 | mm_dec_nr_pmds(mm); | ||
72 | no_pmd: | 73 | no_pmd: |
73 | free_pages((unsigned long)new_pgd, 0); | 74 | free_pages((unsigned long)new_pgd, 0); |
74 | no_pgd: | 75 | no_pgd: |
@@ -96,7 +97,9 @@ void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd) | |||
96 | pte = pmd_pgtable(*pmd); | 97 | pte = pmd_pgtable(*pmd); |
97 | pmd_clear(pmd); | 98 | pmd_clear(pmd); |
98 | pte_free(mm, pte); | 99 | pte_free(mm, pte); |
100 | atomic_long_dec(&mm->nr_ptes); | ||
99 | pmd_free(mm, pmd); | 101 | pmd_free(mm, pmd); |
102 | mm_dec_nr_pmds(mm); | ||
100 | free: | 103 | free: |
101 | free_pages((unsigned long) pgd, 0); | 104 | free_pages((unsigned long) pgd, 0); |
102 | } | 105 | } |
diff --git a/kernel/fork.c b/kernel/fork.c index c99098c52641..66e19c251581 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -606,6 +606,14 @@ static void check_mm(struct mm_struct *mm) | |||
606 | printk(KERN_ALERT "BUG: Bad rss-counter state " | 606 | printk(KERN_ALERT "BUG: Bad rss-counter state " |
607 | "mm:%p idx:%d val:%ld\n", mm, i, x); | 607 | "mm:%p idx:%d val:%ld\n", mm, i, x); |
608 | } | 608 | } |
609 | |||
610 | if (atomic_long_read(&mm->nr_ptes)) | ||
611 | pr_alert("BUG: non-zero nr_ptes on freeing mm: %ld\n", | ||
612 | atomic_long_read(&mm->nr_ptes)); | ||
613 | if (mm_nr_pmds(mm)) | ||
614 | pr_alert("BUG: non-zero nr_pmds on freeing mm: %ld\n", | ||
615 | mm_nr_pmds(mm)); | ||
616 | |||
609 | #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS | 617 | #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS |
610 | VM_BUG_ON_MM(mm->pmd_huge_pte, mm); | 618 | VM_BUG_ON_MM(mm->pmd_huge_pte, mm); |
611 | #endif | 619 | #endif |
@@ -2851,11 +2851,6 @@ void exit_mmap(struct mm_struct *mm) | |||
2851 | vma = remove_vma(vma); | 2851 | vma = remove_vma(vma); |
2852 | } | 2852 | } |
2853 | vm_unacct_memory(nr_accounted); | 2853 | vm_unacct_memory(nr_accounted); |
2854 | |||
2855 | WARN_ON(atomic_long_read(&mm->nr_ptes) > | ||
2856 | round_up(FIRST_USER_ADDRESS, PMD_SIZE) >> PMD_SHIFT); | ||
2857 | WARN_ON(mm_nr_pmds(mm) > | ||
2858 | round_up(FIRST_USER_ADDRESS, PUD_SIZE) >> PUD_SHIFT); | ||
2859 | } | 2854 | } |
2860 | 2855 | ||
2861 | /* Insert vm structure into process list sorted by address | 2856 | /* Insert vm structure into process list sorted by address |