s390/kvm: Add PGSTE manipulation functions
authorClaudio Imbrenda <imbrenda@linux.vnet.ibm.com>
Thu, 20 Apr 2017 08:03:45 +0000 (10:03 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 20 Apr 2017 11:33:08 +0000 (13:33 +0200)
Add PGSTE manipulation functions:
* set_pgste_bits sets specific bits in a PGSTE
* get_pgste returns the whole PGSTE
* pgste_perform_essa manipulates a PGSTE to set specific storage states
* ESSA_[SG]ET_* macros used to indicate the action for manipulate_pgste

Signed-off-by: Claudio Imbrenda <imbrenda@linux.vnet.ibm.com>
Reviewed-by: Janosch Frank <frankja@de.ibm.com>
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/page-states.h [new file with mode: 0644]
arch/s390/include/asm/pgtable.h
arch/s390/mm/page-states.c
arch/s390/mm/pgtable.c

diff --git a/arch/s390/include/asm/page-states.h b/arch/s390/include/asm/page-states.h
new file mode 100644 (file)
index 0000000..42267a2
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ *    Copyright IBM Corp. 2017
+ *    Author(s): Claudio Imbrenda <imbrenda@linux.vnet.ibm.com>
+ */
+
+#ifndef PAGE_STATES_H
+#define PAGE_STATES_H
+
+#define ESSA_GET_STATE                 0
+#define ESSA_SET_STABLE                        1
+#define ESSA_SET_UNUSED                        2
+#define ESSA_SET_VOLATILE              3
+#define ESSA_SET_POT_VOLATILE          4
+#define ESSA_SET_STABLE_RESIDENT       5
+#define ESSA_SET_STABLE_IF_RESIDENT    6
+
+#define ESSA_MAX       ESSA_SET_STABLE_IF_RESIDENT
+
+#endif
index 93e37b12e88237766821369e19827e5e2d844a1b..bdabbbb3e925ebcfee6059cbb10279a9761e3c08 100644 (file)
@@ -372,10 +372,12 @@ static inline int is_module_addr(void *addr)
 #define PGSTE_VSIE_BIT 0x0000200000000000UL    /* ref'd in a shadow table */
 
 /* Guest Page State used for virtualization */
-#define _PGSTE_GPS_ZERO                0x0000000080000000UL
-#define _PGSTE_GPS_USAGE_MASK  0x0000000003000000UL
-#define _PGSTE_GPS_USAGE_STABLE 0x0000000000000000UL
-#define _PGSTE_GPS_USAGE_UNUSED 0x0000000001000000UL
+#define _PGSTE_GPS_ZERO                        0x0000000080000000UL
+#define _PGSTE_GPS_USAGE_MASK          0x0000000003000000UL
+#define _PGSTE_GPS_USAGE_STABLE                0x0000000000000000UL
+#define _PGSTE_GPS_USAGE_UNUSED                0x0000000001000000UL
+#define _PGSTE_GPS_USAGE_POT_VOLATILE  0x0000000002000000UL
+#define _PGSTE_GPS_USAGE_VOLATILE      _PGSTE_GPS_USAGE_MASK
 
 /*
  * A user page table pointer has the space-switch-event bit, the
@@ -1041,6 +1043,12 @@ int reset_guest_reference_bit(struct mm_struct *mm, unsigned long addr);
 int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,
                          unsigned char *key);
 
+int set_pgste_bits(struct mm_struct *mm, unsigned long addr,
+                               unsigned long bits, unsigned long value);
+int get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep);
+int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc,
+                       unsigned long *oldpte, unsigned long *oldpgste);
+
 /*
  * Certain architectures need to do special things when PTEs
  * within a page table are directly modified.  Thus, the following
index 3330ea124eec32af519f0a496a471b3732cb2101..69a7b01ae746f02c3ab6dea4bf158e8fe04efd41 100644 (file)
@@ -13,8 +13,7 @@
 #include <linux/gfp.h>
 #include <linux/init.h>
 
-#define ESSA_SET_STABLE                1
-#define ESSA_SET_UNUSED                2
+#include <asm/page-states.h>
 
 static int cmma_flag = 1;
 
index 463e5ef02304bb99c352c8468c7cf0ce57f0ba4e..947b66a5cdba730573a4f0f4bd299a6d7b7b52bd 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/tlb.h>
 #include <asm/tlbflush.h>
 #include <asm/mmu_context.h>
+#include <asm/page-states.h>
 
 static inline pte_t ptep_flush_direct(struct mm_struct *mm,
                                      unsigned long addr, pte_t *ptep)
@@ -787,4 +788,156 @@ int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,
        return 0;
 }
 EXPORT_SYMBOL(get_guest_storage_key);
+
+/**
+ * pgste_perform_essa - perform ESSA actions on the PGSTE.
+ * @mm: the memory context. It must have PGSTEs, no check is performed here!
+ * @hva: the host virtual address of the page whose PGSTE is to be processed
+ * @orc: the specific action to perform, see the ESSA_SET_* macros.
+ * @oldpte: the PTE will be saved there if the pointer is not NULL.
+ * @oldpgste: the old PGSTE will be saved there if the pointer is not NULL.
+ *
+ * Return: 1 if the page is to be added to the CBRL, otherwise 0,
+ *        or < 0 in case of error. -EINVAL is returned for invalid values
+ *        of orc, -EFAULT for invalid addresses.
+ */
+int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc,
+                       unsigned long *oldpte, unsigned long *oldpgste)
+{
+       unsigned long pgstev;
+       spinlock_t *ptl;
+       pgste_t pgste;
+       pte_t *ptep;
+       int res = 0;
+
+       WARN_ON_ONCE(orc > ESSA_MAX);
+       if (unlikely(orc > ESSA_MAX))
+               return -EINVAL;
+       ptep = get_locked_pte(mm, hva, &ptl);
+       if (unlikely(!ptep))
+               return -EFAULT;
+       pgste = pgste_get_lock(ptep);
+       pgstev = pgste_val(pgste);
+       if (oldpte)
+               *oldpte = pte_val(*ptep);
+       if (oldpgste)
+               *oldpgste = pgstev;
+
+       switch (orc) {
+       case ESSA_GET_STATE:
+               break;
+       case ESSA_SET_STABLE:
+               pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+               pgstev |= _PGSTE_GPS_USAGE_STABLE;
+               break;
+       case ESSA_SET_UNUSED:
+               pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+               pgstev |= _PGSTE_GPS_USAGE_UNUSED;
+               if (pte_val(*ptep) & _PAGE_INVALID)
+                       res = 1;
+               break;
+       case ESSA_SET_VOLATILE:
+               pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+               pgstev |= _PGSTE_GPS_USAGE_VOLATILE;
+               if (pte_val(*ptep) & _PAGE_INVALID)
+                       res = 1;
+               break;
+       case ESSA_SET_POT_VOLATILE:
+               pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+               if (!(pte_val(*ptep) & _PAGE_INVALID)) {
+                       pgstev |= _PGSTE_GPS_USAGE_POT_VOLATILE;
+                       break;
+               }
+               if (pgstev & _PGSTE_GPS_ZERO) {
+                       pgstev |= _PGSTE_GPS_USAGE_VOLATILE;
+                       break;
+               }
+               if (!(pgstev & PGSTE_GC_BIT)) {
+                       pgstev |= _PGSTE_GPS_USAGE_VOLATILE;
+                       res = 1;
+                       break;
+               }
+               break;
+       case ESSA_SET_STABLE_RESIDENT:
+               pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+               pgstev |= _PGSTE_GPS_USAGE_STABLE;
+               /*
+                * Since the resident state can go away any time after this
+                * call, we will not make this page resident. We can revisit
+                * this decision if a guest will ever start using this.
+                */
+               break;
+       case ESSA_SET_STABLE_IF_RESIDENT:
+               if (!(pte_val(*ptep) & _PAGE_INVALID)) {
+                       pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+                       pgstev |= _PGSTE_GPS_USAGE_STABLE;
+               }
+               break;
+       default:
+               /* we should never get here! */
+               break;
+       }
+       /* If we are discarding a page, set it to logical zero */
+       if (res)
+               pgstev |= _PGSTE_GPS_ZERO;
+
+       pgste_val(pgste) = pgstev;
+       pgste_set_unlock(ptep, pgste);
+       pte_unmap_unlock(ptep, ptl);
+       return res;
+}
+EXPORT_SYMBOL(pgste_perform_essa);
+
+/**
+ * set_pgste_bits - set specific PGSTE bits.
+ * @mm: the memory context. It must have PGSTEs, no check is performed here!
+ * @hva: the host virtual address of the page whose PGSTE is to be processed
+ * @bits: a bitmask representing the bits that will be touched
+ * @value: the values of the bits to be written. Only the bits in the mask
+ *        will be written.
+ *
+ * Return: 0 on success, < 0 in case of error.
+ */
+int set_pgste_bits(struct mm_struct *mm, unsigned long hva,
+                       unsigned long bits, unsigned long value)
+{
+       spinlock_t *ptl;
+       pgste_t new;
+       pte_t *ptep;
+
+       ptep = get_locked_pte(mm, hva, &ptl);
+       if (unlikely(!ptep))
+               return -EFAULT;
+       new = pgste_get_lock(ptep);
+
+       pgste_val(new) &= ~bits;
+       pgste_val(new) |= value & bits;
+
+       pgste_set_unlock(ptep, new);
+       pte_unmap_unlock(ptep, ptl);
+       return 0;
+}
+EXPORT_SYMBOL(set_pgste_bits);
+
+/**
+ * get_pgste - get the current PGSTE for the given address.
+ * @mm: the memory context. It must have PGSTEs, no check is performed here!
+ * @hva: the host virtual address of the page whose PGSTE is to be processed
+ * @pgstep: will be written with the current PGSTE for the given address.
+ *
+ * Return: 0 on success, < 0 in case of error.
+ */
+int get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep)
+{
+       spinlock_t *ptl;
+       pte_t *ptep;
+
+       ptep = get_locked_pte(mm, hva, &ptl);
+       if (unlikely(!ptep))
+               return -EFAULT;
+       *pgstep = pgste_val(pgste_get(ptep));
+       pte_unmap_unlock(ptep, ptl);
+       return 0;
+}
+EXPORT_SYMBOL(get_pgste);
 #endif