diff options
-rw-r--r-- | arch/x86/include/asm/pgtable.h | 15 | ||||
-rw-r--r-- | arch/x86/include/asm/pgtable_types.h | 13 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 21 | ||||
-rw-r--r-- | include/asm-generic/pgtable.h | 15 | ||||
-rw-r--r-- | include/linux/swapops.h | 2 | ||||
-rw-r--r-- | mm/memory.c | 2 | ||||
-rw-r--r-- | mm/rmap.c | 6 | ||||
-rw-r--r-- | mm/swapfile.c | 19 |
8 files changed, 84 insertions, 9 deletions
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 7dc305a46058..bd0518a7f197 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h | |||
@@ -314,6 +314,21 @@ static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) | |||
314 | return pmd_set_flags(pmd, _PAGE_SOFT_DIRTY); | 314 | return pmd_set_flags(pmd, _PAGE_SOFT_DIRTY); |
315 | } | 315 | } |
316 | 316 | ||
317 | static inline pte_t pte_swp_mksoft_dirty(pte_t pte) | ||
318 | { | ||
319 | return pte_set_flags(pte, _PAGE_SWP_SOFT_DIRTY); | ||
320 | } | ||
321 | |||
322 | static inline int pte_swp_soft_dirty(pte_t pte) | ||
323 | { | ||
324 | return pte_flags(pte) & _PAGE_SWP_SOFT_DIRTY; | ||
325 | } | ||
326 | |||
327 | static inline pte_t pte_swp_clear_soft_dirty(pte_t pte) | ||
328 | { | ||
329 | return pte_clear_flags(pte, _PAGE_SWP_SOFT_DIRTY); | ||
330 | } | ||
331 | |||
317 | /* | 332 | /* |
318 | * Mask out unsupported bits in a present pgprot. Non-present pgprots | 333 | * Mask out unsupported bits in a present pgprot. Non-present pgprots |
319 | * can use those bits for other purposes, so leave them be. | 334 | * can use those bits for other purposes, so leave them be. |
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h index c98ac63aae48..5e8442f178f9 100644 --- a/arch/x86/include/asm/pgtable_types.h +++ b/arch/x86/include/asm/pgtable_types.h | |||
@@ -67,6 +67,19 @@ | |||
67 | #define _PAGE_SOFT_DIRTY (_AT(pteval_t, 0)) | 67 | #define _PAGE_SOFT_DIRTY (_AT(pteval_t, 0)) |
68 | #endif | 68 | #endif |
69 | 69 | ||
70 | /* | ||
71 | * Tracking soft dirty bit when a page goes to a swap is tricky. | ||
72 | * We need a bit which can be stored in pte _and_ not conflict | ||
73 | * with swap entry format. On x86 bits 6 and 7 are *not* involved | ||
74 | * into swap entry computation, but bit 6 is used for nonlinear | ||
75 | * file mapping, so we borrow bit 7 for soft dirty tracking. | ||
76 | */ | ||
77 | #ifdef CONFIG_MEM_SOFT_DIRTY | ||
78 | #define _PAGE_SWP_SOFT_DIRTY _PAGE_PSE | ||
79 | #else | ||
80 | #define _PAGE_SWP_SOFT_DIRTY (_AT(pteval_t, 0)) | ||
81 | #endif | ||
82 | |||
70 | #if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE) | 83 | #if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE) |
71 | #define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX) | 84 | #define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX) |
72 | #else | 85 | #else |
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index dbf61f6174f0..e2d9bdce5e7e 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -730,8 +730,14 @@ static inline void clear_soft_dirty(struct vm_area_struct *vma, | |||
730 | * of how soft-dirty works. | 730 | * of how soft-dirty works. |
731 | */ | 731 | */ |
732 | pte_t ptent = *pte; | 732 | pte_t ptent = *pte; |
733 | ptent = pte_wrprotect(ptent); | 733 | |
734 | ptent = pte_clear_flags(ptent, _PAGE_SOFT_DIRTY); | 734 | if (pte_present(ptent)) { |
735 | ptent = pte_wrprotect(ptent); | ||
736 | ptent = pte_clear_flags(ptent, _PAGE_SOFT_DIRTY); | ||
737 | } else if (is_swap_pte(ptent)) { | ||
738 | ptent = pte_swp_clear_soft_dirty(ptent); | ||
739 | } | ||
740 | |||
735 | set_pte_at(vma->vm_mm, addr, pte, ptent); | 741 | set_pte_at(vma->vm_mm, addr, pte, ptent); |
736 | #endif | 742 | #endif |
737 | } | 743 | } |
@@ -752,14 +758,15 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, | |||
752 | pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); | 758 | pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); |
753 | for (; addr != end; pte++, addr += PAGE_SIZE) { | 759 | for (; addr != end; pte++, addr += PAGE_SIZE) { |
754 | ptent = *pte; | 760 | ptent = *pte; |
755 | if (!pte_present(ptent)) | ||
756 | continue; | ||
757 | 761 | ||
758 | if (cp->type == CLEAR_REFS_SOFT_DIRTY) { | 762 | if (cp->type == CLEAR_REFS_SOFT_DIRTY) { |
759 | clear_soft_dirty(vma, addr, pte); | 763 | clear_soft_dirty(vma, addr, pte); |
760 | continue; | 764 | continue; |
761 | } | 765 | } |
762 | 766 | ||
767 | if (!pte_present(ptent)) | ||
768 | continue; | ||
769 | |||
763 | page = vm_normal_page(vma, addr, ptent); | 770 | page = vm_normal_page(vma, addr, ptent); |
764 | if (!page) | 771 | if (!page) |
765 | continue; | 772 | continue; |
@@ -930,8 +937,10 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | |||
930 | flags = PM_PRESENT; | 937 | flags = PM_PRESENT; |
931 | page = vm_normal_page(vma, addr, pte); | 938 | page = vm_normal_page(vma, addr, pte); |
932 | } else if (is_swap_pte(pte)) { | 939 | } else if (is_swap_pte(pte)) { |
933 | swp_entry_t entry = pte_to_swp_entry(pte); | 940 | swp_entry_t entry; |
934 | 941 | if (pte_swp_soft_dirty(pte)) | |
942 | flags2 |= __PM_SOFT_DIRTY; | ||
943 | entry = pte_to_swp_entry(pte); | ||
935 | frame = swp_type(entry) | | 944 | frame = swp_type(entry) | |
936 | (swp_offset(entry) << MAX_SWAPFILES_SHIFT); | 945 | (swp_offset(entry) << MAX_SWAPFILES_SHIFT); |
937 | flags = PM_SWAP; | 946 | flags = PM_SWAP; |
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 2f47ade1b567..2a7e0d10ad9a 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h | |||
@@ -417,6 +417,21 @@ static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) | |||
417 | { | 417 | { |
418 | return pmd; | 418 | return pmd; |
419 | } | 419 | } |
420 | |||
421 | static inline pte_t pte_swp_mksoft_dirty(pte_t pte) | ||
422 | { | ||
423 | return pte; | ||
424 | } | ||
425 | |||
426 | static inline int pte_swp_soft_dirty(pte_t pte) | ||
427 | { | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | static inline pte_t pte_swp_clear_soft_dirty(pte_t pte) | ||
432 | { | ||
433 | return pte; | ||
434 | } | ||
420 | #endif | 435 | #endif |
421 | 436 | ||
422 | #ifndef __HAVE_PFNMAP_TRACKING | 437 | #ifndef __HAVE_PFNMAP_TRACKING |
diff --git a/include/linux/swapops.h b/include/linux/swapops.h index c5fd30d2a415..8d4fa82bfb91 100644 --- a/include/linux/swapops.h +++ b/include/linux/swapops.h | |||
@@ -67,6 +67,8 @@ static inline swp_entry_t pte_to_swp_entry(pte_t pte) | |||
67 | swp_entry_t arch_entry; | 67 | swp_entry_t arch_entry; |
68 | 68 | ||
69 | BUG_ON(pte_file(pte)); | 69 | BUG_ON(pte_file(pte)); |
70 | if (pte_swp_soft_dirty(pte)) | ||
71 | pte = pte_swp_clear_soft_dirty(pte); | ||
70 | arch_entry = __pte_to_swp_entry(pte); | 72 | arch_entry = __pte_to_swp_entry(pte); |
71 | return swp_entry(__swp_type(arch_entry), __swp_offset(arch_entry)); | 73 | return swp_entry(__swp_type(arch_entry), __swp_offset(arch_entry)); |
72 | } | 74 | } |
diff --git a/mm/memory.c b/mm/memory.c index 1ce2e2a734fc..e98ecad2b9c8 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -3115,6 +3115,8 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
3115 | exclusive = 1; | 3115 | exclusive = 1; |
3116 | } | 3116 | } |
3117 | flush_icache_page(vma, page); | 3117 | flush_icache_page(vma, page); |
3118 | if (pte_swp_soft_dirty(orig_pte)) | ||
3119 | pte = pte_mksoft_dirty(pte); | ||
3118 | set_pte_at(mm, address, page_table, pte); | 3120 | set_pte_at(mm, address, page_table, pte); |
3119 | if (page == swapcache) | 3121 | if (page == swapcache) |
3120 | do_page_add_anon_rmap(page, vma, address, exclusive); | 3122 | do_page_add_anon_rmap(page, vma, address, exclusive); |
@@ -1236,6 +1236,7 @@ int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, | |||
1236 | swp_entry_to_pte(make_hwpoison_entry(page))); | 1236 | swp_entry_to_pte(make_hwpoison_entry(page))); |
1237 | } else if (PageAnon(page)) { | 1237 | } else if (PageAnon(page)) { |
1238 | swp_entry_t entry = { .val = page_private(page) }; | 1238 | swp_entry_t entry = { .val = page_private(page) }; |
1239 | pte_t swp_pte; | ||
1239 | 1240 | ||
1240 | if (PageSwapCache(page)) { | 1241 | if (PageSwapCache(page)) { |
1241 | /* | 1242 | /* |
@@ -1264,7 +1265,10 @@ int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, | |||
1264 | BUG_ON(TTU_ACTION(flags) != TTU_MIGRATION); | 1265 | BUG_ON(TTU_ACTION(flags) != TTU_MIGRATION); |
1265 | entry = make_migration_entry(page, pte_write(pteval)); | 1266 | entry = make_migration_entry(page, pte_write(pteval)); |
1266 | } | 1267 | } |
1267 | set_pte_at(mm, address, pte, swp_entry_to_pte(entry)); | 1268 | swp_pte = swp_entry_to_pte(entry); |
1269 | if (pte_soft_dirty(pteval)) | ||
1270 | swp_pte = pte_swp_mksoft_dirty(swp_pte); | ||
1271 | set_pte_at(mm, address, pte, swp_pte); | ||
1268 | BUG_ON(pte_file(*pte)); | 1272 | BUG_ON(pte_file(*pte)); |
1269 | } else if (IS_ENABLED(CONFIG_MIGRATION) && | 1273 | } else if (IS_ENABLED(CONFIG_MIGRATION) && |
1270 | (TTU_ACTION(flags) == TTU_MIGRATION)) { | 1274 | (TTU_ACTION(flags) == TTU_MIGRATION)) { |
diff --git a/mm/swapfile.c b/mm/swapfile.c index 36af6eeaa67e..6cf2e60983b7 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -866,6 +866,21 @@ unsigned int count_swap_pages(int type, int free) | |||
866 | } | 866 | } |
867 | #endif /* CONFIG_HIBERNATION */ | 867 | #endif /* CONFIG_HIBERNATION */ |
868 | 868 | ||
869 | static inline int maybe_same_pte(pte_t pte, pte_t swp_pte) | ||
870 | { | ||
871 | #ifdef CONFIG_MEM_SOFT_DIRTY | ||
872 | /* | ||
873 | * When pte keeps soft dirty bit the pte generated | ||
874 | * from swap entry does not has it, still it's same | ||
875 | * pte from logical point of view. | ||
876 | */ | ||
877 | pte_t swp_pte_dirty = pte_swp_mksoft_dirty(swp_pte); | ||
878 | return pte_same(pte, swp_pte) || pte_same(pte, swp_pte_dirty); | ||
879 | #else | ||
880 | return pte_same(pte, swp_pte); | ||
881 | #endif | ||
882 | } | ||
883 | |||
869 | /* | 884 | /* |
870 | * No need to decide whether this PTE shares the swap entry with others, | 885 | * No need to decide whether this PTE shares the swap entry with others, |
871 | * just let do_wp_page work it out if a write is requested later - to | 886 | * just let do_wp_page work it out if a write is requested later - to |
@@ -892,7 +907,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd, | |||
892 | } | 907 | } |
893 | 908 | ||
894 | pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); | 909 | pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); |
895 | if (unlikely(!pte_same(*pte, swp_entry_to_pte(entry)))) { | 910 | if (unlikely(!maybe_same_pte(*pte, swp_entry_to_pte(entry)))) { |
896 | mem_cgroup_cancel_charge_swapin(memcg); | 911 | mem_cgroup_cancel_charge_swapin(memcg); |
897 | ret = 0; | 912 | ret = 0; |
898 | goto out; | 913 | goto out; |
@@ -947,7 +962,7 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd, | |||
947 | * swapoff spends a _lot_ of time in this loop! | 962 | * swapoff spends a _lot_ of time in this loop! |
948 | * Test inline before going to call unuse_pte. | 963 | * Test inline before going to call unuse_pte. |
949 | */ | 964 | */ |
950 | if (unlikely(pte_same(*pte, swp_pte))) { | 965 | if (unlikely(maybe_same_pte(*pte, swp_pte))) { |
951 | pte_unmap(pte); | 966 | pte_unmap(pte); |
952 | ret = unuse_pte(vma, pmd, addr, entry, page); | 967 | ret = unuse_pte(vma, pmd, addr, entry, page); |
953 | if (ret) | 968 | if (ret) |