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>
This commit is contained in:
parent
b13de4b7ad
commit
2d42f94773
4 changed files with 185 additions and 6 deletions
19
arch/s390/include/asm/page-states.h
Normal file
19
arch/s390/include/asm/page-states.h
Normal file
|
@ -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
|
|
@ -372,10 +372,12 @@ static inline int is_module_addr(void *addr)
|
||||||
#define PGSTE_VSIE_BIT 0x0000200000000000UL /* ref'd in a shadow table */
|
#define PGSTE_VSIE_BIT 0x0000200000000000UL /* ref'd in a shadow table */
|
||||||
|
|
||||||
/* Guest Page State used for virtualization */
|
/* Guest Page State used for virtualization */
|
||||||
#define _PGSTE_GPS_ZERO 0x0000000080000000UL
|
#define _PGSTE_GPS_ZERO 0x0000000080000000UL
|
||||||
#define _PGSTE_GPS_USAGE_MASK 0x0000000003000000UL
|
#define _PGSTE_GPS_USAGE_MASK 0x0000000003000000UL
|
||||||
#define _PGSTE_GPS_USAGE_STABLE 0x0000000000000000UL
|
#define _PGSTE_GPS_USAGE_STABLE 0x0000000000000000UL
|
||||||
#define _PGSTE_GPS_USAGE_UNUSED 0x0000000001000000UL
|
#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
|
* 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,
|
int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,
|
||||||
unsigned char *key);
|
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
|
* Certain architectures need to do special things when PTEs
|
||||||
* within a page table are directly modified. Thus, the following
|
* within a page table are directly modified. Thus, the following
|
||||||
|
|
|
@ -13,8 +13,7 @@
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
|
||||||
#define ESSA_SET_STABLE 1
|
#include <asm/page-states.h>
|
||||||
#define ESSA_SET_UNUSED 2
|
|
||||||
|
|
||||||
static int cmma_flag = 1;
|
static int cmma_flag = 1;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <asm/tlb.h>
|
#include <asm/tlb.h>
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
#include <asm/mmu_context.h>
|
#include <asm/mmu_context.h>
|
||||||
|
#include <asm/page-states.h>
|
||||||
|
|
||||||
static inline pte_t ptep_flush_direct(struct mm_struct *mm,
|
static inline pte_t ptep_flush_direct(struct mm_struct *mm,
|
||||||
unsigned long addr, pte_t *ptep)
|
unsigned long addr, pte_t *ptep)
|
||||||
|
@ -787,4 +788,156 @@ int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(get_guest_storage_key);
|
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
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue