mm: fix false-positive warning on exit due mm_nr_pmds(mm)
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>
Wed, 11 Feb 2015 23:26:53 +0000 (15:26 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Feb 2015 01:06:04 +0000 (17:06 -0800)
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>
arch/arm/mm/pgd.c
arch/unicore32/mm/pgd.c
kernel/fork.c
mm/mmap.c

index 249379535be2c151bc2eacd41b6c8a15f586c3cb..a3681f11dd9f12ceb8260df36e09a11ec0c22066 100644 (file)
@@ -97,6 +97,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 
 no_pte:
        pmd_free(mm, new_pmd);
+       mm_dec_nr_pmds(mm);
 no_pmd:
        pud_free(mm, new_pud);
 no_pud:
@@ -130,9 +131,11 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd_base)
        pte = pmd_pgtable(*pmd);
        pmd_clear(pmd);
        pte_free(mm, pte);
+       atomic_long_dec(&mm->nr_ptes);
 no_pmd:
        pud_clear(pud);
        pmd_free(mm, pmd);
+       mm_dec_nr_pmds(mm);
 no_pud:
        pgd_clear(pgd);
        pud_free(mm, pud);
@@ -152,6 +155,7 @@ no_pgd:
                pmd = pmd_offset(pud, 0);
                pud_clear(pud);
                pmd_free(mm, pmd);
+               mm_dec_nr_pmds(mm);
                pgd_clear(pgd);
                pud_free(mm, pud);
        }
index 08b8d4295e70037877ae80dd273f50c23b756075..2ade20d8eab3965d749e2c44d475cc931b52a3c3 100644 (file)
@@ -69,6 +69,7 @@ pgd_t *get_pgd_slow(struct mm_struct *mm)
 
 no_pte:
        pmd_free(mm, new_pmd);
+       mm_dec_nr_pmds(mm);
 no_pmd:
        free_pages((unsigned long)new_pgd, 0);
 no_pgd:
@@ -96,7 +97,9 @@ void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd)
        pte = pmd_pgtable(*pmd);
        pmd_clear(pmd);
        pte_free(mm, pte);
+       atomic_long_dec(&mm->nr_ptes);
        pmd_free(mm, pmd);
+       mm_dec_nr_pmds(mm);
 free:
        free_pages((unsigned long) pgd, 0);
 }
index c99098c5264125c99e872ce29c14e6a3458d5f30..66e19c251581d1ddf52090bc37a23c645d19dd80 100644 (file)
@@ -606,6 +606,14 @@ static void check_mm(struct mm_struct *mm)
                        printk(KERN_ALERT "BUG: Bad rss-counter state "
                                          "mm:%p idx:%d val:%ld\n", mm, i, x);
        }
+
+       if (atomic_long_read(&mm->nr_ptes))
+               pr_alert("BUG: non-zero nr_ptes on freeing mm: %ld\n",
+                               atomic_long_read(&mm->nr_ptes));
+       if (mm_nr_pmds(mm))
+               pr_alert("BUG: non-zero nr_pmds on freeing mm: %ld\n",
+                               mm_nr_pmds(mm));
+
 #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
        VM_BUG_ON_MM(mm->pmd_huge_pte, mm);
 #endif
index 6a7d36d133fbd47a046f4a6f0ac19877580ae040..c5f44682c0d1757b68e90cce7d9076d44143748f 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2851,11 +2851,6 @@ void exit_mmap(struct mm_struct *mm)
                vma = remove_vma(vma);
        }
        vm_unacct_memory(nr_accounted);
-
-       WARN_ON(atomic_long_read(&mm->nr_ptes) >
-                       round_up(FIRST_USER_ADDRESS, PMD_SIZE) >> PMD_SHIFT);
-       WARN_ON(mm_nr_pmds(mm) >
-                       round_up(FIRST_USER_ADDRESS, PUD_SIZE) >> PUD_SHIFT);
 }
 
 /* Insert vm structure into process list sorted by address