Merge branch 'sthyi' into features
Add the store-hypervisor-information code into features using a tip branch for parallel merging into the KVM tree.
This commit is contained in:
commit
3bdf5679c9
|
@ -736,7 +736,6 @@ struct kvm_arch{
|
|||
wait_queue_head_t ipte_wq;
|
||||
int ipte_lock_count;
|
||||
struct mutex ipte_mutex;
|
||||
struct ratelimit_state sthyi_limit;
|
||||
spinlock_t start_stop_lock;
|
||||
struct sie_page2 *sie_page2;
|
||||
struct kvm_s390_cpu_model model;
|
||||
|
|
|
@ -199,4 +199,5 @@ struct service_level {
|
|||
int register_service_level(struct service_level *);
|
||||
int unregister_service_level(struct service_level *);
|
||||
|
||||
int sthyi_fill(void *dst, u64 *rc);
|
||||
#endif /* __ASM_S390_SYSINFO_H */
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef _UAPI_ASM_STHYI_H
|
||||
#define _UAPI_ASM_STHYI_H
|
||||
|
||||
#define STHYI_FC_CP_IFL_CAP 0
|
||||
|
||||
#endif /* _UAPI_ASM_STHYI_H */
|
|
@ -315,7 +315,8 @@
|
|||
#define __NR_pwritev2 377
|
||||
#define __NR_s390_guarded_storage 378
|
||||
#define __NR_statx 379
|
||||
#define NR_syscalls 380
|
||||
#define __NR_s390_sthyi 380
|
||||
#define NR_syscalls 381
|
||||
|
||||
/*
|
||||
* There are some system calls that are not present on 64 bit, some
|
||||
|
|
|
@ -57,7 +57,7 @@ obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
|
|||
obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
|
||||
obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o
|
||||
obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
|
||||
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o
|
||||
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
|
||||
obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o
|
||||
|
||||
extra-y += head.o head64.o vmlinux.lds
|
||||
|
|
|
@ -180,3 +180,4 @@ COMPAT_SYSCALL_WRAP3(mlock2, unsigned long, start, size_t, len, int, flags);
|
|||
COMPAT_SYSCALL_WRAP6(copy_file_range, int, fd_in, loff_t __user *, off_in, int, fd_out, loff_t __user *, off_out, size_t, len, unsigned int, flags);
|
||||
COMPAT_SYSCALL_WRAP2(s390_guarded_storage, int, command, struct gs_cb *, gs_cb);
|
||||
COMPAT_SYSCALL_WRAP5(statx, int, dfd, const char __user *, path, unsigned, flags, unsigned, mask, struct statx __user *, buffer);
|
||||
COMPAT_SYSCALL_WRAP4(s390_sthyi, unsigned long, code, void __user *, info, u64 __user *, rc, unsigned long, flags);
|
||||
|
|
|
@ -77,6 +77,7 @@ long sys_s390_runtime_instr(int command, int signum);
|
|||
long sys_s390_guarded_storage(int command, struct gs_cb __user *);
|
||||
long sys_s390_pci_mmio_write(unsigned long, const void __user *, size_t);
|
||||
long sys_s390_pci_mmio_read(unsigned long, void __user *, size_t);
|
||||
long sys_s390_sthyi(unsigned long function_code, void __user *buffer, u64 __user *return_code, unsigned long flags);
|
||||
|
||||
DECLARE_PER_CPU(u64, mt_cycles[8]);
|
||||
|
||||
|
|
|
@ -8,22 +8,19 @@
|
|||
* Copyright IBM Corp. 2016
|
||||
* Author(s): Janosch Frank <frankja@linux.vnet.ibm.com>
|
||||
*/
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include <asm/kvm_host.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/sclp.h>
|
||||
#include <asm/diag.h>
|
||||
#include <asm/sysinfo.h>
|
||||
#include <asm/ebcdic.h>
|
||||
|
||||
#include "kvm-s390.h"
|
||||
#include "gaccess.h"
|
||||
#include "trace.h"
|
||||
#include <asm/facility.h>
|
||||
#include <asm/sthyi.h>
|
||||
#include "entry.h"
|
||||
|
||||
#define DED_WEIGHT 0xffff
|
||||
/*
|
||||
|
@ -144,6 +141,21 @@ struct lpar_cpu_inf {
|
|||
struct cpu_inf ifl;
|
||||
};
|
||||
|
||||
/*
|
||||
* STHYI requires extensive locking in the higher hypervisors
|
||||
* and is very computational/memory expensive. Therefore we
|
||||
* cache the retrieved data whose valid period is 1s.
|
||||
*/
|
||||
#define CACHE_VALID_JIFFIES HZ
|
||||
|
||||
struct sthyi_info {
|
||||
void *info;
|
||||
unsigned long end;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(sthyi_mutex);
|
||||
static struct sthyi_info sthyi_cache;
|
||||
|
||||
static inline u64 cpu_id(u8 ctidx, void *diag224_buf)
|
||||
{
|
||||
return *((u64 *)(diag224_buf + (ctidx + 1) * DIAG204_CPU_NAME_LEN));
|
||||
|
@ -382,88 +394,124 @@ out:
|
|||
vfree(diag204_buf);
|
||||
}
|
||||
|
||||
static int sthyi(u64 vaddr)
|
||||
static int sthyi(u64 vaddr, u64 *rc)
|
||||
{
|
||||
register u64 code asm("0") = 0;
|
||||
register u64 addr asm("2") = vaddr;
|
||||
register u64 rcode asm("3");
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
".insn rre,0xB2560000,%[code],%[addr]\n"
|
||||
"ipm %[cc]\n"
|
||||
"srl %[cc],28\n"
|
||||
: [cc] "=d" (cc)
|
||||
: [cc] "=d" (cc), "=d" (rcode)
|
||||
: [code] "d" (code), [addr] "a" (addr)
|
||||
: "3", "memory", "cc");
|
||||
: "memory", "cc");
|
||||
*rc = rcode;
|
||||
return cc;
|
||||
}
|
||||
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu)
|
||||
static int fill_dst(void *dst, u64 *rc)
|
||||
{
|
||||
int reg1, reg2, r = 0;
|
||||
u64 code, addr, cc = 0;
|
||||
struct sthyi_sctns *sctns = NULL;
|
||||
|
||||
if (!test_kvm_facility(vcpu->kvm, 74))
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
|
||||
struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst;
|
||||
|
||||
/*
|
||||
* STHYI requires extensive locking in the higher hypervisors
|
||||
* and is very computational/memory expensive. Therefore we
|
||||
* ratelimit the executions per VM.
|
||||
* If the facility is on, we don't want to emulate the instruction.
|
||||
* We ask the hypervisor to provide the data.
|
||||
*/
|
||||
if (!__ratelimit(&vcpu->kvm->arch.sthyi_limit)) {
|
||||
kvm_s390_retry_instr(vcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
|
||||
code = vcpu->run->s.regs.gprs[reg1];
|
||||
addr = vcpu->run->s.regs.gprs[reg2];
|
||||
|
||||
vcpu->stat.instruction_sthyi++;
|
||||
VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr);
|
||||
trace_kvm_s390_handle_sthyi(vcpu, code, addr);
|
||||
|
||||
if (reg1 == reg2 || reg1 & 1 || reg2 & 1)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
if (code & 0xffff) {
|
||||
cc = 3;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (addr & ~PAGE_MASK)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
sctns = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!sctns)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* If we are a guest, we don't want to emulate an emulated
|
||||
* instruction. We ask the hypervisor to provide the data.
|
||||
*/
|
||||
if (test_facility(74)) {
|
||||
cc = sthyi((u64)sctns);
|
||||
goto out;
|
||||
}
|
||||
if (test_facility(74))
|
||||
return sthyi((u64)dst, rc);
|
||||
|
||||
fill_hdr(sctns);
|
||||
fill_stsi(sctns);
|
||||
fill_diag(sctns);
|
||||
|
||||
out:
|
||||
if (!cc) {
|
||||
r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE);
|
||||
if (r) {
|
||||
free_page((unsigned long)sctns);
|
||||
return kvm_s390_inject_prog_cond(vcpu, r);
|
||||
}
|
||||
*rc = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_page((unsigned long)sctns);
|
||||
vcpu->run->s.regs.gprs[reg2 + 1] = cc ? 4 : 0;
|
||||
kvm_s390_set_psw_cc(vcpu, cc);
|
||||
static int sthyi_init_cache(void)
|
||||
{
|
||||
if (sthyi_cache.info)
|
||||
return 0;
|
||||
sthyi_cache.info = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!sthyi_cache.info)
|
||||
return -ENOMEM;
|
||||
sthyi_cache.end = jiffies - 1; /* expired */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sthyi_update_cache(u64 *rc)
|
||||
{
|
||||
int r;
|
||||
|
||||
memset(sthyi_cache.info, 0, PAGE_SIZE);
|
||||
r = fill_dst(sthyi_cache.info, rc);
|
||||
if (r)
|
||||
return r;
|
||||
sthyi_cache.end = jiffies + CACHE_VALID_JIFFIES;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* sthyi_fill - Fill page with data returned by the STHYI instruction
|
||||
*
|
||||
* @dst: Pointer to zeroed page
|
||||
* @rc: Pointer for storing the return code of the instruction
|
||||
*
|
||||
* Fills the destination with system information returned by the STHYI
|
||||
* instruction. The data is generated by emulation or execution of STHYI,
|
||||
* if available. The return value is the condition code that would be
|
||||
* returned, the rc parameter is the return code which is passed in
|
||||
* register R2 + 1.
|
||||
*/
|
||||
int sthyi_fill(void *dst, u64 *rc)
|
||||
{
|
||||
int r;
|
||||
|
||||
mutex_lock(&sthyi_mutex);
|
||||
r = sthyi_init_cache();
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
if (time_is_before_jiffies(sthyi_cache.end)) {
|
||||
/* cache expired */
|
||||
r = sthyi_update_cache(rc);
|
||||
if (r)
|
||||
goto out;
|
||||
}
|
||||
*rc = 0;
|
||||
memcpy(dst, sthyi_cache.info, PAGE_SIZE);
|
||||
out:
|
||||
mutex_unlock(&sthyi_mutex);
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sthyi_fill);
|
||||
|
||||
SYSCALL_DEFINE4(s390_sthyi, unsigned long, function_code, void __user *, buffer,
|
||||
u64 __user *, return_code, unsigned long, flags)
|
||||
{
|
||||
u64 sthyi_rc;
|
||||
void *info;
|
||||
int r;
|
||||
|
||||
if (flags)
|
||||
return -EINVAL;
|
||||
if (function_code != STHYI_FC_CP_IFL_CAP)
|
||||
return -EOPNOTSUPP;
|
||||
info = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
r = sthyi_fill(info, &sthyi_rc);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
if (return_code && put_user(sthyi_rc, return_code)) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if (copy_to_user(buffer, info, PAGE_SIZE))
|
||||
r = -EFAULT;
|
||||
out:
|
||||
free_page((unsigned long)info);
|
||||
return r;
|
||||
}
|
|
@ -388,3 +388,4 @@ SYSCALL(sys_preadv2,compat_sys_preadv2)
|
|||
SYSCALL(sys_pwritev2,compat_sys_pwritev2)
|
||||
SYSCALL(sys_s390_guarded_storage,compat_sys_s390_guarded_storage) /* 378 */
|
||||
SYSCALL(sys_statx,compat_sys_statx)
|
||||
SYSCALL(sys_s390_sthyi,compat_sys_s390_sthyi)
|
||||
|
|
|
@ -12,6 +12,6 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqch
|
|||
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
|
||||
|
||||
kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o
|
||||
kvm-objs += diag.o gaccess.o guestdbg.o sthyi.o vsie.o
|
||||
kvm-objs += diag.o gaccess.o guestdbg.o vsie.o
|
||||
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <asm/kvm_host.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/sysinfo.h>
|
||||
|
||||
#include "kvm-s390.h"
|
||||
#include "gaccess.h"
|
||||
|
@ -360,6 +361,61 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the sthyi instruction that provides the guest with system
|
||||
* information, like current CPU resources available at each level of
|
||||
* the machine.
|
||||
*/
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int reg1, reg2, r = 0;
|
||||
u64 code, addr, cc = 0, rc = 0;
|
||||
struct sthyi_sctns *sctns = NULL;
|
||||
|
||||
if (!test_kvm_facility(vcpu->kvm, 74))
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
|
||||
|
||||
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
|
||||
code = vcpu->run->s.regs.gprs[reg1];
|
||||
addr = vcpu->run->s.regs.gprs[reg2];
|
||||
|
||||
vcpu->stat.instruction_sthyi++;
|
||||
VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr);
|
||||
trace_kvm_s390_handle_sthyi(vcpu, code, addr);
|
||||
|
||||
if (reg1 == reg2 || reg1 & 1 || reg2 & 1)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
if (code & 0xffff) {
|
||||
cc = 3;
|
||||
rc = 4;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (addr & ~PAGE_MASK)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
sctns = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!sctns)
|
||||
return -ENOMEM;
|
||||
|
||||
cc = sthyi_fill(sctns, &rc);
|
||||
|
||||
out:
|
||||
if (!cc) {
|
||||
r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE);
|
||||
if (r) {
|
||||
free_page((unsigned long)sctns);
|
||||
return kvm_s390_inject_prog_cond(vcpu, r);
|
||||
}
|
||||
}
|
||||
|
||||
free_page((unsigned long)sctns);
|
||||
vcpu->run->s.regs.gprs[reg2 + 1] = rc;
|
||||
kvm_s390_set_psw_cc(vcpu, cc);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int handle_operexc(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
psw_t oldpsw, newpsw;
|
||||
|
|
|
@ -1884,8 +1884,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
|
||||
rc = -ENOMEM;
|
||||
|
||||
ratelimit_state_init(&kvm->arch.sthyi_limit, 5 * HZ, 500);
|
||||
|
||||
kvm->arch.use_esca = 0; /* start with basic SCA */
|
||||
if (!sclp.has_64bscao)
|
||||
alloc_flags |= GFP_DMA;
|
||||
|
|
|
@ -242,6 +242,8 @@ static inline void kvm_s390_retry_instr(struct kvm_vcpu *vcpu)
|
|||
kvm_s390_rewind_psw(vcpu, kvm_s390_get_ilen(vcpu));
|
||||
}
|
||||
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* implemented in priv.c */
|
||||
int is_valid_psw(psw_t *psw);
|
||||
int kvm_s390_handle_aa(struct kvm_vcpu *vcpu);
|
||||
|
@ -268,9 +270,6 @@ void kvm_s390_vsie_destroy(struct kvm *kvm);
|
|||
int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
|
||||
int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* implemented in sthyi.c */
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* implemented in kvm-s390.c */
|
||||
void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
|
||||
const struct kvm_s390_vm_tod_clock *gtod);
|
||||
|
|
Loading…
Reference in New Issue