diff options
author | Claudio Imbrenda <imbrenda@linux.vnet.ibm.com> | 2017-04-20 04:03:45 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2017-04-20 07:33:08 -0400 |
commit | 2d42f9477320befd33846c4083cab898998cdee5 (patch) | |
tree | 71045127b1000beafd1760b509e6ba9e66e1d3c6 | |
parent | b13de4b7adeb7a5e37a5aa78d5a4926c3cd4e131 (diff) |
s390/kvm: Add PGSTE manipulation functions
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>
-rw-r--r-- | arch/s390/include/asm/page-states.h | 19 | ||||
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 16 | ||||
-rw-r--r-- | arch/s390/mm/page-states.c | 3 | ||||
-rw-r--r-- | arch/s390/mm/pgtable.c | 153 |
4 files changed, 185 insertions, 6 deletions
diff --git a/arch/s390/include/asm/page-states.h b/arch/s390/include/asm/page-states.h new file mode 100644 index 000000000000..42267a2fe29e --- /dev/null +++ b/arch/s390/include/asm/page-states.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corp. 2017 | ||
3 | * Author(s): Claudio Imbrenda <imbrenda@linux.vnet.ibm.com> | ||
4 | */ | ||
5 | |||
6 | #ifndef PAGE_STATES_H | ||
7 | #define PAGE_STATES_H | ||
8 | |||
9 | #define ESSA_GET_STATE 0 | ||
10 | #define ESSA_SET_STABLE 1 | ||
11 | #define ESSA_SET_UNUSED 2 | ||
12 | #define ESSA_SET_VOLATILE 3 | ||
13 | #define ESSA_SET_POT_VOLATILE 4 | ||
14 | #define ESSA_SET_STABLE_RESIDENT 5 | ||
15 | #define ESSA_SET_STABLE_IF_RESIDENT 6 | ||
16 | |||
17 | #define ESSA_MAX ESSA_SET_STABLE_IF_RESIDENT | ||
18 | |||
19 | #endif | ||
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 93e37b12e882..bdabbbb3e925 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -372,10 +372,12 @@ static inline int is_module_addr(void *addr) | |||
372 | #define PGSTE_VSIE_BIT 0x0000200000000000UL /* ref'd in a shadow table */ | 372 | #define PGSTE_VSIE_BIT 0x0000200000000000UL /* ref'd in a shadow table */ |
373 | 373 | ||
374 | /* Guest Page State used for virtualization */ | 374 | /* Guest Page State used for virtualization */ |
375 | #define _PGSTE_GPS_ZERO 0x0000000080000000UL | 375 | #define _PGSTE_GPS_ZERO 0x0000000080000000UL |
376 | #define _PGSTE_GPS_USAGE_MASK 0x0000000003000000UL | 376 | #define _PGSTE_GPS_USAGE_MASK 0x0000000003000000UL |
377 | #define _PGSTE_GPS_USAGE_STABLE 0x0000000000000000UL | 377 | #define _PGSTE_GPS_USAGE_STABLE 0x0000000000000000UL |
378 | #define _PGSTE_GPS_USAGE_UNUSED 0x0000000001000000UL | 378 | #define _PGSTE_GPS_USAGE_UNUSED 0x0000000001000000UL |
379 | #define _PGSTE_GPS_USAGE_POT_VOLATILE 0x0000000002000000UL | ||
380 | #define _PGSTE_GPS_USAGE_VOLATILE _PGSTE_GPS_USAGE_MASK | ||
379 | 381 | ||
380 | /* | 382 | /* |
381 | * A user page table pointer has the space-switch-event bit, the | 383 | * 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); | |||
1041 | int get_guest_storage_key(struct mm_struct *mm, unsigned long addr, | 1043 | int get_guest_storage_key(struct mm_struct *mm, unsigned long addr, |
1042 | unsigned char *key); | 1044 | unsigned char *key); |
1043 | 1045 | ||
1046 | int set_pgste_bits(struct mm_struct *mm, unsigned long addr, | ||
1047 | unsigned long bits, unsigned long value); | ||
1048 | int get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep); | ||
1049 | int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc, | ||
1050 | unsigned long *oldpte, unsigned long *oldpgste); | ||
1051 | |||
1044 | /* | 1052 | /* |
1045 | * Certain architectures need to do special things when PTEs | 1053 | * Certain architectures need to do special things when PTEs |
1046 | * within a page table are directly modified. Thus, the following | 1054 | * within a page table are directly modified. Thus, the following |
diff --git a/arch/s390/mm/page-states.c b/arch/s390/mm/page-states.c index 3330ea124eec..69a7b01ae746 100644 --- a/arch/s390/mm/page-states.c +++ b/arch/s390/mm/page-states.c | |||
@@ -13,8 +13,7 @@ | |||
13 | #include <linux/gfp.h> | 13 | #include <linux/gfp.h> |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | 15 | ||
16 | #define ESSA_SET_STABLE 1 | 16 | #include <asm/page-states.h> |
17 | #define ESSA_SET_UNUSED 2 | ||
18 | 17 | ||
19 | static int cmma_flag = 1; | 18 | static int cmma_flag = 1; |
20 | 19 | ||
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 463e5ef02304..947b66a5cdba 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <asm/tlb.h> | 23 | #include <asm/tlb.h> |
24 | #include <asm/tlbflush.h> | 24 | #include <asm/tlbflush.h> |
25 | #include <asm/mmu_context.h> | 25 | #include <asm/mmu_context.h> |
26 | #include <asm/page-states.h> | ||
26 | 27 | ||
27 | static inline pte_t ptep_flush_direct(struct mm_struct *mm, | 28 | static inline pte_t ptep_flush_direct(struct mm_struct *mm, |
28 | unsigned long addr, pte_t *ptep) | 29 | unsigned long addr, pte_t *ptep) |
@@ -787,4 +788,156 @@ int get_guest_storage_key(struct mm_struct *mm, unsigned long addr, | |||
787 | return 0; | 788 | return 0; |
788 | } | 789 | } |
789 | EXPORT_SYMBOL(get_guest_storage_key); | 790 | EXPORT_SYMBOL(get_guest_storage_key); |
791 | |||
792 | /** | ||
793 | * pgste_perform_essa - perform ESSA actions on the PGSTE. | ||
794 | * @mm: the memory context. It must have PGSTEs, no check is performed here! | ||
795 | * @hva: the host virtual address of the page whose PGSTE is to be processed | ||
796 | * @orc: the specific action to perform, see the ESSA_SET_* macros. | ||
797 | * @oldpte: the PTE will be saved there if the pointer is not NULL. | ||
798 | * @oldpgste: the old PGSTE will be saved there if the pointer is not NULL. | ||
799 | * | ||
800 | * Return: 1 if the page is to be added to the CBRL, otherwise 0, | ||
801 | * or < 0 in case of error. -EINVAL is returned for invalid values | ||
802 | * of orc, -EFAULT for invalid addresses. | ||
803 | */ | ||
804 | int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc, | ||
805 | unsigned long *oldpte, unsigned long *oldpgste) | ||
806 | { | ||
807 | unsigned long pgstev; | ||
808 | spinlock_t *ptl; | ||
809 | pgste_t pgste; | ||
810 | pte_t *ptep; | ||
811 | int res = 0; | ||
812 | |||
813 | WARN_ON_ONCE(orc > ESSA_MAX); | ||
814 | if (unlikely(orc > ESSA_MAX)) | ||
815 | return -EINVAL; | ||
816 | ptep = get_locked_pte(mm, hva, &ptl); | ||
817 | if (unlikely(!ptep)) | ||
818 | return -EFAULT; | ||
819 | pgste = pgste_get_lock(ptep); | ||
820 | pgstev = pgste_val(pgste); | ||
821 | if (oldpte) | ||
822 | *oldpte = pte_val(*ptep); | ||
823 | if (oldpgste) | ||
824 | *oldpgste = pgstev; | ||
825 | |||
826 | switch (orc) { | ||
827 | case ESSA_GET_STATE: | ||
828 | break; | ||
829 | case ESSA_SET_STABLE: | ||
830 | pgstev &= ~_PGSTE_GPS_USAGE_MASK; | ||
831 | pgstev |= _PGSTE_GPS_USAGE_STABLE; | ||
832 | break; | ||
833 | case ESSA_SET_UNUSED: | ||
834 | pgstev &= ~_PGSTE_GPS_USAGE_MASK; | ||
835 | pgstev |= _PGSTE_GPS_USAGE_UNUSED; | ||
836 | if (pte_val(*ptep) & _PAGE_INVALID) | ||
837 | res = 1; | ||
838 | break; | ||
839 | case ESSA_SET_VOLATILE: | ||
840 | pgstev &= ~_PGSTE_GPS_USAGE_MASK; | ||
841 | pgstev |= _PGSTE_GPS_USAGE_VOLATILE; | ||
842 | if (pte_val(*ptep) & _PAGE_INVALID) | ||
843 | res = 1; | ||
844 | break; | ||
845 | case ESSA_SET_POT_VOLATILE: | ||
846 | pgstev &= ~_PGSTE_GPS_USAGE_MASK; | ||
847 | if (!(pte_val(*ptep) & _PAGE_INVALID)) { | ||
848 | pgstev |= _PGSTE_GPS_USAGE_POT_VOLATILE; | ||
849 | break; | ||
850 | } | ||
851 | if (pgstev & _PGSTE_GPS_ZERO) { | ||
852 | pgstev |= _PGSTE_GPS_USAGE_VOLATILE; | ||
853 | break; | ||
854 | } | ||
855 | if (!(pgstev & PGSTE_GC_BIT)) { | ||
856 | pgstev |= _PGSTE_GPS_USAGE_VOLATILE; | ||
857 | res = 1; | ||
858 | break; | ||
859 | } | ||
860 | break; | ||
861 | case ESSA_SET_STABLE_RESIDENT: | ||
862 | pgstev &= ~_PGSTE_GPS_USAGE_MASK; | ||
863 | pgstev |= _PGSTE_GPS_USAGE_STABLE; | ||
864 | /* | ||
865 | * Since the resident state can go away any time after this | ||
866 | * call, we will not make this page resident. We can revisit | ||
867 | * this decision if a guest will ever start using this. | ||
868 | */ | ||
869 | break; | ||
870 | case ESSA_SET_STABLE_IF_RESIDENT: | ||
871 | if (!(pte_val(*ptep) & _PAGE_INVALID)) { | ||
872 | pgstev &= ~_PGSTE_GPS_USAGE_MASK; | ||
873 | pgstev |= _PGSTE_GPS_USAGE_STABLE; | ||
874 | } | ||
875 | break; | ||
876 | default: | ||
877 | /* we should never get here! */ | ||
878 | break; | ||
879 | } | ||
880 | /* If we are discarding a page, set it to logical zero */ | ||
881 | if (res) | ||
882 | pgstev |= _PGSTE_GPS_ZERO; | ||
883 | |||
884 | pgste_val(pgste) = pgstev; | ||
885 | pgste_set_unlock(ptep, pgste); | ||
886 | pte_unmap_unlock(ptep, ptl); | ||
887 | return res; | ||
888 | } | ||
889 | EXPORT_SYMBOL(pgste_perform_essa); | ||
890 | |||
891 | /** | ||
892 | * set_pgste_bits - set specific PGSTE bits. | ||
893 | * @mm: the memory context. It must have PGSTEs, no check is performed here! | ||
894 | * @hva: the host virtual address of the page whose PGSTE is to be processed | ||
895 | * @bits: a bitmask representing the bits that will be touched | ||
896 | * @value: the values of the bits to be written. Only the bits in the mask | ||
897 | * will be written. | ||
898 | * | ||
899 | * Return: 0 on success, < 0 in case of error. | ||
900 | */ | ||
901 | int set_pgste_bits(struct mm_struct *mm, unsigned long hva, | ||
902 | unsigned long bits, unsigned long value) | ||
903 | { | ||
904 | spinlock_t *ptl; | ||
905 | pgste_t new; | ||
906 | pte_t *ptep; | ||
907 | |||
908 | ptep = get_locked_pte(mm, hva, &ptl); | ||
909 | if (unlikely(!ptep)) | ||
910 | return -EFAULT; | ||
911 | new = pgste_get_lock(ptep); | ||
912 | |||
913 | pgste_val(new) &= ~bits; | ||
914 | pgste_val(new) |= value & bits; | ||
915 | |||
916 | pgste_set_unlock(ptep, new); | ||
917 | pte_unmap_unlock(ptep, ptl); | ||
918 | return 0; | ||
919 | } | ||
920 | EXPORT_SYMBOL(set_pgste_bits); | ||
921 | |||
922 | /** | ||
923 | * get_pgste - get the current PGSTE for the given address. | ||
924 | * @mm: the memory context. It must have PGSTEs, no check is performed here! | ||
925 | * @hva: the host virtual address of the page whose PGSTE is to be processed | ||
926 | * @pgstep: will be written with the current PGSTE for the given address. | ||
927 | * | ||
928 | * Return: 0 on success, < 0 in case of error. | ||
929 | */ | ||
930 | int get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep) | ||
931 | { | ||
932 | spinlock_t *ptl; | ||
933 | pte_t *ptep; | ||
934 | |||
935 | ptep = get_locked_pte(mm, hva, &ptl); | ||
936 | if (unlikely(!ptep)) | ||
937 | return -EFAULT; | ||
938 | *pgstep = pgste_val(pgste_get(ptep)); | ||
939 | pte_unmap_unlock(ptep, ptl); | ||
940 | return 0; | ||
941 | } | ||
942 | EXPORT_SYMBOL(get_pgste); | ||
790 | #endif | 943 | #endif |