@@ -64,6 +64,8 @@
#define _PAGE_FILE (_AT(pteval_t, 1) << _PAGE_BIT_FILE)
#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
+#define _PAGE_VRANGE _PAGE_BIT_PSE
+
/*
* _PAGE_NUMA indicates that this page will trigger a numa hinting
* minor page fault to gather numa placement statistics (see
@@ -479,6 +479,17 @@ static inline unsigned long my_zero_pfn(unsigned long addr)
#ifdef CONFIG_MMU
+static inline pte_t pte_mkvrange(pte_t pte)
+{
+ pte = pte_set_flags(pte, _PAGE_VRANGE);
+ return pte_clear_flags(pte, _PAGE_PRESENT);
+}
+
+static inline int pte_vrange(pte_t pte)
+{
+ return ((pte_flags(pte) | _PAGE_PRESENT) == _PAGE_VRANGE);
+}
+
#ifndef CONFIG_TRANSPARENT_HUGEPAGE
static inline int pmd_trans_huge(pmd_t pmd)
{
@@ -41,6 +41,8 @@ int discard_vpage(struct page *page);
bool vrange_address(struct mm_struct *mm, unsigned long start,
unsigned long end);
+extern bool is_purged_vrange(struct mm_struct *mm, unsigned long address);
+
#else
static inline void vrange_init(void) {};
@@ -59,6 +59,7 @@
#include <linux/gfp.h>
#include <linux/migrate.h>
#include <linux/string.h>
+#include <linux/vrange.h>
#include <asm/io.h>
#include <asm/pgalloc.h>
@@ -841,7 +842,7 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
/* pte contains position in swap or file, so copy. */
if (unlikely(!pte_present(pte))) {
- if (!pte_file(pte)) {
+ if (!pte_file(pte) && !pte_vrange(pte)) {
swp_entry_t entry = pte_to_swp_entry(pte);
if (swap_duplicate(entry) < 0)
@@ -1181,7 +1182,7 @@ again:
if (pte_file(ptent)) {
if (unlikely(!(vma->vm_flags & VM_NONLINEAR)))
print_bad_pte(vma, addr, ptent, NULL);
- } else {
+ } else if (!pte_vrange(ptent)) {
swp_entry_t entry = pte_to_swp_entry(ptent);
if (!non_swap_entry(entry))
@@ -3716,9 +3717,27 @@ int handle_pte_fault(struct mm_struct *mm,
return do_linear_fault(mm, vma, address,
pte, pmd, flags, entry);
}
+anon:
return do_anonymous_page(mm, vma, address,
pte, pmd, flags);
}
+
+ if (unlikely(pte_vrange(entry))) {
+ if (!is_purged_vrange(mm, address)) {
+ /* zap pte */
+ ptl = pte_lockptr(mm, pmd);
+ spin_lock(ptl);
+ if (unlikely(!pte_same(*pte, entry)))
+ goto unlock;
+ flush_cache_page(vma, address, pte_pfn(*pte));
+ ptep_clear_flush(vma, address, pte);
+ pte_unmap_unlock(pte, ptl);
+ goto anon;
+ }
+
+ return VM_FAULT_SIGBUS;
+ }
+
if (pte_file(entry))
return do_nonlinear_fault(mm, vma, address,
pte, pmd, flags, entry);
@@ -477,7 +477,9 @@ int try_to_discard_one(struct page *page, struct vm_area_struct *vma,
present = pte_present(*pte);
flush_cache_page(vma, address, page_to_pfn(page));
- pteval = ptep_clear_flush(vma, address, pte);
+
+ ptep_clear_flush(vma, address, pte);
+ pteval = pte_mkvrange(*pte);
update_hiwater_rss(mm);
dec_mm_counter(mm, MM_ANONPAGES);
@@ -491,6 +493,7 @@ int try_to_discard_one(struct page *page, struct vm_area_struct *vma,
BUG_ON(1);
}
+ set_pte_at(mm, address, pte, pteval);
pte_unmap_unlock(pte, ptl);
mmu_notifier_invalidate_page(mm, address);
ret = 1;
@@ -588,3 +591,22 @@ int discard_vpage(struct page *page)
return 0;
}
+bool is_purged_vrange(struct mm_struct *mm, unsigned long address)
+{
+ struct vrange_root *vroot = &mm->vroot;
+ struct interval_tree_node *node;
+ struct vrange *range;
+ bool ret = false;
+
+ vrange_lock(vroot);
+ node = interval_tree_iter_first(&vroot->v_rb, address,
+ address + PAGE_SIZE - 1);
+ if (node) {
+ range = container_of(node, struct vrange, node);
+ if (range->purged)
+ ret = true;
+ }
+ vrange_unlock(vroot);
+ return ret;
+}
+