Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88784396 authored by Hugh Dickins's avatar Hugh Dickins Committed by Linus Torvalds
Browse files

mm: fix bad rss-counter if remap_file_pages raced migration



Fix some "Bad rss-counter state" reports on exit, arising from the
interaction between page migration and remap_file_pages(): zap_pte()
must count a migration entry when zapping it.

And yes, it is possible (though very unusual) to find an anon page or
swap entry in a VM_SHARED nonlinear mapping: coming from that horrid
get_user_pages(write, force) case which COWs even in a shared mapping.

Signed-off-by: default avatarHugh Dickins <hughd@google.com>
Tested-by: default avatarSasha Levin <sasha.levin@oracle.com>
Tested-by: default avatarDave Jones <davej@redhat.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 7c1cfacc
Loading
Loading
Loading
Loading
+22 −6
Original line number Diff line number Diff line
@@ -23,28 +23,44 @@

#include "internal.h"

static int mm_counter(struct page *page)
{
	return PageAnon(page) ? MM_ANONPAGES : MM_FILEPAGES;
}

static void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma,
			unsigned long addr, pte_t *ptep)
{
	pte_t pte = *ptep;

	if (pte_present(pte)) {
	struct page *page;
	swp_entry_t entry;

	if (pte_present(pte)) {
		flush_cache_page(vma, addr, pte_pfn(pte));
		pte = ptep_clear_flush(vma, addr, ptep);
		page = vm_normal_page(vma, addr, pte);
		if (page) {
			if (pte_dirty(pte))
				set_page_dirty(page);
			update_hiwater_rss(mm);
			dec_mm_counter(mm, mm_counter(page));
			page_remove_rmap(page);
			page_cache_release(page);
		}
	} else {	/* zap_pte() is not called when pte_none() */
		if (!pte_file(pte)) {
			update_hiwater_rss(mm);
			dec_mm_counter(mm, MM_FILEPAGES);
			entry = pte_to_swp_entry(pte);
			if (non_swap_entry(entry)) {
				if (is_migration_entry(entry)) {
					page = migration_entry_to_page(entry);
					dec_mm_counter(mm, mm_counter(page));
				}
			} else {
		if (!pte_file(pte))
			free_swap_and_cache(pte_to_swp_entry(pte));
				free_swap_and_cache(entry);
				dec_mm_counter(mm, MM_SWAPENTS);
			}
		}
		pte_clear_not_present_full(mm, addr, ptep, 0);
	}
}