|
@@ -8,22 +8,19 @@
|
|
* Copyright IBM Corp. 2016
|
|
* Copyright IBM Corp. 2016
|
|
* Author(s): Janosch Frank <frankja@linux.vnet.ibm.com>
|
|
* Author(s): Janosch Frank <frankja@linux.vnet.ibm.com>
|
|
*/
|
|
*/
|
|
-#include <linux/kvm_host.h>
|
|
|
|
#include <linux/errno.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/vmalloc.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/asm-offsets.h>
|
|
#include <asm/sclp.h>
|
|
#include <asm/sclp.h>
|
|
#include <asm/diag.h>
|
|
#include <asm/diag.h>
|
|
#include <asm/sysinfo.h>
|
|
#include <asm/sysinfo.h>
|
|
#include <asm/ebcdic.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
|
|
#define DED_WEIGHT 0xffff
|
|
/*
|
|
/*
|
|
@@ -144,6 +141,21 @@ struct lpar_cpu_inf {
|
|
struct cpu_inf ifl;
|
|
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)
|
|
static inline u64 cpu_id(u8 ctidx, void *diag224_buf)
|
|
{
|
|
{
|
|
return *((u64 *)(diag224_buf + (ctidx + 1) * DIAG204_CPU_NAME_LEN));
|
|
return *((u64 *)(diag224_buf + (ctidx + 1) * DIAG204_CPU_NAME_LEN));
|
|
@@ -382,88 +394,124 @@ out:
|
|
vfree(diag204_buf);
|
|
vfree(diag204_buf);
|
|
}
|
|
}
|
|
|
|
|
|
-static int sthyi(u64 vaddr)
|
|
|
|
|
|
+static int sthyi(u64 vaddr, u64 *rc)
|
|
{
|
|
{
|
|
register u64 code asm("0") = 0;
|
|
register u64 code asm("0") = 0;
|
|
register u64 addr asm("2") = vaddr;
|
|
register u64 addr asm("2") = vaddr;
|
|
|
|
+ register u64 rcode asm("3");
|
|
int cc;
|
|
int cc;
|
|
|
|
|
|
asm volatile(
|
|
asm volatile(
|
|
".insn rre,0xB2560000,%[code],%[addr]\n"
|
|
".insn rre,0xB2560000,%[code],%[addr]\n"
|
|
"ipm %[cc]\n"
|
|
"ipm %[cc]\n"
|
|
"srl %[cc],28\n"
|
|
"srl %[cc],28\n"
|
|
- : [cc] "=d" (cc)
|
|
|
|
|
|
+ : [cc] "=d" (cc), "=d" (rcode)
|
|
: [code] "d" (code), [addr] "a" (addr)
|
|
: [code] "d" (code), [addr] "a" (addr)
|
|
- : "3", "memory", "cc");
|
|
|
|
|
|
+ : "memory", "cc");
|
|
|
|
+ *rc = rcode;
|
|
return cc;
|
|
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);
|
|
|
|
|
|
+ if (test_facility(74))
|
|
|
|
+ return sthyi((u64)dst, rc);
|
|
|
|
+
|
|
|
|
+ fill_hdr(sctns);
|
|
|
|
+ fill_stsi(sctns);
|
|
|
|
+ fill_diag(sctns);
|
|
|
|
+ *rc = 0;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int sthyi_init_cache(void)
|
|
|
|
+{
|
|
|
|
+ if (sthyi_cache.info)
|
|
return 0;
|
|
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;
|
|
|
|
+}
|
|
|
|
|
|
- kvm_s390_get_regs_rre(vcpu, ®1, ®2);
|
|
|
|
- code = vcpu->run->s.regs.gprs[reg1];
|
|
|
|
- addr = vcpu->run->s.regs.gprs[reg2];
|
|
|
|
|
|
+static int sthyi_update_cache(u64 *rc)
|
|
|
|
+{
|
|
|
|
+ int r;
|
|
|
|
|
|
- vcpu->stat.instruction_sthyi++;
|
|
|
|
- VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr);
|
|
|
|
- trace_kvm_s390_handle_sthyi(vcpu, code, addr);
|
|
|
|
|
|
+ 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;
|
|
|
|
+}
|
|
|
|
|
|
- if (reg1 == reg2 || reg1 & 1 || reg2 & 1)
|
|
|
|
- return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
|
|
|
|
|
+/*
|
|
|
|
+ * 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;
|
|
|
|
|
|
- if (code & 0xffff) {
|
|
|
|
- cc = 3;
|
|
|
|
|
|
+ mutex_lock(&sthyi_mutex);
|
|
|
|
+ r = sthyi_init_cache();
|
|
|
|
+ if (r)
|
|
goto out;
|
|
goto out;
|
|
- }
|
|
|
|
|
|
|
|
- if (addr & ~PAGE_MASK)
|
|
|
|
- return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
|
|
|
|
|
+ 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);
|
|
|
|
|
|
- sctns = (void *)get_zeroed_page(GFP_KERNEL);
|
|
|
|
- if (!sctns)
|
|
|
|
|
|
+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;
|
|
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);
|
|
|
|
|
|
+ r = sthyi_fill(info, &sthyi_rc);
|
|
|
|
+ if (r < 0)
|
|
|
|
+ goto out;
|
|
|
|
+ if (return_code && put_user(sthyi_rc, return_code)) {
|
|
|
|
+ r = -EFAULT;
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
-
|
|
|
|
- fill_hdr(sctns);
|
|
|
|
- fill_stsi(sctns);
|
|
|
|
- fill_diag(sctns);
|
|
|
|
-
|
|
|
|
|
|
+ if (copy_to_user(buffer, info, PAGE_SIZE))
|
|
|
|
+ r = -EFAULT;
|
|
out:
|
|
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] = cc ? 4 : 0;
|
|
|
|
- kvm_s390_set_psw_cc(vcpu, cc);
|
|
|
|
|
|
+ free_page((unsigned long)info);
|
|
return r;
|
|
return r;
|
|
}
|
|
}
|