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

Commit 42da9cbd authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds
Browse files

[PATCH] mm: mincore anon



Make mincore work for anon mappings, nonlinear, and migration entries.
Based on patch from Linus Torvalds <torvalds@linux-foundation.org>.

Signed-off-by: default avatarNick Piggin <npiggin@suse.de>
Acked-by: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 22cd25ed
Loading
Loading
Loading
Loading
+76 −26
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/syscalls.h>
#include <linux/swap.h>
#include <linux/swapops.h>

#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -22,14 +24,22 @@
 * and is up to date; i.e. that no page-in operation would be required
 * at this time if an application were to map and access this page.
 */
static unsigned char mincore_page(struct vm_area_struct * vma,
	unsigned long pgoff)
static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff)
{
	unsigned char present = 0;
	struct address_space * as = vma->vm_file->f_mapping;
	struct page *page;

	page = find_get_page(as, pgoff);
	/*
	 * When tmpfs swaps out a page from a file, any process mapping that
	 * file will not get a swp_entry_t in its pte, but rather it is like
	 * any other file mapping (ie. marked !present and faulted in with
	 * tmpfs's .nopage). So swapped out tmpfs mappings are tested here.
	 *
	 * However when tmpfs moves the page from pagecache and into swapcache,
	 * it is still in core, but the find_get_page below won't find it.
	 * No big deal, but make a note of it.
	 */
	page = find_get_page(mapping, pgoff);
	if (page) {
		present = PageUptodate(page);
		page_cache_release(page);
@@ -45,7 +55,14 @@ static unsigned char mincore_page(struct vm_area_struct * vma,
 */
static long do_mincore(unsigned long addr, unsigned char *vec, unsigned long pages)
{
	unsigned long i, nr, pgoff;
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *ptep;
	spinlock_t *ptl;
	unsigned long nr;
	int i;
	pgoff_t pgoff;
	struct vm_area_struct *vma = find_vma(current->mm, addr);

	/*
@@ -56,31 +73,64 @@ static long do_mincore(unsigned long addr, unsigned char *vec, unsigned long pag
		return -ENOMEM;

	/*
	 * Ok, got it. But check whether it's a segment we support
	 * mincore() on. Right now, we don't do any anonymous mappings.
	 *
	 * FIXME: This is just stupid. And returning ENOMEM is 
	 * stupid too. We should just look at the page tables. But
	 * this is what we've traditionally done, so we'll just
	 * continue doing it.
	 */
	if (!vma->vm_file)
		return -ENOMEM;

	/*
	 * Calculate how many pages there are left in the vma, and
	 * what the pgoff is for our address.
	 * Calculate how many pages there are left in the last level of the
	 * PTE array for our address.
	 */
	nr = (vma->vm_end - addr) >> PAGE_SHIFT;
	nr = PTRS_PER_PTE - ((addr >> PAGE_SHIFT) & (PTRS_PER_PTE-1));
	if (nr > pages)
		nr = pages;

	pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
	pgoff += vma->vm_pgoff;
	pgd = pgd_offset(vma->vm_mm, addr);
	if (pgd_none_or_clear_bad(pgd))
		goto none_mapped;
	pud = pud_offset(pgd, addr);
	if (pud_none_or_clear_bad(pud))
		goto none_mapped;
	pmd = pmd_offset(pud, addr);
	if (pmd_none_or_clear_bad(pmd))
		goto none_mapped;

	ptep = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
	for (i = 0; i < nr; i++, ptep++, addr += PAGE_SIZE) {
		unsigned char present;
		pte_t pte = *ptep;

		if (pte_present(pte)) {
			present = 1;

		} else if (pte_none(pte)) {
			if (vma->vm_file) {
				pgoff = linear_page_index(vma, addr);
				present = mincore_page(vma->vm_file->f_mapping,
							pgoff);
			} else
				present = 0;

		} else if (pte_file(pte)) {
			pgoff = pte_to_pgoff(pte);
			present = mincore_page(vma->vm_file->f_mapping, pgoff);

		} else { /* pte is a swap entry */
			swp_entry_t entry = pte_to_swp_entry(pte);
			if (is_migration_entry(entry)) {
				/* migration entries are always uptodate */
				present = 1;
			} else {
				pgoff = entry.val;
				present = mincore_page(&swapper_space, pgoff);
			}
		}
	}
	pte_unmap_unlock(ptep-1, ptl);

	/* And then we just fill the sucker in.. */
	return nr;

none_mapped:
	if (vma->vm_file) {
		pgoff = linear_page_index(vma, addr);
		for (i = 0; i < nr; i++, pgoff++)
		vec[i] = mincore_page(vma, pgoff);
			vec[i] = mincore_page(vma->vm_file->f_mapping, pgoff);
	}

	return nr;
}