mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-12 01:56:43 +07:00
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
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 */
|
||||
|
||||
/* 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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user