diff options
-rw-r--r-- | Documentation/filesystems/proc.txt | 7 | ||||
-rw-r--r-- | Documentation/vm/soft-dirty.txt | 36 | ||||
-rw-r--r-- | arch/Kconfig | 3 | ||||
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/pgtable.h | 24 | ||||
-rw-r--r-- | arch/x86/include/asm/pgtable_types.h | 12 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 47 | ||||
-rw-r--r-- | include/asm-generic/pgtable.h | 22 | ||||
-rw-r--r-- | mm/Kconfig | 12 | ||||
-rw-r--r-- | mm/huge_memory.c | 2 | ||||
-rw-r--r-- | mm/mremap.c | 2 |
11 files changed, 158 insertions, 10 deletions
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index fd8d0d594fc7..fcc22c982a25 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt | |||
@@ -473,7 +473,8 @@ This file is only present if the CONFIG_MMU kernel configuration option is | |||
473 | enabled. | 473 | enabled. |
474 | 474 | ||
475 | The /proc/PID/clear_refs is used to reset the PG_Referenced and ACCESSED/YOUNG | 475 | The /proc/PID/clear_refs is used to reset the PG_Referenced and ACCESSED/YOUNG |
476 | bits on both physical and virtual pages associated with a process. | 476 | bits on both physical and virtual pages associated with a process, and the |
477 | soft-dirty bit on pte (see Documentation/vm/soft-dirty.txt for details). | ||
477 | To clear the bits for all the pages associated with the process | 478 | To clear the bits for all the pages associated with the process |
478 | > echo 1 > /proc/PID/clear_refs | 479 | > echo 1 > /proc/PID/clear_refs |
479 | 480 | ||
@@ -482,6 +483,10 @@ To clear the bits for the anonymous pages associated with the process | |||
482 | 483 | ||
483 | To clear the bits for the file mapped pages associated with the process | 484 | To clear the bits for the file mapped pages associated with the process |
484 | > echo 3 > /proc/PID/clear_refs | 485 | > echo 3 > /proc/PID/clear_refs |
486 | |||
487 | To clear the soft-dirty bit | ||
488 | > echo 4 > /proc/PID/clear_refs | ||
489 | |||
485 | Any other value written to /proc/PID/clear_refs will have no effect. | 490 | Any other value written to /proc/PID/clear_refs will have no effect. |
486 | 491 | ||
487 | The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags | 492 | The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags |
diff --git a/Documentation/vm/soft-dirty.txt b/Documentation/vm/soft-dirty.txt new file mode 100644 index 000000000000..9a12a5956bc0 --- /dev/null +++ b/Documentation/vm/soft-dirty.txt | |||
@@ -0,0 +1,36 @@ | |||
1 | SOFT-DIRTY PTEs | ||
2 | |||
3 | The soft-dirty is a bit on a PTE which helps to track which pages a task | ||
4 | writes to. In order to do this tracking one should | ||
5 | |||
6 | 1. Clear soft-dirty bits from the task's PTEs. | ||
7 | |||
8 | This is done by writing "4" into the /proc/PID/clear_refs file of the | ||
9 | task in question. | ||
10 | |||
11 | 2. Wait some time. | ||
12 | |||
13 | 3. Read soft-dirty bits from the PTEs. | ||
14 | |||
15 | This is done by reading from the /proc/PID/pagemap. The bit 55 of the | ||
16 | 64-bit qword is the soft-dirty one. If set, the respective PTE was | ||
17 | written to since step 1. | ||
18 | |||
19 | |||
20 | Internally, to do this tracking, the writable bit is cleared from PTEs | ||
21 | when the soft-dirty bit is cleared. So, after this, when the task tries to | ||
22 | modify a page at some virtual address the #PF occurs and the kernel sets | ||
23 | the soft-dirty bit on the respective PTE. | ||
24 | |||
25 | Note, that although all the task's address space is marked as r/o after the | ||
26 | soft-dirty bits clear, the #PF-s that occur after that are processed fast. | ||
27 | This is so, since the pages are still mapped to physical memory, and thus all | ||
28 | the kernel does is finds this fact out and puts both writable and soft-dirty | ||
29 | bits on the PTE. | ||
30 | |||
31 | |||
32 | This feature is actively used by the checkpoint-restore project. You | ||
33 | can find more details about it on http://criu.org | ||
34 | |||
35 | |||
36 | -- Pavel Emelyanov, Apr 9, 2013 | ||
diff --git a/arch/Kconfig b/arch/Kconfig index a4429bcd609e..8d2ae24b9f4a 100644 --- a/arch/Kconfig +++ b/arch/Kconfig | |||
@@ -365,6 +365,9 @@ config HAVE_IRQ_TIME_ACCOUNTING | |||
365 | config HAVE_ARCH_TRANSPARENT_HUGEPAGE | 365 | config HAVE_ARCH_TRANSPARENT_HUGEPAGE |
366 | bool | 366 | bool |
367 | 367 | ||
368 | config HAVE_ARCH_SOFT_DIRTY | ||
369 | bool | ||
370 | |||
368 | config HAVE_MOD_ARCH_SPECIFIC | 371 | config HAVE_MOD_ARCH_SPECIFIC |
369 | bool | 372 | bool |
370 | help | 373 | help |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b094816a7e0f..10764a3d62cc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -102,6 +102,7 @@ config X86 | |||
102 | select HAVE_ARCH_SECCOMP_FILTER | 102 | select HAVE_ARCH_SECCOMP_FILTER |
103 | select BUILDTIME_EXTABLE_SORT | 103 | select BUILDTIME_EXTABLE_SORT |
104 | select GENERIC_CMOS_UPDATE | 104 | select GENERIC_CMOS_UPDATE |
105 | select HAVE_ARCH_SOFT_DIRTY | ||
105 | select CLOCKSOURCE_WATCHDOG | 106 | select CLOCKSOURCE_WATCHDOG |
106 | select GENERIC_CLOCKEVENTS | 107 | select GENERIC_CLOCKEVENTS |
107 | select ARCH_CLOCKSOURCE_DATA if X86_64 | 108 | select ARCH_CLOCKSOURCE_DATA if X86_64 |
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 5b0818bc8963..7dc305a46058 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h | |||
@@ -207,7 +207,7 @@ static inline pte_t pte_mkexec(pte_t pte) | |||
207 | 207 | ||
208 | static inline pte_t pte_mkdirty(pte_t pte) | 208 | static inline pte_t pte_mkdirty(pte_t pte) |
209 | { | 209 | { |
210 | return pte_set_flags(pte, _PAGE_DIRTY); | 210 | return pte_set_flags(pte, _PAGE_DIRTY | _PAGE_SOFT_DIRTY); |
211 | } | 211 | } |
212 | 212 | ||
213 | static inline pte_t pte_mkyoung(pte_t pte) | 213 | static inline pte_t pte_mkyoung(pte_t pte) |
@@ -271,7 +271,7 @@ static inline pmd_t pmd_wrprotect(pmd_t pmd) | |||
271 | 271 | ||
272 | static inline pmd_t pmd_mkdirty(pmd_t pmd) | 272 | static inline pmd_t pmd_mkdirty(pmd_t pmd) |
273 | { | 273 | { |
274 | return pmd_set_flags(pmd, _PAGE_DIRTY); | 274 | return pmd_set_flags(pmd, _PAGE_DIRTY | _PAGE_SOFT_DIRTY); |
275 | } | 275 | } |
276 | 276 | ||
277 | static inline pmd_t pmd_mkhuge(pmd_t pmd) | 277 | static inline pmd_t pmd_mkhuge(pmd_t pmd) |
@@ -294,6 +294,26 @@ static inline pmd_t pmd_mknotpresent(pmd_t pmd) | |||
294 | return pmd_clear_flags(pmd, _PAGE_PRESENT); | 294 | return pmd_clear_flags(pmd, _PAGE_PRESENT); |
295 | } | 295 | } |
296 | 296 | ||
297 | static inline int pte_soft_dirty(pte_t pte) | ||
298 | { | ||
299 | return pte_flags(pte) & _PAGE_SOFT_DIRTY; | ||
300 | } | ||
301 | |||
302 | static inline int pmd_soft_dirty(pmd_t pmd) | ||
303 | { | ||
304 | return pmd_flags(pmd) & _PAGE_SOFT_DIRTY; | ||
305 | } | ||
306 | |||
307 | static inline pte_t pte_mksoft_dirty(pte_t pte) | ||
308 | { | ||
309 | return pte_set_flags(pte, _PAGE_SOFT_DIRTY); | ||
310 | } | ||
311 | |||
312 | static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) | ||
313 | { | ||
314 | return pmd_set_flags(pmd, _PAGE_SOFT_DIRTY); | ||
315 | } | ||
316 | |||
297 | /* | 317 | /* |
298 | * Mask out unsupported bits in a present pgprot. Non-present pgprots | 318 | * Mask out unsupported bits in a present pgprot. Non-present pgprots |
299 | * can use those bits for other purposes, so leave them be. | 319 | * 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 e6423002c10b..c98ac63aae48 100644 --- a/arch/x86/include/asm/pgtable_types.h +++ b/arch/x86/include/asm/pgtable_types.h | |||
@@ -55,6 +55,18 @@ | |||
55 | #define _PAGE_HIDDEN (_AT(pteval_t, 0)) | 55 | #define _PAGE_HIDDEN (_AT(pteval_t, 0)) |
56 | #endif | 56 | #endif |
57 | 57 | ||
58 | /* | ||
59 | * The same hidden bit is used by kmemcheck, but since kmemcheck | ||
60 | * works on kernel pages while soft-dirty engine on user space, | ||
61 | * they do not conflict with each other. | ||
62 | */ | ||
63 | |||
64 | #ifdef CONFIG_MEM_SOFT_DIRTY | ||
65 | #define _PAGE_SOFT_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_HIDDEN) | ||
66 | #else | ||
67 | #define _PAGE_SOFT_DIRTY (_AT(pteval_t, 0)) | ||
68 | #endif | ||
69 | |||
58 | #if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE) | 70 | #if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE) |
59 | #define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX) | 71 | #define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX) |
60 | #else | 72 | #else |
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 39d641292579..a18e065c1c3e 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/rmap.h> | 11 | #include <linux/rmap.h> |
12 | #include <linux/swap.h> | 12 | #include <linux/swap.h> |
13 | #include <linux/swapops.h> | 13 | #include <linux/swapops.h> |
14 | #include <linux/mmu_notifier.h> | ||
14 | 15 | ||
15 | #include <asm/elf.h> | 16 | #include <asm/elf.h> |
16 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
@@ -692,13 +693,32 @@ enum clear_refs_types { | |||
692 | CLEAR_REFS_ALL = 1, | 693 | CLEAR_REFS_ALL = 1, |
693 | CLEAR_REFS_ANON, | 694 | CLEAR_REFS_ANON, |
694 | CLEAR_REFS_MAPPED, | 695 | CLEAR_REFS_MAPPED, |
696 | CLEAR_REFS_SOFT_DIRTY, | ||
695 | CLEAR_REFS_LAST, | 697 | CLEAR_REFS_LAST, |
696 | }; | 698 | }; |
697 | 699 | ||
698 | struct clear_refs_private { | 700 | struct clear_refs_private { |
699 | struct vm_area_struct *vma; | 701 | struct vm_area_struct *vma; |
702 | enum clear_refs_types type; | ||
700 | }; | 703 | }; |
701 | 704 | ||
705 | static inline void clear_soft_dirty(struct vm_area_struct *vma, | ||
706 | unsigned long addr, pte_t *pte) | ||
707 | { | ||
708 | #ifdef CONFIG_MEM_SOFT_DIRTY | ||
709 | /* | ||
710 | * The soft-dirty tracker uses #PF-s to catch writes | ||
711 | * to pages, so write-protect the pte as well. See the | ||
712 | * Documentation/vm/soft-dirty.txt for full description | ||
713 | * of how soft-dirty works. | ||
714 | */ | ||
715 | pte_t ptent = *pte; | ||
716 | ptent = pte_wrprotect(ptent); | ||
717 | ptent = pte_clear_flags(ptent, _PAGE_SOFT_DIRTY); | ||
718 | set_pte_at(vma->vm_mm, addr, pte, ptent); | ||
719 | #endif | ||
720 | } | ||
721 | |||
702 | static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, | 722 | static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, |
703 | unsigned long end, struct mm_walk *walk) | 723 | unsigned long end, struct mm_walk *walk) |
704 | { | 724 | { |
@@ -718,6 +738,11 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, | |||
718 | if (!pte_present(ptent)) | 738 | if (!pte_present(ptent)) |
719 | continue; | 739 | continue; |
720 | 740 | ||
741 | if (cp->type == CLEAR_REFS_SOFT_DIRTY) { | ||
742 | clear_soft_dirty(vma, addr, pte); | ||
743 | continue; | ||
744 | } | ||
745 | |||
721 | page = vm_normal_page(vma, addr, ptent); | 746 | page = vm_normal_page(vma, addr, ptent); |
722 | if (!page) | 747 | if (!page) |
723 | continue; | 748 | continue; |
@@ -759,6 +784,7 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, | |||
759 | mm = get_task_mm(task); | 784 | mm = get_task_mm(task); |
760 | if (mm) { | 785 | if (mm) { |
761 | struct clear_refs_private cp = { | 786 | struct clear_refs_private cp = { |
787 | .type = type, | ||
762 | }; | 788 | }; |
763 | struct mm_walk clear_refs_walk = { | 789 | struct mm_walk clear_refs_walk = { |
764 | .pmd_entry = clear_refs_pte_range, | 790 | .pmd_entry = clear_refs_pte_range, |
@@ -766,6 +792,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, | |||
766 | .private = &cp, | 792 | .private = &cp, |
767 | }; | 793 | }; |
768 | down_read(&mm->mmap_sem); | 794 | down_read(&mm->mmap_sem); |
795 | if (type == CLEAR_REFS_SOFT_DIRTY) | ||
796 | mmu_notifier_invalidate_range_start(mm, 0, -1); | ||
769 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | 797 | for (vma = mm->mmap; vma; vma = vma->vm_next) { |
770 | cp.vma = vma; | 798 | cp.vma = vma; |
771 | if (is_vm_hugetlb_page(vma)) | 799 | if (is_vm_hugetlb_page(vma)) |
@@ -786,6 +814,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, | |||
786 | walk_page_range(vma->vm_start, vma->vm_end, | 814 | walk_page_range(vma->vm_start, vma->vm_end, |
787 | &clear_refs_walk); | 815 | &clear_refs_walk); |
788 | } | 816 | } |
817 | if (type == CLEAR_REFS_SOFT_DIRTY) | ||
818 | mmu_notifier_invalidate_range_end(mm, 0, -1); | ||
789 | flush_tlb_mm(mm); | 819 | flush_tlb_mm(mm); |
790 | up_read(&mm->mmap_sem); | 820 | up_read(&mm->mmap_sem); |
791 | mmput(mm); | 821 | mmput(mm); |
@@ -827,6 +857,7 @@ struct pagemapread { | |||
827 | /* in "new" pagemap pshift bits are occupied with more status bits */ | 857 | /* in "new" pagemap pshift bits are occupied with more status bits */ |
828 | #define PM_STATUS2(v2, x) (__PM_PSHIFT(v2 ? x : PAGE_SHIFT)) | 858 | #define PM_STATUS2(v2, x) (__PM_PSHIFT(v2 ? x : PAGE_SHIFT)) |
829 | 859 | ||
860 | #define __PM_SOFT_DIRTY (1LL) | ||
830 | #define PM_PRESENT PM_STATUS(4LL) | 861 | #define PM_PRESENT PM_STATUS(4LL) |
831 | #define PM_SWAP PM_STATUS(2LL) | 862 | #define PM_SWAP PM_STATUS(2LL) |
832 | #define PM_FILE PM_STATUS(1LL) | 863 | #define PM_FILE PM_STATUS(1LL) |
@@ -868,6 +899,7 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | |||
868 | { | 899 | { |
869 | u64 frame, flags; | 900 | u64 frame, flags; |
870 | struct page *page = NULL; | 901 | struct page *page = NULL; |
902 | int flags2 = 0; | ||
871 | 903 | ||
872 | if (pte_present(pte)) { | 904 | if (pte_present(pte)) { |
873 | frame = pte_pfn(pte); | 905 | frame = pte_pfn(pte); |
@@ -888,13 +920,15 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | |||
888 | 920 | ||
889 | if (page && !PageAnon(page)) | 921 | if (page && !PageAnon(page)) |
890 | flags |= PM_FILE; | 922 | flags |= PM_FILE; |
923 | if (pte_soft_dirty(pte)) | ||
924 | flags2 |= __PM_SOFT_DIRTY; | ||
891 | 925 | ||
892 | *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, 0) | flags); | 926 | *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags); |
893 | } | 927 | } |
894 | 928 | ||
895 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 929 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
896 | static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | 930 | static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, |
897 | pmd_t pmd, int offset) | 931 | pmd_t pmd, int offset, int pmd_flags2) |
898 | { | 932 | { |
899 | /* | 933 | /* |
900 | * Currently pmd for thp is always present because thp can not be | 934 | * Currently pmd for thp is always present because thp can not be |
@@ -903,13 +937,13 @@ static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *p | |||
903 | */ | 937 | */ |
904 | if (pmd_present(pmd)) | 938 | if (pmd_present(pmd)) |
905 | *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) | 939 | *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) |
906 | | PM_STATUS2(pm->v2, 0) | PM_PRESENT); | 940 | | PM_STATUS2(pm->v2, pmd_flags2) | PM_PRESENT); |
907 | else | 941 | else |
908 | *pme = make_pme(PM_NOT_PRESENT(pm->v2)); | 942 | *pme = make_pme(PM_NOT_PRESENT(pm->v2)); |
909 | } | 943 | } |
910 | #else | 944 | #else |
911 | static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | 945 | static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, |
912 | pmd_t pmd, int offset) | 946 | pmd_t pmd, int offset, int pmd_flags2) |
913 | { | 947 | { |
914 | } | 948 | } |
915 | #endif | 949 | #endif |
@@ -926,12 +960,15 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
926 | /* find the first VMA at or above 'addr' */ | 960 | /* find the first VMA at or above 'addr' */ |
927 | vma = find_vma(walk->mm, addr); | 961 | vma = find_vma(walk->mm, addr); |
928 | if (vma && pmd_trans_huge_lock(pmd, vma) == 1) { | 962 | if (vma && pmd_trans_huge_lock(pmd, vma) == 1) { |
963 | int pmd_flags2; | ||
964 | |||
965 | pmd_flags2 = (pmd_soft_dirty(*pmd) ? __PM_SOFT_DIRTY : 0); | ||
929 | for (; addr != end; addr += PAGE_SIZE) { | 966 | for (; addr != end; addr += PAGE_SIZE) { |
930 | unsigned long offset; | 967 | unsigned long offset; |
931 | 968 | ||
932 | offset = (addr & ~PAGEMAP_WALK_MASK) >> | 969 | offset = (addr & ~PAGEMAP_WALK_MASK) >> |
933 | PAGE_SHIFT; | 970 | PAGE_SHIFT; |
934 | thp_pmd_to_pagemap_entry(&pme, pm, *pmd, offset); | 971 | thp_pmd_to_pagemap_entry(&pme, pm, *pmd, offset, pmd_flags2); |
935 | err = add_to_pagemap(addr, &pme, pm); | 972 | err = add_to_pagemap(addr, &pme, pm); |
936 | if (err) | 973 | if (err) |
937 | break; | 974 | break; |
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index b1836987d506..a7126d28f4cf 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h | |||
@@ -396,6 +396,28 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, | |||
396 | #define arch_start_context_switch(prev) do {} while (0) | 396 | #define arch_start_context_switch(prev) do {} while (0) |
397 | #endif | 397 | #endif |
398 | 398 | ||
399 | #ifndef CONFIG_HAVE_ARCH_SOFT_DIRTY | ||
400 | static inline int pte_soft_dirty(pte_t pte) | ||
401 | { | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static inline int pmd_soft_dirty(pmd_t pmd) | ||
406 | { | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static inline pte_t pte_mksoft_dirty(pte_t pte) | ||
411 | { | ||
412 | return pte; | ||
413 | } | ||
414 | |||
415 | static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) | ||
416 | { | ||
417 | return pmd; | ||
418 | } | ||
419 | #endif | ||
420 | |||
399 | #ifndef __HAVE_PFNMAP_TRACKING | 421 | #ifndef __HAVE_PFNMAP_TRACKING |
400 | /* | 422 | /* |
401 | * Interfaces that can be used by architecture code to keep track of | 423 | * Interfaces that can be used by architecture code to keep track of |
diff --git a/mm/Kconfig b/mm/Kconfig index f5e698e30d4a..7e28ecfa8aa4 100644 --- a/mm/Kconfig +++ b/mm/Kconfig | |||
@@ -477,3 +477,15 @@ config FRONTSWAP | |||
477 | and swap data is stored as normal on the matching swap device. | 477 | and swap data is stored as normal on the matching swap device. |
478 | 478 | ||
479 | If unsure, say Y to enable frontswap. | 479 | If unsure, say Y to enable frontswap. |
480 | |||
481 | config MEM_SOFT_DIRTY | ||
482 | bool "Track memory changes" | ||
483 | depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY | ||
484 | select PROC_PAGE_MONITOR | ||
485 | help | ||
486 | This option enables memory changes tracking by introducing a | ||
487 | soft-dirty bit on pte-s. This bit it set when someone writes | ||
488 | into a page just as regular dirty bit, but unlike the latter | ||
489 | it can be cleared by hands. | ||
490 | |||
491 | See Documentation/vm/soft-dirty.txt for more details. | ||
diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 362c329b83fe..d8b3b850150c 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c | |||
@@ -1429,7 +1429,7 @@ int move_huge_pmd(struct vm_area_struct *vma, struct vm_area_struct *new_vma, | |||
1429 | if (ret == 1) { | 1429 | if (ret == 1) { |
1430 | pmd = pmdp_get_and_clear(mm, old_addr, old_pmd); | 1430 | pmd = pmdp_get_and_clear(mm, old_addr, old_pmd); |
1431 | VM_BUG_ON(!pmd_none(*new_pmd)); | 1431 | VM_BUG_ON(!pmd_none(*new_pmd)); |
1432 | set_pmd_at(mm, new_addr, new_pmd, pmd); | 1432 | set_pmd_at(mm, new_addr, new_pmd, pmd_mksoft_dirty(pmd)); |
1433 | spin_unlock(&mm->page_table_lock); | 1433 | spin_unlock(&mm->page_table_lock); |
1434 | } | 1434 | } |
1435 | out: | 1435 | out: |
diff --git a/mm/mremap.c b/mm/mremap.c index 463a25705ac6..3708655378e9 100644 --- a/mm/mremap.c +++ b/mm/mremap.c | |||
@@ -126,7 +126,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, | |||
126 | continue; | 126 | continue; |
127 | pte = ptep_get_and_clear(mm, old_addr, old_pte); | 127 | pte = ptep_get_and_clear(mm, old_addr, old_pte); |
128 | pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr); | 128 | pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr); |
129 | set_pte_at(mm, new_addr, new_pte, pte); | 129 | set_pte_at(mm, new_addr, new_pte, pte_mksoft_dirty(pte)); |
130 | } | 130 | } |
131 | 131 | ||
132 | arch_leave_lazy_mmu_mode(); | 132 | arch_leave_lazy_mmu_mode(); |