aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2015-02-11 18:26:53 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 20:06:04 -0500
commitb30fe6c7ced70f62862c3d09357e7e8084e98d9f (patch)
tree317c0298b2111b342e1ff031b5da5aad2b894c00
parentdc6c9a35b66b520cf67e05d8ca60ebecad3b0479 (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.c4
-rw-r--r--arch/unicore32/mm/pgd.c3
-rw-r--r--kernel/fork.c8
-rw-r--r--mm/mmap.c5
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
98no_pte: 98no_pte:
99 pmd_free(mm, new_pmd); 99 pmd_free(mm, new_pmd);
100 mm_dec_nr_pmds(mm);
100no_pmd: 101no_pmd:
101 pud_free(mm, new_pud); 102 pud_free(mm, new_pud);
102no_pud: 103no_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);
133no_pmd: 135no_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);
136no_pud: 139no_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
70no_pte: 70no_pte:
71 pmd_free(mm, new_pmd); 71 pmd_free(mm, new_pmd);
72 mm_dec_nr_pmds(mm);
72no_pmd: 73no_pmd:
73 free_pages((unsigned long)new_pgd, 0); 74 free_pages((unsigned long)new_pgd, 0);
74no_pgd: 75no_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);
100free: 103free:
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
diff --git a/mm/mmap.c b/mm/mmap.c
index 6a7d36d133fb..c5f44682c0d1 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -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