diff options
| -rw-r--r-- | arch/s390/include/asm/page.h | 56 | ||||
| -rw-r--r-- | arch/s390/include/asm/pgtable.h | 58 | ||||
| -rw-r--r-- | include/asm-generic/pgtable.h | 12 | ||||
| -rw-r--r-- | include/linux/page-flags.h | 2 | ||||
| -rw-r--r-- | mm/rmap.c | 11 |
5 files changed, 68 insertions, 71 deletions
diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h index 3c987e9ec8d6..81ee2776088d 100644 --- a/arch/s390/include/asm/page.h +++ b/arch/s390/include/asm/page.h | |||
| @@ -107,8 +107,8 @@ typedef pte_t *pgtable_t; | |||
| 107 | #define __pgd(x) ((pgd_t) { (x) } ) | 107 | #define __pgd(x) ((pgd_t) { (x) } ) |
| 108 | #define __pgprot(x) ((pgprot_t) { (x) } ) | 108 | #define __pgprot(x) ((pgprot_t) { (x) } ) |
| 109 | 109 | ||
| 110 | static inline void | 110 | static inline void page_set_storage_key(unsigned long addr, |
| 111 | page_set_storage_key(unsigned long addr, unsigned int skey, int mapped) | 111 | unsigned char skey, int mapped) |
| 112 | { | 112 | { |
| 113 | if (!mapped) | 113 | if (!mapped) |
| 114 | asm volatile(".insn rrf,0xb22b0000,%0,%1,8,0" | 114 | asm volatile(".insn rrf,0xb22b0000,%0,%1,8,0" |
| @@ -117,15 +117,59 @@ page_set_storage_key(unsigned long addr, unsigned int skey, int mapped) | |||
| 117 | asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); | 117 | asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | static inline unsigned int | 120 | static inline unsigned char page_get_storage_key(unsigned long addr) |
| 121 | page_get_storage_key(unsigned long addr) | ||
| 122 | { | 121 | { |
| 123 | unsigned int skey; | 122 | unsigned char skey; |
| 124 | 123 | ||
| 125 | asm volatile("iske %0,%1" : "=d" (skey) : "a" (addr), "0" (0)); | 124 | asm volatile("iske %0,%1" : "=d" (skey) : "a" (addr)); |
| 126 | return skey; | 125 | return skey; |
| 127 | } | 126 | } |
| 128 | 127 | ||
| 128 | static inline int page_reset_referenced(unsigned long addr) | ||
| 129 | { | ||
| 130 | unsigned int ipm; | ||
| 131 | |||
| 132 | asm volatile( | ||
| 133 | " rrbe 0,%1\n" | ||
| 134 | " ipm %0\n" | ||
| 135 | : "=d" (ipm) : "a" (addr) : "cc"); | ||
| 136 | return !!(ipm & 0x20000000); | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Bits int the storage key */ | ||
| 140 | #define _PAGE_CHANGED 0x02 /* HW changed bit */ | ||
| 141 | #define _PAGE_REFERENCED 0x04 /* HW referenced bit */ | ||
| 142 | #define _PAGE_FP_BIT 0x08 /* HW fetch protection bit */ | ||
| 143 | #define _PAGE_ACC_BITS 0xf0 /* HW access control bits */ | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Test and clear dirty bit in storage key. | ||
| 147 | * We can't clear the changed bit atomically. This is a potential | ||
| 148 | * race against modification of the referenced bit. This function | ||
| 149 | * should therefore only be called if it is not mapped in any | ||
| 150 | * address space. | ||
| 151 | */ | ||
| 152 | #define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY | ||
| 153 | static inline int page_test_and_clear_dirty(unsigned long pfn, int mapped) | ||
| 154 | { | ||
| 155 | unsigned char skey; | ||
| 156 | |||
| 157 | skey = page_get_storage_key(pfn << PAGE_SHIFT); | ||
| 158 | if (!(skey & _PAGE_CHANGED)) | ||
| 159 | return 0; | ||
| 160 | page_set_storage_key(pfn << PAGE_SHIFT, skey & ~_PAGE_CHANGED, mapped); | ||
| 161 | return 1; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* | ||
| 165 | * Test and clear referenced bit in storage key. | ||
| 166 | */ | ||
| 167 | #define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG | ||
| 168 | static inline int page_test_and_clear_young(unsigned long pfn) | ||
| 169 | { | ||
| 170 | return page_reset_referenced(pfn << PAGE_SHIFT); | ||
| 171 | } | ||
| 172 | |||
| 129 | struct page; | 173 | struct page; |
| 130 | void arch_free_page(struct page *page, int order); | 174 | void arch_free_page(struct page *page, int order); |
| 131 | void arch_alloc_page(struct page *page, int order); | 175 | void arch_alloc_page(struct page *page, int order); |
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 763620ec7925..4ca4dd2b329a 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
| @@ -373,10 +373,6 @@ extern unsigned long VMALLOC_START; | |||
| 373 | #define _ASCE_USER_BITS (_ASCE_SPACE_SWITCH | _ASCE_PRIVATE_SPACE | \ | 373 | #define _ASCE_USER_BITS (_ASCE_SPACE_SWITCH | _ASCE_PRIVATE_SPACE | \ |
| 374 | _ASCE_ALT_EVENT) | 374 | _ASCE_ALT_EVENT) |
| 375 | 375 | ||
| 376 | /* Bits int the storage key */ | ||
| 377 | #define _PAGE_CHANGED 0x02 /* HW changed bit */ | ||
| 378 | #define _PAGE_REFERENCED 0x04 /* HW referenced bit */ | ||
| 379 | |||
| 380 | /* | 376 | /* |
| 381 | * Page protection definitions. | 377 | * Page protection definitions. |
| 382 | */ | 378 | */ |
| @@ -555,8 +551,6 @@ static inline void rcp_unlock(pte_t *ptep) | |||
| 555 | #endif | 551 | #endif |
| 556 | } | 552 | } |
| 557 | 553 | ||
| 558 | /* forward declaration for SetPageUptodate in page-flags.h*/ | ||
| 559 | static inline void page_clear_dirty(struct page *page, int mapped); | ||
| 560 | #include <linux/page-flags.h> | 554 | #include <linux/page-flags.h> |
| 561 | 555 | ||
| 562 | static inline void ptep_rcp_copy(pte_t *ptep) | 556 | static inline void ptep_rcp_copy(pte_t *ptep) |
| @@ -566,7 +560,7 @@ static inline void ptep_rcp_copy(pte_t *ptep) | |||
| 566 | unsigned int skey; | 560 | unsigned int skey; |
| 567 | unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE); | 561 | unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE); |
| 568 | 562 | ||
| 569 | skey = page_get_storage_key(page_to_phys(page)); | 563 | skey = page_get_storage_key(pte_val(*ptep) >> PAGE_SHIFT); |
| 570 | if (skey & _PAGE_CHANGED) { | 564 | if (skey & _PAGE_CHANGED) { |
| 571 | set_bit_simple(RCP_GC_BIT, pgste); | 565 | set_bit_simple(RCP_GC_BIT, pgste); |
| 572 | set_bit_simple(KVM_UD_BIT, pgste); | 566 | set_bit_simple(KVM_UD_BIT, pgste); |
| @@ -760,6 +754,7 @@ static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm, | |||
| 760 | { | 754 | { |
| 761 | int dirty; | 755 | int dirty; |
| 762 | unsigned long *pgste; | 756 | unsigned long *pgste; |
| 757 | unsigned long pfn; | ||
| 763 | struct page *page; | 758 | struct page *page; |
| 764 | unsigned int skey; | 759 | unsigned int skey; |
| 765 | 760 | ||
| @@ -767,8 +762,9 @@ static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm, | |||
| 767 | return -EINVAL; | 762 | return -EINVAL; |
| 768 | rcp_lock(ptep); | 763 | rcp_lock(ptep); |
| 769 | pgste = (unsigned long *) (ptep + PTRS_PER_PTE); | 764 | pgste = (unsigned long *) (ptep + PTRS_PER_PTE); |
| 770 | page = virt_to_page(pte_val(*ptep)); | 765 | pfn = pte_val(*ptep) >> PAGE_SHIFT; |
| 771 | skey = page_get_storage_key(page_to_phys(page)); | 766 | page = pfn_to_page(pfn); |
| 767 | skey = page_get_storage_key(pfn); | ||
| 772 | if (skey & _PAGE_CHANGED) { | 768 | if (skey & _PAGE_CHANGED) { |
| 773 | set_bit_simple(RCP_GC_BIT, pgste); | 769 | set_bit_simple(RCP_GC_BIT, pgste); |
| 774 | set_bit_simple(KVM_UD_BIT, pgste); | 770 | set_bit_simple(KVM_UD_BIT, pgste); |
| @@ -779,7 +775,7 @@ static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm, | |||
| 779 | } | 775 | } |
| 780 | dirty = test_and_clear_bit_simple(KVM_UD_BIT, pgste); | 776 | dirty = test_and_clear_bit_simple(KVM_UD_BIT, pgste); |
| 781 | if (skey & _PAGE_CHANGED) | 777 | if (skey & _PAGE_CHANGED) |
| 782 | page_clear_dirty(page, 1); | 778 | page_set_storage_key(pfn, skey & ~_PAGE_CHANGED, 1); |
| 783 | rcp_unlock(ptep); | 779 | rcp_unlock(ptep); |
| 784 | return dirty; | 780 | return dirty; |
| 785 | } | 781 | } |
| @@ -790,16 +786,16 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, | |||
| 790 | unsigned long addr, pte_t *ptep) | 786 | unsigned long addr, pte_t *ptep) |
| 791 | { | 787 | { |
| 792 | #ifdef CONFIG_PGSTE | 788 | #ifdef CONFIG_PGSTE |
| 793 | unsigned long physpage; | 789 | unsigned long pfn; |
| 794 | int young; | 790 | int young; |
| 795 | unsigned long *pgste; | 791 | unsigned long *pgste; |
| 796 | 792 | ||
| 797 | if (!vma->vm_mm->context.has_pgste) | 793 | if (!vma->vm_mm->context.has_pgste) |
| 798 | return 0; | 794 | return 0; |
| 799 | physpage = pte_val(*ptep) & PAGE_MASK; | 795 | pfn = pte_val(*ptep) >> PAGE_SHIFT; |
| 800 | pgste = (unsigned long *) (ptep + PTRS_PER_PTE); | 796 | pgste = (unsigned long *) (ptep + PTRS_PER_PTE); |
| 801 | 797 | ||
| 802 | young = ((page_get_storage_key(physpage) & _PAGE_REFERENCED) != 0); | 798 | young = ((page_get_storage_key(pfn) & _PAGE_REFERENCED) != 0); |
| 803 | rcp_lock(ptep); | 799 | rcp_lock(ptep); |
| 804 | if (young) | 800 | if (young) |
| 805 | set_bit_simple(RCP_GR_BIT, pgste); | 801 | set_bit_simple(RCP_GR_BIT, pgste); |
| @@ -937,42 +933,6 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, | |||
| 937 | }) | 933 | }) |
| 938 | 934 | ||
| 939 | /* | 935 | /* |
| 940 | * Test and clear dirty bit in storage key. | ||
| 941 | * We can't clear the changed bit atomically. This is a potential | ||
| 942 | * race against modification of the referenced bit. This function | ||
| 943 | * should therefore only be called if it is not mapped in any | ||
| 944 | * address space. | ||
| 945 | */ | ||
| 946 | #define __HAVE_ARCH_PAGE_TEST_DIRTY | ||
| 947 | static inline int page_test_dirty(struct page *page) | ||
| 948 | { | ||
| 949 | return (page_get_storage_key(page_to_phys(page)) & _PAGE_CHANGED) != 0; | ||
| 950 | } | ||
| 951 | |||
| 952 | #define __HAVE_ARCH_PAGE_CLEAR_DIRTY | ||
| 953 | static inline void page_clear_dirty(struct page *page, int mapped) | ||
| 954 | { | ||
| 955 | page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY, mapped); | ||
| 956 | } | ||
| 957 | |||
| 958 | /* | ||
| 959 | * Test and clear referenced bit in storage key. | ||
| 960 | */ | ||
| 961 | #define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG | ||
| 962 | static inline int page_test_and_clear_young(struct page *page) | ||
| 963 | { | ||
| 964 | unsigned long physpage = page_to_phys(page); | ||
| 965 | int ccode; | ||
| 966 | |||
| 967 | asm volatile( | ||
| 968 | " rrbe 0,%1\n" | ||
| 969 | " ipm %0\n" | ||
| 970 | " srl %0,28\n" | ||
| 971 | : "=d" (ccode) : "a" (physpage) : "cc" ); | ||
| 972 | return ccode & 2; | ||
| 973 | } | ||
| 974 | |||
| 975 | /* | ||
| 976 | * Conversion functions: convert a page and protection to a page entry, | 936 | * Conversion functions: convert a page and protection to a page entry, |
| 977 | * and a page entry and page directory to the page they refer to. | 937 | * and a page entry and page directory to the page they refer to. |
| 978 | */ | 938 | */ |
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index b4bfe338ea0e..e9b8e5926bef 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h | |||
| @@ -184,22 +184,18 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b) | |||
| 184 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | 184 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ |
| 185 | #endif | 185 | #endif |
| 186 | 186 | ||
| 187 | #ifndef __HAVE_ARCH_PAGE_TEST_DIRTY | 187 | #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY |
| 188 | #define page_test_dirty(page) (0) | 188 | #define page_test_and_clear_dirty(pfn, mapped) (0) |
| 189 | #endif | 189 | #endif |
| 190 | 190 | ||
| 191 | #ifndef __HAVE_ARCH_PAGE_CLEAR_DIRTY | 191 | #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY |
| 192 | #define page_clear_dirty(page, mapped) do { } while (0) | ||
| 193 | #endif | ||
| 194 | |||
| 195 | #ifndef __HAVE_ARCH_PAGE_TEST_DIRTY | ||
| 196 | #define pte_maybe_dirty(pte) pte_dirty(pte) | 192 | #define pte_maybe_dirty(pte) pte_dirty(pte) |
| 197 | #else | 193 | #else |
| 198 | #define pte_maybe_dirty(pte) (1) | 194 | #define pte_maybe_dirty(pte) (1) |
| 199 | #endif | 195 | #endif |
| 200 | 196 | ||
| 201 | #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG | 197 | #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG |
| 202 | #define page_test_and_clear_young(page) (0) | 198 | #define page_test_and_clear_young(pfn) (0) |
| 203 | #endif | 199 | #endif |
| 204 | 200 | ||
| 205 | #ifndef __HAVE_ARCH_PGD_OFFSET_GATE | 201 | #ifndef __HAVE_ARCH_PGD_OFFSET_GATE |
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 811183de1ef5..79a6700b7162 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h | |||
| @@ -308,7 +308,7 @@ static inline void SetPageUptodate(struct page *page) | |||
| 308 | { | 308 | { |
| 309 | #ifdef CONFIG_S390 | 309 | #ifdef CONFIG_S390 |
| 310 | if (!test_and_set_bit(PG_uptodate, &page->flags)) | 310 | if (!test_and_set_bit(PG_uptodate, &page->flags)) |
| 311 | page_clear_dirty(page, 0); | 311 | page_set_storage_key(page_to_pfn(page), PAGE_DEFAULT_KEY, 0); |
| 312 | #else | 312 | #else |
| 313 | /* | 313 | /* |
| 314 | * Memory barrier must be issued before setting the PG_uptodate bit, | 314 | * Memory barrier must be issued before setting the PG_uptodate bit, |
| @@ -719,7 +719,7 @@ int page_referenced(struct page *page, | |||
| 719 | unlock_page(page); | 719 | unlock_page(page); |
| 720 | } | 720 | } |
| 721 | out: | 721 | out: |
| 722 | if (page_test_and_clear_young(page)) | 722 | if (page_test_and_clear_young(page_to_pfn(page))) |
| 723 | referenced++; | 723 | referenced++; |
| 724 | 724 | ||
| 725 | return referenced; | 725 | return referenced; |
| @@ -785,10 +785,8 @@ int page_mkclean(struct page *page) | |||
| 785 | struct address_space *mapping = page_mapping(page); | 785 | struct address_space *mapping = page_mapping(page); |
| 786 | if (mapping) { | 786 | if (mapping) { |
| 787 | ret = page_mkclean_file(mapping, page); | 787 | ret = page_mkclean_file(mapping, page); |
| 788 | if (page_test_dirty(page)) { | 788 | if (page_test_and_clear_dirty(page_to_pfn(page), 1)) |
| 789 | page_clear_dirty(page, 1); | ||
| 790 | ret = 1; | 789 | ret = 1; |
| 791 | } | ||
| 792 | } | 790 | } |
| 793 | } | 791 | } |
| 794 | 792 | ||
| @@ -981,10 +979,9 @@ void page_remove_rmap(struct page *page) | |||
| 981 | * not if it's in swapcache - there might be another pte slot | 979 | * not if it's in swapcache - there might be another pte slot |
| 982 | * containing the swap entry, but page not yet written to swap. | 980 | * containing the swap entry, but page not yet written to swap. |
| 983 | */ | 981 | */ |
| 984 | if ((!PageAnon(page) || PageSwapCache(page)) && page_test_dirty(page)) { | 982 | if ((!PageAnon(page) || PageSwapCache(page)) && |
| 985 | page_clear_dirty(page, 1); | 983 | page_test_and_clear_dirty(page_to_pfn(page), 1)) |
| 986 | set_page_dirty(page); | 984 | set_page_dirty(page); |
| 987 | } | ||
| 988 | /* | 985 | /* |
| 989 | * Hugepages are not counted in NR_ANON_PAGES nor NR_FILE_MAPPED | 986 | * Hugepages are not counted in NR_ANON_PAGES nor NR_FILE_MAPPED |
| 990 | * and not charged by memcg for now. | 987 | * and not charged by memcg for now. |
