s390/mm: Clear skeys for newly mapped huge guest pmds
authorJanosch Frank <frankja@linux.ibm.com>
Fri, 13 Jul 2018 10:28:26 +0000 (11:28 +0100)
committerJanosch Frank <frankja@linux.ibm.com>
Mon, 30 Jul 2018 10:20:18 +0000 (11:20 +0100)
Similarly to the pte skey handling, where we set the storage key to
the default key for each newly mapped pte, we have to also do that for
huge pmds.

With the PG_arch_1 flag we keep track if the area has already been
cleared of its skeys.

Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
Reviewed-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/hugetlb.h
arch/s390/mm/gmap.c
arch/s390/mm/hugetlbpage.c

index 9c5fc50204dd636cf9483d18f795c616a3b10b53..2d1afa58a4b6bf92c9fab01c021f7923dc199bc3 100644 (file)
@@ -37,7 +37,10 @@ static inline int prepare_hugepage_range(struct file *file,
        return 0;
 }
 
-#define arch_clear_hugepage_flags(page)                do { } while (0)
+static inline void arch_clear_hugepage_flags(struct page *page)
+{
+       clear_bit(PG_arch_1, &page->flags);
+}
 
 static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
                                  pte_t *ptep, unsigned long sz)
index a8e32e60226b42ab6ea77aec128e80fab4e940e7..a6738c0c4499aa73770eb2634dae7a30742e78f0 100644 (file)
@@ -2553,6 +2553,7 @@ static int __s390_enable_skey_hugetlb(pte_t *pte, unsigned long addr,
 {
        pmd_t *pmd = (pmd_t *)pte;
        unsigned long start, end;
+       struct page *page = pmd_page(*pmd);
 
        /*
         * The write check makes sure we do not set a key on shared
@@ -2567,6 +2568,7 @@ static int __s390_enable_skey_hugetlb(pte_t *pte, unsigned long addr,
        start = pmd_val(*pmd) & HPAGE_MASK;
        end = start + HPAGE_SIZE - 1;
        __storage_key_init_range(start, end);
+       set_bit(PG_arch_1, &page->flags);
        return 0;
 }
 
index e804090f4470fce93446cdde89cd5170d6c25644..b0246c705a192aac8462ee99f7a7e32081a9c6ce 100644 (file)
@@ -123,6 +123,29 @@ static inline pte_t __rste_to_pte(unsigned long rste)
        return pte;
 }
 
+static void clear_huge_pte_skeys(struct mm_struct *mm, unsigned long rste)
+{
+       struct page *page;
+       unsigned long size, paddr;
+
+       if (!mm_uses_skeys(mm) ||
+           rste & _SEGMENT_ENTRY_INVALID)
+               return;
+
+       if ((rste & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) {
+               page = pud_page(__pud(rste));
+               size = PUD_SIZE;
+               paddr = rste & PUD_MASK;
+       } else {
+               page = pmd_page(__pmd(rste));
+               size = PMD_SIZE;
+               paddr = rste & PMD_MASK;
+       }
+
+       if (!test_and_set_bit(PG_arch_1, &page->flags))
+               __storage_key_init_range(paddr, paddr + size - 1);
+}
+
 void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
                     pte_t *ptep, pte_t pte)
 {
@@ -137,6 +160,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
                rste |= _REGION_ENTRY_TYPE_R3 | _REGION3_ENTRY_LARGE;
        else
                rste |= _SEGMENT_ENTRY_LARGE;
+       clear_huge_pte_skeys(mm, rste);
        pte_val(*ptep) = rste;
 }