|
|
@@ -660,17 +660,22 @@ int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+struct page_referenced_arg {
|
|
|
+ int mapcount;
|
|
|
+ int referenced;
|
|
|
+ unsigned long vm_flags;
|
|
|
+ struct mem_cgroup *memcg;
|
|
|
+};
|
|
|
/*
|
|
|
- * Subfunctions of page_referenced: page_referenced_one called
|
|
|
- * repeatedly from either page_referenced_anon or page_referenced_file.
|
|
|
+ * arg: page_referenced_arg will be passed
|
|
|
*/
|
|
|
int page_referenced_one(struct page *page, struct vm_area_struct *vma,
|
|
|
- unsigned long address, unsigned int *mapcount,
|
|
|
- unsigned long *vm_flags)
|
|
|
+ unsigned long address, void *arg)
|
|
|
{
|
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
|
spinlock_t *ptl;
|
|
|
int referenced = 0;
|
|
|
+ struct page_referenced_arg *pra = arg;
|
|
|
|
|
|
if (unlikely(PageTransHuge(page))) {
|
|
|
pmd_t *pmd;
|
|
|
@@ -682,13 +687,12 @@ int page_referenced_one(struct page *page, struct vm_area_struct *vma,
|
|
|
pmd = page_check_address_pmd(page, mm, address,
|
|
|
PAGE_CHECK_ADDRESS_PMD_FLAG, &ptl);
|
|
|
if (!pmd)
|
|
|
- goto out;
|
|
|
+ return SWAP_AGAIN;
|
|
|
|
|
|
if (vma->vm_flags & VM_LOCKED) {
|
|
|
spin_unlock(ptl);
|
|
|
- *mapcount = 0; /* break early from loop */
|
|
|
- *vm_flags |= VM_LOCKED;
|
|
|
- goto out;
|
|
|
+ pra->vm_flags |= VM_LOCKED;
|
|
|
+ return SWAP_FAIL; /* To break the loop */
|
|
|
}
|
|
|
|
|
|
/* go ahead even if the pmd is pmd_trans_splitting() */
|
|
|
@@ -704,13 +708,12 @@ int page_referenced_one(struct page *page, struct vm_area_struct *vma,
|
|
|
*/
|
|
|
pte = page_check_address(page, mm, address, &ptl, 0);
|
|
|
if (!pte)
|
|
|
- goto out;
|
|
|
+ return SWAP_AGAIN;
|
|
|
|
|
|
if (vma->vm_flags & VM_LOCKED) {
|
|
|
pte_unmap_unlock(pte, ptl);
|
|
|
- *mapcount = 0; /* break early from loop */
|
|
|
- *vm_flags |= VM_LOCKED;
|
|
|
- goto out;
|
|
|
+ pra->vm_flags |= VM_LOCKED;
|
|
|
+ return SWAP_FAIL; /* To break the loop */
|
|
|
}
|
|
|
|
|
|
if (ptep_clear_flush_young_notify(vma, address, pte)) {
|
|
|
@@ -727,113 +730,27 @@ int page_referenced_one(struct page *page, struct vm_area_struct *vma,
|
|
|
pte_unmap_unlock(pte, ptl);
|
|
|
}
|
|
|
|
|
|
- (*mapcount)--;
|
|
|
-
|
|
|
- if (referenced)
|
|
|
- *vm_flags |= vma->vm_flags;
|
|
|
-out:
|
|
|
- return referenced;
|
|
|
-}
|
|
|
-
|
|
|
-static int page_referenced_anon(struct page *page,
|
|
|
- struct mem_cgroup *memcg,
|
|
|
- unsigned long *vm_flags)
|
|
|
-{
|
|
|
- unsigned int mapcount;
|
|
|
- struct anon_vma *anon_vma;
|
|
|
- pgoff_t pgoff;
|
|
|
- struct anon_vma_chain *avc;
|
|
|
- int referenced = 0;
|
|
|
-
|
|
|
- anon_vma = page_lock_anon_vma_read(page);
|
|
|
- if (!anon_vma)
|
|
|
- return referenced;
|
|
|
-
|
|
|
- mapcount = page_mapcount(page);
|
|
|
- pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
|
|
|
- anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root, pgoff, pgoff) {
|
|
|
- struct vm_area_struct *vma = avc->vma;
|
|
|
- unsigned long address = vma_address(page, vma);
|
|
|
- /*
|
|
|
- * If we are reclaiming on behalf of a cgroup, skip
|
|
|
- * counting on behalf of references from different
|
|
|
- * cgroups
|
|
|
- */
|
|
|
- if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
|
|
|
- continue;
|
|
|
- referenced += page_referenced_one(page, vma, address,
|
|
|
- &mapcount, vm_flags);
|
|
|
- if (!mapcount)
|
|
|
- break;
|
|
|
+ if (referenced) {
|
|
|
+ pra->referenced++;
|
|
|
+ pra->vm_flags |= vma->vm_flags;
|
|
|
}
|
|
|
|
|
|
- page_unlock_anon_vma_read(anon_vma);
|
|
|
- return referenced;
|
|
|
+ pra->mapcount--;
|
|
|
+ if (!pra->mapcount)
|
|
|
+ return SWAP_SUCCESS; /* To break the loop */
|
|
|
+
|
|
|
+ return SWAP_AGAIN;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * page_referenced_file - referenced check for object-based rmap
|
|
|
- * @page: the page we're checking references on.
|
|
|
- * @memcg: target memory control group
|
|
|
- * @vm_flags: collect encountered vma->vm_flags who actually referenced the page
|
|
|
- *
|
|
|
- * For an object-based mapped page, find all the places it is mapped and
|
|
|
- * check/clear the referenced flag. This is done by following the page->mapping
|
|
|
- * pointer, then walking the chain of vmas it holds. It returns the number
|
|
|
- * of references it found.
|
|
|
- *
|
|
|
- * This function is only called from page_referenced for object-based pages.
|
|
|
- */
|
|
|
-static int page_referenced_file(struct page *page,
|
|
|
- struct mem_cgroup *memcg,
|
|
|
- unsigned long *vm_flags)
|
|
|
+static bool invalid_page_referenced_vma(struct vm_area_struct *vma, void *arg)
|
|
|
{
|
|
|
- unsigned int mapcount;
|
|
|
- struct address_space *mapping = page->mapping;
|
|
|
- pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
|
|
|
- struct vm_area_struct *vma;
|
|
|
- int referenced = 0;
|
|
|
-
|
|
|
- /*
|
|
|
- * The caller's checks on page->mapping and !PageAnon have made
|
|
|
- * sure that this is a file page: the check for page->mapping
|
|
|
- * excludes the case just before it gets set on an anon page.
|
|
|
- */
|
|
|
- BUG_ON(PageAnon(page));
|
|
|
+ struct page_referenced_arg *pra = arg;
|
|
|
+ struct mem_cgroup *memcg = pra->memcg;
|
|
|
|
|
|
- /*
|
|
|
- * The page lock not only makes sure that page->mapping cannot
|
|
|
- * suddenly be NULLified by truncation, it makes sure that the
|
|
|
- * structure at mapping cannot be freed and reused yet,
|
|
|
- * so we can safely take mapping->i_mmap_mutex.
|
|
|
- */
|
|
|
- BUG_ON(!PageLocked(page));
|
|
|
-
|
|
|
- mutex_lock(&mapping->i_mmap_mutex);
|
|
|
-
|
|
|
- /*
|
|
|
- * i_mmap_mutex does not stabilize mapcount at all, but mapcount
|
|
|
- * is more likely to be accurate if we note it after spinning.
|
|
|
- */
|
|
|
- mapcount = page_mapcount(page);
|
|
|
-
|
|
|
- vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
|
|
|
- unsigned long address = vma_address(page, vma);
|
|
|
- /*
|
|
|
- * If we are reclaiming on behalf of a cgroup, skip
|
|
|
- * counting on behalf of references from different
|
|
|
- * cgroups
|
|
|
- */
|
|
|
- if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
|
|
|
- continue;
|
|
|
- referenced += page_referenced_one(page, vma, address,
|
|
|
- &mapcount, vm_flags);
|
|
|
- if (!mapcount)
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (!mm_match_cgroup(vma->vm_mm, memcg))
|
|
|
+ return true;
|
|
|
|
|
|
- mutex_unlock(&mapping->i_mmap_mutex);
|
|
|
- return referenced;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -851,32 +768,47 @@ int page_referenced(struct page *page,
|
|
|
struct mem_cgroup *memcg,
|
|
|
unsigned long *vm_flags)
|
|
|
{
|
|
|
- int referenced = 0;
|
|
|
+ int ret;
|
|
|
int we_locked = 0;
|
|
|
+ struct page_referenced_arg pra = {
|
|
|
+ .mapcount = page_mapcount(page),
|
|
|
+ .memcg = memcg,
|
|
|
+ };
|
|
|
+ struct rmap_walk_control rwc = {
|
|
|
+ .rmap_one = page_referenced_one,
|
|
|
+ .arg = (void *)&pra,
|
|
|
+ .anon_lock = page_lock_anon_vma_read,
|
|
|
+ };
|
|
|
|
|
|
*vm_flags = 0;
|
|
|
- if (page_mapped(page) && page_rmapping(page)) {
|
|
|
- if (!is_locked && (!PageAnon(page) || PageKsm(page))) {
|
|
|
- we_locked = trylock_page(page);
|
|
|
- if (!we_locked) {
|
|
|
- referenced++;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- }
|
|
|
- if (unlikely(PageKsm(page)))
|
|
|
- referenced += page_referenced_ksm(page, memcg,
|
|
|
- vm_flags);
|
|
|
- else if (PageAnon(page))
|
|
|
- referenced += page_referenced_anon(page, memcg,
|
|
|
- vm_flags);
|
|
|
- else if (page->mapping)
|
|
|
- referenced += page_referenced_file(page, memcg,
|
|
|
- vm_flags);
|
|
|
- if (we_locked)
|
|
|
- unlock_page(page);
|
|
|
+ if (!page_mapped(page))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (!page_rmapping(page))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (!is_locked && (!PageAnon(page) || PageKsm(page))) {
|
|
|
+ we_locked = trylock_page(page);
|
|
|
+ if (!we_locked)
|
|
|
+ return 1;
|
|
|
}
|
|
|
-out:
|
|
|
- return referenced;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we are reclaiming on behalf of a cgroup, skip
|
|
|
+ * counting on behalf of references from different
|
|
|
+ * cgroups
|
|
|
+ */
|
|
|
+ if (memcg) {
|
|
|
+ rwc.invalid_vma = invalid_page_referenced_vma;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = rmap_walk(page, &rwc);
|
|
|
+ *vm_flags = pra.vm_flags;
|
|
|
+
|
|
|
+ if (we_locked)
|
|
|
+ unlock_page(page);
|
|
|
+
|
|
|
+ return pra.referenced;
|
|
|
}
|
|
|
|
|
|
static int page_mkclean_one(struct page *page, struct vm_area_struct *vma,
|
|
|
@@ -1700,6 +1632,14 @@ static int rmap_walk_file(struct page *page, struct rmap_walk_control *rwc)
|
|
|
struct vm_area_struct *vma;
|
|
|
int ret = SWAP_AGAIN;
|
|
|
|
|
|
+ /*
|
|
|
+ * The page lock not only makes sure that page->mapping cannot
|
|
|
+ * suddenly be NULLified by truncation, it makes sure that the
|
|
|
+ * structure at mapping cannot be freed and reused yet,
|
|
|
+ * so we can safely take mapping->i_mmap_mutex.
|
|
|
+ */
|
|
|
+ VM_BUG_ON(!PageLocked(page));
|
|
|
+
|
|
|
if (!mapping)
|
|
|
return ret;
|
|
|
mutex_lock(&mapping->i_mmap_mutex);
|
|
|
@@ -1731,8 +1671,6 @@ done:
|
|
|
|
|
|
int rmap_walk(struct page *page, struct rmap_walk_control *rwc)
|
|
|
{
|
|
|
- VM_BUG_ON(!PageLocked(page));
|
|
|
-
|
|
|
if (unlikely(PageKsm(page)))
|
|
|
return rmap_walk_ksm(page, rwc);
|
|
|
else if (PageAnon(page))
|