Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[S390] merge page_test_dirty and page_clear_dirty

The page_clear_dirty primitive always sets the default storage key
which resets the access control bits and the fetch protection bit.
That will surprise a KVM guest that sets non-zero access control
bits or the fetch protection bit. Merge page_test_dirty and
page_clear_dirty back to a single function and only clear the
dirty bit from the storage key.

In addition move the function page_test_and_clear_dirty and
page_test_and_clear_young to page.h where they belong. This
requires to change the parameter from a struct page * to a page
frame number.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+68 -71
+50 -6
arch/s390/include/asm/page.h
··· 107 107 #define __pgd(x) ((pgd_t) { (x) } ) 108 108 #define __pgprot(x) ((pgprot_t) { (x) } ) 109 109 110 - static inline void 111 - page_set_storage_key(unsigned long addr, unsigned int skey, int mapped) 110 + static inline void page_set_storage_key(unsigned long addr, 111 + unsigned char skey, int mapped) 112 112 { 113 113 if (!mapped) 114 114 asm volatile(".insn rrf,0xb22b0000,%0,%1,8,0" ··· 117 117 asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); 118 118 } 119 119 120 - static inline unsigned int 121 - page_get_storage_key(unsigned long addr) 120 + static inline unsigned char 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 125 return skey; 126 + } 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); 127 171 } 128 172 129 173 struct page;
+9 -49
arch/s390/include/asm/pgtable.h
··· 373 373 #define _ASCE_USER_BITS (_ASCE_SPACE_SWITCH | _ASCE_PRIVATE_SPACE | \ 374 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 377 * Page protection definitions. 382 378 */ ··· 551 555 #endif 552 556 } 553 557 554 - /* forward declaration for SetPageUptodate in page-flags.h*/ 555 - static inline void page_clear_dirty(struct page *page, int mapped); 556 558 #include <linux/page-flags.h> 557 559 558 560 static inline void ptep_rcp_copy(pte_t *ptep) ··· 560 566 unsigned int skey; 561 567 unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE); 562 568 563 - skey = page_get_storage_key(page_to_phys(page)); 569 + skey = page_get_storage_key(pte_val(*ptep) >> PAGE_SHIFT); 564 570 if (skey & _PAGE_CHANGED) { 565 571 set_bit_simple(RCP_GC_BIT, pgste); 566 572 set_bit_simple(KVM_UD_BIT, pgste); ··· 754 760 { 755 761 int dirty; 756 762 unsigned long *pgste; 763 + unsigned long pfn; 757 764 struct page *page; 758 765 unsigned int skey; 759 766 ··· 762 767 return -EINVAL; 763 768 rcp_lock(ptep); 764 769 pgste = (unsigned long *) (ptep + PTRS_PER_PTE); 765 - page = virt_to_page(pte_val(*ptep)); 766 - skey = page_get_storage_key(page_to_phys(page)); 770 + pfn = pte_val(*ptep) >> PAGE_SHIFT; 771 + page = pfn_to_page(pfn); 772 + skey = page_get_storage_key(pfn); 767 773 if (skey & _PAGE_CHANGED) { 768 774 set_bit_simple(RCP_GC_BIT, pgste); 769 775 set_bit_simple(KVM_UD_BIT, pgste); ··· 775 779 } 776 780 dirty = test_and_clear_bit_simple(KVM_UD_BIT, pgste); 777 781 if (skey & _PAGE_CHANGED) 778 - page_clear_dirty(page, 1); 782 + page_set_storage_key(pfn, skey & ~_PAGE_CHANGED, 1); 779 783 rcp_unlock(ptep); 780 784 return dirty; 781 785 } ··· 786 790 unsigned long addr, pte_t *ptep) 787 791 { 788 792 #ifdef CONFIG_PGSTE 789 - unsigned long physpage; 793 + unsigned long pfn; 790 794 int young; 791 795 unsigned long *pgste; 792 796 793 797 if (!vma->vm_mm->context.has_pgste) 794 798 return 0; 795 - physpage = pte_val(*ptep) & PAGE_MASK; 799 + pfn = pte_val(*ptep) >> PAGE_SHIFT; 796 800 pgste = (unsigned long *) (ptep + PTRS_PER_PTE); 797 801 798 - young = ((page_get_storage_key(physpage) & _PAGE_REFERENCED) != 0); 802 + young = ((page_get_storage_key(pfn) & _PAGE_REFERENCED) != 0); 799 803 rcp_lock(ptep); 800 804 if (young) 801 805 set_bit_simple(RCP_GR_BIT, pgste); ··· 931 935 } \ 932 936 __changed; \ 933 937 }) 934 - 935 - /* 936 - * Test and clear dirty bit in storage key. 937 - * We can't clear the changed bit atomically. This is a potential 938 - * race against modification of the referenced bit. This function 939 - * should therefore only be called if it is not mapped in any 940 - * address space. 941 - */ 942 - #define __HAVE_ARCH_PAGE_TEST_DIRTY 943 - static inline int page_test_dirty(struct page *page) 944 - { 945 - return (page_get_storage_key(page_to_phys(page)) & _PAGE_CHANGED) != 0; 946 - } 947 - 948 - #define __HAVE_ARCH_PAGE_CLEAR_DIRTY 949 - static inline void page_clear_dirty(struct page *page, int mapped) 950 - { 951 - page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY, mapped); 952 - } 953 - 954 - /* 955 - * Test and clear referenced bit in storage key. 956 - */ 957 - #define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG 958 - static inline int page_test_and_clear_young(struct page *page) 959 - { 960 - unsigned long physpage = page_to_phys(page); 961 - int ccode; 962 - 963 - asm volatile( 964 - " rrbe 0,%1\n" 965 - " ipm %0\n" 966 - " srl %0,28\n" 967 - : "=d" (ccode) : "a" (physpage) : "cc" ); 968 - return ccode & 2; 969 - } 970 938 971 939 /* 972 940 * Conversion functions: convert a page and protection to a page entry,
+4 -8
include/asm-generic/pgtable.h
··· 184 184 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 185 185 #endif 186 186 187 - #ifndef __HAVE_ARCH_PAGE_TEST_DIRTY 188 - #define page_test_dirty(page) (0) 187 + #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY 188 + #define page_test_and_clear_dirty(pfn, mapped) (0) 189 189 #endif 190 190 191 - #ifndef __HAVE_ARCH_PAGE_CLEAR_DIRTY 192 - #define page_clear_dirty(page, mapped) do { } while (0) 193 - #endif 194 - 195 - #ifndef __HAVE_ARCH_PAGE_TEST_DIRTY 191 + #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY 196 192 #define pte_maybe_dirty(pte) pte_dirty(pte) 197 193 #else 198 194 #define pte_maybe_dirty(pte) (1) 199 195 #endif 200 196 201 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 199 #endif 204 200 205 201 #ifndef __HAVE_ARCH_PGD_OFFSET_GATE
+1 -1
include/linux/page-flags.h
··· 308 308 { 309 309 #ifdef CONFIG_S390 310 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 312 #else 313 313 /* 314 314 * Memory barrier must be issued before setting the PG_uptodate bit,
+4 -7
mm/rmap.c
··· 719 719 unlock_page(page); 720 720 } 721 721 out: 722 - if (page_test_and_clear_young(page)) 722 + if (page_test_and_clear_young(page_to_pfn(page))) 723 723 referenced++; 724 724 725 725 return referenced; ··· 785 785 struct address_space *mapping = page_mapping(page); 786 786 if (mapping) { 787 787 ret = page_mkclean_file(mapping, page); 788 - if (page_test_dirty(page)) { 789 - page_clear_dirty(page, 1); 788 + if (page_test_and_clear_dirty(page_to_pfn(page), 1)) 790 789 ret = 1; 791 - } 792 790 } 793 791 } 794 792 ··· 979 981 * not if it's in swapcache - there might be another pte slot 980 982 * containing the swap entry, but page not yet written to swap. 981 983 */ 982 - if ((!PageAnon(page) || PageSwapCache(page)) && page_test_dirty(page)) { 983 - page_clear_dirty(page, 1); 984 + if ((!PageAnon(page) || PageSwapCache(page)) && 985 + page_test_and_clear_dirty(page_to_pfn(page), 1)) 984 986 set_page_dirty(page); 985 - } 986 987 /* 987 988 * Hugepages are not counted in NR_ANON_PAGES nor NR_FILE_MAPPED 988 989 * and not charged by memcg for now.