KVM: s390: Clear storage keys
authorDominik Dingel <dingel@linux.vnet.ibm.com>
Wed, 29 Jan 2014 15:02:32 +0000 (16:02 +0100)
committerChristian Borntraeger <borntraeger@de.ibm.com>
Tue, 22 Apr 2014 07:36:24 +0000 (09:36 +0200)
page_table_reset_pgste() already does a complete page table walk to
reset the pgste. Enhance it to initialize the storage keys to
PAGE_DEFAULT_KEY if requested by the caller. This will be used
for lazy storage key handling. Also provide an empty stub for
!CONFIG_PGSTE

Lets adopt the current code (diag 308) to not clear the keys.

Signed-off-by: Dominik Dingel <dingel@linux.vnet.ibm.com>
Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
arch/s390/include/asm/pgalloc.h
arch/s390/kvm/diag.c
arch/s390/mm/pgtable.c

index 884017cbfa9fade412372f7f781e503b3f39513b..9e18a61d3df39c0c96f81032ae5f67ad427fa32c 100644 (file)
@@ -22,7 +22,8 @@ unsigned long *page_table_alloc(struct mm_struct *, unsigned long);
 void page_table_free(struct mm_struct *, unsigned long *);
 void page_table_free_rcu(struct mmu_gather *, unsigned long *);
 
-void page_table_reset_pgste(struct mm_struct *, unsigned long, unsigned long);
+void page_table_reset_pgste(struct mm_struct *, unsigned long, unsigned long,
+                           bool init_skey);
 int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
                          unsigned long key, bool nq);
 
index 08dfc839a6cfeeb3655f64d850ce1ed6e60d49cc..44dcfa8860b5330c74841e304e3e47b08db1a3e3 100644 (file)
@@ -169,15 +169,15 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
        switch (subcode) {
        case 0:
        case 1:
-               page_table_reset_pgste(current->mm, 0, TASK_SIZE);
+               page_table_reset_pgste(current->mm, 0, TASK_SIZE, false);
                return -EOPNOTSUPP;
        case 3:
                vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR;
-               page_table_reset_pgste(current->mm, 0, TASK_SIZE);
+               page_table_reset_pgste(current->mm, 0, TASK_SIZE, false);
                break;
        case 4:
                vcpu->run->s390_reset_flags = 0;
-               page_table_reset_pgste(current->mm, 0, TASK_SIZE);
+               page_table_reset_pgste(current->mm, 0, TASK_SIZE, false);
                break;
        default:
                return -EOPNOTSUPP;
index d7cfd57815fbe484283819a98745f7e26e6d1d47..be80f55a1b78fe6135e8a2643415fd9c2e42f72c 100644 (file)
@@ -883,8 +883,8 @@ static inline void page_table_free_pgste(unsigned long *table)
        __free_page(page);
 }
 
-static inline unsigned long page_table_reset_pte(struct mm_struct *mm,
-                       pmd_t *pmd, unsigned long addr, unsigned long end)
+static inline unsigned long page_table_reset_pte(struct mm_struct *mm, pmd_t *pmd,
+                       unsigned long addr, unsigned long end, bool init_skey)
 {
        pte_t *start_pte, *pte;
        spinlock_t *ptl;
@@ -895,6 +895,22 @@ static inline unsigned long page_table_reset_pte(struct mm_struct *mm,
        do {
                pgste = pgste_get_lock(pte);
                pgste_val(pgste) &= ~_PGSTE_GPS_USAGE_MASK;
+               if (init_skey) {
+                       unsigned long address;
+
+                       pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT |
+                                             PGSTE_GR_BIT | PGSTE_GC_BIT);
+
+                       /* skip invalid and not writable pages */
+                       if (pte_val(*pte) & _PAGE_INVALID ||
+                           !(pte_val(*pte) & _PAGE_WRITE)) {
+                               pgste_set_unlock(pte, pgste);
+                               continue;
+                       }
+
+                       address = pte_val(*pte) & PAGE_MASK;
+                       page_set_storage_key(address, PAGE_DEFAULT_KEY, 1);
+               }
                pgste_set_unlock(pte, pgste);
        } while (pte++, addr += PAGE_SIZE, addr != end);
        pte_unmap_unlock(start_pte, ptl);
@@ -902,8 +918,8 @@ static inline unsigned long page_table_reset_pte(struct mm_struct *mm,
        return addr;
 }
 
-static inline unsigned long page_table_reset_pmd(struct mm_struct *mm,
-                       pud_t *pud, unsigned long addr, unsigned long end)
+static inline unsigned long page_table_reset_pmd(struct mm_struct *mm, pud_t *pud,
+                       unsigned long addr, unsigned long end, bool init_skey)
 {
        unsigned long next;
        pmd_t *pmd;
@@ -913,14 +929,14 @@ static inline unsigned long page_table_reset_pmd(struct mm_struct *mm,
                next = pmd_addr_end(addr, end);
                if (pmd_none_or_clear_bad(pmd))
                        continue;
-               next = page_table_reset_pte(mm, pmd, addr, next);
+               next = page_table_reset_pte(mm, pmd, addr, next, init_skey);
        } while (pmd++, addr = next, addr != end);
 
        return addr;
 }
 
-static inline unsigned long page_table_reset_pud(struct mm_struct *mm,
-                       pgd_t *pgd, unsigned long addr, unsigned long end)
+static inline unsigned long page_table_reset_pud(struct mm_struct *mm, pgd_t *pgd,
+                       unsigned long addr, unsigned long end, bool init_skey)
 {
        unsigned long next;
        pud_t *pud;
@@ -930,14 +946,14 @@ static inline unsigned long page_table_reset_pud(struct mm_struct *mm,
                next = pud_addr_end(addr, end);
                if (pud_none_or_clear_bad(pud))
                        continue;
-               next = page_table_reset_pmd(mm, pud, addr, next);
+               next = page_table_reset_pmd(mm, pud, addr, next, init_skey);
        } while (pud++, addr = next, addr != end);
 
        return addr;
 }
 
-void page_table_reset_pgste(struct mm_struct *mm,
-                       unsigned long start, unsigned long end)
+void page_table_reset_pgste(struct mm_struct *mm, unsigned long start,
+                           unsigned long end, bool init_skey)
 {
        unsigned long addr, next;
        pgd_t *pgd;
@@ -949,7 +965,7 @@ void page_table_reset_pgste(struct mm_struct *mm,
                next = pgd_addr_end(addr, end);
                if (pgd_none_or_clear_bad(pgd))
                        continue;
-               next = page_table_reset_pud(mm, pgd, addr, next);
+               next = page_table_reset_pud(mm, pgd, addr, next, init_skey);
        } while (pgd++, addr = next, addr != end);
        up_read(&mm->mmap_sem);
 }
@@ -1011,6 +1027,11 @@ static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
        return NULL;
 }
 
+void page_table_reset_pgste(struct mm_struct *mm, unsigned long start,
+                           unsigned long end, bool init_skey)
+{
+}
+
 static inline void page_table_free_pgste(unsigned long *table)
 {
 }