From 152125b7a882df36a55a8eadbea6d0edf1461ee7 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 24 Jul 2014 11:03:41 +0200 Subject: s390/mm: implement dirty bits for large segment table entries The large segment table entry format has block of bits for the ACC/F values for the large page. These bits are valid only if another bit (AV bit 0x10000) of the segment table entry is set. The ACC/F bits do not have a meaning if the AV bit is off. This allows to put the THP splitting bit, the segment young bit and the new segment dirty bit into the ACC/F bits as long as the AV bit stays off. The dirty and young information is only available if the pmd is large. Signed-off-by: Martin Schwidefsky --- arch/s390/mm/hugetlbpage.c | 103 +++++++++++++++++++++------------------------ arch/s390/mm/pgtable.c | 3 ++ 2 files changed, 50 insertions(+), 56 deletions(-) (limited to 'arch/s390/mm') diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 0ff66a7e29bb..389bc17934b7 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -10,42 +10,33 @@ static inline pmd_t __pte_to_pmd(pte_t pte) { - int none, young, prot; pmd_t pmd; /* - * Convert encoding pte bits pmd bits - * .IR...wrdytp ..R...I...y. - * empty .10...000000 -> ..0...1...0. - * prot-none, clean, old .11...000001 -> ..0...1...1. - * prot-none, clean, young .11...000101 -> ..1...1...1. - * prot-none, dirty, old .10...001001 -> ..0...1...1. - * prot-none, dirty, young .10...001101 -> ..1...1...1. - * read-only, clean, old .11...010001 -> ..1...1...0. - * read-only, clean, young .01...010101 -> ..1...0...1. - * read-only, dirty, old .11...011001 -> ..1...1...0. - * read-only, dirty, young .01...011101 -> ..1...0...1. - * read-write, clean, old .11...110001 -> ..0...1...0. - * read-write, clean, young .01...110101 -> ..0...0...1. - * read-write, dirty, old .10...111001 -> ..0...1...0. - * read-write, dirty, young .00...111101 -> ..0...0...1. - * Huge ptes are dirty by definition, a clean pte is made dirty - * by the conversion. + * Convert encoding pte bits pmd bits + * .IR...wrdytp dy..R...I...wr + * empty .10...000000 -> 00..0...1...00 + * prot-none, clean, old .11...000001 -> 00..1...1...00 + * prot-none, clean, young .11...000101 -> 01..1...1...00 + * prot-none, dirty, old .10...001001 -> 10..1...1...00 + * prot-none, dirty, young .10...001101 -> 11..1...1...00 + * read-only, clean, old .11...010001 -> 00..1...1...01 + * read-only, clean, young .01...010101 -> 01..1...0...01 + * read-only, dirty, old .11...011001 -> 10..1...1...01 + * read-only, dirty, young .01...011101 -> 11..1...0...01 + * read-write, clean, old .11...110001 -> 00..0...1...11 + * read-write, clean, young .01...110101 -> 01..0...0...11 + * read-write, dirty, old .10...111001 -> 10..0...1...11 + * read-write, dirty, young .00...111101 -> 11..0...0...11 */ if (pte_present(pte)) { pmd_val(pmd) = pte_val(pte) & PAGE_MASK; - if (pte_val(pte) & _PAGE_INVALID) - pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID; - none = (pte_val(pte) & _PAGE_PRESENT) && - !(pte_val(pte) & _PAGE_READ) && - !(pte_val(pte) & _PAGE_WRITE); - prot = (pte_val(pte) & _PAGE_PROTECT) && - !(pte_val(pte) & _PAGE_WRITE); - young = pte_val(pte) & _PAGE_YOUNG; - if (none || young) - pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG; - if (prot || (none && young)) - pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; + pmd_val(pmd) |= (pte_val(pte) & _PAGE_READ) >> 4; + pmd_val(pmd) |= (pte_val(pte) & _PAGE_WRITE) >> 4; + pmd_val(pmd) |= (pte_val(pte) & _PAGE_INVALID) >> 5; + pmd_val(pmd) |= (pte_val(pte) & _PAGE_PROTECT); + pmd_val(pmd) |= (pte_val(pte) & _PAGE_DIRTY) << 10; + pmd_val(pmd) |= (pte_val(pte) & _PAGE_YOUNG) << 10; } else pmd_val(pmd) = _SEGMENT_ENTRY_INVALID; return pmd; @@ -56,34 +47,31 @@ static inline pte_t __pmd_to_pte(pmd_t pmd) pte_t pte; /* - * Convert encoding pmd bits pte bits - * ..R...I...y. .IR...wrdytp - * empty ..0...1...0. -> .10...000000 - * prot-none, old ..0...1...1. -> .10...001001 - * prot-none, young ..1...1...1. -> .10...001101 - * read-only, old ..1...1...0. -> .11...011001 - * read-only, young ..1...0...1. -> .01...011101 - * read-write, old ..0...1...0. -> .10...111001 - * read-write, young ..0...0...1. -> .00...111101 - * Huge ptes are dirty by definition + * Convert encoding pmd bits pte bits + * dy..R...I...wr .IR...wrdytp + * empty 00..0...1...00 -> .10...001100 + * prot-none, clean, old 00..0...1...00 -> .10...000001 + * prot-none, clean, young 01..0...1...00 -> .10...000101 + * prot-none, dirty, old 10..0...1...00 -> .10...001001 + * prot-none, dirty, young 11..0...1...00 -> .10...001101 + * read-only, clean, old 00..1...1...01 -> .11...010001 + * read-only, clean, young 01..1...1...01 -> .11...010101 + * read-only, dirty, old 10..1...1...01 -> .11...011001 + * read-only, dirty, young 11..1...1...01 -> .11...011101 + * read-write, clean, old 00..0...1...11 -> .10...110001 + * read-write, clean, young 01..0...1...11 -> .10...110101 + * read-write, dirty, old 10..0...1...11 -> .10...111001 + * read-write, dirty, young 11..0...1...11 -> .10...111101 */ if (pmd_present(pmd)) { - pte_val(pte) = _PAGE_PRESENT | _PAGE_LARGE | _PAGE_DIRTY | - (pmd_val(pmd) & PAGE_MASK); - if (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) - pte_val(pte) |= _PAGE_INVALID; - if (pmd_prot_none(pmd)) { - if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) - pte_val(pte) |= _PAGE_YOUNG; - } else { - pte_val(pte) |= _PAGE_READ; - if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) - pte_val(pte) |= _PAGE_PROTECT; - else - pte_val(pte) |= _PAGE_WRITE; - if (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) - pte_val(pte) |= _PAGE_YOUNG; - } + pte_val(pte) = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN_LARGE; + pte_val(pte) |= _PAGE_LARGE | _PAGE_PRESENT; + pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_READ) << 4; + pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_WRITE) << 4; + pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) << 5; + pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT); + pmd_val(pmd) |= (pte_val(pte) & _PAGE_DIRTY) << 10; + pmd_val(pmd) |= (pte_val(pte) & _PAGE_YOUNG) << 10; } else pte_val(pte) = _PAGE_INVALID; return pte; @@ -96,6 +84,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pmd = __pte_to_pmd(pte); if (!MACHINE_HAS_HPAGE) { + /* Emulated huge ptes loose the dirty and young bit */ pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; pmd_val(pmd) |= pte_page(pte)[1].index; } else @@ -113,6 +102,8 @@ pte_t huge_ptep_get(pte_t *ptep) origin = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN; pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; pmd_val(pmd) |= *(unsigned long *) origin; + /* Emulated huge ptes are young and dirty by definition */ + pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG | _SEGMENT_ENTRY_DIRTY; } return __pmd_to_pte(pmd); } diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index f90ad8592b36..19daa53a3da4 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -1433,6 +1433,9 @@ int pmdp_set_access_flags(struct vm_area_struct *vma, { VM_BUG_ON(address & ~HPAGE_PMD_MASK); + entry = pmd_mkyoung(entry); + if (dirty) + entry = pmd_mkdirty(entry); if (pmd_same(*pmdp, entry)) return 0; pmdp_invalidate(vma, address, pmdp); -- cgit v1.2.3-59-g8ed1b