* [PATCH] kvm: powerpc: add exit timing statistics v2
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
@ 2008-11-10 17:39 ` Christian Ehrhardt
2008-11-11 14:48 ` [PATCH] kvm: powerpc: add exit timing statistics v3 Christian Ehrhardt
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Christian Ehrhardt @ 2008-11-10 17:39 UTC (permalink / raw)
To: kvm-ppc
From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
*resend with header file in diff*
*update to v2*
The update fixes accounting for sets to MSR[WE] which should not be accoutned
as instruction emulation. While adding that and analyzing the data it became
obvious that several types of emulations hould be accounted separately.
I'm not yet really happy with adding all these EMULATE_*_DONE types but I had
no better idea to not break flood the code with account calls and split
account/set_type. The issue is that emulation is also called e.g. for some mmio
paths and therefore the accouting should not be called inside emulation, but on
the returning path that can now evaluate the different kind of emulations done.
This is not yet finished, and posted as RFC only.
Other existing kvm statistics are either just counters (kvm_stat) reported for
kvm generally or trace based aproaches like kvm_trace.
For kvm on powerpc we had the need to track the timings of the different exit
types. While this could be achieved parsing data created with a kvm_trace
extension this adds too muhc overhead (at least on embedded powerpc) slowing
down the workloads we wanted to measure.
Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
code. These statistic is available per vm&vcpu under the kvm debugfs directory.
As this statistic is low, but still some overhead it can be enabled via a
.config entry and should be off by default.
Since this patch touched all powerpc kvm_stat code anyway this code is now
merged and simpliefied together with the exit timing statistic code (still
working with exit timing disabled in .config).
Here is a sample output (after postprocessing with the awk script I'll post in reply to this patch) how the results look like.
sum of time 27504898
MMIO: count 824 min 51 max 555 sum 75825 sum_quad 9232871 avg 92.0206 stddev 52.318 % 0.28
DCR: count 140 min 44 max 92 sum 6746 sum_quad 327658 avg 48.1857 stddev 4.307 % 0.02
SIGNAL: count 2 min 309 max 993 sum 1302 sum_quad 1081530 avg 651.0000 stddev 342.000 % 0.00
ITLBREAL: count 293 min 11 max 14 sum 3515 sum_quad 42175 avg 11.9966 stddev 0.155 % 0.01
ITLBVIRT: count 113822 min 20 max 338 sum 2595412 sum_quad 60256824 avg 22.8024 stddev 3.074 % 9.44
DTLBREAL: count 242 min 11 max 14 sum 2908 sum_quad 34974 avg 12.0165 stddev 0.352 % 0.01
DTLBVIRT: count 66687 min 21 max 329 sum 1530048 sum_quad 35434926 avg 22.9437 stddev 2.224 % 5.56
SYSCALL: count 72 min 9 max 10 sum 649 sum_quad 5851 avg 9.0139 stddev 0.117 % 0.00
ISI: count 56 min 9 max 10 sum 506 sum_quad 4574 avg 9.0357 stddev 0.186 % 0.00
DSI: count 49 min 9 max 10 sum 448 sum_quad 4102 avg 9.1429 stddev 0.350 % 0.00
EMULINST: count 211220 min 7 max 7700 sum 3292984 sum_quad 5797023806 avg 15.5903 stddev 164.931 % 11.97
DEC: count 6582 min 49 max 322 sum 367567 sum_quad 22996737 avg 55.8443 stddev 19.373 % 1.34
EXTINT: count 4 min 79 max 513 sum 797 sum_quad 290423 avg 199.2500 stddev 181.398 % 0.00
TIMEINGUEST: count 399993 min 0 max 3952 sum 19626191 sum_quad 61148587807 avg 49.0663 stddev 387.900 % 71.36
Signed-off-by: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
---
[diffstat]
include/asm/kvm_host.h | 56 ++++++++++++++++
include/asm/kvm_ppc.h | 16 +++-
kernel/asm-offsets.c | 11 +++
kvm/44x_emulate.c | 12 ++-
kvm/44x_tlb.c | 4 -
kvm/Kconfig | 9 ++
kvm/booke.c | 74 +++++++++++++++------
kvm/booke.h | 7 +-
kvm/booke_interrupts.S | 24 ++++++
kvm/powerpc.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++-
10 files changed, 348 insertions(+), 34 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -71,7 +71,50 @@ struct kvmppc_44x_tlbe {
u32 word2;
};
+enum kvm_exit_types {
+ MMIO_EXITS,
+ DCR_EXITS,
+ SIGNAL_EXITS,
+ ITLB_REAL_MISS_EXITS,
+ ITLB_VIRT_MISS_EXITS,
+ DTLB_REAL_MISS_EXITS,
+ DTLB_VIRT_MISS_EXITS,
+ SYSCALL_EXITS,
+ ISI_EXITS,
+ DSI_EXITS,
+ EMULATED_INST_EXITS,
+ EMULATED_MTMSRWE_EXITS,
+ EMULATED_COREOP_EXITS,
+ EMULATED_COREMTSPR_EXITS,
+ EMULATED_COREMFSPR_EXITS,
+ EMULATED_COREMTMSR_EXITS,
+ EMULATED_TLBSX_EXITS,
+ EMULATED_TLBWE_EXITS,
+ EMULATED_RFI_EXITS,
+ DEC_EXITS,
+ EXT_INTR_EXITS,
+ HALT_WAKEUP,
+ USR_PR_INST,
+ FP_UNAVAIL,
+ DEBUG_EXITS,
+ TIMEINGUEST,
+ __NUMBER_OF_KVM_EXIT_TYPES
+};
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+/* allow access to big endian 32bit upper/lower parts and 64bit var */
+typedef union {
+ u64 tv64;
+ struct {
+ u32 tbu, tbl;
+ } tv32;
+} exit_timing_t;
+#endif
+
struct kvm_arch {
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ unsigned int vm_id;
+#endif
};
struct kvm_vcpu_arch {
@@ -130,6 +173,19 @@ struct kvm_vcpu_arch {
u32 dbcr0;
u32 dbcr1;
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ exit_timing_t timing_exit;
+ exit_timing_t timing_last_enter;
+ u32 last_exit_type;
+ u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_quad_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_min_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_max_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_last_exit;
+ struct dentry *debugfs_exit_timing;
+#endif
+
u32 last_inst;
ulong fault_dear;
ulong fault_esr;
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -30,10 +30,18 @@
#include <linux/kvm_host.h>
enum emulation_result {
- EMULATE_DONE, /* no further processing */
- EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
- EMULATE_DO_DCR, /* kvm_run filled with DCR request */
- EMULATE_FAIL, /* can't emulate this instruction */
+ EMULATE_DONE, /* no further processing */
+ EMULATE_COREOP_DONE, /* no further processing */
+ EMULATE_COREMTSPR_DONE, /* no further processing */
+ EMULATE_COREMFSPR_DONE, /* no further processing */
+ EMULATE_COREMTMSR_DONE, /* no further processing */
+ EMULATE_MTMSRWE_DONE, /* no further processing */
+ EMULATE_TLBSX_DONE, /* no further processing */
+ EMULATE_TLBWE_DONE, /* no further processing */
+ EMULATE_RFI_DONE, /* no further processing */
+ EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
+ EMULATE_DO_DCR, /* kvm_run filled with DCR request */
+ EMULATE_FAIL, /* can't emulate this instruction */
};
extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
diff --git a/arch/powerpc/include/asm/kvm_timing_stats.h b/arch/powerpc/include/asm/kvm_timing_stats.h
new file mode 100644
--- /dev/null
+++ b/arch/powerpc/include/asm/kvm_timing_stats.h
@@ -0,0 +1,176 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ */
+
+#ifndef __POWERPC_KVM_EXITTIMING_H__
+#define __POWERPC_KVM_EXITTIMING_H__
+
+#include <linux/kvm_host.h>
+#include <asm/time.h>
+#include <asm-generic/div64.h>
+#include <linux/sysrq.h>
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+static inline void init_timing_stats(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ /* pause guest execution to avoid concurrent updates */
+ local_irq_disable();
+ mutex_lock(&vcpu->mutex);
+
+ vcpu->arch.last_exit_type = 0xDEAD;
+ for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
+ vcpu->arch.timing_count_type[i] = 0;
+ vcpu->arch.timing_max_duration[i] = 0;
+ vcpu->arch.timing_min_duration[i] = 0xFFFFFFFF;
+ vcpu->arch.timing_sum_duration[i] = 0;
+ vcpu->arch.timing_sum_quad_duration[i] = 0;
+ }
+ vcpu->arch.timing_last_exit = 0;
+ vcpu->arch.timing_exit.tv64 = 0;
+ vcpu->arch.timing_last_enter.tv64 = 0;
+
+ mutex_unlock(&vcpu->mutex);
+ local_irq_enable();
+}
+
+static inline void add_exit_timing(struct kvm_vcpu *vcpu,
+ u64 duration, int type)
+{
+ u64 old;
+
+ do_div(duration, tb_ticks_per_usec);
+ if (unlikely(duration > 0xFFFFFFFF)) {
+ printk(KERN_ERR"%s - duration too big -> overflow"
+ " duration %lld type %d exit #%d\n",
+ __func__, duration, type,
+ vcpu->arch.timing_count_type[type]);
+ return;
+ }
+
+ vcpu->arch.timing_count_type[type]++;
+
+ /* sum */
+ old = vcpu->arch.timing_sum_duration[type];
+ vcpu->arch.timing_sum_duration[type] += duration;
+ if (unlikely(old > vcpu->arch.timing_sum_duration[type])) {
+ printk(KERN_ERR"%s - wrap adding sum of durations"
+ " old %lld new %lld type %d exit # of type %d\n",
+ __func__, old, vcpu->arch.timing_sum_duration[type],
+ type, vcpu->arch.timing_count_type[type]);
+ }
+
+ /* square sum */
+ old = vcpu->arch.timing_sum_quad_duration[type];
+ vcpu->arch.timing_sum_quad_duration[type] += (duration*duration);
+ if (unlikely(old > vcpu->arch.timing_sum_quad_duration[type])) {
+ printk(KERN_ERR"%s - wrap adding sum of squared durations"
+ " old %lld new %lld type %d exit # of type %d\n",
+ __func__, old,
+ vcpu->arch.timing_sum_quad_duration[type],
+ type, vcpu->arch.timing_count_type[type]);
+ }
+
+ /* set min/max */
+ if (unlikely(duration < vcpu->arch.timing_min_duration[type]))
+ vcpu->arch.timing_min_duration[type] = duration;
+ if (unlikely(duration > vcpu->arch.timing_max_duration[type]))
+ vcpu->arch.timing_max_duration[type] = duration;
+}
+
+static inline void update_timing_stats(struct kvm_vcpu *vcpu)
+{
+ u64 exit = vcpu->arch.timing_last_exit;
+ u64 enter = vcpu->arch.timing_last_enter.tv64;
+
+ /* save exit time, used next exit when the reenter time is known */
+ vcpu->arch.timing_last_exit = vcpu->arch.timing_exit.tv64;
+
+ if (unlikely(vcpu->arch.last_exit_type = 0xDEAD || exit = 0))
+ return; /* skip incomplete cycle (e.g. after reset) */
+
+ /* update statistics for average and standard deviation */
+ add_exit_timing(vcpu, (enter - exit), vcpu->arch.last_exit_type);
+ /* enter -> timing_last_exit is time spent in guest - log this too */
+ add_exit_timing(vcpu, (vcpu->arch.timing_last_exit - enter),
+ TIMEINGUEST);
+}
+#else
+#define init_timing_stats(x) do { } while (0)
+#define update_timing_stats(x) do { } while (0)
+#endif /* CONFIG_KVM_BOOKE_EXIT_TIMING */
+
+static inline void account_exit(struct kvm_vcpu *vcpu, int type)
+{
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ vcpu->arch.last_exit_type = type;
+#endif
+ /* type is usually known at build time */
+ switch (type) {
+ case EXT_INTR_EXITS:
+ vcpu->stat.ext_intr_exits++;
+ break;
+ case DEC_EXITS:
+ vcpu->stat.dec_exits++;
+ break;
+ case EMULATED_INST_EXITS:
+ case EMULATED_MTMSRWE_EXITS:
+ case EMULATED_COREOP_EXITS:
+ case EMULATED_COREMTSPR_EXITS:
+ case EMULATED_COREMFSPR_EXITS:
+ case EMULATED_COREMTMSR_EXITS:
+ case EMULATED_TLBSX_EXITS:
+ case EMULATED_TLBWE_EXITS:
+ case EMULATED_RFI_EXITS:
+ vcpu->stat.emulated_inst_exits++;
+ break;
+ case DCR_EXITS:
+ vcpu->stat.dcr_exits++;
+ break;
+ case DSI_EXITS:
+ vcpu->stat.dsi_exits++;
+ break;
+ case ISI_EXITS:
+ vcpu->stat.isi_exits++;
+ break;
+ case SYSCALL_EXITS:
+ vcpu->stat.syscall_exits++;
+ break;
+ case DTLB_REAL_MISS_EXITS:
+ vcpu->stat.dtlb_real_miss_exits++;
+ break;
+ case DTLB_VIRT_MISS_EXITS:
+ vcpu->stat.dtlb_virt_miss_exits++;
+ break;
+ case MMIO_EXITS:
+ vcpu->stat.mmio_exits++;
+ break;
+ case ITLB_REAL_MISS_EXITS:
+ vcpu->stat.itlb_real_miss_exits++;
+ break;
+ case ITLB_VIRT_MISS_EXITS:
+ vcpu->stat.itlb_virt_miss_exits++;
+ break;
+ case SIGNAL_EXITS:
+ vcpu->stat.signal_exits++;
+ break;
+ }
+}
+
+#endif /* __POWERPC_KVM_EXITTIMING_H__ */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -381,5 +381,16 @@ int main(void)
DEFINE(PTE_SHIFT, PTE_SHIFT);
#endif
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbu));
+ DEFINE(VCPU_TIMING_EXIT_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbl));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbu));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbl));
+#endif
+
return 0;
}
diff --git a/arch/powerpc/kvm/44x_emulate.c b/arch/powerpc/kvm/44x_emulate.c
--- a/arch/powerpc/kvm/44x_emulate.c
+++ b/arch/powerpc/kvm/44x_emulate.c
@@ -48,7 +48,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
unsigned int inst, int *advance)
{
- int emulated = EMULATE_DONE;
+ int emulated = EMULATE_COREOP_DONE;
int dcrn;
int ra;
int rb;
@@ -63,6 +63,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
switch (get_xop(inst)) {
case XOP_RFI:
kvmppc_emul_rfi(vcpu);
+ emulated = EMULATE_RFI_DONE;
*advance = 0;
break;
@@ -82,7 +83,10 @@ int kvmppc_core_emulate_op(struct kvm_ru
case XOP_MTMSR:
rs = get_rs(inst);
- kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
+ if (kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]) = EMULATE_MTMSRWE_DONE)
+ emulated = EMULATE_MTMSRWE_DONE;
+ else
+ emulated = EMULATE_COREMTMSR_DONE;
break;
case XOP_WRTEE:
@@ -276,7 +280,7 @@ int kvmppc_core_emulate_mtspr(struct kvm
return EMULATE_FAIL;
}
- return EMULATE_DONE;
+ return EMULATE_COREMTSPR_DONE;
}
int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
@@ -357,6 +361,6 @@ int kvmppc_core_emulate_mfspr(struct kvm
return EMULATE_FAIL;
}
- return EMULATE_DONE;
+ return EMULATE_COREMFSPR_DONE;
}
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -457,7 +457,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcp
KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
tlbe->word1, tlbe->word2, handler);
- return EMULATE_DONE;
+ return EMULATE_TLBWE_DONE;
}
int kvmppc_44x_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, u8 rc)
@@ -480,5 +480,5 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcp
}
vcpu->arch.gpr[rt] = gtlb_index;
- return EMULATE_DONE;
+ return EMULATE_TLBSX_DONE;
}
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -32,6 +32,15 @@ config KVM_440
If unsure, say N.
+config KVM_BOOKE_EXIT_TIMING
+ bool "Trace detailed exit Timing"
+ depends on KVM && 44x
+ ---help---
+ Inserts code to trace timestamps for every exit/enter cycle. A per vcpu
+ report is available in debugfs kvm/VM_###/VPCU_###_exit_timing.
+ The overhead is relatively small, however it is not recommended for
+ production environments.
+
config KVM_TRACE
bool "KVM trace support"
depends on KVM && MARKERS && SYSFS
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -28,6 +28,7 @@
#include <asm/cputable.h>
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
+#include <asm/kvm_timing_stats.h>
#include <asm/cacheflush.h>
#include <asm/kvm_44x.h>
@@ -185,6 +186,9 @@ int kvmppc_handle_exit(struct kvm_run *r
enum emulation_result er;
int r = RESUME_HOST;
+ /* update before a new last_exit_type is rewritten */
+ update_timing_stats(vcpu);
+
local_irq_enable();
run->exit_reason = KVM_EXIT_UNKNOWN;
@@ -198,7 +202,7 @@ int kvmppc_handle_exit(struct kvm_run *r
break;
case BOOKE_INTERRUPT_EXTERNAL:
- vcpu->stat.ext_intr_exits++;
+ account_exit(vcpu, EXT_INTR_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -208,8 +212,7 @@ int kvmppc_handle_exit(struct kvm_run *r
/* Since we switched IVPR back to the host's value, the host
* handled this interrupt the moment we enabled interrupts.
* Now we just offer it a chance to reschedule the guest. */
-
- vcpu->stat.dec_exits++;
+ account_exit(vcpu, DEC_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -222,20 +225,15 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
r = RESUME_GUEST;
+ account_exit(vcpu, USR_PR_INST);
break;
}
er = kvmppc_emulate_instruction(run, vcpu);
switch (er) {
- case EMULATE_DONE:
- /* Future optimization: only reload non-volatiles if
- * they were actually modified by emulation. */
- vcpu->stat.emulated_inst_exits++;
- r = RESUME_GUEST_NV;
- break;
case EMULATE_DO_DCR:
run->exit_reason = KVM_EXIT_DCR;
- vcpu->stat.dcr_exits++;
+ account_exit(vcpu, DCR_EXITS);
r = RESUME_HOST;
break;
case EMULATE_FAIL:
@@ -249,12 +247,45 @@ int kvmppc_handle_exit(struct kvm_run *r
r = RESUME_HOST;
break;
default:
- BUG();
+ switch (er) {
+ case EMULATE_DONE:
+ account_exit(vcpu, EMULATED_INST_EXITS);
+ break;
+ case EMULATE_COREOP_DONE:
+ account_exit(vcpu, EMULATED_COREOP_EXITS);
+ break;
+ case EMULATE_COREMTSPR_DONE:
+ account_exit(vcpu, EMULATED_COREMTSPR_EXITS);
+ break;
+ case EMULATE_COREMFSPR_DONE:
+ account_exit(vcpu, EMULATED_COREMFSPR_EXITS);
+ break;
+ case EMULATE_COREMTMSR_DONE:
+ account_exit(vcpu, EMULATED_COREMTMSR_EXITS);
+ break;
+ case EMULATE_MTMSRWE_DONE:
+ account_exit(vcpu, EMULATED_MTMSRWE_EXITS);
+ break;
+ case EMULATE_TLBSX_DONE:
+ account_exit(vcpu, EMULATED_TLBSX_EXITS);
+ break;
+ case EMULATE_TLBWE_DONE:
+ account_exit(vcpu, EMULATED_TLBWE_EXITS);
+ break;
+ case EMULATE_RFI_DONE:
+ account_exit(vcpu, EMULATED_RFI_EXITS);
+ break;
+ default:
+ BUG();
+ }
+ r = RESUME_GUEST_NV;
+ break;
}
break;
case BOOKE_INTERRUPT_FP_UNAVAIL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_FP_UNAVAIL);
+ account_exit(vcpu, FP_UNAVAIL);
r = RESUME_GUEST;
break;
@@ -262,20 +293,20 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
- vcpu->stat.dsi_exits++;
+ account_exit(vcpu, DSI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_INST_STORAGE:
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
- vcpu->stat.isi_exits++;
+ account_exit(vcpu, ISI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_SYSCALL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
- vcpu->stat.syscall_exits++;
+ account_exit(vcpu, SYSCALL_EXITS);
r = RESUME_GUEST;
break;
@@ -294,7 +325,7 @@ int kvmppc_handle_exit(struct kvm_run *r
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
- vcpu->stat.dtlb_real_miss_exits++;
+ account_exit(vcpu, DTLB_REAL_MISS_EXITS);
r = RESUME_GUEST;
break;
}
@@ -312,13 +343,13 @@ int kvmppc_handle_exit(struct kvm_run *r
* invoking the guest. */
kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
- vcpu->stat.dtlb_virt_miss_exits++;
+ account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
r = RESUME_GUEST;
} else {
/* Guest has mapped and accessed a page which is not
* actually RAM. */
r = kvmppc_emulate_mmio(run, vcpu);
- vcpu->stat.mmio_exits++;
+ account_exit(vcpu, MMIO_EXITS);
}
break;
@@ -340,11 +371,11 @@ int kvmppc_handle_exit(struct kvm_run *r
if (gtlb_index < 0) {
/* The guest didn't have a mapping for it. */
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
- vcpu->stat.itlb_real_miss_exits++;
+ account_exit(vcpu, ITLB_REAL_MISS_EXITS);
break;
}
- vcpu->stat.itlb_virt_miss_exits++;
+ account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
gpaddr = tlb_xlate(gtlbe, eaddr);
@@ -378,6 +409,7 @@ int kvmppc_handle_exit(struct kvm_run *r
mtspr(SPRN_DBSR, dbsr);
run->exit_reason = KVM_EXIT_DEBUG;
+ account_exit(vcpu, DEBUG_EXITS);
r = RESUME_HOST;
break;
}
@@ -398,7 +430,7 @@ int kvmppc_handle_exit(struct kvm_run *r
if (signal_pending(current)) {
run->exit_reason = KVM_EXIT_INTR;
r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV);
- vcpu->stat.signal_exits++;
+ account_exit(vcpu, SIGNAL_EXITS);
}
}
@@ -417,6 +449,8 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu
/* Eye-catching number so we know if the guest takes an interrupt
* before it's programmed its own IVPR. */
vcpu->arch.ivpr = 0x55550000;
+
+ init_timing_stats(vcpu);
return kvmppc_core_vcpu_setup(vcpu);
}
diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
--- a/arch/powerpc/kvm/booke.h
+++ b/arch/powerpc/kvm/booke.h
@@ -22,6 +22,7 @@
#include <linux/types.h>
#include <linux/kvm_host.h>
+#include <asm/kvm_timing_stats.h>
/* interrupt priortity ordering */
#define BOOKE_IRQPRIO_DATA_STORAGE 0
@@ -43,15 +44,17 @@
/* Helper function for "full" MSR writes. No need to call this if only EE is
* changing. */
-static inline void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
+static inline int kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
{
if ((new_msr & MSR_PR) != (vcpu->arch.msr & MSR_PR))
kvmppc_mmu_priv_switch(vcpu, new_msr & MSR_PR);
vcpu->arch.msr = new_msr;
- if (vcpu->arch.msr & MSR_WE)
+ if (vcpu->arch.msr & MSR_WE) {
kvm_vcpu_block(vcpu);
+ return EMULATE_MTMSRWE_DONE;
+ }
}
#endif /* __KVM_BOOKE_H__ */
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -106,6 +106,18 @@ _GLOBAL(kvmppc_resume_host)
li r6, 1
slw r6, r6, r5
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save exit time */
+..exit_tbu_overflow_loop:
+ mfspr r7, SPRN_TBRU
+ mfspr r8, SPRN_TBRL
+ mfspr r9, SPRN_TBRU
+ cmpw r9, r7
+ bne ..exit_tbu_overflow_loop
+ stw r8, VCPU_TIMING_EXIT_TBL(r4)
+ stw r9, VCPU_TIMING_EXIT_TBU(r4)
+#endif
/* Save the faulting instruction and all GPRs for emulation. */
andi. r7, r6, NEED_INST_MASK
@@ -375,6 +387,18 @@ lightweight_exit:
lwz r3, VCPU_SPRG7(r4)
mtspr SPRN_SPRG7, r3
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save enter time */
+..enter_tbu_overflow_loop:
+ mfspr r6, SPRN_TBRU
+ mfspr r7, SPRN_TBRL
+ mfspr r8, SPRN_TBRU
+ cmpw r8, r6
+ bne ..enter_tbu_overflow_loop
+ stw r7, VCPU_TIMING_LAST_ENTER_TBL(r4)
+ stw r8, VCPU_TIMING_LAST_ENTER_TBU(r4)
+#endif
+
/* Finish loading guest volatiles and jump to guest. */
lwz r3, VCPU_CTR(r4)
mtctr r3
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -28,7 +28,10 @@
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
#include <asm/tlbflush.h>
-
+#include <asm/kvm_timing_stats.h>
+#include <asm/atomic.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
{
@@ -101,6 +104,160 @@ void kvm_arch_check_processor_compat(voi
*(int *)rtn = kvmppc_core_check_processor_compat();
}
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+static const char *kvm_exit_names[__NUMBER_OF_KVM_EXIT_TYPES] = {
+ "MMIO",
+ "DCR",
+ "SIGNAL",
+ "ITLBREAL",
+ "ITLBVIRT",
+ "DTLBREAL",
+ "DTLBVIRT",
+ "SYSCALL",
+ "ISI",
+ "DSI",
+ "EMULINST",
+ "EMUL_MSR_WE",
+ "EMUL_CORE",
+ "EMUL_MTSPR",
+ "EMUL_MFSPR",
+ "EMUL_MTMSR",
+ "EMUL_TLBSX",
+ "EMUL_TLBWE",
+ "EMUL_RFI",
+ "DEC",
+ "EXTINT",
+ "HALT",
+ "USR_PR_INST",
+ "FP_UNAVAIL",
+ "DEBUG",
+ "TIMEINGUEST"
+};
+
+/* assign a unique number to each vm created */
+static atomic_t vm_count = ATOMIC_INIT(0);
+
+static int kvmppc_exit_timing_show(struct seq_file *m, void *private)
+{
+ struct kvm_vcpu *vcpu = m->private;
+ int i;
+ u64 min, max;
+
+ for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
+ if (vcpu->arch.timing_min_duration[i] = 0xFFFFFFFF)
+ min = 0;
+ else
+ min = vcpu->arch.timing_min_duration[i];
+ if (vcpu->arch.timing_max_duration[i] = 0)
+ max = 0;
+ else
+ max = vcpu->arch.timing_max_duration[i];
+
+ seq_printf(m, "%12s: count %10d min %10lld "
+ "max %10lld sum %20lld sum_quad %20lld\n",
+ kvm_exit_names[i], vcpu->arch.timing_count_type[i],
+ vcpu->arch.timing_min_duration[i],
+ vcpu->arch.timing_max_duration[i],
+ vcpu->arch.timing_sum_duration[i],
+ vcpu->arch.timing_sum_quad_duration[i]);
+ }
+ return 0;
+}
+
+static ssize_t kvmppc_exit_timing_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ size_t len;
+ int err;
+ const char __user *p;
+ char c;
+
+ len = 0;
+ p = user_buf;
+ while (len < count) {
+ if (get_user(c, p++))
+ err = -EFAULT;
+ if (c = 0 || c = '\n')
+ break;
+ len++;
+ }
+
+ if (len > 1) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&c, user_buf, sizeof(c))) {
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (c = 'c') {
+ struct seq_file *seqf = (struct seq_file *)file->private_data;
+ struct kvm_vcpu *vcpu = seqf->private;
+ /* write does not affect out buffers previsously generated with
+ * show. Seq file is locked here to prevent races of init with
+ * a show call */
+ mutex_lock(&seqf->lock);
+ init_timing_stats(vcpu);
+ mutex_unlock(&seqf->lock);
+ err = count;
+ } else {
+ err = -EINVAL;
+ goto done;
+ }
+
+done:
+ return err;
+}
+
+static int kvmppc_exit_timing_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, kvmppc_exit_timing_show, inode->i_private);
+}
+
+static struct file_operations kvmppc_exit_timing_fops = {
+ .owner = THIS_MODULE,
+ .open = kvmppc_exit_timing_open,
+ .read = seq_read,
+ .write = kvmppc_exit_timing_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id)
+{
+ static char dbg_fname[28];
+ struct dentry *debugfs_file;
+
+ snprintf(dbg_fname, sizeof(dbg_fname), "VM_%03u_VCPU_%03u_exit_timing",
+ vcpu->kvm->arch.vm_id, id);
+ debugfs_file = debugfs_create_file(dbg_fname, 0666,
+ kvm_debugfs_dir, vcpu,
+ &kvmppc_exit_timing_fops);
+
+ if (!debugfs_file) {
+ printk(KERN_ERR"%s: error creating debugfs file %s\n",
+ __func__, dbg_fname);
+ return;
+ }
+
+ vcpu->arch.debugfs_exit_timing = debugfs_file;
+}
+
+static void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu)
+{
+ if (vcpu->arch.debugfs_exit_timing) {
+ debugfs_remove(vcpu->arch.debugfs_exit_timing);
+ vcpu->arch.debugfs_exit_timing = NULL;
+ }
+}
+#else
+#define kvmppc_create_vcpu_debugfs(x, y) do { } while (0)
+#define kvmppc_remove_vcpu_debugfs(x) do { } while (0)
+#endif /* CONFIG_KVM_BOOKE_EXIT_TIMING */
+
struct kvm *kvm_arch_create_vm(void)
{
struct kvm *kvm;
@@ -108,6 +265,10 @@ struct kvm *kvm_arch_create_vm(void)
kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL);
if (!kvm)
return ERR_PTR(-ENOMEM);
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ kvm->arch.vm_id = atomic_inc_return(&vm_count);
+#endif
return kvm;
}
@@ -170,11 +331,15 @@ void kvm_arch_flush_shadow(struct kvm *k
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
{
- return kvmppc_core_vcpu_create(kvm, id);
+ struct kvm_vcpu *vcpu;
+ vcpu = kvmppc_core_vcpu_create(kvm, id);
+ kvmppc_create_vcpu_debugfs(vcpu, id);
+ return vcpu;
}
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
+ kvmppc_remove_vcpu_debugfs(vcpu);
kvmppc_core_vcpu_free(vcpu);
}
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] kvm: powerpc: add exit timing statistics v3
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
2008-11-10 17:39 ` [PATCH] kvm: powerpc: add exit timing statistics v2 Christian Ehrhardt
@ 2008-11-11 14:48 ` Christian Ehrhardt
2008-11-11 14:55 ` Christian Ehrhardt
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Christian Ehrhardt @ 2008-11-11 14:48 UTC (permalink / raw)
To: kvm-ppc
From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
*update to v3*
- ensure build time optimization when calling exit accouting functions using
build time bug / constant check
- migrate most of the exit timing code from powerpc.c and
kvm_timing_stats.h to a separate exittiming.c file
- renamed a lot of constants used to better fit generic/core specific code
- added an accidenially removed optimization comment
- use pid of userspace process instead of an own atomic count to identify a vm
- changed loop labels in tul/tbu loops (booke_interrupt.S) to anonymous 1/1b
- removed the indirection of additional EMULATE_*_DONE types. Instead now
the exit timing supports "accounting" an exit which consists of set type and
increase kvm_stat counters. But also provides both sub-tasks as separate
functions. Using that emulation now sets a default EMUL_INST type that can
be overwritten by detailed subcategories (set_exit_type). Accouting for
kvm_stat is done with account_exit_stat for all emulinst exits together on
top level (as it was before).
*update to v2*
The update fixes accounting for sets to MSR[WE] which should not be accoutned
as instruction emulation. While adding that and analyzing the data it became
obvious that several types of emulations hould be accounted separately.
I'm not yet really happy with adding all these EMULATE_*_DONE types but I had
no better idea to not break flood the code with account calls and split
account/set_type. The issue is that emulation is also called e.g. for some mmio
paths and therefore the accouting should not be called inside emulation, but on
the returning path that can now evaluate the different kind of emulations done.
This is not yet finished, and posted as RFC only.
Other existing kvm statistics are either just counters (kvm_stat) reported for
kvm generally or trace based aproaches like kvm_trace.
For kvm on powerpc we had the need to track the timings of the different exit
types. While this could be achieved parsing data created with a kvm_trace
extension this adds too muhc overhead (at least on embedded powerpc) slowing
down the workloads we wanted to measure.
Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
code. These statistic is available per vm&vcpu under the kvm debugfs directory.
As this statistic is low, but still some overhead it can be enabled via a
.config entry and should be off by default.
Since this patch touched all powerpc kvm_stat code anyway this code is now
merged and simpliefied together with the exit timing statistic code (still
working with exit timing disabled in .config).
* another update in v3*
An updated awk script printing the data in a more narrow layout can be found
on our wiki pages about the exit timing topic.
(http://kvm.qumranet.com/kvmwiki/PowerPC_Exittimings)
Here is a sample output how the results look like.
processing file timing_boot.log
sum of time 8309940
type count min max sum avg stddev %
MMIO: 9402 44 1997 1697610 180.5584 155.768 20.43
DCR: 680 41 99 32096 47.2000 7.008 0.39
SIGNAL: 1 98 98 98 98.0000 0.000 0.00
ITLBREAL: 926 8 14 7810 8.4341 0.658 0.09
ITLBVIRT: 3595 18 202 76185 21.1919 4.954 0.92
DTLBREAL: 950 8 16 8891 9.3589 1.427 0.11
DTLBVIRT: 6695 18 282 156727 23.4096 13.781 1.89
SYSCALL: 1801 6 59 11372 6.3143 2.575 0.14
ISI: 116 6 8 764 6.5862 0.588 0.01
DSI: 43 6 7 292 6.7907 0.407 0.00
EMULINST: 65247 7 96 484081 7.4192 1.818 5.83
EMUL_WAIT: 801 659 9200 3721789 4646.4282 1687.218 44.79
EMUL_CORE: 66806 7 86 540053 8.0839 1.895 6.50
EMUL_MTSPR: 13415 8 62 111358 8.3010 2.583 1.34
EMUL_MFSPR: 7635 8 61 62772 8.2216 1.921 0.76
EMUL_MTMSR: 5678 8 59 45704 8.0493 1.434 0.55
EMUL_MFMSR: 32853 7 67 267603 8.1455 1.875 3.22
EMUL_TLBSX: 354 9 60 3745 10.5791 3.919 0.05
EMUL_TLBWE: 6403 9 112 99522 15.5430 7.668 1.20
EMUL_RFI: 9515 7 57 71420 7.5060 2.108 0.86
DEC: 290 49 161 15786 54.4345 9.708 0.19
EXTINT: 7 74 75 522 74.5714 0.495 0.01
TIMEINGUEST: 233213 0 3954 893740 3.8323 65.837 10.76
Signed-off-by: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
---
[diffstat]
include/asm/kvm_host.h | 59 ++++++++++++++++++++++++
include/asm/kvm_ppc.h | 8 +--
include/asm/kvm_timing_stats.h | 100 +++++++++++++++++++++++++++++++++++++++++
kernel/asm-offsets.c | 11 ++++
kvm/44x_emulate.c | 10 +++-
kvm/44x_tlb.c | 3 +
kvm/Kconfig | 9 +++
kvm/Makefile | 1
kvm/booke.c | 36 +++++++++-----
kvm/booke.h | 5 +-
kvm/booke_interrupts.S | 24 +++++++++
kvm/emulate.c | 4 +
kvm/powerpc.c | 11 +++-
13 files changed, 259 insertions(+), 22 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -71,7 +71,53 @@ struct kvmppc_44x_tlbe {
u32 word2;
};
+enum kvm_exit_types {
+ MMIO_EXITS,
+ DCR_EXITS,
+ SIGNAL_EXITS,
+ ITLB_REAL_MISS_EXITS,
+ ITLB_VIRT_MISS_EXITS,
+ DTLB_REAL_MISS_EXITS,
+ DTLB_VIRT_MISS_EXITS,
+ SYSCALL_EXITS,
+ ISI_EXITS,
+ DSI_EXITS,
+ EMULATED_INST_EXITS,
+ EMULATED_MTMSRWE_EXITS,
+ EMULATED_COREOP_EXITS,
+ EMULATED_MTSPR_EXITS,
+ EMULATED_MFSPR_EXITS,
+ EMULATED_MTMSR_EXITS,
+ EMULATED_MFMSR_EXITS,
+ EMULATED_TLBSX_EXITS,
+ EMULATED_TLBWE_EXITS,
+ EMULATED_RFI_EXITS,
+ DEC_EXITS,
+ EXT_INTR_EXITS,
+ HALT_WAKEUP,
+ USR_PR_INST,
+ FP_UNAVAIL,
+ DEBUG_EXITS,
+ TIMEINGUEST,
+ __NUMBER_OF_KVM_EXIT_TYPES
+};
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+/* allow access to big endian 32bit upper/lower parts and 64bit var */
+struct exit_timing {
+ union {
+ u64 tv64;
+ struct {
+ u32 tbu, tbl;
+ } tv32;
+ };
+};
+#endif
+
struct kvm_arch {
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ unsigned int vm_id;
+#endif
};
struct kvm_vcpu_arch {
@@ -130,6 +176,19 @@ struct kvm_vcpu_arch {
u32 dbcr0;
u32 dbcr1;
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ struct exit_timing timing_exit;
+ struct exit_timing timing_last_enter;
+ u32 last_exit_type;
+ u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_quad_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_min_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_max_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_last_exit;
+ struct dentry *debugfs_exit_timing;
+#endif
+
u32 last_inst;
ulong fault_dear;
ulong fault_esr;
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -30,10 +30,10 @@
#include <linux/kvm_host.h>
enum emulation_result {
- EMULATE_DONE, /* no further processing */
- EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
- EMULATE_DO_DCR, /* kvm_run filled with DCR request */
- EMULATE_FAIL, /* can't emulate this instruction */
+ EMULATE_DONE, /* no further processing */
+ EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
+ EMULATE_DO_DCR, /* kvm_run filled with DCR request */
+ EMULATE_FAIL, /* can't emulate this instruction */
};
extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
diff --git a/arch/powerpc/include/asm/kvm_timing_stats.h b/arch/powerpc/include/asm/kvm_timing_stats.h
new file mode 100644
--- /dev/null
+++ b/arch/powerpc/include/asm/kvm_timing_stats.h
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ */
+
+#ifndef __POWERPC_KVM_EXITTIMING_H__
+#define __POWERPC_KVM_EXITTIMING_H__
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_host.h>
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+void init_timing_stats(struct kvm_vcpu *vcpu);
+void update_timing_stats(struct kvm_vcpu *vcpu);
+void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id);
+void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu);
+
+static inline void set_exit_type(struct kvm_vcpu *vcpu, int type)
+{
+ vcpu->arch.last_exit_type = type;
+}
+
+#else
+#define init_timing_stats(x) do { } while (0)
+#define update_timing_stats(x) do { } while (0)
+#define kvmppc_create_vcpu_debugfs(x, y) do { } while (0)
+#define kvmppc_remove_vcpu_debugfs(x) do { } while (0)
+#define set_exit_type(x, y) do { } while (0)
+#endif /* CONFIG_KVM_BOOKE_EXIT_TIMING */
+
+/* account the exit in kvm_stats */
+static inline void account_exit_stat(struct kvm_vcpu *vcpu, int type)
+{
+ /* type has to be known at build time for optimization */
+ BUILD_BUG_ON(__builtin_constant_p(type));
+ switch (type) {
+ case EXT_INTR_EXITS:
+ vcpu->stat.ext_intr_exits++;
+ break;
+ case DEC_EXITS:
+ vcpu->stat.dec_exits++;
+ break;
+ case EMULATED_INST_EXITS:
+ vcpu->stat.emulated_inst_exits++;
+ break;
+ case DCR_EXITS:
+ vcpu->stat.dcr_exits++;
+ break;
+ case DSI_EXITS:
+ vcpu->stat.dsi_exits++;
+ break;
+ case ISI_EXITS:
+ vcpu->stat.isi_exits++;
+ break;
+ case SYSCALL_EXITS:
+ vcpu->stat.syscall_exits++;
+ break;
+ case DTLB_REAL_MISS_EXITS:
+ vcpu->stat.dtlb_real_miss_exits++;
+ break;
+ case DTLB_VIRT_MISS_EXITS:
+ vcpu->stat.dtlb_virt_miss_exits++;
+ break;
+ case MMIO_EXITS:
+ vcpu->stat.mmio_exits++;
+ break;
+ case ITLB_REAL_MISS_EXITS:
+ vcpu->stat.itlb_real_miss_exits++;
+ break;
+ case ITLB_VIRT_MISS_EXITS:
+ vcpu->stat.itlb_virt_miss_exits++;
+ break;
+ case SIGNAL_EXITS:
+ vcpu->stat.signal_exits++;
+ break;
+ }
+}
+
+/* wrapper to set exit time and account for it in kvm_stats */
+static inline void account_exit(struct kvm_vcpu *vcpu, int type)
+{
+ set_exit_type(vcpu, type);
+ account_exit_stat(vcpu, type);
+}
+
+#endif /* __POWERPC_KVM_EXITTIMING_H__ */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -381,5 +381,16 @@ int main(void)
DEFINE(PTE_SHIFT, PTE_SHIFT);
#endif
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbu));
+ DEFINE(VCPU_TIMING_EXIT_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbl));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbu));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbl));
+#endif
+
return 0;
}
diff --git a/arch/powerpc/kvm/44x_emulate.c b/arch/powerpc/kvm/44x_emulate.c
--- a/arch/powerpc/kvm/44x_emulate.c
+++ b/arch/powerpc/kvm/44x_emulate.c
@@ -22,6 +22,7 @@
#include <asm/dcr-regs.h>
#include <asm/disassemble.h>
#include <asm/kvm_44x.h>
+#include <asm/kvm_timing_stats.h>
#include "booke.h"
#include "44x_tlb.h"
@@ -57,12 +58,13 @@ int kvmppc_core_emulate_op(struct kvm_ru
int rt;
int ws;
+ set_exit_type(vcpu, EMULATED_COREOP_EXITS);
switch (get_op(inst)) {
-
case OP_RFI:
switch (get_xop(inst)) {
case XOP_RFI:
kvmppc_emul_rfi(vcpu);
+ set_exit_type(vcpu, EMULATED_RFI_EXITS);
*advance = 0;
break;
@@ -78,10 +80,12 @@ int kvmppc_core_emulate_op(struct kvm_ru
case XOP_MFMSR:
rt = get_rt(inst);
vcpu->arch.gpr[rt] = vcpu->arch.msr;
+ set_exit_type(vcpu, EMULATED_MFMSR_EXITS);
break;
case XOP_MTMSR:
rs = get_rs(inst);
+ set_exit_type(vcpu, EMULATED_MTMSR_EXITS);
kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
break;
@@ -127,6 +131,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.is_write = 0;
vcpu->arch.io_gpr = rt;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -146,6 +151,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.data = vcpu->arch.gpr[rs];
run->dcr.is_write = 1;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -276,6 +282,7 @@ int kvmppc_core_emulate_mtspr(struct kvm
return EMULATE_FAIL;
}
+ set_exit_type(vcpu, EMULATED_MTSPR_EXITS);
return EMULATE_DONE;
}
@@ -357,6 +364,7 @@ int kvmppc_core_emulate_mfspr(struct kvm
return EMULATE_FAIL;
}
+ set_exit_type(vcpu, EMULATED_MFSPR_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -27,6 +27,7 @@
#include <asm/mmu-44x.h>
#include <asm/kvm_ppc.h>
#include <asm/kvm_44x.h>
+#include <asm/kvm_timing_stats.h>
#include "44x_tlb.h"
@@ -457,6 +458,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcp
KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
tlbe->word1, tlbe->word2, handler);
+ set_exit_type(vcpu, EMULATED_TLBWE_EXITS);
return EMULATE_DONE;
}
@@ -480,5 +482,6 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcp
}
vcpu->arch.gpr[rt] = gtlb_index;
+ set_exit_type(vcpu, EMULATED_TLBSX_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -32,6 +32,15 @@ config KVM_440
If unsure, say N.
+config KVM_BOOKE_EXIT_TIMING
+ bool "Trace detailed exit Timing"
+ depends on KVM && 44x
+ ---help---
+ Inserts code to trace timestamps for every exit/enter cycle. A per vcpu
+ report is available in debugfs kvm/VM_###/VPCU_###_exit_timing.
+ The overhead is relatively small, however it is not recommended for
+ production environments.
+
config KVM_TRACE
bool "KVM trace support"
depends on KVM && MARKERS && SYSFS
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -9,6 +9,7 @@ common-objs-$(CONFIG_KVM_TRACE) += $(ad
common-objs-$(CONFIG_KVM_TRACE) += $(addprefix ../../../virt/kvm/, kvm_trace.o)
kvm-objs := $(common-objs-y) powerpc.o emulate.o
+obj-$(CONFIG_KVM_BOOKE_EXIT_TIMING) += exittiming.o
obj-$(CONFIG_KVM) += kvm.o
AFLAGS_booke_interrupts.o := -I$(obj)
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -28,6 +28,7 @@
#include <asm/cputable.h>
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
+#include <asm/kvm_timing_stats.h>
#include <asm/cacheflush.h>
#include <asm/kvm_44x.h>
@@ -185,6 +186,9 @@ int kvmppc_handle_exit(struct kvm_run *r
enum emulation_result er;
int r = RESUME_HOST;
+ /* update before a new last_exit_type is rewritten */
+ update_timing_stats(vcpu);
+
local_irq_enable();
run->exit_reason = KVM_EXIT_UNKNOWN;
@@ -198,7 +202,7 @@ int kvmppc_handle_exit(struct kvm_run *r
break;
case BOOKE_INTERRUPT_EXTERNAL:
- vcpu->stat.ext_intr_exits++;
+ account_exit(vcpu, EXT_INTR_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -208,8 +212,7 @@ int kvmppc_handle_exit(struct kvm_run *r
/* Since we switched IVPR back to the host's value, the host
* handled this interrupt the moment we enabled interrupts.
* Now we just offer it a chance to reschedule the guest. */
-
- vcpu->stat.dec_exits++;
+ account_exit(vcpu, DEC_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -222,20 +225,21 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
r = RESUME_GUEST;
+ account_exit(vcpu, USR_PR_INST);
break;
}
er = kvmppc_emulate_instruction(run, vcpu);
switch (er) {
case EMULATE_DONE:
+ /* don't overwrite subtypes, just account kvm_stats */
+ account_exit_stat(vcpu, EMULATED_INST_EXITS);
/* Future optimization: only reload non-volatiles if
* they were actually modified by emulation. */
- vcpu->stat.emulated_inst_exits++;
r = RESUME_GUEST_NV;
break;
case EMULATE_DO_DCR:
run->exit_reason = KVM_EXIT_DCR;
- vcpu->stat.dcr_exits++;
r = RESUME_HOST;
break;
case EMULATE_FAIL:
@@ -255,6 +259,7 @@ int kvmppc_handle_exit(struct kvm_run *r
case BOOKE_INTERRUPT_FP_UNAVAIL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_FP_UNAVAIL);
+ account_exit(vcpu, FP_UNAVAIL);
r = RESUME_GUEST;
break;
@@ -262,20 +267,20 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
- vcpu->stat.dsi_exits++;
+ account_exit(vcpu, DSI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_INST_STORAGE:
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
- vcpu->stat.isi_exits++;
+ account_exit(vcpu, ISI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_SYSCALL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
- vcpu->stat.syscall_exits++;
+ account_exit(vcpu, SYSCALL_EXITS);
r = RESUME_GUEST;
break;
@@ -294,7 +299,7 @@ int kvmppc_handle_exit(struct kvm_run *r
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
- vcpu->stat.dtlb_real_miss_exits++;
+ account_exit(vcpu, DTLB_REAL_MISS_EXITS);
r = RESUME_GUEST;
break;
}
@@ -312,13 +317,13 @@ int kvmppc_handle_exit(struct kvm_run *r
* invoking the guest. */
kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
- vcpu->stat.dtlb_virt_miss_exits++;
+ account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
r = RESUME_GUEST;
} else {
/* Guest has mapped and accessed a page which is not
* actually RAM. */
r = kvmppc_emulate_mmio(run, vcpu);
- vcpu->stat.mmio_exits++;
+ account_exit(vcpu, MMIO_EXITS);
}
break;
@@ -340,11 +345,11 @@ int kvmppc_handle_exit(struct kvm_run *r
if (gtlb_index < 0) {
/* The guest didn't have a mapping for it. */
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
- vcpu->stat.itlb_real_miss_exits++;
+ account_exit(vcpu, ITLB_REAL_MISS_EXITS);
break;
}
- vcpu->stat.itlb_virt_miss_exits++;
+ account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
gpaddr = tlb_xlate(gtlbe, eaddr);
@@ -378,6 +383,7 @@ int kvmppc_handle_exit(struct kvm_run *r
mtspr(SPRN_DBSR, dbsr);
run->exit_reason = KVM_EXIT_DEBUG;
+ account_exit(vcpu, DEBUG_EXITS);
r = RESUME_HOST;
break;
}
@@ -398,7 +404,7 @@ int kvmppc_handle_exit(struct kvm_run *r
if (signal_pending(current)) {
run->exit_reason = KVM_EXIT_INTR;
r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV);
- vcpu->stat.signal_exits++;
+ account_exit(vcpu, SIGNAL_EXITS);
}
}
@@ -417,6 +423,8 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu
/* Eye-catching number so we know if the guest takes an interrupt
* before it's programmed its own IVPR. */
vcpu->arch.ivpr = 0x55550000;
+
+ init_timing_stats(vcpu);
return kvmppc_core_vcpu_setup(vcpu);
}
diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
--- a/arch/powerpc/kvm/booke.h
+++ b/arch/powerpc/kvm/booke.h
@@ -22,6 +22,7 @@
#include <linux/types.h>
#include <linux/kvm_host.h>
+#include <asm/kvm_timing_stats.h>
/* interrupt priortity ordering */
#define BOOKE_IRQPRIO_DATA_STORAGE 0
@@ -50,8 +51,10 @@ static inline void kvmppc_set_msr(struct
vcpu->arch.msr = new_msr;
- if (vcpu->arch.msr & MSR_WE)
+ if (vcpu->arch.msr & MSR_WE) {
kvm_vcpu_block(vcpu);
+ set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
+ };
}
#endif /* __KVM_BOOKE_H__ */
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -106,6 +106,18 @@ _GLOBAL(kvmppc_resume_host)
li r6, 1
slw r6, r6, r5
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save exit time */
+1:
+ mfspr r7, SPRN_TBRU
+ mfspr r8, SPRN_TBRL
+ mfspr r9, SPRN_TBRU
+ cmpw r9, r7
+ bne 1b
+ stw r8, VCPU_TIMING_EXIT_TBL(r4)
+ stw r9, VCPU_TIMING_EXIT_TBU(r4)
+#endif
/* Save the faulting instruction and all GPRs for emulation. */
andi. r7, r6, NEED_INST_MASK
@@ -375,6 +387,18 @@ lightweight_exit:
lwz r3, VCPU_SPRG7(r4)
mtspr SPRN_SPRG7, r3
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save enter time */
+1:
+ mfspr r6, SPRN_TBRU
+ mfspr r7, SPRN_TBRL
+ mfspr r8, SPRN_TBRU
+ cmpw r8, r6
+ bne 1b
+ stw r7, VCPU_TIMING_LAST_ENTER_TBL(r4)
+ stw r8, VCPU_TIMING_LAST_ENTER_TBU(r4)
+#endif
+
/* Finish loading guest volatiles and jump to guest. */
lwz r3, VCPU_CTR(r4)
mtctr r3
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -28,6 +28,7 @@
#include <asm/byteorder.h>
#include <asm/kvm_ppc.h>
#include <asm/disassemble.h>
+#include <asm/kvm_timing_stats.h>
void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
{
@@ -72,6 +73,9 @@ int kvmppc_emulate_instruction(struct kv
int sprn;
enum emulation_result emulated = EMULATE_DONE;
int advance = 1;
+
+ /* this default type might be overwritten by subcategories */
+ set_exit_type(vcpu, EMULATED_INST_EXITS);
switch (get_op(inst)) {
case 3: /* trap */
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -28,7 +28,10 @@
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
#include <asm/tlbflush.h>
-
+#include <asm/kvm_timing_stats.h>
+#include <asm/atomic.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
{
@@ -170,11 +173,15 @@ void kvm_arch_flush_shadow(struct kvm *k
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
{
- return kvmppc_core_vcpu_create(kvm, id);
+ struct kvm_vcpu *vcpu;
+ vcpu = kvmppc_core_vcpu_create(kvm, id);
+ kvmppc_create_vcpu_debugfs(vcpu, id);
+ return vcpu;
}
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
+ kvmppc_remove_vcpu_debugfs(vcpu);
kvmppc_core_vcpu_free(vcpu);
}
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] kvm: powerpc: add exit timing statistics v3
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
2008-11-10 17:39 ` [PATCH] kvm: powerpc: add exit timing statistics v2 Christian Ehrhardt
2008-11-11 14:48 ` [PATCH] kvm: powerpc: add exit timing statistics v3 Christian Ehrhardt
@ 2008-11-11 14:55 ` Christian Ehrhardt
2008-11-11 15:29 ` Christian Ehrhardt
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Christian Ehrhardt @ 2008-11-11 14:55 UTC (permalink / raw)
To: kvm-ppc
And btw - the wiki page is updated with new versions of the exittimings
using all of our new memory management improvements.
The page now also has some extended descriptions about the simple
workloads used, holds the current version of the postprocessing script etc.
Christian Ehrhardt wrote:
> From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
>
> *update to v3*
> - ensure build time optimization when calling exit accouting functions using
> build time bug / constant check
> - migrate most of the exit timing code from powerpc.c and
> kvm_timing_stats.h to a separate exittiming.c file
> - renamed a lot of constants used to better fit generic/core specific code
> - added an accidenially removed optimization comment
> - use pid of userspace process instead of an own atomic count to identify a vm
> - changed loop labels in tul/tbu loops (booke_interrupt.S) to anonymous 1/1b
> - removed the indirection of additional EMULATE_*_DONE types. Instead now
> the exit timing supports "accounting" an exit which consists of set type and
> increase kvm_stat counters. But also provides both sub-tasks as separate
> functions. Using that emulation now sets a default EMUL_INST type that can
> be overwritten by detailed subcategories (set_exit_type). Accouting for
> kvm_stat is done with account_exit_stat for all emulinst exits together on
> top level (as it was before).
>
> *update to v2*
> The update fixes accounting for sets to MSR[WE] which should not be accoutned
> as instruction emulation. While adding that and analyzing the data it became
> obvious that several types of emulations hould be accounted separately.
> I'm not yet really happy with adding all these EMULATE_*_DONE types but I had
> no better idea to not break flood the code with account calls and split
> account/set_type. The issue is that emulation is also called e.g. for some mmio
> paths and therefore the accouting should not be called inside emulation, but on
> the returning path that can now evaluate the different kind of emulations done.
> This is not yet finished, and posted as RFC only.
>
> Other existing kvm statistics are either just counters (kvm_stat) reported for
> kvm generally or trace based aproaches like kvm_trace.
> For kvm on powerpc we had the need to track the timings of the different exit
> types. While this could be achieved parsing data created with a kvm_trace
> extension this adds too muhc overhead (at least on embedded powerpc) slowing
> down the workloads we wanted to measure.
>
> Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
> code. These statistic is available per vm&vcpu under the kvm debugfs directory.
> As this statistic is low, but still some overhead it can be enabled via a
> .config entry and should be off by default.
>
> Since this patch touched all powerpc kvm_stat code anyway this code is now
> merged and simpliefied together with the exit timing statistic code (still
> working with exit timing disabled in .config).
>
> * another update in v3*
> An updated awk script printing the data in a more narrow layout can be found
> on our wiki pages about the exit timing topic.
> (http://kvm.qumranet.com/kvmwiki/PowerPC_Exittimings)
> Here is a sample output how the results look like.
> processing file timing_boot.log
> sum of time 8309940
> type count min max sum avg stddev %
> MMIO: 9402 44 1997 1697610 180.5584 155.768 20.43
> DCR: 680 41 99 32096 47.2000 7.008 0.39
> SIGNAL: 1 98 98 98 98.0000 0.000 0.00
> ITLBREAL: 926 8 14 7810 8.4341 0.658 0.09
> ITLBVIRT: 3595 18 202 76185 21.1919 4.954 0.92
> DTLBREAL: 950 8 16 8891 9.3589 1.427 0.11
> DTLBVIRT: 6695 18 282 156727 23.4096 13.781 1.89
> SYSCALL: 1801 6 59 11372 6.3143 2.575 0.14
> ISI: 116 6 8 764 6.5862 0.588 0.01
> DSI: 43 6 7 292 6.7907 0.407 0.00
> EMULINST: 65247 7 96 484081 7.4192 1.818 5.83
> EMUL_WAIT: 801 659 9200 3721789 4646.4282 1687.218 44.79
> EMUL_CORE: 66806 7 86 540053 8.0839 1.895 6.50
> EMUL_MTSPR: 13415 8 62 111358 8.3010 2.583 1.34
> EMUL_MFSPR: 7635 8 61 62772 8.2216 1.921 0.76
> EMUL_MTMSR: 5678 8 59 45704 8.0493 1.434 0.55
> EMUL_MFMSR: 32853 7 67 267603 8.1455 1.875 3.22
> EMUL_TLBSX: 354 9 60 3745 10.5791 3.919 0.05
> EMUL_TLBWE: 6403 9 112 99522 15.5430 7.668 1.20
> EMUL_RFI: 9515 7 57 71420 7.5060 2.108 0.86
> DEC: 290 49 161 15786 54.4345 9.708 0.19
> EXTINT: 7 74 75 522 74.5714 0.495 0.01
> TIMEINGUEST: 233213 0 3954 893740 3.8323 65.837 10.76
>
> Signed-off-by: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
> ---
>
> [diffstat]
> include/asm/kvm_host.h | 59 ++++++++++++++++++++++++
> include/asm/kvm_ppc.h | 8 +--
> include/asm/kvm_timing_stats.h | 100 +++++++++++++++++++++++++++++++++++++++++
> kernel/asm-offsets.c | 11 ++++
> kvm/44x_emulate.c | 10 +++-
> kvm/44x_tlb.c | 3 +
> kvm/Kconfig | 9 +++
> kvm/Makefile | 1
> kvm/booke.c | 36 +++++++++-----
> kvm/booke.h | 5 +-
> kvm/booke_interrupts.S | 24 +++++++++
> kvm/emulate.c | 4 +
> kvm/powerpc.c | 11 +++-
> 13 files changed, 259 insertions(+), 22 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
> --- a/arch/powerpc/include/asm/kvm_host.h
> +++ b/arch/powerpc/include/asm/kvm_host.h
> @@ -71,7 +71,53 @@ struct kvmppc_44x_tlbe {
> u32 word2;
> };
>
> +enum kvm_exit_types {
> + MMIO_EXITS,
> + DCR_EXITS,
> + SIGNAL_EXITS,
> + ITLB_REAL_MISS_EXITS,
> + ITLB_VIRT_MISS_EXITS,
> + DTLB_REAL_MISS_EXITS,
> + DTLB_VIRT_MISS_EXITS,
> + SYSCALL_EXITS,
> + ISI_EXITS,
> + DSI_EXITS,
> + EMULATED_INST_EXITS,
> + EMULATED_MTMSRWE_EXITS,
> + EMULATED_COREOP_EXITS,
> + EMULATED_MTSPR_EXITS,
> + EMULATED_MFSPR_EXITS,
> + EMULATED_MTMSR_EXITS,
> + EMULATED_MFMSR_EXITS,
> + EMULATED_TLBSX_EXITS,
> + EMULATED_TLBWE_EXITS,
> + EMULATED_RFI_EXITS,
> + DEC_EXITS,
> + EXT_INTR_EXITS,
> + HALT_WAKEUP,
> + USR_PR_INST,
> + FP_UNAVAIL,
> + DEBUG_EXITS,
> + TIMEINGUEST,
> + __NUMBER_OF_KVM_EXIT_TYPES
> +};
> +
> +#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
> +/* allow access to big endian 32bit upper/lower parts and 64bit var */
> +struct exit_timing {
> + union {
> + u64 tv64;
> + struct {
> + u32 tbu, tbl;
> + } tv32;
> + };
> +};
> +#endif
> +
> struct kvm_arch {
> +#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
> + unsigned int vm_id;
> +#endif
> };
>
> struct kvm_vcpu_arch {
> @@ -130,6 +176,19 @@ struct kvm_vcpu_arch {
> u32 dbcr0;
> u32 dbcr1;
>
> +#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
> + struct exit_timing timing_exit;
> + struct exit_timing timing_last_enter;
> + u32 last_exit_type;
> + u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES];
> + u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES];
> + u64 timing_sum_quad_duration[__NUMBER_OF_KVM_EXIT_TYPES];
> + u64 timing_min_duration[__NUMBER_OF_KVM_EXIT_TYPES];
> + u64 timing_max_duration[__NUMBER_OF_KVM_EXIT_TYPES];
> + u64 timing_last_exit;
> + struct dentry *debugfs_exit_timing;
> +#endif
> +
> u32 last_inst;
> ulong fault_dear;
> ulong fault_esr;
> diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
> --- a/arch/powerpc/include/asm/kvm_ppc.h
> +++ b/arch/powerpc/include/asm/kvm_ppc.h
> @@ -30,10 +30,10 @@
> #include <linux/kvm_host.h>
>
> enum emulation_result {
> - EMULATE_DONE, /* no further processing */
> - EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
> - EMULATE_DO_DCR, /* kvm_run filled with DCR request */
> - EMULATE_FAIL, /* can't emulate this instruction */
> + EMULATE_DONE, /* no further processing */
> + EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
> + EMULATE_DO_DCR, /* kvm_run filled with DCR request */
> + EMULATE_FAIL, /* can't emulate this instruction */
> };
>
> extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
> diff --git a/arch/powerpc/include/asm/kvm_timing_stats.h b/arch/powerpc/include/asm/kvm_timing_stats.h
> new file mode 100644
> --- /dev/null
> +++ b/arch/powerpc/include/asm/kvm_timing_stats.h
> @@ -0,0 +1,100 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + * Copyright IBM Corp. 2008
> + *
> + * Authors: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
> + */
> +
> +#ifndef __POWERPC_KVM_EXITTIMING_H__
> +#define __POWERPC_KVM_EXITTIMING_H__
> +
> +#include <linux/kvm_host.h>
> +#include <asm/kvm_host.h>
> +
> +#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
> +void init_timing_stats(struct kvm_vcpu *vcpu);
> +void update_timing_stats(struct kvm_vcpu *vcpu);
> +void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id);
> +void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu);
> +
> +static inline void set_exit_type(struct kvm_vcpu *vcpu, int type)
> +{
> + vcpu->arch.last_exit_type = type;
> +}
> +
> +#else
> +#define init_timing_stats(x) do { } while (0)
> +#define update_timing_stats(x) do { } while (0)
> +#define kvmppc_create_vcpu_debugfs(x, y) do { } while (0)
> +#define kvmppc_remove_vcpu_debugfs(x) do { } while (0)
> +#define set_exit_type(x, y) do { } while (0)
> +#endif /* CONFIG_KVM_BOOKE_EXIT_TIMING */
> +
> +/* account the exit in kvm_stats */
> +static inline void account_exit_stat(struct kvm_vcpu *vcpu, int type)
> +{
> + /* type has to be known at build time for optimization */
> + BUILD_BUG_ON(__builtin_constant_p(type));
> + switch (type) {
> + case EXT_INTR_EXITS:
> + vcpu->stat.ext_intr_exits++;
> + break;
> + case DEC_EXITS:
> + vcpu->stat.dec_exits++;
> + break;
> + case EMULATED_INST_EXITS:
> + vcpu->stat.emulated_inst_exits++;
> + break;
> + case DCR_EXITS:
> + vcpu->stat.dcr_exits++;
> + break;
> + case DSI_EXITS:
> + vcpu->stat.dsi_exits++;
> + break;
> + case ISI_EXITS:
> + vcpu->stat.isi_exits++;
> + break;
> + case SYSCALL_EXITS:
> + vcpu->stat.syscall_exits++;
> + break;
> + case DTLB_REAL_MISS_EXITS:
> + vcpu->stat.dtlb_real_miss_exits++;
> + break;
> + case DTLB_VIRT_MISS_EXITS:
> + vcpu->stat.dtlb_virt_miss_exits++;
> + break;
> + case MMIO_EXITS:
> + vcpu->stat.mmio_exits++;
> + break;
> + case ITLB_REAL_MISS_EXITS:
> + vcpu->stat.itlb_real_miss_exits++;
> + break;
> + case ITLB_VIRT_MISS_EXITS:
> + vcpu->stat.itlb_virt_miss_exits++;
> + break;
> + case SIGNAL_EXITS:
> + vcpu->stat.signal_exits++;
> + break;
> + }
> +}
> +
> +/* wrapper to set exit time and account for it in kvm_stats */
> +static inline void account_exit(struct kvm_vcpu *vcpu, int type)
> +{
> + set_exit_type(vcpu, type);
> + account_exit_stat(vcpu, type);
> +}
> +
> +#endif /* __POWERPC_KVM_EXITTIMING_H__ */
> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -381,5 +381,16 @@ int main(void)
> DEFINE(PTE_SHIFT, PTE_SHIFT);
> #endif
>
> +#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
> + DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
> + arch.timing_exit.tv32.tbu));
> + DEFINE(VCPU_TIMING_EXIT_TBL, offsetof(struct kvm_vcpu,
> + arch.timing_exit.tv32.tbl));
> + DEFINE(VCPU_TIMING_LAST_ENTER_TBU, offsetof(struct kvm_vcpu,
> + arch.timing_last_enter.tv32.tbu));
> + DEFINE(VCPU_TIMING_LAST_ENTER_TBL, offsetof(struct kvm_vcpu,
> + arch.timing_last_enter.tv32.tbl));
> +#endif
> +
> return 0;
> }
> diff --git a/arch/powerpc/kvm/44x_emulate.c b/arch/powerpc/kvm/44x_emulate.c
> --- a/arch/powerpc/kvm/44x_emulate.c
> +++ b/arch/powerpc/kvm/44x_emulate.c
> @@ -22,6 +22,7 @@
> #include <asm/dcr-regs.h>
> #include <asm/disassemble.h>
> #include <asm/kvm_44x.h>
> +#include <asm/kvm_timing_stats.h>
>
> #include "booke.h"
> #include "44x_tlb.h"
> @@ -57,12 +58,13 @@ int kvmppc_core_emulate_op(struct kvm_ru
> int rt;
> int ws;
>
> + set_exit_type(vcpu, EMULATED_COREOP_EXITS);
> switch (get_op(inst)) {
> -
> case OP_RFI:
> switch (get_xop(inst)) {
> case XOP_RFI:
> kvmppc_emul_rfi(vcpu);
> + set_exit_type(vcpu, EMULATED_RFI_EXITS);
> *advance = 0;
> break;
>
> @@ -78,10 +80,12 @@ int kvmppc_core_emulate_op(struct kvm_ru
> case XOP_MFMSR:
> rt = get_rt(inst);
> vcpu->arch.gpr[rt] = vcpu->arch.msr;
> + set_exit_type(vcpu, EMULATED_MFMSR_EXITS);
> break;
>
> case XOP_MTMSR:
> rs = get_rs(inst);
> + set_exit_type(vcpu, EMULATED_MTMSR_EXITS);
> kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
> break;
>
> @@ -127,6 +131,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
> run->dcr.is_write = 0;
> vcpu->arch.io_gpr = rt;
> vcpu->arch.dcr_needed = 1;
> + account_exit(vcpu, DCR_EXITS);
> emulated = EMULATE_DO_DCR;
> }
>
> @@ -146,6 +151,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
> run->dcr.data = vcpu->arch.gpr[rs];
> run->dcr.is_write = 1;
> vcpu->arch.dcr_needed = 1;
> + account_exit(vcpu, DCR_EXITS);
> emulated = EMULATE_DO_DCR;
> }
>
> @@ -276,6 +282,7 @@ int kvmppc_core_emulate_mtspr(struct kvm
> return EMULATE_FAIL;
> }
>
> + set_exit_type(vcpu, EMULATED_MTSPR_EXITS);
> return EMULATE_DONE;
> }
>
> @@ -357,6 +364,7 @@ int kvmppc_core_emulate_mfspr(struct kvm
> return EMULATE_FAIL;
> }
>
> + set_exit_type(vcpu, EMULATED_MFSPR_EXITS);
> return EMULATE_DONE;
> }
>
> diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
> --- a/arch/powerpc/kvm/44x_tlb.c
> +++ b/arch/powerpc/kvm/44x_tlb.c
> @@ -27,6 +27,7 @@
> #include <asm/mmu-44x.h>
> #include <asm/kvm_ppc.h>
> #include <asm/kvm_44x.h>
> +#include <asm/kvm_timing_stats.h>
>
> #include "44x_tlb.h"
>
> @@ -457,6 +458,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcp
> KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
> tlbe->word1, tlbe->word2, handler);
>
> + set_exit_type(vcpu, EMULATED_TLBWE_EXITS);
> return EMULATE_DONE;
> }
>
> @@ -480,5 +482,6 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcp
> }
> vcpu->arch.gpr[rt] = gtlb_index;
>
> + set_exit_type(vcpu, EMULATED_TLBSX_EXITS);
> return EMULATE_DONE;
> }
> diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
> --- a/arch/powerpc/kvm/Kconfig
> +++ b/arch/powerpc/kvm/Kconfig
> @@ -32,6 +32,15 @@ config KVM_440
>
> If unsure, say N.
>
> +config KVM_BOOKE_EXIT_TIMING
> + bool "Trace detailed exit Timing"
> + depends on KVM && 44x
> + ---help---
> + Inserts code to trace timestamps for every exit/enter cycle. A per vcpu
> + report is available in debugfs kvm/VM_###/VPCU_###_exit_timing.
> + The overhead is relatively small, however it is not recommended for
> + production environments.
> +
> config KVM_TRACE
> bool "KVM trace support"
> depends on KVM && MARKERS && SYSFS
> diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
> --- a/arch/powerpc/kvm/Makefile
> +++ b/arch/powerpc/kvm/Makefile
> @@ -9,6 +9,7 @@ common-objs-$(CONFIG_KVM_TRACE) += $(ad
> common-objs-$(CONFIG_KVM_TRACE) += $(addprefix ../../../virt/kvm/, kvm_trace.o)
>
> kvm-objs := $(common-objs-y) powerpc.o emulate.o
> +obj-$(CONFIG_KVM_BOOKE_EXIT_TIMING) += exittiming.o
> obj-$(CONFIG_KVM) += kvm.o
>
> AFLAGS_booke_interrupts.o := -I$(obj)
> diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
> --- a/arch/powerpc/kvm/booke.c
> +++ b/arch/powerpc/kvm/booke.c
> @@ -28,6 +28,7 @@
> #include <asm/cputable.h>
> #include <asm/uaccess.h>
> #include <asm/kvm_ppc.h>
> +#include <asm/kvm_timing_stats.h>
> #include <asm/cacheflush.h>
> #include <asm/kvm_44x.h>
>
> @@ -185,6 +186,9 @@ int kvmppc_handle_exit(struct kvm_run *r
> enum emulation_result er;
> int r = RESUME_HOST;
>
> + /* update before a new last_exit_type is rewritten */
> + update_timing_stats(vcpu);
> +
> local_irq_enable();
>
> run->exit_reason = KVM_EXIT_UNKNOWN;
> @@ -198,7 +202,7 @@ int kvmppc_handle_exit(struct kvm_run *r
> break;
>
> case BOOKE_INTERRUPT_EXTERNAL:
> - vcpu->stat.ext_intr_exits++;
> + account_exit(vcpu, EXT_INTR_EXITS);
> if (need_resched())
> cond_resched();
> r = RESUME_GUEST;
> @@ -208,8 +212,7 @@ int kvmppc_handle_exit(struct kvm_run *r
> /* Since we switched IVPR back to the host's value, the host
> * handled this interrupt the moment we enabled interrupts.
> * Now we just offer it a chance to reschedule the guest. */
> -
> - vcpu->stat.dec_exits++;
> + account_exit(vcpu, DEC_EXITS);
> if (need_resched())
> cond_resched();
> r = RESUME_GUEST;
> @@ -222,20 +225,21 @@ int kvmppc_handle_exit(struct kvm_run *r
> vcpu->arch.esr = vcpu->arch.fault_esr;
> kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
> r = RESUME_GUEST;
> + account_exit(vcpu, USR_PR_INST);
> break;
> }
>
> er = kvmppc_emulate_instruction(run, vcpu);
> switch (er) {
> case EMULATE_DONE:
> + /* don't overwrite subtypes, just account kvm_stats */
> + account_exit_stat(vcpu, EMULATED_INST_EXITS);
> /* Future optimization: only reload non-volatiles if
> * they were actually modified by emulation. */
> - vcpu->stat.emulated_inst_exits++;
> r = RESUME_GUEST_NV;
> break;
> case EMULATE_DO_DCR:
> run->exit_reason = KVM_EXIT_DCR;
> - vcpu->stat.dcr_exits++;
> r = RESUME_HOST;
> break;
> case EMULATE_FAIL:
> @@ -255,6 +259,7 @@ int kvmppc_handle_exit(struct kvm_run *r
>
> case BOOKE_INTERRUPT_FP_UNAVAIL:
> kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_FP_UNAVAIL);
> + account_exit(vcpu, FP_UNAVAIL);
> r = RESUME_GUEST;
> break;
>
> @@ -262,20 +267,20 @@ int kvmppc_handle_exit(struct kvm_run *r
> vcpu->arch.dear = vcpu->arch.fault_dear;
> vcpu->arch.esr = vcpu->arch.fault_esr;
> kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
> - vcpu->stat.dsi_exits++;
> + account_exit(vcpu, DSI_EXITS);
> r = RESUME_GUEST;
> break;
>
> case BOOKE_INTERRUPT_INST_STORAGE:
> vcpu->arch.esr = vcpu->arch.fault_esr;
> kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
> - vcpu->stat.isi_exits++;
> + account_exit(vcpu, ISI_EXITS);
> r = RESUME_GUEST;
> break;
>
> case BOOKE_INTERRUPT_SYSCALL:
> kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
> - vcpu->stat.syscall_exits++;
> + account_exit(vcpu, SYSCALL_EXITS);
> r = RESUME_GUEST;
> break;
>
> @@ -294,7 +299,7 @@ int kvmppc_handle_exit(struct kvm_run *r
> kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
> vcpu->arch.dear = vcpu->arch.fault_dear;
> vcpu->arch.esr = vcpu->arch.fault_esr;
> - vcpu->stat.dtlb_real_miss_exits++;
> + account_exit(vcpu, DTLB_REAL_MISS_EXITS);
> r = RESUME_GUEST;
> break;
> }
> @@ -312,13 +317,13 @@ int kvmppc_handle_exit(struct kvm_run *r
> * invoking the guest. */
> kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
> gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
> - vcpu->stat.dtlb_virt_miss_exits++;
> + account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
> r = RESUME_GUEST;
> } else {
> /* Guest has mapped and accessed a page which is not
> * actually RAM. */
> r = kvmppc_emulate_mmio(run, vcpu);
> - vcpu->stat.mmio_exits++;
> + account_exit(vcpu, MMIO_EXITS);
> }
>
> break;
> @@ -340,11 +345,11 @@ int kvmppc_handle_exit(struct kvm_run *r
> if (gtlb_index < 0) {
> /* The guest didn't have a mapping for it. */
> kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
> - vcpu->stat.itlb_real_miss_exits++;
> + account_exit(vcpu, ITLB_REAL_MISS_EXITS);
> break;
> }
>
> - vcpu->stat.itlb_virt_miss_exits++;
> + account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
>
> gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
> gpaddr = tlb_xlate(gtlbe, eaddr);
> @@ -378,6 +383,7 @@ int kvmppc_handle_exit(struct kvm_run *r
> mtspr(SPRN_DBSR, dbsr);
>
> run->exit_reason = KVM_EXIT_DEBUG;
> + account_exit(vcpu, DEBUG_EXITS);
> r = RESUME_HOST;
> break;
> }
> @@ -398,7 +404,7 @@ int kvmppc_handle_exit(struct kvm_run *r
> if (signal_pending(current)) {
> run->exit_reason = KVM_EXIT_INTR;
> r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV);
> - vcpu->stat.signal_exits++;
> + account_exit(vcpu, SIGNAL_EXITS);
> }
> }
>
> @@ -417,6 +423,8 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu
> /* Eye-catching number so we know if the guest takes an interrupt
> * before it's programmed its own IVPR. */
> vcpu->arch.ivpr = 0x55550000;
> +
> + init_timing_stats(vcpu);
>
> return kvmppc_core_vcpu_setup(vcpu);
> }
> diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
> --- a/arch/powerpc/kvm/booke.h
> +++ b/arch/powerpc/kvm/booke.h
> @@ -22,6 +22,7 @@
>
> #include <linux/types.h>
> #include <linux/kvm_host.h>
> +#include <asm/kvm_timing_stats.h>
>
> /* interrupt priortity ordering */
> #define BOOKE_IRQPRIO_DATA_STORAGE 0
> @@ -50,8 +51,10 @@ static inline void kvmppc_set_msr(struct
>
> vcpu->arch.msr = new_msr;
>
> - if (vcpu->arch.msr & MSR_WE)
> + if (vcpu->arch.msr & MSR_WE) {
> kvm_vcpu_block(vcpu);
> + set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
> + };
> }
>
> #endif /* __KVM_BOOKE_H__ */
> diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
> --- a/arch/powerpc/kvm/booke_interrupts.S
> +++ b/arch/powerpc/kvm/booke_interrupts.S
> @@ -106,6 +106,18 @@ _GLOBAL(kvmppc_resume_host)
>
> li r6, 1
> slw r6, r6, r5
> +
> +#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
> + /* save exit time */
> +1:
> + mfspr r7, SPRN_TBRU
> + mfspr r8, SPRN_TBRL
> + mfspr r9, SPRN_TBRU
> + cmpw r9, r7
> + bne 1b
> + stw r8, VCPU_TIMING_EXIT_TBL(r4)
> + stw r9, VCPU_TIMING_EXIT_TBU(r4)
> +#endif
>
> /* Save the faulting instruction and all GPRs for emulation. */
> andi. r7, r6, NEED_INST_MASK
> @@ -375,6 +387,18 @@ lightweight_exit:
> lwz r3, VCPU_SPRG7(r4)
> mtspr SPRN_SPRG7, r3
>
> +#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
> + /* save enter time */
> +1:
> + mfspr r6, SPRN_TBRU
> + mfspr r7, SPRN_TBRL
> + mfspr r8, SPRN_TBRU
> + cmpw r8, r6
> + bne 1b
> + stw r7, VCPU_TIMING_LAST_ENTER_TBL(r4)
> + stw r8, VCPU_TIMING_LAST_ENTER_TBU(r4)
> +#endif
> +
> /* Finish loading guest volatiles and jump to guest. */
> lwz r3, VCPU_CTR(r4)
> mtctr r3
> diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
> --- a/arch/powerpc/kvm/emulate.c
> +++ b/arch/powerpc/kvm/emulate.c
> @@ -28,6 +28,7 @@
> #include <asm/byteorder.h>
> #include <asm/kvm_ppc.h>
> #include <asm/disassemble.h>
> +#include <asm/kvm_timing_stats.h>
>
> void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
> {
> @@ -72,6 +73,9 @@ int kvmppc_emulate_instruction(struct kv
> int sprn;
> enum emulation_result emulated = EMULATE_DONE;
> int advance = 1;
> +
> + /* this default type might be overwritten by subcategories */
> + set_exit_type(vcpu, EMULATED_INST_EXITS);
>
> switch (get_op(inst)) {
> case 3: /* trap */
> diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
> --- a/arch/powerpc/kvm/powerpc.c
> +++ b/arch/powerpc/kvm/powerpc.c
> @@ -28,7 +28,10 @@
> #include <asm/uaccess.h>
> #include <asm/kvm_ppc.h>
> #include <asm/tlbflush.h>
> -
> +#include <asm/kvm_timing_stats.h>
> +#include <asm/atomic.h>
> +#include <linux/seq_file.h>
> +#include <linux/debugfs.h>
>
> gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
> {
> @@ -170,11 +173,15 @@ void kvm_arch_flush_shadow(struct kvm *k
>
> struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
> {
> - return kvmppc_core_vcpu_create(kvm, id);
> + struct kvm_vcpu *vcpu;
> + vcpu = kvmppc_core_vcpu_create(kvm, id);
> + kvmppc_create_vcpu_debugfs(vcpu, id);
> + return vcpu;
> }
>
> void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
> {
> + kvmppc_remove_vcpu_debugfs(vcpu);
> kvmppc_core_vcpu_free(vcpu);
> }
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Grüsse / regards,
Christian Ehrhardt
IBM Linux Technology Center, Open Virtualization
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] kvm: powerpc: add exit timing statistics v3
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
` (2 preceding siblings ...)
2008-11-11 14:55 ` Christian Ehrhardt
@ 2008-11-11 15:29 ` Christian Ehrhardt
2008-11-11 15:43 ` [PATCH] kvm: powerpc: add exit timing statistics v4 Christian Ehrhardt
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Christian Ehrhardt @ 2008-11-11 15:29 UTC (permalink / raw)
To: kvm-ppc
From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
*update to v3*
- ensure build time optimization when calling exit accouting functions using
build time bug / constant check
- migrate most of the exit timing code from powerpc.c and
kvm_timing_stats.h to a separate exittiming.c file
- renamed a lot of constants used to better fit generic/core specific code
- added an accidenially removed optimization comment
- use pid of userspace process instead of an own atomic count to identify a vm
- changed loop labels in tul/tbu loops (booke_interrupt.S) to anonymous 1/1b
- removed the indirection of additional EMULATE_*_DONE types. Instead now
the exit timing supports "accounting" an exit which consists of set type and
increase kvm_stat counters. But also provides both sub-tasks as separate
functions. Using that emulation now sets a default EMUL_INST type that can
be overwritten by detailed subcategories (set_exit_type). Accouting for
kvm_stat is done with account_exit_stat for all emulinst exits together on
top level (as it was before).
*update to v2*
The update fixes accounting for sets to MSR[WE] which should not be accoutned
as instruction emulation. While adding that and analyzing the data it became
obvious that several types of emulations hould be accounted separately.
I'm not yet really happy with adding all these EMULATE_*_DONE types but I had
no better idea to not break flood the code with account calls and split
account/set_type. The issue is that emulation is also called e.g. for some mmio
paths and therefore the accouting should not be called inside emulation, but on
the returning path that can now evaluate the different kind of emulations done.
This is not yet finished, and posted as RFC only.
Other existing kvm statistics are either just counters (kvm_stat) reported for
kvm generally or trace based aproaches like kvm_trace.
For kvm on powerpc we had the need to track the timings of the different exit
types. While this could be achieved parsing data created with a kvm_trace
extension this adds too muhc overhead (at least on embedded powerpc) slowing
down the workloads we wanted to measure.
Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
code. These statistic is available per vm&vcpu under the kvm debugfs directory.
As this statistic is low, but still some overhead it can be enabled via a
.config entry and should be off by default.
Since this patch touched all powerpc kvm_stat code anyway this code is now
merged and simpliefied together with the exit timing statistic code (still
working with exit timing disabled in .config).
* another update in v3*
An updated awk script printing the data in a more narrow layout can be found
on our wiki pages about the exit timing topic.
(http://kvm.qumranet.com/kvmwiki/PowerPC_Exittimings)
Here is a sample output how the results look like.
processing file timing_boot.log
sum of time 8309940
type count min max sum avg stddev %
MMIO: 9402 44 1997 1697610 180.5584 155.768 20.43
DCR: 680 41 99 32096 47.2000 7.008 0.39
SIGNAL: 1 98 98 98 98.0000 0.000 0.00
ITLBREAL: 926 8 14 7810 8.4341 0.658 0.09
ITLBVIRT: 3595 18 202 76185 21.1919 4.954 0.92
DTLBREAL: 950 8 16 8891 9.3589 1.427 0.11
DTLBVIRT: 6695 18 282 156727 23.4096 13.781 1.89
SYSCALL: 1801 6 59 11372 6.3143 2.575 0.14
ISI: 116 6 8 764 6.5862 0.588 0.01
DSI: 43 6 7 292 6.7907 0.407 0.00
EMULINST: 65247 7 96 484081 7.4192 1.818 5.83
EMUL_WAIT: 801 659 9200 3721789 4646.4282 1687.218 44.79
EMUL_CORE: 66806 7 86 540053 8.0839 1.895 6.50
EMUL_MTSPR: 13415 8 62 111358 8.3010 2.583 1.34
EMUL_MFSPR: 7635 8 61 62772 8.2216 1.921 0.76
EMUL_MTMSR: 5678 8 59 45704 8.0493 1.434 0.55
EMUL_MFMSR: 32853 7 67 267603 8.1455 1.875 3.22
EMUL_TLBSX: 354 9 60 3745 10.5791 3.919 0.05
EMUL_TLBWE: 6403 9 112 99522 15.5430 7.668 1.20
EMUL_RFI: 9515 7 57 71420 7.5060 2.108 0.86
DEC: 290 49 161 15786 54.4345 9.708 0.19
EXTINT: 7 74 75 522 74.5714 0.495 0.01
TIMEINGUEST: 233213 0 3954 893740 3.8323 65.837 10.76
Signed-off-by: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
---
[diffstat]
include/asm/kvm_host.h | 59 ++++++++++++++++++++++++
include/asm/kvm_ppc.h | 8 +--
include/asm/kvm_timing_stats.h | 100 +++++++++++++++++++++++++++++++++++++++++
kernel/asm-offsets.c | 11 ++++
kvm/44x_emulate.c | 10 +++-
kvm/44x_tlb.c | 3 +
kvm/Kconfig | 9 +++
kvm/Makefile | 1
kvm/booke.c | 36 +++++++++-----
kvm/booke.h | 5 +-
kvm/booke_interrupts.S | 24 +++++++++
kvm/emulate.c | 4 +
kvm/powerpc.c | 11 +++-
13 files changed, 259 insertions(+), 22 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -71,7 +71,53 @@ struct kvmppc_44x_tlbe {
u32 word2;
};
+enum kvm_exit_types {
+ MMIO_EXITS,
+ DCR_EXITS,
+ SIGNAL_EXITS,
+ ITLB_REAL_MISS_EXITS,
+ ITLB_VIRT_MISS_EXITS,
+ DTLB_REAL_MISS_EXITS,
+ DTLB_VIRT_MISS_EXITS,
+ SYSCALL_EXITS,
+ ISI_EXITS,
+ DSI_EXITS,
+ EMULATED_INST_EXITS,
+ EMULATED_MTMSRWE_EXITS,
+ EMULATED_WRTEE_EXITS,
+ EMULATED_MTSPR_EXITS,
+ EMULATED_MFSPR_EXITS,
+ EMULATED_MTMSR_EXITS,
+ EMULATED_MFMSR_EXITS,
+ EMULATED_TLBSX_EXITS,
+ EMULATED_TLBWE_EXITS,
+ EMULATED_RFI_EXITS,
+ DEC_EXITS,
+ EXT_INTR_EXITS,
+ HALT_WAKEUP,
+ USR_PR_INST,
+ FP_UNAVAIL,
+ DEBUG_EXITS,
+ TIMEINGUEST,
+ __NUMBER_OF_KVM_EXIT_TYPES
+};
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+/* allow access to big endian 32bit upper/lower parts and 64bit var */
+struct exit_timing {
+ union {
+ u64 tv64;
+ struct {
+ u32 tbu, tbl;
+ } tv32;
+ };
+};
+#endif
+
struct kvm_arch {
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ unsigned int vm_id;
+#endif
};
struct kvm_vcpu_arch {
@@ -130,6 +176,19 @@ struct kvm_vcpu_arch {
u32 dbcr0;
u32 dbcr1;
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ struct exit_timing timing_exit;
+ struct exit_timing timing_last_enter;
+ u32 last_exit_type;
+ u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_quad_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_min_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_max_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_last_exit;
+ struct dentry *debugfs_exit_timing;
+#endif
+
u32 last_inst;
ulong fault_dear;
ulong fault_esr;
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -30,10 +30,10 @@
#include <linux/kvm_host.h>
enum emulation_result {
- EMULATE_DONE, /* no further processing */
- EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
- EMULATE_DO_DCR, /* kvm_run filled with DCR request */
- EMULATE_FAIL, /* can't emulate this instruction */
+ EMULATE_DONE, /* no further processing */
+ EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
+ EMULATE_DO_DCR, /* kvm_run filled with DCR request */
+ EMULATE_FAIL, /* can't emulate this instruction */
};
extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
diff --git a/arch/powerpc/include/asm/kvm_timing_stats.h b/arch/powerpc/include/asm/kvm_timing_stats.h
new file mode 100644
--- /dev/null
+++ b/arch/powerpc/include/asm/kvm_timing_stats.h
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ */
+
+#ifndef __POWERPC_KVM_EXITTIMING_H__
+#define __POWERPC_KVM_EXITTIMING_H__
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_host.h>
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+void init_timing_stats(struct kvm_vcpu *vcpu);
+void update_timing_stats(struct kvm_vcpu *vcpu);
+void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id);
+void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu);
+
+static inline void set_exit_type(struct kvm_vcpu *vcpu, int type)
+{
+ vcpu->arch.last_exit_type = type;
+}
+
+#else
+#define init_timing_stats(x) do { } while (0)
+#define update_timing_stats(x) do { } while (0)
+#define kvmppc_create_vcpu_debugfs(x, y) do { } while (0)
+#define kvmppc_remove_vcpu_debugfs(x) do { } while (0)
+#define set_exit_type(x, y) do { } while (0)
+#endif /* CONFIG_KVM_BOOKE_EXIT_TIMING */
+
+/* account the exit in kvm_stats */
+static inline void account_exit_stat(struct kvm_vcpu *vcpu, int type)
+{
+ /* type has to be known at build time for optimization */
+ BUILD_BUG_ON(__builtin_constant_p(type));
+ switch (type) {
+ case EXT_INTR_EXITS:
+ vcpu->stat.ext_intr_exits++;
+ break;
+ case DEC_EXITS:
+ vcpu->stat.dec_exits++;
+ break;
+ case EMULATED_INST_EXITS:
+ vcpu->stat.emulated_inst_exits++;
+ break;
+ case DCR_EXITS:
+ vcpu->stat.dcr_exits++;
+ break;
+ case DSI_EXITS:
+ vcpu->stat.dsi_exits++;
+ break;
+ case ISI_EXITS:
+ vcpu->stat.isi_exits++;
+ break;
+ case SYSCALL_EXITS:
+ vcpu->stat.syscall_exits++;
+ break;
+ case DTLB_REAL_MISS_EXITS:
+ vcpu->stat.dtlb_real_miss_exits++;
+ break;
+ case DTLB_VIRT_MISS_EXITS:
+ vcpu->stat.dtlb_virt_miss_exits++;
+ break;
+ case MMIO_EXITS:
+ vcpu->stat.mmio_exits++;
+ break;
+ case ITLB_REAL_MISS_EXITS:
+ vcpu->stat.itlb_real_miss_exits++;
+ break;
+ case ITLB_VIRT_MISS_EXITS:
+ vcpu->stat.itlb_virt_miss_exits++;
+ break;
+ case SIGNAL_EXITS:
+ vcpu->stat.signal_exits++;
+ break;
+ }
+}
+
+/* wrapper to set exit time and account for it in kvm_stats */
+static inline void account_exit(struct kvm_vcpu *vcpu, int type)
+{
+ set_exit_type(vcpu, type);
+ account_exit_stat(vcpu, type);
+}
+
+#endif /* __POWERPC_KVM_EXITTIMING_H__ */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -381,5 +381,16 @@ int main(void)
DEFINE(PTE_SHIFT, PTE_SHIFT);
#endif
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbu));
+ DEFINE(VCPU_TIMING_EXIT_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbl));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbu));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbl));
+#endif
+
return 0;
}
diff --git a/arch/powerpc/kvm/44x_emulate.c b/arch/powerpc/kvm/44x_emulate.c
--- a/arch/powerpc/kvm/44x_emulate.c
+++ b/arch/powerpc/kvm/44x_emulate.c
@@ -22,6 +22,7 @@
#include <asm/dcr-regs.h>
#include <asm/disassemble.h>
#include <asm/kvm_44x.h>
+#include <asm/kvm_timing_stats.h>
#include "booke.h"
#include "44x_tlb.h"
@@ -58,11 +59,11 @@ int kvmppc_core_emulate_op(struct kvm_ru
int ws;
switch (get_op(inst)) {
-
case OP_RFI:
switch (get_xop(inst)) {
case XOP_RFI:
kvmppc_emul_rfi(vcpu);
+ set_exit_type(vcpu, EMULATED_RFI_EXITS);
*advance = 0;
break;
@@ -78,10 +79,12 @@ int kvmppc_core_emulate_op(struct kvm_ru
case XOP_MFMSR:
rt = get_rt(inst);
vcpu->arch.gpr[rt] = vcpu->arch.msr;
+ set_exit_type(vcpu, EMULATED_MFMSR_EXITS);
break;
case XOP_MTMSR:
rs = get_rs(inst);
+ set_exit_type(vcpu, EMULATED_MTMSR_EXITS);
kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
break;
@@ -89,11 +92,13 @@ int kvmppc_core_emulate_op(struct kvm_ru
rs = get_rs(inst);
vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
| (vcpu->arch.gpr[rs] & MSR_EE);
+ set_exit_type(vcpu, EMULATED_WRTEE_EXITS);
break;
case XOP_WRTEEI:
vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
| (inst & MSR_EE);
+ set_exit_type(vcpu, EMULATED_WRTEE_EXITS);
break;
case XOP_MFDCR:
@@ -127,6 +132,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.is_write = 0;
vcpu->arch.io_gpr = rt;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -146,6 +152,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.data = vcpu->arch.gpr[rs];
run->dcr.is_write = 1;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -276,6 +283,7 @@ int kvmppc_core_emulate_mtspr(struct kvm
return EMULATE_FAIL;
}
+ set_exit_type(vcpu, EMULATED_MTSPR_EXITS);
return EMULATE_DONE;
}
@@ -357,6 +365,7 @@ int kvmppc_core_emulate_mfspr(struct kvm
return EMULATE_FAIL;
}
+ set_exit_type(vcpu, EMULATED_MFSPR_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -27,6 +27,7 @@
#include <asm/mmu-44x.h>
#include <asm/kvm_ppc.h>
#include <asm/kvm_44x.h>
+#include <asm/kvm_timing_stats.h>
#include "44x_tlb.h"
@@ -457,6 +458,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcp
KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
tlbe->word1, tlbe->word2, handler);
+ set_exit_type(vcpu, EMULATED_TLBWE_EXITS);
return EMULATE_DONE;
}
@@ -480,5 +482,6 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcp
}
vcpu->arch.gpr[rt] = gtlb_index;
+ set_exit_type(vcpu, EMULATED_TLBSX_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -32,6 +32,15 @@ config KVM_440
If unsure, say N.
+config KVM_BOOKE_EXIT_TIMING
+ bool "Trace detailed exit Timing"
+ depends on KVM && 44x
+ ---help---
+ Inserts code to trace timestamps for every exit/enter cycle. A per vcpu
+ report is available in debugfs kvm/VM_###/VPCU_###_exit_timing.
+ The overhead is relatively small, however it is not recommended for
+ production environments.
+
config KVM_TRACE
bool "KVM trace support"
depends on KVM && MARKERS && SYSFS
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -9,6 +9,7 @@ common-objs-$(CONFIG_KVM_TRACE) += $(ad
common-objs-$(CONFIG_KVM_TRACE) += $(addprefix ../../../virt/kvm/, kvm_trace.o)
kvm-objs := $(common-objs-y) powerpc.o emulate.o
+obj-$(CONFIG_KVM_BOOKE_EXIT_TIMING) += exittiming.o
obj-$(CONFIG_KVM) += kvm.o
AFLAGS_booke_interrupts.o := -I$(obj)
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -28,6 +28,7 @@
#include <asm/cputable.h>
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
+#include <asm/kvm_timing_stats.h>
#include <asm/cacheflush.h>
#include <asm/kvm_44x.h>
@@ -185,6 +186,9 @@ int kvmppc_handle_exit(struct kvm_run *r
enum emulation_result er;
int r = RESUME_HOST;
+ /* update before a new last_exit_type is rewritten */
+ update_timing_stats(vcpu);
+
local_irq_enable();
run->exit_reason = KVM_EXIT_UNKNOWN;
@@ -198,7 +202,7 @@ int kvmppc_handle_exit(struct kvm_run *r
break;
case BOOKE_INTERRUPT_EXTERNAL:
- vcpu->stat.ext_intr_exits++;
+ account_exit(vcpu, EXT_INTR_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -208,8 +212,7 @@ int kvmppc_handle_exit(struct kvm_run *r
/* Since we switched IVPR back to the host's value, the host
* handled this interrupt the moment we enabled interrupts.
* Now we just offer it a chance to reschedule the guest. */
-
- vcpu->stat.dec_exits++;
+ account_exit(vcpu, DEC_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -222,20 +225,21 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
r = RESUME_GUEST;
+ account_exit(vcpu, USR_PR_INST);
break;
}
er = kvmppc_emulate_instruction(run, vcpu);
switch (er) {
case EMULATE_DONE:
+ /* don't overwrite subtypes, just account kvm_stats */
+ account_exit_stat(vcpu, EMULATED_INST_EXITS);
/* Future optimization: only reload non-volatiles if
* they were actually modified by emulation. */
- vcpu->stat.emulated_inst_exits++;
r = RESUME_GUEST_NV;
break;
case EMULATE_DO_DCR:
run->exit_reason = KVM_EXIT_DCR;
- vcpu->stat.dcr_exits++;
r = RESUME_HOST;
break;
case EMULATE_FAIL:
@@ -255,6 +259,7 @@ int kvmppc_handle_exit(struct kvm_run *r
case BOOKE_INTERRUPT_FP_UNAVAIL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_FP_UNAVAIL);
+ account_exit(vcpu, FP_UNAVAIL);
r = RESUME_GUEST;
break;
@@ -262,20 +267,20 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
- vcpu->stat.dsi_exits++;
+ account_exit(vcpu, DSI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_INST_STORAGE:
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
- vcpu->stat.isi_exits++;
+ account_exit(vcpu, ISI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_SYSCALL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
- vcpu->stat.syscall_exits++;
+ account_exit(vcpu, SYSCALL_EXITS);
r = RESUME_GUEST;
break;
@@ -294,7 +299,7 @@ int kvmppc_handle_exit(struct kvm_run *r
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
- vcpu->stat.dtlb_real_miss_exits++;
+ account_exit(vcpu, DTLB_REAL_MISS_EXITS);
r = RESUME_GUEST;
break;
}
@@ -312,13 +317,13 @@ int kvmppc_handle_exit(struct kvm_run *r
* invoking the guest. */
kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
- vcpu->stat.dtlb_virt_miss_exits++;
+ account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
r = RESUME_GUEST;
} else {
/* Guest has mapped and accessed a page which is not
* actually RAM. */
r = kvmppc_emulate_mmio(run, vcpu);
- vcpu->stat.mmio_exits++;
+ account_exit(vcpu, MMIO_EXITS);
}
break;
@@ -340,11 +345,11 @@ int kvmppc_handle_exit(struct kvm_run *r
if (gtlb_index < 0) {
/* The guest didn't have a mapping for it. */
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
- vcpu->stat.itlb_real_miss_exits++;
+ account_exit(vcpu, ITLB_REAL_MISS_EXITS);
break;
}
- vcpu->stat.itlb_virt_miss_exits++;
+ account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
gpaddr = tlb_xlate(gtlbe, eaddr);
@@ -378,6 +383,7 @@ int kvmppc_handle_exit(struct kvm_run *r
mtspr(SPRN_DBSR, dbsr);
run->exit_reason = KVM_EXIT_DEBUG;
+ account_exit(vcpu, DEBUG_EXITS);
r = RESUME_HOST;
break;
}
@@ -398,7 +404,7 @@ int kvmppc_handle_exit(struct kvm_run *r
if (signal_pending(current)) {
run->exit_reason = KVM_EXIT_INTR;
r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV);
- vcpu->stat.signal_exits++;
+ account_exit(vcpu, SIGNAL_EXITS);
}
}
@@ -417,6 +423,8 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu
/* Eye-catching number so we know if the guest takes an interrupt
* before it's programmed its own IVPR. */
vcpu->arch.ivpr = 0x55550000;
+
+ init_timing_stats(vcpu);
return kvmppc_core_vcpu_setup(vcpu);
}
diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
--- a/arch/powerpc/kvm/booke.h
+++ b/arch/powerpc/kvm/booke.h
@@ -22,6 +22,7 @@
#include <linux/types.h>
#include <linux/kvm_host.h>
+#include <asm/kvm_timing_stats.h>
/* interrupt priortity ordering */
#define BOOKE_IRQPRIO_DATA_STORAGE 0
@@ -50,8 +51,10 @@ static inline void kvmppc_set_msr(struct
vcpu->arch.msr = new_msr;
- if (vcpu->arch.msr & MSR_WE)
+ if (vcpu->arch.msr & MSR_WE) {
kvm_vcpu_block(vcpu);
+ set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
+ };
}
#endif /* __KVM_BOOKE_H__ */
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -106,6 +106,18 @@ _GLOBAL(kvmppc_resume_host)
li r6, 1
slw r6, r6, r5
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save exit time */
+1:
+ mfspr r7, SPRN_TBRU
+ mfspr r8, SPRN_TBRL
+ mfspr r9, SPRN_TBRU
+ cmpw r9, r7
+ bne 1b
+ stw r8, VCPU_TIMING_EXIT_TBL(r4)
+ stw r9, VCPU_TIMING_EXIT_TBU(r4)
+#endif
/* Save the faulting instruction and all GPRs for emulation. */
andi. r7, r6, NEED_INST_MASK
@@ -375,6 +387,18 @@ lightweight_exit:
lwz r3, VCPU_SPRG7(r4)
mtspr SPRN_SPRG7, r3
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save enter time */
+1:
+ mfspr r6, SPRN_TBRU
+ mfspr r7, SPRN_TBRL
+ mfspr r8, SPRN_TBRU
+ cmpw r8, r6
+ bne 1b
+ stw r7, VCPU_TIMING_LAST_ENTER_TBL(r4)
+ stw r8, VCPU_TIMING_LAST_ENTER_TBU(r4)
+#endif
+
/* Finish loading guest volatiles and jump to guest. */
lwz r3, VCPU_CTR(r4)
mtctr r3
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -28,6 +28,7 @@
#include <asm/byteorder.h>
#include <asm/kvm_ppc.h>
#include <asm/disassemble.h>
+#include <asm/kvm_timing_stats.h>
void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
{
@@ -72,6 +73,9 @@ int kvmppc_emulate_instruction(struct kv
int sprn;
enum emulation_result emulated = EMULATE_DONE;
int advance = 1;
+
+ /* this default type might be overwritten by subcategories */
+ set_exit_type(vcpu, EMULATED_INST_EXITS);
switch (get_op(inst)) {
case 3: /* trap */
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -28,7 +28,10 @@
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
#include <asm/tlbflush.h>
-
+#include <asm/kvm_timing_stats.h>
+#include <asm/atomic.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
{
@@ -170,11 +173,15 @@ void kvm_arch_flush_shadow(struct kvm *k
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
{
- return kvmppc_core_vcpu_create(kvm, id);
+ struct kvm_vcpu *vcpu;
+ vcpu = kvmppc_core_vcpu_create(kvm, id);
+ kvmppc_create_vcpu_debugfs(vcpu, id);
+ return vcpu;
}
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
+ kvmppc_remove_vcpu_debugfs(vcpu);
kvmppc_core_vcpu_free(vcpu);
}
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] kvm: powerpc: add exit timing statistics v4
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
` (3 preceding siblings ...)
2008-11-11 15:29 ` Christian Ehrhardt
@ 2008-11-11 15:43 ` Christian Ehrhardt
2008-11-11 20:22 ` Hollis Blanchard
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Christian Ehrhardt @ 2008-11-11 15:43 UTC (permalink / raw)
To: kvm-ppc
From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
*update to v4*
- EMUL_CORE no longer had more than wrtee emulation, therefore it now accounts
for WRTEE in the output and set_exit_type calls are in the wrtee handlers to
let any residual core op be counted as EMULINST"
*update to v3*
- ensure build time optimization when calling exit accouting functions using
build time bug / constant check
- migrate most of the exit timing code from powerpc.c and
kvm_timing_stats.h to a separate exittiming.c file
- renamed a lot of constants used to better fit generic/core specific code
- added an accidenially removed optimization comment
- use pid of userspace process instead of an own atomic count to identify a vm
- changed loop labels in tul/tbu loops (booke_interrupt.S) to anonymous 1/1b
- removed the indirection of additional EMULATE_*_DONE types. Instead now
the exit timing supports "accounting" an exit which consists of set type and
increase kvm_stat counters. But also provides both sub-tasks as separate
functions. Using that emulation now sets a default EMUL_INST type that can
be overwritten by detailed subcategories (set_exit_type). Accouting for
kvm_stat is done with account_exit_stat for all emulinst exits together on
top level (as it was before).
*update to v2*
The update fixes accounting for sets to MSR[WE] which should not be accoutned
as instruction emulation. While adding that and analyzing the data it became
obvious that several types of emulations hould be accounted separately.
I'm not yet really happy with adding all these EMULATE_*_DONE types but I had
no better idea to not break flood the code with account calls and split
account/set_type. The issue is that emulation is also called e.g. for some mmio
paths and therefore the accouting should not be called inside emulation, but on
the returning path that can now evaluate the different kind of emulations done.
This is not yet finished, and posted as RFC only.
Other existing kvm statistics are either just counters (kvm_stat) reported for
kvm generally or trace based aproaches like kvm_trace.
For kvm on powerpc we had the need to track the timings of the different exit
types. While this could be achieved parsing data created with a kvm_trace
extension this adds too muhc overhead (at least on embedded powerpc) slowing
down the workloads we wanted to measure.
Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
code. These statistic is available per vm&vcpu under the kvm debugfs directory.
As this statistic is low, but still some overhead it can be enabled via a
.config entry and should be off by default.
Since this patch touched all powerpc kvm_stat code anyway this code is now
merged and simpliefied together with the exit timing statistic code (still
working with exit timing disabled in .config).
* another update in v3*
An updated awk script printing the data in a more narrow layout can be found
on our wiki pages about the exit timing topic.
(http://kvm.qumranet.com/kvmwiki/PowerPC_Exittimings)
Here is a sample output how the results look like.
processing file timing_boot.log
sum of time 8309940
type count min max sum avg stddev %
MMIO: 9402 44 1997 1697610 180.5584 155.768 20.43
DCR: 680 41 99 32096 47.2000 7.008 0.39
SIGNAL: 1 98 98 98 98.0000 0.000 0.00
ITLBREAL: 926 8 14 7810 8.4341 0.658 0.09
ITLBVIRT: 3595 18 202 76185 21.1919 4.954 0.92
DTLBREAL: 950 8 16 8891 9.3589 1.427 0.11
DTLBVIRT: 6695 18 282 156727 23.4096 13.781 1.89
SYSCALL: 1801 6 59 11372 6.3143 2.575 0.14
ISI: 116 6 8 764 6.5862 0.588 0.01
DSI: 43 6 7 292 6.7907 0.407 0.00
EMULINST: 65247 7 96 484081 7.4192 1.818 5.83
EMUL_WAIT: 801 659 9200 3721789 4646.4282 1687.218 44.79
EMUL_WRTEE: 66806 7 86 540053 8.0839 1.895 6.50
EMUL_MTSPR: 13415 8 62 111358 8.3010 2.583 1.34
EMUL_MFSPR: 7635 8 61 62772 8.2216 1.921 0.76
EMUL_MTMSR: 5678 8 59 45704 8.0493 1.434 0.55
EMUL_MFMSR: 32853 7 67 267603 8.1455 1.875 3.22
EMUL_TLBSX: 354 9 60 3745 10.5791 3.919 0.05
EMUL_TLBWE: 6403 9 112 99522 15.5430 7.668 1.20
EMUL_RFI: 9515 7 57 71420 7.5060 2.108 0.86
DEC: 290 49 161 15786 54.4345 9.708 0.19
EXTINT: 7 74 75 522 74.5714 0.495 0.01
TIMEINGUEST: 233213 0 3954 893740 3.8323 65.837 10.76
Signed-off-by: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
---
[diffstat]
include/asm/kvm_host.h | 59 ++++++++++++++++++++++++
include/asm/kvm_ppc.h | 8 +--
include/asm/kvm_timing_stats.h | 100 +++++++++++++++++++++++++++++++++++++++++
kernel/asm-offsets.c | 11 ++++
kvm/44x_emulate.c | 11 ++++
kvm/44x_tlb.c | 3 +
kvm/Kconfig | 9 +++
kvm/Makefile | 1
kvm/booke.c | 36 +++++++++-----
kvm/booke.h | 5 +-
kvm/booke_interrupts.S | 24 +++++++++
kvm/emulate.c | 4 +
kvm/powerpc.c | 11 +++-
13 files changed, 260 insertions(+), 22 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -71,7 +71,53 @@ struct kvmppc_44x_tlbe {
u32 word2;
};
+enum kvm_exit_types {
+ MMIO_EXITS,
+ DCR_EXITS,
+ SIGNAL_EXITS,
+ ITLB_REAL_MISS_EXITS,
+ ITLB_VIRT_MISS_EXITS,
+ DTLB_REAL_MISS_EXITS,
+ DTLB_VIRT_MISS_EXITS,
+ SYSCALL_EXITS,
+ ISI_EXITS,
+ DSI_EXITS,
+ EMULATED_INST_EXITS,
+ EMULATED_MTMSRWE_EXITS,
+ EMULATED_WRTEE_EXITS,
+ EMULATED_MTSPR_EXITS,
+ EMULATED_MFSPR_EXITS,
+ EMULATED_MTMSR_EXITS,
+ EMULATED_MFMSR_EXITS,
+ EMULATED_TLBSX_EXITS,
+ EMULATED_TLBWE_EXITS,
+ EMULATED_RFI_EXITS,
+ DEC_EXITS,
+ EXT_INTR_EXITS,
+ HALT_WAKEUP,
+ USR_PR_INST,
+ FP_UNAVAIL,
+ DEBUG_EXITS,
+ TIMEINGUEST,
+ __NUMBER_OF_KVM_EXIT_TYPES
+};
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+/* allow access to big endian 32bit upper/lower parts and 64bit var */
+struct exit_timing {
+ union {
+ u64 tv64;
+ struct {
+ u32 tbu, tbl;
+ } tv32;
+ };
+};
+#endif
+
struct kvm_arch {
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ unsigned int vm_id;
+#endif
};
struct kvm_vcpu_arch {
@@ -130,6 +176,19 @@ struct kvm_vcpu_arch {
u32 dbcr0;
u32 dbcr1;
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ struct exit_timing timing_exit;
+ struct exit_timing timing_last_enter;
+ u32 last_exit_type;
+ u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_quad_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_min_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_max_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_last_exit;
+ struct dentry *debugfs_exit_timing;
+#endif
+
u32 last_inst;
ulong fault_dear;
ulong fault_esr;
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -30,10 +30,10 @@
#include <linux/kvm_host.h>
enum emulation_result {
- EMULATE_DONE, /* no further processing */
- EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
- EMULATE_DO_DCR, /* kvm_run filled with DCR request */
- EMULATE_FAIL, /* can't emulate this instruction */
+ EMULATE_DONE, /* no further processing */
+ EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
+ EMULATE_DO_DCR, /* kvm_run filled with DCR request */
+ EMULATE_FAIL, /* can't emulate this instruction */
};
extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
diff --git a/arch/powerpc/include/asm/kvm_timing_stats.h b/arch/powerpc/include/asm/kvm_timing_stats.h
new file mode 100644
--- /dev/null
+++ b/arch/powerpc/include/asm/kvm_timing_stats.h
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ */
+
+#ifndef __POWERPC_KVM_EXITTIMING_H__
+#define __POWERPC_KVM_EXITTIMING_H__
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_host.h>
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+void init_timing_stats(struct kvm_vcpu *vcpu);
+void update_timing_stats(struct kvm_vcpu *vcpu);
+void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id);
+void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu);
+
+static inline void set_exit_type(struct kvm_vcpu *vcpu, int type)
+{
+ vcpu->arch.last_exit_type = type;
+}
+
+#else
+#define init_timing_stats(x) do { } while (0)
+#define update_timing_stats(x) do { } while (0)
+#define kvmppc_create_vcpu_debugfs(x, y) do { } while (0)
+#define kvmppc_remove_vcpu_debugfs(x) do { } while (0)
+#define set_exit_type(x, y) do { } while (0)
+#endif /* CONFIG_KVM_BOOKE_EXIT_TIMING */
+
+/* account the exit in kvm_stats */
+static inline void account_exit_stat(struct kvm_vcpu *vcpu, int type)
+{
+ /* type has to be known at build time for optimization */
+ BUILD_BUG_ON(__builtin_constant_p(type));
+ switch (type) {
+ case EXT_INTR_EXITS:
+ vcpu->stat.ext_intr_exits++;
+ break;
+ case DEC_EXITS:
+ vcpu->stat.dec_exits++;
+ break;
+ case EMULATED_INST_EXITS:
+ vcpu->stat.emulated_inst_exits++;
+ break;
+ case DCR_EXITS:
+ vcpu->stat.dcr_exits++;
+ break;
+ case DSI_EXITS:
+ vcpu->stat.dsi_exits++;
+ break;
+ case ISI_EXITS:
+ vcpu->stat.isi_exits++;
+ break;
+ case SYSCALL_EXITS:
+ vcpu->stat.syscall_exits++;
+ break;
+ case DTLB_REAL_MISS_EXITS:
+ vcpu->stat.dtlb_real_miss_exits++;
+ break;
+ case DTLB_VIRT_MISS_EXITS:
+ vcpu->stat.dtlb_virt_miss_exits++;
+ break;
+ case MMIO_EXITS:
+ vcpu->stat.mmio_exits++;
+ break;
+ case ITLB_REAL_MISS_EXITS:
+ vcpu->stat.itlb_real_miss_exits++;
+ break;
+ case ITLB_VIRT_MISS_EXITS:
+ vcpu->stat.itlb_virt_miss_exits++;
+ break;
+ case SIGNAL_EXITS:
+ vcpu->stat.signal_exits++;
+ break;
+ }
+}
+
+/* wrapper to set exit time and account for it in kvm_stats */
+static inline void account_exit(struct kvm_vcpu *vcpu, int type)
+{
+ set_exit_type(vcpu, type);
+ account_exit_stat(vcpu, type);
+}
+
+#endif /* __POWERPC_KVM_EXITTIMING_H__ */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -381,5 +381,16 @@ int main(void)
DEFINE(PTE_SHIFT, PTE_SHIFT);
#endif
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbu));
+ DEFINE(VCPU_TIMING_EXIT_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbl));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbu));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbl));
+#endif
+
return 0;
}
diff --git a/arch/powerpc/kvm/44x_emulate.c b/arch/powerpc/kvm/44x_emulate.c
--- a/arch/powerpc/kvm/44x_emulate.c
+++ b/arch/powerpc/kvm/44x_emulate.c
@@ -22,6 +22,7 @@
#include <asm/dcr-regs.h>
#include <asm/disassemble.h>
#include <asm/kvm_44x.h>
+#include <asm/kvm_timing_stats.h>
#include "booke.h"
#include "44x_tlb.h"
@@ -58,11 +59,11 @@ int kvmppc_core_emulate_op(struct kvm_ru
int ws;
switch (get_op(inst)) {
-
case OP_RFI:
switch (get_xop(inst)) {
case XOP_RFI:
kvmppc_emul_rfi(vcpu);
+ set_exit_type(vcpu, EMULATED_RFI_EXITS);
*advance = 0;
break;
@@ -78,10 +79,12 @@ int kvmppc_core_emulate_op(struct kvm_ru
case XOP_MFMSR:
rt = get_rt(inst);
vcpu->arch.gpr[rt] = vcpu->arch.msr;
+ set_exit_type(vcpu, EMULATED_MFMSR_EXITS);
break;
case XOP_MTMSR:
rs = get_rs(inst);
+ set_exit_type(vcpu, EMULATED_MTMSR_EXITS);
kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
break;
@@ -89,11 +92,13 @@ int kvmppc_core_emulate_op(struct kvm_ru
rs = get_rs(inst);
vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
| (vcpu->arch.gpr[rs] & MSR_EE);
+ set_exit_type(vcpu, EMULATED_WRTEE_EXITS);
break;
case XOP_WRTEEI:
vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
| (inst & MSR_EE);
+ set_exit_type(vcpu, EMULATED_WRTEE_EXITS);
break;
case XOP_MFDCR:
@@ -127,6 +132,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.is_write = 0;
vcpu->arch.io_gpr = rt;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -146,6 +152,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.data = vcpu->arch.gpr[rs];
run->dcr.is_write = 1;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -276,6 +283,7 @@ int kvmppc_core_emulate_mtspr(struct kvm
return EMULATE_FAIL;
}
+ set_exit_type(vcpu, EMULATED_MTSPR_EXITS);
return EMULATE_DONE;
}
@@ -357,6 +365,7 @@ int kvmppc_core_emulate_mfspr(struct kvm
return EMULATE_FAIL;
}
+ set_exit_type(vcpu, EMULATED_MFSPR_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -27,6 +27,7 @@
#include <asm/mmu-44x.h>
#include <asm/kvm_ppc.h>
#include <asm/kvm_44x.h>
+#include <asm/kvm_timing_stats.h>
#include "44x_tlb.h"
@@ -457,6 +458,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcp
KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
tlbe->word1, tlbe->word2, handler);
+ set_exit_type(vcpu, EMULATED_TLBWE_EXITS);
return EMULATE_DONE;
}
@@ -480,5 +482,6 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcp
}
vcpu->arch.gpr[rt] = gtlb_index;
+ set_exit_type(vcpu, EMULATED_TLBSX_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -32,6 +32,15 @@ config KVM_440
If unsure, say N.
+config KVM_BOOKE_EXIT_TIMING
+ bool "Trace detailed exit Timing"
+ depends on KVM && 44x
+ ---help---
+ Inserts code to trace timestamps for every exit/enter cycle. A per vcpu
+ report is available in debugfs kvm/VM_###/VPCU_###_exit_timing.
+ The overhead is relatively small, however it is not recommended for
+ production environments.
+
config KVM_TRACE
bool "KVM trace support"
depends on KVM && MARKERS && SYSFS
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -9,6 +9,7 @@ common-objs-$(CONFIG_KVM_TRACE) += $(ad
common-objs-$(CONFIG_KVM_TRACE) += $(addprefix ../../../virt/kvm/, kvm_trace.o)
kvm-objs := $(common-objs-y) powerpc.o emulate.o
+obj-$(CONFIG_KVM_BOOKE_EXIT_TIMING) += exittiming.o
obj-$(CONFIG_KVM) += kvm.o
AFLAGS_booke_interrupts.o := -I$(obj)
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -28,6 +28,7 @@
#include <asm/cputable.h>
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
+#include <asm/kvm_timing_stats.h>
#include <asm/cacheflush.h>
#include <asm/kvm_44x.h>
@@ -185,6 +186,9 @@ int kvmppc_handle_exit(struct kvm_run *r
enum emulation_result er;
int r = RESUME_HOST;
+ /* update before a new last_exit_type is rewritten */
+ update_timing_stats(vcpu);
+
local_irq_enable();
run->exit_reason = KVM_EXIT_UNKNOWN;
@@ -198,7 +202,7 @@ int kvmppc_handle_exit(struct kvm_run *r
break;
case BOOKE_INTERRUPT_EXTERNAL:
- vcpu->stat.ext_intr_exits++;
+ account_exit(vcpu, EXT_INTR_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -208,8 +212,7 @@ int kvmppc_handle_exit(struct kvm_run *r
/* Since we switched IVPR back to the host's value, the host
* handled this interrupt the moment we enabled interrupts.
* Now we just offer it a chance to reschedule the guest. */
-
- vcpu->stat.dec_exits++;
+ account_exit(vcpu, DEC_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -222,20 +225,21 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
r = RESUME_GUEST;
+ account_exit(vcpu, USR_PR_INST);
break;
}
er = kvmppc_emulate_instruction(run, vcpu);
switch (er) {
case EMULATE_DONE:
+ /* don't overwrite subtypes, just account kvm_stats */
+ account_exit_stat(vcpu, EMULATED_INST_EXITS);
/* Future optimization: only reload non-volatiles if
* they were actually modified by emulation. */
- vcpu->stat.emulated_inst_exits++;
r = RESUME_GUEST_NV;
break;
case EMULATE_DO_DCR:
run->exit_reason = KVM_EXIT_DCR;
- vcpu->stat.dcr_exits++;
r = RESUME_HOST;
break;
case EMULATE_FAIL:
@@ -255,6 +259,7 @@ int kvmppc_handle_exit(struct kvm_run *r
case BOOKE_INTERRUPT_FP_UNAVAIL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_FP_UNAVAIL);
+ account_exit(vcpu, FP_UNAVAIL);
r = RESUME_GUEST;
break;
@@ -262,20 +267,20 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
- vcpu->stat.dsi_exits++;
+ account_exit(vcpu, DSI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_INST_STORAGE:
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
- vcpu->stat.isi_exits++;
+ account_exit(vcpu, ISI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_SYSCALL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
- vcpu->stat.syscall_exits++;
+ account_exit(vcpu, SYSCALL_EXITS);
r = RESUME_GUEST;
break;
@@ -294,7 +299,7 @@ int kvmppc_handle_exit(struct kvm_run *r
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
- vcpu->stat.dtlb_real_miss_exits++;
+ account_exit(vcpu, DTLB_REAL_MISS_EXITS);
r = RESUME_GUEST;
break;
}
@@ -312,13 +317,13 @@ int kvmppc_handle_exit(struct kvm_run *r
* invoking the guest. */
kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
- vcpu->stat.dtlb_virt_miss_exits++;
+ account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
r = RESUME_GUEST;
} else {
/* Guest has mapped and accessed a page which is not
* actually RAM. */
r = kvmppc_emulate_mmio(run, vcpu);
- vcpu->stat.mmio_exits++;
+ account_exit(vcpu, MMIO_EXITS);
}
break;
@@ -340,11 +345,11 @@ int kvmppc_handle_exit(struct kvm_run *r
if (gtlb_index < 0) {
/* The guest didn't have a mapping for it. */
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
- vcpu->stat.itlb_real_miss_exits++;
+ account_exit(vcpu, ITLB_REAL_MISS_EXITS);
break;
}
- vcpu->stat.itlb_virt_miss_exits++;
+ account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
gpaddr = tlb_xlate(gtlbe, eaddr);
@@ -378,6 +383,7 @@ int kvmppc_handle_exit(struct kvm_run *r
mtspr(SPRN_DBSR, dbsr);
run->exit_reason = KVM_EXIT_DEBUG;
+ account_exit(vcpu, DEBUG_EXITS);
r = RESUME_HOST;
break;
}
@@ -398,7 +404,7 @@ int kvmppc_handle_exit(struct kvm_run *r
if (signal_pending(current)) {
run->exit_reason = KVM_EXIT_INTR;
r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV);
- vcpu->stat.signal_exits++;
+ account_exit(vcpu, SIGNAL_EXITS);
}
}
@@ -417,6 +423,8 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu
/* Eye-catching number so we know if the guest takes an interrupt
* before it's programmed its own IVPR. */
vcpu->arch.ivpr = 0x55550000;
+
+ init_timing_stats(vcpu);
return kvmppc_core_vcpu_setup(vcpu);
}
diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
--- a/arch/powerpc/kvm/booke.h
+++ b/arch/powerpc/kvm/booke.h
@@ -22,6 +22,7 @@
#include <linux/types.h>
#include <linux/kvm_host.h>
+#include <asm/kvm_timing_stats.h>
/* interrupt priortity ordering */
#define BOOKE_IRQPRIO_DATA_STORAGE 0
@@ -50,8 +51,10 @@ static inline void kvmppc_set_msr(struct
vcpu->arch.msr = new_msr;
- if (vcpu->arch.msr & MSR_WE)
+ if (vcpu->arch.msr & MSR_WE) {
kvm_vcpu_block(vcpu);
+ set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
+ };
}
#endif /* __KVM_BOOKE_H__ */
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -106,6 +106,18 @@ _GLOBAL(kvmppc_resume_host)
li r6, 1
slw r6, r6, r5
+
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save exit time */
+1:
+ mfspr r7, SPRN_TBRU
+ mfspr r8, SPRN_TBRL
+ mfspr r9, SPRN_TBRU
+ cmpw r9, r7
+ bne 1b
+ stw r8, VCPU_TIMING_EXIT_TBL(r4)
+ stw r9, VCPU_TIMING_EXIT_TBU(r4)
+#endif
/* Save the faulting instruction and all GPRs for emulation. */
andi. r7, r6, NEED_INST_MASK
@@ -375,6 +387,18 @@ lightweight_exit:
lwz r3, VCPU_SPRG7(r4)
mtspr SPRN_SPRG7, r3
+#ifdef CONFIG_KVM_BOOKE_EXIT_TIMING
+ /* save enter time */
+1:
+ mfspr r6, SPRN_TBRU
+ mfspr r7, SPRN_TBRL
+ mfspr r8, SPRN_TBRU
+ cmpw r8, r6
+ bne 1b
+ stw r7, VCPU_TIMING_LAST_ENTER_TBL(r4)
+ stw r8, VCPU_TIMING_LAST_ENTER_TBU(r4)
+#endif
+
/* Finish loading guest volatiles and jump to guest. */
lwz r3, VCPU_CTR(r4)
mtctr r3
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -28,6 +28,7 @@
#include <asm/byteorder.h>
#include <asm/kvm_ppc.h>
#include <asm/disassemble.h>
+#include <asm/kvm_timing_stats.h>
void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
{
@@ -72,6 +73,9 @@ int kvmppc_emulate_instruction(struct kv
int sprn;
enum emulation_result emulated = EMULATE_DONE;
int advance = 1;
+
+ /* this default type might be overwritten by subcategories */
+ set_exit_type(vcpu, EMULATED_INST_EXITS);
switch (get_op(inst)) {
case 3: /* trap */
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -28,7 +28,10 @@
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
#include <asm/tlbflush.h>
-
+#include <asm/kvm_timing_stats.h>
+#include <asm/atomic.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
{
@@ -170,11 +173,15 @@ void kvm_arch_flush_shadow(struct kvm *k
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
{
- return kvmppc_core_vcpu_create(kvm, id);
+ struct kvm_vcpu *vcpu;
+ vcpu = kvmppc_core_vcpu_create(kvm, id);
+ kvmppc_create_vcpu_debugfs(vcpu, id);
+ return vcpu;
}
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
+ kvmppc_remove_vcpu_debugfs(vcpu);
kvmppc_core_vcpu_free(vcpu);
}
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] kvm: powerpc: add exit timing statistics v4
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
` (4 preceding siblings ...)
2008-11-11 15:43 ` [PATCH] kvm: powerpc: add exit timing statistics v4 Christian Ehrhardt
@ 2008-11-11 20:22 ` Hollis Blanchard
2008-11-12 9:13 ` Christian Ehrhardt
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Hollis Blanchard @ 2008-11-11 20:22 UTC (permalink / raw)
To: kvm-ppc
On Tue, 2008-11-11 at 16:43 +0100, Christian Ehrhardt wrote:
> From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
>
> *update to v4*
> - EMUL_CORE no longer had more than wrtee emulation, therefore it now accounts
> for WRTEE in the output and set_exit_type calls are in the wrtee handlers to
> let any residual core op be counted as EMULINST"
>
> *update to v3*
> - ensure build time optimization when calling exit accouting functions using
> build time bug / constant check
> - migrate most of the exit timing code from powerpc.c and
> kvm_timing_stats.h to a separate exittiming.c file
> - renamed a lot of constants used to better fit generic/core specific code
> - added an accidenially removed optimization comment
> - use pid of userspace process instead of an own atomic count to identify a vm
> - changed loop labels in tul/tbu loops (booke_interrupt.S) to anonymous 1/1b
> - removed the indirection of additional EMULATE_*_DONE types. Instead now
> the exit timing supports "accounting" an exit which consists of set type and
> increase kvm_stat counters. But also provides both sub-tasks as separate
> functions. Using that emulation now sets a default EMUL_INST type that can
> be overwritten by detailed subcategories (set_exit_type). Accouting for
> kvm_stat is done with account_exit_stat for all emulinst exits together on
> top level (as it was before).
>
> *update to v2*
> The update fixes accounting for sets to MSR[WE] which should not be accoutned
> as instruction emulation. While adding that and analyzing the data it became
> obvious that several types of emulations hould be accounted separately.
> I'm not yet really happy with adding all these EMULATE_*_DONE types but I had
> no better idea to not break flood the code with account calls and split
> account/set_type. The issue is that emulation is also called e.g. for some mmio
> paths and therefore the accouting should not be called inside emulation, but on
> the returning path that can now evaluate the different kind of emulations done.
> This is not yet finished, and posted as RFC only.
>
> Other existing kvm statistics are either just counters (kvm_stat) reported for
> kvm generally or trace based aproaches like kvm_trace.
> For kvm on powerpc we had the need to track the timings of the different exit
> types. While this could be achieved parsing data created with a kvm_trace
> extension this adds too muhc overhead (at least on embedded powerpc) slowing
> down the workloads we wanted to measure.
>
> Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
> code. These statistic is available per vm&vcpu under the kvm debugfs directory.
> As this statistic is low, but still some overhead it can be enabled via a
> .config entry and should be off by default.
>
> Since this patch touched all powerpc kvm_stat code anyway this code is now
> merged and simpliefied together with the exit timing statistic code (still
> working with exit timing disabled in .config).
You forgot to include exittiming.c in this patch. Since I can't build
it, I might as well tell you the additional changes I was going to
make... :)
* Prefix all the functions in kvm_timing_stats.h to begin with
"kvmppc_". (I really like to keep the layering as clear as
possible.) I don't think we need to add "booke" in there, since
a hypothetical kvmppc 970 implementation could use the same
function names, just a different set of exit types.
* Rename kvm_timing_stats.h. Does that need to go in the asm
directory btw? If so, call it kvm_timing.h; if not, please put
it inside arch/powerpc/kvm, and name it timing.h.
* Rename exittiming.c to match whatever you name the header.
* Use empty static functions instead of empty macros in
kvm_timing.h. (I'm not the only one who doesn't like macros; see
http://lwn.net/Articles/306045/)
* I know it's just leftovers from previous iterations of this
patch, but drop the whitespace changes to include/asm/kvm_ppc.h
(and send as a separate patch if you like).
* Remove vm_id from kvm_arch (you just missed this one spot :).
* I don't like the debugfs file names you've chosen, but I'm not
sure what's best. Definitely make them lowercase and omit the
underscore, but the directory layout feels a little odd to me.
Maybe it should be /sys/kernel/debug/kvm/exit_times/vm52/vcpu0/
instead of /sys/kernel/debug/kvm/vm52/vcpu0/exit_times ?
* Remove atomic.h and seq_file.h from arch/powerpc/kvm/powerpc.c
* Slight edits for Kconfig:
config KVM_EXIT_TIMING
bool "Detailed exit timing"
depends on KVM
---help---
Calculate elapsed time for every exit/enter cycle. A per-vcpu
report is available in debugfs kvm/vm###/vcpu###_exit_timing.
The overhead is relatively small, however it is not recommended for
production environments.
If unsure, say N.
A lot of the naming stuff is just personal preference, but in general I
feel like the names you chose are too verbose and inconsistent. I've
found that name changes are easiest to do in the unapplied patch itself
with global find/replace.
I'm happy with the rest of what I can see. :) If I have comments after
you've left for vacation, I'll apply those on top as a separate patch.
This patch is really important, and I feel like we're almost there...
Thanks very much for http://kvm.qumranet.com/kvmwiki/PowerPC_Exittimings
too; that's critical data.
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] kvm: powerpc: add exit timing statistics v4
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
` (5 preceding siblings ...)
2008-11-11 20:22 ` Hollis Blanchard
@ 2008-11-12 9:13 ` Christian Ehrhardt
2008-11-12 9:22 ` [PATCH] kvm: powerpc: add exit timing statistics v5 Christian Ehrhardt
2008-11-12 23:27 ` Hollis Blanchard
8 siblings, 0 replies; 10+ messages in thread
From: Christian Ehrhardt @ 2008-11-12 9:13 UTC (permalink / raw)
To: kvm-ppc
Hollis Blanchard wrote:
> On Tue, 2008-11-11 at 16:43 +0100, Christian Ehrhardt wrote:
>
>> From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
>>
>> *update to v4*
>> - EMUL_CORE no longer had more than wrtee emulation, therefore it now accounts
>> for WRTEE in the output and set_exit_type calls are in the wrtee handlers to
>> let any residual core op be counted as EMULINST"
>>
>> *update to v3*
>> - ensure build time optimization when calling exit accouting functions using
>> build time bug / constant check
>> - migrate most of the exit timing code from powerpc.c and
>> kvm_timing_stats.h to a separate exittiming.c file
>> - renamed a lot of constants used to better fit generic/core specific code
>> - added an accidenially removed optimization comment
>> - use pid of userspace process instead of an own atomic count to identify a vm
>> - changed loop labels in tul/tbu loops (booke_interrupt.S) to anonymous 1/1b
>> - removed the indirection of additional EMULATE_*_DONE types. Instead now
>> the exit timing supports "accounting" an exit which consists of set type and
>> increase kvm_stat counters. But also provides both sub-tasks as separate
>> functions. Using that emulation now sets a default EMUL_INST type that can
>> be overwritten by detailed subcategories (set_exit_type). Accouting for
>> kvm_stat is done with account_exit_stat for all emulinst exits together on
>> top level (as it was before).
>>
>> *update to v2*
>> The update fixes accounting for sets to MSR[WE] which should not be accoutned
>> as instruction emulation. While adding that and analyzing the data it became
>> obvious that several types of emulations hould be accounted separately.
>> I'm not yet really happy with adding all these EMULATE_*_DONE types but I had
>> no better idea to not break flood the code with account calls and split
>> account/set_type. The issue is that emulation is also called e.g. for some mmio
>> paths and therefore the accouting should not be called inside emulation, but on
>> the returning path that can now evaluate the different kind of emulations done.
>> This is not yet finished, and posted as RFC only.
>>
>> Other existing kvm statistics are either just counters (kvm_stat) reported for
>> kvm generally or trace based aproaches like kvm_trace.
>> For kvm on powerpc we had the need to track the timings of the different exit
>> types. While this could be achieved parsing data created with a kvm_trace
>> extension this adds too muhc overhead (at least on embedded powerpc) slowing
>> down the workloads we wanted to measure.
>>
>> Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
>> code. These statistic is available per vm&vcpu under the kvm debugfs directory.
>> As this statistic is low, but still some overhead it can be enabled via a
>> .config entry and should be off by default.
>>
>> Since this patch touched all powerpc kvm_stat code anyway this code is now
>> merged and simpliefied together with the exit timing statistic code (still
>> working with exit timing disabled in .config).
>>
>
> You forgot to include exittiming.c in this patch. Since I can't build
> it, I might as well tell you the additional changes I was going to
> make... :)
>
sigh - I hate missing such things. I should include a check via hg
status for ? files with a .c/.h type in my hg email :-/
I'll resubmit the patch as v5 including the changes you describe here
> * Prefix all the functions in kvm_timing_stats.h to begin with
> "kvmppc_". (I really like to keep the layering as clear as
> possible.) I don't think we need to add "booke" in there, since
> a hypothetical kvmppc 970 implementation could use the same
> function names, just a different set of exit types.
>
very reasonable, and as some sort of advocacy I wanted to mention that I
already did that with some of the functions.
But yes I should be consequent and do this for all of them.
> * Rename kvm_timing_stats.h. Does that need to go in the asm
> directory btw? If so, call it kvm_timing.h; if not, please put
> it inside arch/powerpc/kvm, and name it timing.h.
>
done
> * Rename exittiming.c to match whatever you name the header.
>
done
> * Use empty static functions instead of empty macros in
> kvm_timing.h. (I'm not the only one who doesn't like macros; see
> http://lwn.net/Articles/306045/)
>
I had no precedence 'til now and just used what I see most in code and
that where defines :-)
But it makes sense.
Btw - I would even change it if only you wouldn't like macros :-P
> * I know it's just leftovers from previous iterations of this
> patch, but drop the whitespace changes to include/asm/kvm_ppc.h
> (and send as a separate patch if you like).
>
at least the whitespace fix is correct ;-)
I'll submit it in a separate patch
> * Remove vm_id from kvm_arch (you just missed this one spot :).
>
done
> * I don't like the debugfs file names you've chosen, but I'm not
> sure what's best. Definitely make them lowercase and omit the
> underscore, but the directory layout feels a little odd to me.
> Maybe it should be /sys/kernel/debug/kvm/exit_times/vm52/vcpu0/
> instead of /sys/kernel/debug/kvm/vm52/vcpu0/exit_times ?
>
btw it's no more a directory layout anymore - just a flat file
(explained below).
I can make them lower case, but the dir version is not what you want.
I had that in the past and it works nice as long as things go fine.
But I found that when you (intentionally or not) crash the process (e.g.
kill -9) the cleanup in the kvm code cleans up the vm before the vcpu's.
While initialization is vm -> vcpu.
This gives you
>vm
>vcpu's
<vm
<vcpu's
Eventually this forces you in your cleanup of the VM to iterate over the
vcpu's in your own loop, because otherwise you cant remove the vm
directory because you have stale files in it. And once the vcpu's are
cleaned the vm clean will no more be called and you have a stale dir.
However I know this could be solved, but all would add loops or
refcount/childencount checks, ...
While the simple approach to handle it in one file is just better to
read in code and less bug prone.
I changed it to lower case with only a underscore between vm/vcpu/timing
no more between entity and their numbers (vcpu_1 -> vcpu1)
> * Remove atomic.h and seq_file.h from arch/powerpc/kvm/powerpc.c
>
done and removed debugfs.h as c cod is now in a separate file.
> * Slight edits for Kconfig:
>
> config KVM_EXIT_TIMING
> bool "Detailed exit timing"
> depends on KVM
> ---help---
> Calculate elapsed time for every exit/enter cycle. A per-vcpu
> report is available in debugfs kvm/vm###/vcpu###_exit_timing.
> The overhead is relatively small, however it is not recommended for
> production environments.
>
> If unsure, say N.
>
included with a modification to the debugfs layout where the data can be
found
> A lot of the naming stuff is just personal preference, but in general I
> feel like the names you chose are too verbose and inconsistent. I've
> found that name changes are easiest to do in the unapplied patch itself
> with global find/replace.
>
> I'm happy with the rest of what I can see. :) If I have comments after
> you've left for vacation, I'll apply those on top as a separate patch.
> This patch is really important, and I feel like we're almost there...
>
>
I hope it now feels even closer
> Thanks very much for http://kvm.qumranet.com/kvmwiki/PowerPC_Exittimings
> too; that's critical data.
I hope to get blktrace data onto that pages today too.
Thanks for your review Hollis, you should find the updated patch in your
inbox and on kvm-ppc soon.
--
Grüsse / regards,
Christian Ehrhardt
IBM Linux Technology Center, Open Virtualization
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] kvm: powerpc: add exit timing statistics v5
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
` (6 preceding siblings ...)
2008-11-12 9:13 ` Christian Ehrhardt
@ 2008-11-12 9:22 ` Christian Ehrhardt
2008-11-12 23:27 ` Hollis Blanchard
8 siblings, 0 replies; 10+ messages in thread
From: Christian Ehrhardt @ 2008-11-12 9:22 UTC (permalink / raw)
To: kvm-ppc
From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
*update to v5*
- add exittiming.c to diff content
- prefix all exit timing functions with kvmppc to prevent name collisions (was
already done for some of the functions, now its consequently done for all of
them)
- renamed header & c-file and relocated the header from asm to arch/powerpc/kvm
- changed the empty define function stubs to empty static inlines
- removed unrelated whitespace change and leftover of atomic vm_id
- changed debugs filename to lower case and fewer underscores
- updated the Kconfig text
Other existing kvm statistics are either just counters (kvm_stat) reported for
kvm generally or trace based aproaches like kvm_trace.
For kvm on powerpc we had the need to track the timings of the different exit
types. While this could be achieved parsing data created with a kvm_trace
extension this adds too muhc overhead (at least on embedded powerpc) slowing
down the workloads we wanted to measure.
Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
code. These statistic is available per vm&vcpu under the kvm debugfs directory.
As this statistic is low, but still some overhead it can be enabled via a
.config entry and should be off by default.
Since this patch touched all powerpc kvm_stat code anyway this code is now
merged and simpliefied together with the exit timing statistic code (still
working with exit timing disabled in .config).
An awk script printing the data in a more narrow layout can be found on our
wiki pages about the exit timing topic.
(http://kvm.qumranet.com/kvmwiki/PowerPC_Exittimings)
Here is a sample output how the results look like.
processing file timing_boot.log
sum of time 8309940
type count min max sum avg stddev %
MMIO: 9402 44 1997 1697610 180.5584 155.768 20.43
DCR: 680 41 99 32096 47.2000 7.008 0.39
SIGNAL: 1 98 98 98 98.0000 0.000 0.00
ITLBREAL: 926 8 14 7810 8.4341 0.658 0.09
ITLBVIRT: 3595 18 202 76185 21.1919 4.954 0.92
DTLBREAL: 950 8 16 8891 9.3589 1.427 0.11
DTLBVIRT: 6695 18 282 156727 23.4096 13.781 1.89
SYSCALL: 1801 6 59 11372 6.3143 2.575 0.14
ISI: 116 6 8 764 6.5862 0.588 0.01
DSI: 43 6 7 292 6.7907 0.407 0.00
EMULINST: 65247 7 96 484081 7.4192 1.818 5.83
EMUL_WAIT: 801 659 9200 3721789 4646.4282 1687.218 44.79
EMUL_WRTEE: 66806 7 86 540053 8.0839 1.895 6.50
EMUL_MTSPR: 13415 8 62 111358 8.3010 2.583 1.34
EMUL_MFSPR: 7635 8 61 62772 8.2216 1.921 0.76
EMUL_MTMSR: 5678 8 59 45704 8.0493 1.434 0.55
EMUL_MFMSR: 32853 7 67 267603 8.1455 1.875 3.22
EMUL_TLBSX: 354 9 60 3745 10.5791 3.919 0.05
EMUL_TLBWE: 6403 9 112 99522 15.5430 7.668 1.20
EMUL_RFI: 9515 7 57 71420 7.5060 2.108 0.86
DEC: 290 49 161 15786 54.4345 9.708 0.19
EXTINT: 7 74 75 522 74.5714 0.495 0.01
TIMEINGUEST: 233213 0 3954 893740 3.8323 65.837 10.76
Signed-off-by: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
---
[diffstat]
include/asm/kvm_host.h | 56 ++++++++++
kernel/asm-offsets.c | 11 ++
kvm/44x_emulate.c | 11 +-
kvm/44x_tlb.c | 3
kvm/Kconfig | 11 ++
kvm/Makefile | 1
kvm/booke.c | 36 ++++--
kvm/booke.h | 5
kvm/booke_interrupts.S | 24 ++++
kvm/emulate.c | 4
kvm/powerpc.c | 8 +
kvm/timing.c | 262 +++++++++++++++++++++++++++++++++++++++++++++++++
kvm/timing.h | 102 +++++++++++++++++++
13 files changed, 516 insertions(+), 18 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -71,6 +71,49 @@ struct kvmppc_44x_tlbe {
u32 word2;
};
+enum kvm_exit_types {
+ MMIO_EXITS,
+ DCR_EXITS,
+ SIGNAL_EXITS,
+ ITLB_REAL_MISS_EXITS,
+ ITLB_VIRT_MISS_EXITS,
+ DTLB_REAL_MISS_EXITS,
+ DTLB_VIRT_MISS_EXITS,
+ SYSCALL_EXITS,
+ ISI_EXITS,
+ DSI_EXITS,
+ EMULATED_INST_EXITS,
+ EMULATED_MTMSRWE_EXITS,
+ EMULATED_WRTEE_EXITS,
+ EMULATED_MTSPR_EXITS,
+ EMULATED_MFSPR_EXITS,
+ EMULATED_MTMSR_EXITS,
+ EMULATED_MFMSR_EXITS,
+ EMULATED_TLBSX_EXITS,
+ EMULATED_TLBWE_EXITS,
+ EMULATED_RFI_EXITS,
+ DEC_EXITS,
+ EXT_INTR_EXITS,
+ HALT_WAKEUP,
+ USR_PR_INST,
+ FP_UNAVAIL,
+ DEBUG_EXITS,
+ TIMEINGUEST,
+ __NUMBER_OF_KVM_EXIT_TYPES
+};
+
+#ifdef CONFIG_KVM_EXIT_TIMING
+/* allow access to big endian 32bit upper/lower parts and 64bit var */
+struct exit_timing {
+ union {
+ u64 tv64;
+ struct {
+ u32 tbu, tbl;
+ } tv32;
+ };
+};
+#endif
+
struct kvm_arch {
};
@@ -130,6 +173,19 @@ struct kvm_vcpu_arch {
u32 dbcr0;
u32 dbcr1;
+#ifdef CONFIG_KVM_EXIT_TIMING
+ struct exit_timing timing_exit;
+ struct exit_timing timing_last_enter;
+ u32 last_exit_type;
+ u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_sum_quad_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_min_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_max_duration[__NUMBER_OF_KVM_EXIT_TYPES];
+ u64 timing_last_exit;
+ struct dentry *debugfs_exit_timing;
+#endif
+
u32 last_inst;
ulong fault_dear;
ulong fault_esr;
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -381,5 +381,16 @@ int main(void)
DEFINE(PTE_SHIFT, PTE_SHIFT);
#endif
+#ifdef CONFIG_KVM_EXIT_TIMING
+ DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbu));
+ DEFINE(VCPU_TIMING_EXIT_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_exit.tv32.tbl));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBU, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbu));
+ DEFINE(VCPU_TIMING_LAST_ENTER_TBL, offsetof(struct kvm_vcpu,
+ arch.timing_last_enter.tv32.tbl));
+#endif
+
return 0;
}
diff --git a/arch/powerpc/kvm/44x_emulate.c b/arch/powerpc/kvm/44x_emulate.c
--- a/arch/powerpc/kvm/44x_emulate.c
+++ b/arch/powerpc/kvm/44x_emulate.c
@@ -22,6 +22,7 @@
#include <asm/dcr-regs.h>
#include <asm/disassemble.h>
#include <asm/kvm_44x.h>
+#include "timing.h"
#include "booke.h"
#include "44x_tlb.h"
@@ -58,11 +59,11 @@ int kvmppc_core_emulate_op(struct kvm_ru
int ws;
switch (get_op(inst)) {
-
case OP_RFI:
switch (get_xop(inst)) {
case XOP_RFI:
kvmppc_emul_rfi(vcpu);
+ kvmppc_set_exit_type(vcpu, EMULATED_RFI_EXITS);
*advance = 0;
break;
@@ -78,10 +79,12 @@ int kvmppc_core_emulate_op(struct kvm_ru
case XOP_MFMSR:
rt = get_rt(inst);
vcpu->arch.gpr[rt] = vcpu->arch.msr;
+ kvmppc_set_exit_type(vcpu, EMULATED_MFMSR_EXITS);
break;
case XOP_MTMSR:
rs = get_rs(inst);
+ kvmppc_set_exit_type(vcpu, EMULATED_MTMSR_EXITS);
kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
break;
@@ -89,11 +92,13 @@ int kvmppc_core_emulate_op(struct kvm_ru
rs = get_rs(inst);
vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
| (vcpu->arch.gpr[rs] & MSR_EE);
+ kvmppc_set_exit_type(vcpu, EMULATED_WRTEE_EXITS);
break;
case XOP_WRTEEI:
vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
| (inst & MSR_EE);
+ kvmppc_set_exit_type(vcpu, EMULATED_WRTEE_EXITS);
break;
case XOP_MFDCR:
@@ -127,6 +132,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.is_write = 0;
vcpu->arch.io_gpr = rt;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -146,6 +152,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.data = vcpu->arch.gpr[rs];
run->dcr.is_write = 1;
vcpu->arch.dcr_needed = 1;
+ account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -276,6 +283,7 @@ int kvmppc_core_emulate_mtspr(struct kvm
return EMULATE_FAIL;
}
+ kvmppc_set_exit_type(vcpu, EMULATED_MTSPR_EXITS);
return EMULATE_DONE;
}
@@ -357,6 +365,7 @@ int kvmppc_core_emulate_mfspr(struct kvm
return EMULATE_FAIL;
}
+ kvmppc_set_exit_type(vcpu, EMULATED_MFSPR_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -27,6 +27,7 @@
#include <asm/mmu-44x.h>
#include <asm/kvm_ppc.h>
#include <asm/kvm_44x.h>
+#include "timing.h"
#include "44x_tlb.h"
@@ -457,6 +458,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcp
KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
tlbe->word1, tlbe->word2, handler);
+ kvmppc_set_exit_type(vcpu, EMULATED_TLBWE_EXITS);
return EMULATE_DONE;
}
@@ -480,5 +482,6 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcp
}
vcpu->arch.gpr[rt] = gtlb_index;
+ kvmppc_set_exit_type(vcpu, EMULATED_TLBSX_EXITS);
return EMULATE_DONE;
}
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -32,6 +32,17 @@ config KVM_440
If unsure, say N.
+config KVM_EXIT_TIMING
+ bool "Detailed exit timing"
+ depends on KVM
+ ---help---
+ Calculate elapsed time for every exit/enter cycle. A per-vcpu
+ report is available in debugfs kvm/vm#_vcpu#_timing.
+ The overhead is relatively small, however it is not recommended for
+ production environments.
+
+ If unsure, say N.
+
config KVM_TRACE
bool "KVM trace support"
depends on KVM && MARKERS && SYSFS
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -9,6 +9,7 @@ common-objs-$(CONFIG_KVM_TRACE) += $(ad
common-objs-$(CONFIG_KVM_TRACE) += $(addprefix ../../../virt/kvm/, kvm_trace.o)
kvm-objs := $(common-objs-y) powerpc.o emulate.o
+obj-$(CONFIG_KVM_EXIT_TIMING) += timing.o
obj-$(CONFIG_KVM) += kvm.o
AFLAGS_booke_interrupts.o := -I$(obj)
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -28,6 +28,7 @@
#include <asm/cputable.h>
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
+#include "timing.h"
#include <asm/cacheflush.h>
#include <asm/kvm_44x.h>
@@ -185,6 +186,9 @@ int kvmppc_handle_exit(struct kvm_run *r
enum emulation_result er;
int r = RESUME_HOST;
+ /* update before a new last_exit_type is rewritten */
+ kvmppc_update_timing_stats(vcpu);
+
local_irq_enable();
run->exit_reason = KVM_EXIT_UNKNOWN;
@@ -198,7 +202,7 @@ int kvmppc_handle_exit(struct kvm_run *r
break;
case BOOKE_INTERRUPT_EXTERNAL:
- vcpu->stat.ext_intr_exits++;
+ account_exit(vcpu, EXT_INTR_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -208,8 +212,7 @@ int kvmppc_handle_exit(struct kvm_run *r
/* Since we switched IVPR back to the host's value, the host
* handled this interrupt the moment we enabled interrupts.
* Now we just offer it a chance to reschedule the guest. */
-
- vcpu->stat.dec_exits++;
+ account_exit(vcpu, DEC_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -222,20 +225,21 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
r = RESUME_GUEST;
+ account_exit(vcpu, USR_PR_INST);
break;
}
er = kvmppc_emulate_instruction(run, vcpu);
switch (er) {
case EMULATE_DONE:
+ /* don't overwrite subtypes, just account kvm_stats */
+ account_exit_stat(vcpu, EMULATED_INST_EXITS);
/* Future optimization: only reload non-volatiles if
* they were actually modified by emulation. */
- vcpu->stat.emulated_inst_exits++;
r = RESUME_GUEST_NV;
break;
case EMULATE_DO_DCR:
run->exit_reason = KVM_EXIT_DCR;
- vcpu->stat.dcr_exits++;
r = RESUME_HOST;
break;
case EMULATE_FAIL:
@@ -255,6 +259,7 @@ int kvmppc_handle_exit(struct kvm_run *r
case BOOKE_INTERRUPT_FP_UNAVAIL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_FP_UNAVAIL);
+ account_exit(vcpu, FP_UNAVAIL);
r = RESUME_GUEST;
break;
@@ -262,20 +267,20 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
- vcpu->stat.dsi_exits++;
+ account_exit(vcpu, DSI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_INST_STORAGE:
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
- vcpu->stat.isi_exits++;
+ account_exit(vcpu, ISI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_SYSCALL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
- vcpu->stat.syscall_exits++;
+ account_exit(vcpu, SYSCALL_EXITS);
r = RESUME_GUEST;
break;
@@ -294,7 +299,7 @@ int kvmppc_handle_exit(struct kvm_run *r
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
- vcpu->stat.dtlb_real_miss_exits++;
+ account_exit(vcpu, DTLB_REAL_MISS_EXITS);
r = RESUME_GUEST;
break;
}
@@ -312,13 +317,13 @@ int kvmppc_handle_exit(struct kvm_run *r
* invoking the guest. */
kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
- vcpu->stat.dtlb_virt_miss_exits++;
+ account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
r = RESUME_GUEST;
} else {
/* Guest has mapped and accessed a page which is not
* actually RAM. */
r = kvmppc_emulate_mmio(run, vcpu);
- vcpu->stat.mmio_exits++;
+ account_exit(vcpu, MMIO_EXITS);
}
break;
@@ -340,11 +345,11 @@ int kvmppc_handle_exit(struct kvm_run *r
if (gtlb_index < 0) {
/* The guest didn't have a mapping for it. */
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
- vcpu->stat.itlb_real_miss_exits++;
+ account_exit(vcpu, ITLB_REAL_MISS_EXITS);
break;
}
- vcpu->stat.itlb_virt_miss_exits++;
+ account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
gpaddr = tlb_xlate(gtlbe, eaddr);
@@ -378,6 +383,7 @@ int kvmppc_handle_exit(struct kvm_run *r
mtspr(SPRN_DBSR, dbsr);
run->exit_reason = KVM_EXIT_DEBUG;
+ account_exit(vcpu, DEBUG_EXITS);
r = RESUME_HOST;
break;
}
@@ -398,7 +404,7 @@ int kvmppc_handle_exit(struct kvm_run *r
if (signal_pending(current)) {
run->exit_reason = KVM_EXIT_INTR;
r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV);
- vcpu->stat.signal_exits++;
+ account_exit(vcpu, SIGNAL_EXITS);
}
}
@@ -417,6 +423,8 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu
/* Eye-catching number so we know if the guest takes an interrupt
* before it's programmed its own IVPR. */
vcpu->arch.ivpr = 0x55550000;
+
+ kvmppc_init_timing_stats(vcpu);
return kvmppc_core_vcpu_setup(vcpu);
}
diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
--- a/arch/powerpc/kvm/booke.h
+++ b/arch/powerpc/kvm/booke.h
@@ -22,6 +22,7 @@
#include <linux/types.h>
#include <linux/kvm_host.h>
+#include "timing.h"
/* interrupt priortity ordering */
#define BOOKE_IRQPRIO_DATA_STORAGE 0
@@ -50,8 +51,10 @@ static inline void kvmppc_set_msr(struct
vcpu->arch.msr = new_msr;
- if (vcpu->arch.msr & MSR_WE)
+ if (vcpu->arch.msr & MSR_WE) {
kvm_vcpu_block(vcpu);
+ kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
+ };
}
#endif /* __KVM_BOOKE_H__ */
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -106,6 +106,18 @@ _GLOBAL(kvmppc_resume_host)
li r6, 1
slw r6, r6, r5
+
+#ifdef CONFIG_KVM_EXIT_TIMING
+ /* save exit time */
+1:
+ mfspr r7, SPRN_TBRU
+ mfspr r8, SPRN_TBRL
+ mfspr r9, SPRN_TBRU
+ cmpw r9, r7
+ bne 1b
+ stw r8, VCPU_TIMING_EXIT_TBL(r4)
+ stw r9, VCPU_TIMING_EXIT_TBU(r4)
+#endif
/* Save the faulting instruction and all GPRs for emulation. */
andi. r7, r6, NEED_INST_MASK
@@ -375,6 +387,18 @@ lightweight_exit:
lwz r3, VCPU_SPRG7(r4)
mtspr SPRN_SPRG7, r3
+#ifdef CONFIG_KVM_EXIT_TIMING
+ /* save enter time */
+1:
+ mfspr r6, SPRN_TBRU
+ mfspr r7, SPRN_TBRL
+ mfspr r8, SPRN_TBRU
+ cmpw r8, r6
+ bne 1b
+ stw r7, VCPU_TIMING_LAST_ENTER_TBL(r4)
+ stw r8, VCPU_TIMING_LAST_ENTER_TBU(r4)
+#endif
+
/* Finish loading guest volatiles and jump to guest. */
lwz r3, VCPU_CTR(r4)
mtctr r3
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -28,6 +28,7 @@
#include <asm/byteorder.h>
#include <asm/kvm_ppc.h>
#include <asm/disassemble.h>
+#include "timing.h"
void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
{
@@ -72,6 +73,9 @@ int kvmppc_emulate_instruction(struct kv
int sprn;
enum emulation_result emulated = EMULATE_DONE;
int advance = 1;
+
+ /* this default type might be overwritten by subcategories */
+ kvmppc_set_exit_type(vcpu, EMULATED_INST_EXITS);
switch (get_op(inst)) {
case 3: /* trap */
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -28,7 +28,7 @@
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
#include <asm/tlbflush.h>
-
+#include "timing.h"
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
{
@@ -170,11 +170,15 @@ void kvm_arch_flush_shadow(struct kvm *k
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
{
- return kvmppc_core_vcpu_create(kvm, id);
+ struct kvm_vcpu *vcpu;
+ vcpu = kvmppc_core_vcpu_create(kvm, id);
+ kvmppc_create_vcpu_debugfs(vcpu, id);
+ return vcpu;
}
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
+ kvmppc_remove_vcpu_debugfs(vcpu);
kvmppc_core_vcpu_free(vcpu);
}
diff --git a/arch/powerpc/kvm/timing.c b/arch/powerpc/kvm/timing.c
new file mode 100644
--- /dev/null
+++ b/arch/powerpc/kvm/timing.c
@@ -0,0 +1,262 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2007
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ */
+
+#include <linux/kvm_host.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#include "timing.h"
+#include <asm/time.h>
+#include <asm-generic/div64.h>
+
+void kvmppc_init_timing_stats(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ /* pause guest execution to avoid concurrent updates */
+ local_irq_disable();
+ mutex_lock(&vcpu->mutex);
+
+ vcpu->arch.last_exit_type = 0xDEAD;
+ for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
+ vcpu->arch.timing_count_type[i] = 0;
+ vcpu->arch.timing_max_duration[i] = 0;
+ vcpu->arch.timing_min_duration[i] = 0xFFFFFFFF;
+ vcpu->arch.timing_sum_duration[i] = 0;
+ vcpu->arch.timing_sum_quad_duration[i] = 0;
+ }
+ vcpu->arch.timing_last_exit = 0;
+ vcpu->arch.timing_exit.tv64 = 0;
+ vcpu->arch.timing_last_enter.tv64 = 0;
+
+ mutex_unlock(&vcpu->mutex);
+ local_irq_enable();
+}
+
+static void add_exit_timing(struct kvm_vcpu *vcpu,
+ u64 duration, int type)
+{
+ u64 old;
+
+ do_div(duration, tb_ticks_per_usec);
+ if (unlikely(duration > 0xFFFFFFFF)) {
+ printk(KERN_ERR"%s - duration too big -> overflow"
+ " duration %lld type %d exit #%d\n",
+ __func__, duration, type,
+ vcpu->arch.timing_count_type[type]);
+ return;
+ }
+
+ vcpu->arch.timing_count_type[type]++;
+
+ /* sum */
+ old = vcpu->arch.timing_sum_duration[type];
+ vcpu->arch.timing_sum_duration[type] += duration;
+ if (unlikely(old > vcpu->arch.timing_sum_duration[type])) {
+ printk(KERN_ERR"%s - wrap adding sum of durations"
+ " old %lld new %lld type %d exit # of type %d\n",
+ __func__, old, vcpu->arch.timing_sum_duration[type],
+ type, vcpu->arch.timing_count_type[type]);
+ }
+
+ /* square sum */
+ old = vcpu->arch.timing_sum_quad_duration[type];
+ vcpu->arch.timing_sum_quad_duration[type] += (duration*duration);
+ if (unlikely(old > vcpu->arch.timing_sum_quad_duration[type])) {
+ printk(KERN_ERR"%s - wrap adding sum of squared durations"
+ " old %lld new %lld type %d exit # of type %d\n",
+ __func__, old,
+ vcpu->arch.timing_sum_quad_duration[type],
+ type, vcpu->arch.timing_count_type[type]);
+ }
+
+ /* set min/max */
+ if (unlikely(duration < vcpu->arch.timing_min_duration[type]))
+ vcpu->arch.timing_min_duration[type] = duration;
+ if (unlikely(duration > vcpu->arch.timing_max_duration[type]))
+ vcpu->arch.timing_max_duration[type] = duration;
+}
+
+void kvmppc_update_timing_stats(struct kvm_vcpu *vcpu)
+{
+ u64 exit = vcpu->arch.timing_last_exit;
+ u64 enter = vcpu->arch.timing_last_enter.tv64;
+
+ /* save exit time, used next exit when the reenter time is known */
+ vcpu->arch.timing_last_exit = vcpu->arch.timing_exit.tv64;
+
+ if (unlikely(vcpu->arch.last_exit_type = 0xDEAD || exit = 0))
+ return; /* skip incomplete cycle (e.g. after reset) */
+
+ /* update statistics for average and standard deviation */
+ add_exit_timing(vcpu, (enter - exit), vcpu->arch.last_exit_type);
+ /* enter -> timing_last_exit is time spent in guest - log this too */
+ add_exit_timing(vcpu, (vcpu->arch.timing_last_exit - enter),
+ TIMEINGUEST);
+}
+
+static const char *kvm_exit_names[__NUMBER_OF_KVM_EXIT_TYPES] = {
+ [MMIO_EXITS] = "MMIO",
+ [DCR_EXITS] = "DCR",
+ [SIGNAL_EXITS] = "SIGNAL",
+ [ITLB_REAL_MISS_EXITS] = "ITLBREAL",
+ [ITLB_VIRT_MISS_EXITS] = "ITLBVIRT",
+ [DTLB_REAL_MISS_EXITS] = "DTLBREAL",
+ [DTLB_VIRT_MISS_EXITS] = "DTLBVIRT",
+ [SYSCALL_EXITS] = "SYSCALL",
+ [ISI_EXITS] = "ISI",
+ [DSI_EXITS] = "DSI",
+ [EMULATED_INST_EXITS] = "EMULINST",
+ [EMULATED_MTMSRWE_EXITS] = "EMUL_WAIT",
+ [EMULATED_WRTEE_EXITS] = "EMUL_WRTEE",
+ [EMULATED_MTSPR_EXITS] = "EMUL_MTSPR",
+ [EMULATED_MFSPR_EXITS] = "EMUL_MFSPR",
+ [EMULATED_MTMSR_EXITS] = "EMUL_MTMSR",
+ [EMULATED_MFMSR_EXITS] = "EMUL_MFMSR",
+ [EMULATED_TLBSX_EXITS] = "EMUL_TLBSX",
+ [EMULATED_TLBWE_EXITS] = "EMUL_TLBWE",
+ [EMULATED_RFI_EXITS] = "EMUL_RFI",
+ [DEC_EXITS] = "DEC",
+ [EXT_INTR_EXITS] = "EXTINT",
+ [HALT_WAKEUP] = "HALT",
+ [USR_PR_INST] = "USR_PR_INST",
+ [FP_UNAVAIL] = "FP_UNAVAIL",
+ [DEBUG_EXITS] = "DEBUG",
+ [TIMEINGUEST] = "TIMEINGUEST"
+};
+
+static int kvmppc_exit_timing_show(struct seq_file *m, void *private)
+{
+ struct kvm_vcpu *vcpu = m->private;
+ int i;
+ u64 min, max;
+
+ for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
+ if (vcpu->arch.timing_min_duration[i] = 0xFFFFFFFF)
+ min = 0;
+ else
+ min = vcpu->arch.timing_min_duration[i];
+ if (vcpu->arch.timing_max_duration[i] = 0)
+ max = 0;
+ else
+ max = vcpu->arch.timing_max_duration[i];
+
+ seq_printf(m, "%12s: count %10d min %10lld "
+ "max %10lld sum %20lld sum_quad %20lld\n",
+ kvm_exit_names[i], vcpu->arch.timing_count_type[i],
+ vcpu->arch.timing_min_duration[i],
+ vcpu->arch.timing_max_duration[i],
+ vcpu->arch.timing_sum_duration[i],
+ vcpu->arch.timing_sum_quad_duration[i]);
+ }
+ return 0;
+}
+
+static ssize_t kvmppc_exit_timing_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ size_t len;
+ int err;
+ const char __user *p;
+ char c;
+
+ len = 0;
+ p = user_buf;
+ while (len < count) {
+ if (get_user(c, p++))
+ err = -EFAULT;
+ if (c = 0 || c = '\n')
+ break;
+ len++;
+ }
+
+ if (len > 1) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&c, user_buf, sizeof(c))) {
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (c = 'c') {
+ struct seq_file *seqf = (struct seq_file *)file->private_data;
+ struct kvm_vcpu *vcpu = seqf->private;
+ /* write does not affect out buffers previsously generated with
+ * show. Seq file is locked here to prevent races of init with
+ * a show call */
+ mutex_lock(&seqf->lock);
+ kvmppc_init_timing_stats(vcpu);
+ mutex_unlock(&seqf->lock);
+ err = count;
+ } else {
+ err = -EINVAL;
+ goto done;
+ }
+
+done:
+ return err;
+}
+
+static int kvmppc_exit_timing_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, kvmppc_exit_timing_show, inode->i_private);
+}
+
+static struct file_operations kvmppc_exit_timing_fops = {
+ .owner = THIS_MODULE,
+ .open = kvmppc_exit_timing_open,
+ .read = seq_read,
+ .write = kvmppc_exit_timing_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id)
+{
+ static char dbg_fname[50];
+ struct dentry *debugfs_file;
+
+ snprintf(dbg_fname, sizeof(dbg_fname), "vm%u_vcpu%03u_timing",
+ current->pid, id);
+ debugfs_file = debugfs_create_file(dbg_fname, 0666,
+ kvm_debugfs_dir, vcpu,
+ &kvmppc_exit_timing_fops);
+
+ if (!debugfs_file) {
+ printk(KERN_ERR"%s: error creating debugfs file %s\n",
+ __func__, dbg_fname);
+ return;
+ }
+
+ vcpu->arch.debugfs_exit_timing = debugfs_file;
+}
+
+void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu)
+{
+ if (vcpu->arch.debugfs_exit_timing) {
+ debugfs_remove(vcpu->arch.debugfs_exit_timing);
+ vcpu->arch.debugfs_exit_timing = NULL;
+ }
+}
diff --git a/arch/powerpc/kvm/timing.h b/arch/powerpc/kvm/timing.h
new file mode 100644
--- /dev/null
+++ b/arch/powerpc/kvm/timing.h
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ */
+
+#ifndef __POWERPC_KVM_EXITTIMING_H__
+#define __POWERPC_KVM_EXITTIMING_H__
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_host.h>
+
+#ifdef CONFIG_KVM_EXIT_TIMING
+void kvmppc_init_timing_stats(struct kvm_vcpu *vcpu);
+void kvmppc_update_timing_stats(struct kvm_vcpu *vcpu);
+void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id);
+void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu);
+
+static inline void kvmppc_set_exit_type(struct kvm_vcpu *vcpu, int type)
+{
+ vcpu->arch.last_exit_type = type;
+}
+
+#else
+/* if exit timing is not configured there is no need to build the c file */
+static inline void kvmppc_init_timing_stats(struct kvm_vcpu *vcpu) {}
+static inline void kvmppc_update_timing_stats(struct kvm_vcpu *vcpu) {}
+static inline void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu,
+ unsigned int id) {}
+static inline void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu) {}
+static inline void kvmppc_set_exit_type(struct kvm_vcpu *vcpu, int type) {}
+#endif /* CONFIG_KVM_EXIT_TIMING */
+
+/* account the exit in kvm_stats */
+static inline void account_exit_stat(struct kvm_vcpu *vcpu, int type)
+{
+ /* type has to be known at build time for optimization */
+ BUILD_BUG_ON(__builtin_constant_p(type));
+ switch (type) {
+ case EXT_INTR_EXITS:
+ vcpu->stat.ext_intr_exits++;
+ break;
+ case DEC_EXITS:
+ vcpu->stat.dec_exits++;
+ break;
+ case EMULATED_INST_EXITS:
+ vcpu->stat.emulated_inst_exits++;
+ break;
+ case DCR_EXITS:
+ vcpu->stat.dcr_exits++;
+ break;
+ case DSI_EXITS:
+ vcpu->stat.dsi_exits++;
+ break;
+ case ISI_EXITS:
+ vcpu->stat.isi_exits++;
+ break;
+ case SYSCALL_EXITS:
+ vcpu->stat.syscall_exits++;
+ break;
+ case DTLB_REAL_MISS_EXITS:
+ vcpu->stat.dtlb_real_miss_exits++;
+ break;
+ case DTLB_VIRT_MISS_EXITS:
+ vcpu->stat.dtlb_virt_miss_exits++;
+ break;
+ case MMIO_EXITS:
+ vcpu->stat.mmio_exits++;
+ break;
+ case ITLB_REAL_MISS_EXITS:
+ vcpu->stat.itlb_real_miss_exits++;
+ break;
+ case ITLB_VIRT_MISS_EXITS:
+ vcpu->stat.itlb_virt_miss_exits++;
+ break;
+ case SIGNAL_EXITS:
+ vcpu->stat.signal_exits++;
+ break;
+ }
+}
+
+/* wrapper to set exit time and account for it in kvm_stats */
+static inline void account_exit(struct kvm_vcpu *vcpu, int type)
+{
+ kvmppc_set_exit_type(vcpu, type);
+ account_exit_stat(vcpu, type);
+}
+
+#endif /* __POWERPC_KVM_EXITTIMING_H__ */
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] kvm: powerpc: add exit timing statistics v5
2008-11-10 17:19 [PATCH] kvm: powerpc: add exit timing statistics Ehrhardt Christian
` (7 preceding siblings ...)
2008-11-12 9:22 ` [PATCH] kvm: powerpc: add exit timing statistics v5 Christian Ehrhardt
@ 2008-11-12 23:27 ` Hollis Blanchard
8 siblings, 0 replies; 10+ messages in thread
From: Hollis Blanchard @ 2008-11-12 23:27 UTC (permalink / raw)
To: kvm-ppc
On Wed, 2008-11-12 at 10:22 +0100, Christian Ehrhardt wrote:
> From: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
>
> *update to v5*
> - add exittiming.c to diff content
> - prefix all exit timing functions with kvmppc to prevent name collisions (was
> already done for some of the functions, now its consequently done for all of
> them)
> - renamed header & c-file and relocated the header from asm to arch/powerpc/kvm
> - changed the empty define function stubs to empty static inlines
> - removed unrelated whitespace change and leftover of atomic vm_id
> - changed debugs filename to lower case and fewer underscores
> - updated the Kconfig text
>
> Other existing kvm statistics are either just counters (kvm_stat) reported for
> kvm generally or trace based aproaches like kvm_trace.
> For kvm on powerpc we had the need to track the timings of the different exit
> types. While this could be achieved parsing data created with a kvm_trace
> extension this adds too muhc overhead (at least on embedded powerpc) slowing
> down the workloads we wanted to measure.
>
> Therefore this patch adds a in kernel exit timing statistic to the powerpc kvm
> code. These statistic is available per vm&vcpu under the kvm debugfs directory.
> As this statistic is low, but still some overhead it can be enabled via a
> .config entry and should be off by default.
>
> Since this patch touched all powerpc kvm_stat code anyway this code is now
> merged and simpliefied together with the exit timing statistic code (still
> working with exit timing disabled in .config).
Applied, thanks! I also made some mostly cosmetic changes, and I've
attached them below:
kvm: ppc: mostly cosmetic updates to the exit timing accounting code
The only significant change was to kvmppc_exit_timing_write() and
kvmppc_exit_timing_show(), both of which were dramatically simplified.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -102,9 +102,8 @@ enum kvm_exit_types {
__NUMBER_OF_KVM_EXIT_TYPES
};
-#ifdef CONFIG_KVM_EXIT_TIMING
/* allow access to big endian 32bit upper/lower parts and 64bit var */
-struct exit_timing {
+struct kvmppc_exit_timing {
union {
u64 tv64;
struct {
@@ -112,7 +111,6 @@ struct exit_timing {
} tv32;
};
};
-#endif
struct kvm_arch {
};
@@ -174,8 +172,8 @@ struct kvm_vcpu_arch {
u32 dbcr1;
#ifdef CONFIG_KVM_EXIT_TIMING
- struct exit_timing timing_exit;
- struct exit_timing timing_last_enter;
+ struct kvmppc_exit_timing timing_exit;
+ struct kvmppc_exit_timing timing_last_enter;
u32 last_exit_type;
u32 timing_count_type[__NUMBER_OF_KVM_EXIT_TYPES];
u64 timing_sum_duration[__NUMBER_OF_KVM_EXIT_TYPES];
diff --git a/arch/powerpc/kvm/44x_emulate.c b/arch/powerpc/kvm/44x_emulate.c
--- a/arch/powerpc/kvm/44x_emulate.c
+++ b/arch/powerpc/kvm/44x_emulate.c
@@ -132,7 +132,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.is_write = 0;
vcpu->arch.io_gpr = rt;
vcpu->arch.dcr_needed = 1;
- account_exit(vcpu, DCR_EXITS);
+ kvmppc_account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
@@ -152,7 +152,7 @@ int kvmppc_core_emulate_op(struct kvm_ru
run->dcr.data = vcpu->arch.gpr[rs];
run->dcr.is_write = 1;
vcpu->arch.dcr_needed = 1;
- account_exit(vcpu, DCR_EXITS);
+ kvmppc_account_exit(vcpu, DCR_EXITS);
emulated = EMULATE_DO_DCR;
}
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -202,7 +202,7 @@ int kvmppc_handle_exit(struct kvm_run *r
break;
case BOOKE_INTERRUPT_EXTERNAL:
- account_exit(vcpu, EXT_INTR_EXITS);
+ kvmppc_account_exit(vcpu, EXT_INTR_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -212,7 +212,7 @@ int kvmppc_handle_exit(struct kvm_run *r
/* Since we switched IVPR back to the host's value, the host
* handled this interrupt the moment we enabled interrupts.
* Now we just offer it a chance to reschedule the guest. */
- account_exit(vcpu, DEC_EXITS);
+ kvmppc_account_exit(vcpu, DEC_EXITS);
if (need_resched())
cond_resched();
r = RESUME_GUEST;
@@ -225,7 +225,7 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
r = RESUME_GUEST;
- account_exit(vcpu, USR_PR_INST);
+ kvmppc_account_exit(vcpu, USR_PR_INST);
break;
}
@@ -233,7 +233,7 @@ int kvmppc_handle_exit(struct kvm_run *r
switch (er) {
case EMULATE_DONE:
/* don't overwrite subtypes, just account kvm_stats */
- account_exit_stat(vcpu, EMULATED_INST_EXITS);
+ kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS);
/* Future optimization: only reload non-volatiles if
* they were actually modified by emulation. */
r = RESUME_GUEST_NV;
@@ -259,7 +259,7 @@ int kvmppc_handle_exit(struct kvm_run *r
case BOOKE_INTERRUPT_FP_UNAVAIL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_FP_UNAVAIL);
- account_exit(vcpu, FP_UNAVAIL);
+ kvmppc_account_exit(vcpu, FP_UNAVAIL);
r = RESUME_GUEST;
break;
@@ -267,20 +267,20 @@ int kvmppc_handle_exit(struct kvm_run *r
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
- account_exit(vcpu, DSI_EXITS);
+ kvmppc_account_exit(vcpu, DSI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_INST_STORAGE:
vcpu->arch.esr = vcpu->arch.fault_esr;
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
- account_exit(vcpu, ISI_EXITS);
+ kvmppc_account_exit(vcpu, ISI_EXITS);
r = RESUME_GUEST;
break;
case BOOKE_INTERRUPT_SYSCALL:
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
- account_exit(vcpu, SYSCALL_EXITS);
+ kvmppc_account_exit(vcpu, SYSCALL_EXITS);
r = RESUME_GUEST;
break;
@@ -299,7 +299,7 @@ int kvmppc_handle_exit(struct kvm_run *r
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
vcpu->arch.dear = vcpu->arch.fault_dear;
vcpu->arch.esr = vcpu->arch.fault_esr;
- account_exit(vcpu, DTLB_REAL_MISS_EXITS);
+ kvmppc_account_exit(vcpu, DTLB_REAL_MISS_EXITS);
r = RESUME_GUEST;
break;
}
@@ -317,13 +317,13 @@ int kvmppc_handle_exit(struct kvm_run *r
* invoking the guest. */
kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
- account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
+ kvmppc_account_exit(vcpu, DTLB_VIRT_MISS_EXITS);
r = RESUME_GUEST;
} else {
/* Guest has mapped and accessed a page which is not
* actually RAM. */
r = kvmppc_emulate_mmio(run, vcpu);
- account_exit(vcpu, MMIO_EXITS);
+ kvmppc_account_exit(vcpu, MMIO_EXITS);
}
break;
@@ -345,11 +345,11 @@ int kvmppc_handle_exit(struct kvm_run *r
if (gtlb_index < 0) {
/* The guest didn't have a mapping for it. */
kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
- account_exit(vcpu, ITLB_REAL_MISS_EXITS);
+ kvmppc_account_exit(vcpu, ITLB_REAL_MISS_EXITS);
break;
}
- account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
+ kvmppc_account_exit(vcpu, ITLB_VIRT_MISS_EXITS);
gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
gpaddr = tlb_xlate(gtlbe, eaddr);
@@ -383,7 +383,7 @@ int kvmppc_handle_exit(struct kvm_run *r
mtspr(SPRN_DBSR, dbsr);
run->exit_reason = KVM_EXIT_DEBUG;
- account_exit(vcpu, DEBUG_EXITS);
+ kvmppc_account_exit(vcpu, DEBUG_EXITS);
r = RESUME_HOST;
break;
}
@@ -404,7 +404,7 @@ int kvmppc_handle_exit(struct kvm_run *r
if (signal_pending(current)) {
run->exit_reason = KVM_EXIT_INTR;
r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV);
- account_exit(vcpu, SIGNAL_EXITS);
+ kvmppc_account_exit(vcpu, SIGNAL_EXITS);
}
}
diff --git a/arch/powerpc/kvm/timing.c b/arch/powerpc/kvm/timing.c
--- a/arch/powerpc/kvm/timing.c
+++ b/arch/powerpc/kvm/timing.c
@@ -12,7 +12,7 @@
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2008
*
* Authors: Hollis Blanchard <hollisb@us.ibm.com>
* Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
@@ -24,9 +24,10 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
-#include "timing.h"
#include <asm/time.h>
#include <asm-generic/div64.h>
+
+#include "timing.h"
void kvmppc_init_timing_stats(struct kvm_vcpu *vcpu)
{
@@ -52,8 +53,7 @@ void kvmppc_init_timing_stats(struct kvm
local_irq_enable();
}
-static void add_exit_timing(struct kvm_vcpu *vcpu,
- u64 duration, int type)
+static void add_exit_timing(struct kvm_vcpu *vcpu, u64 duration, int type)
{
u64 old;
@@ -115,54 +115,46 @@ void kvmppc_update_timing_stats(struct k
}
static const char *kvm_exit_names[__NUMBER_OF_KVM_EXIT_TYPES] = {
- [MMIO_EXITS] = "MMIO",
- [DCR_EXITS] = "DCR",
- [SIGNAL_EXITS] = "SIGNAL",
- [ITLB_REAL_MISS_EXITS] = "ITLBREAL",
- [ITLB_VIRT_MISS_EXITS] = "ITLBVIRT",
- [DTLB_REAL_MISS_EXITS] = "DTLBREAL",
- [DTLB_VIRT_MISS_EXITS] = "DTLBVIRT",
- [SYSCALL_EXITS] = "SYSCALL",
- [ISI_EXITS] = "ISI",
- [DSI_EXITS] = "DSI",
- [EMULATED_INST_EXITS] = "EMULINST",
- [EMULATED_MTMSRWE_EXITS] = "EMUL_WAIT",
- [EMULATED_WRTEE_EXITS] = "EMUL_WRTEE",
- [EMULATED_MTSPR_EXITS] = "EMUL_MTSPR",
- [EMULATED_MFSPR_EXITS] = "EMUL_MFSPR",
- [EMULATED_MTMSR_EXITS] = "EMUL_MTMSR",
- [EMULATED_MFMSR_EXITS] = "EMUL_MFMSR",
- [EMULATED_TLBSX_EXITS] = "EMUL_TLBSX",
- [EMULATED_TLBWE_EXITS] = "EMUL_TLBWE",
- [EMULATED_RFI_EXITS] = "EMUL_RFI",
- [DEC_EXITS] = "DEC",
- [EXT_INTR_EXITS] = "EXTINT",
- [HALT_WAKEUP] = "HALT",
- [USR_PR_INST] = "USR_PR_INST",
- [FP_UNAVAIL] = "FP_UNAVAIL",
- [DEBUG_EXITS] = "DEBUG",
- [TIMEINGUEST] = "TIMEINGUEST"
+ [MMIO_EXITS] = "MMIO",
+ [DCR_EXITS] = "DCR",
+ [SIGNAL_EXITS] = "SIGNAL",
+ [ITLB_REAL_MISS_EXITS] = "ITLBREAL",
+ [ITLB_VIRT_MISS_EXITS] = "ITLBVIRT",
+ [DTLB_REAL_MISS_EXITS] = "DTLBREAL",
+ [DTLB_VIRT_MISS_EXITS] = "DTLBVIRT",
+ [SYSCALL_EXITS] = "SYSCALL",
+ [ISI_EXITS] = "ISI",
+ [DSI_EXITS] = "DSI",
+ [EMULATED_INST_EXITS] = "EMULINST",
+ [EMULATED_MTMSRWE_EXITS] = "EMUL_WAIT",
+ [EMULATED_WRTEE_EXITS] = "EMUL_WRTEE",
+ [EMULATED_MTSPR_EXITS] = "EMUL_MTSPR",
+ [EMULATED_MFSPR_EXITS] = "EMUL_MFSPR",
+ [EMULATED_MTMSR_EXITS] = "EMUL_MTMSR",
+ [EMULATED_MFMSR_EXITS] = "EMUL_MFMSR",
+ [EMULATED_TLBSX_EXITS] = "EMUL_TLBSX",
+ [EMULATED_TLBWE_EXITS] = "EMUL_TLBWE",
+ [EMULATED_RFI_EXITS] = "EMUL_RFI",
+ [DEC_EXITS] = "DEC",
+ [EXT_INTR_EXITS] = "EXTINT",
+ [HALT_WAKEUP] = "HALT",
+ [USR_PR_INST] = "USR_PR_INST",
+ [FP_UNAVAIL] = "FP_UNAVAIL",
+ [DEBUG_EXITS] = "DEBUG",
+ [TIMEINGUEST] = "TIMEINGUEST"
};
static int kvmppc_exit_timing_show(struct seq_file *m, void *private)
{
struct kvm_vcpu *vcpu = m->private;
int i;
- u64 min, max;
+
+ seq_printf(m, "%s", "type count min max sum sum_squared\n");
for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
- if (vcpu->arch.timing_min_duration[i] = 0xFFFFFFFF)
- min = 0;
- else
- min = vcpu->arch.timing_min_duration[i];
- if (vcpu->arch.timing_max_duration[i] = 0)
- max = 0;
- else
- max = vcpu->arch.timing_max_duration[i];
-
- seq_printf(m, "%12s: count %10d min %10lld "
- "max %10lld sum %20lld sum_quad %20lld\n",
- kvm_exit_names[i], vcpu->arch.timing_count_type[i],
+ seq_printf(m, "%12s %10d %10lld %10lld %20lld %20lld\n",
+ kvm_exit_names[i],
+ vcpu->arch.timing_count_type[i],
vcpu->arch.timing_min_duration[i],
vcpu->arch.timing_max_duration[i],
vcpu->arch.timing_sum_duration[i],
@@ -171,31 +163,19 @@ static int kvmppc_exit_timing_show(struc
return 0;
}
+/* Write 'c' to clear the timing statistics. */
static ssize_t kvmppc_exit_timing_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- size_t len;
- int err;
- const char __user *p;
+ int err = -EINVAL;
char c;
- len = 0;
- p = user_buf;
- while (len < count) {
- if (get_user(c, p++))
- err = -EFAULT;
- if (c = 0 || c = '\n')
- break;
- len++;
- }
-
- if (len > 1) {
- err = -EINVAL;
+ if (count > 1) {
goto done;
}
- if (copy_from_user(&c, user_buf, sizeof(c))) {
+ if (get_user(c, user_buf)) {
err = -EFAULT;
goto done;
}
@@ -203,16 +183,13 @@ static ssize_t kvmppc_exit_timing_write(
if (c = 'c') {
struct seq_file *seqf = (struct seq_file *)file->private_data;
struct kvm_vcpu *vcpu = seqf->private;
- /* write does not affect out buffers previsously generated with
- * show. Seq file is locked here to prevent races of init with
+ /* Write does not affect our buffers previously generated with
+ * show. seq_file is locked here to prevent races of init with
* a show call */
mutex_lock(&seqf->lock);
kvmppc_init_timing_stats(vcpu);
mutex_unlock(&seqf->lock);
err = count;
- } else {
- err = -EINVAL;
- goto done;
}
done:
@@ -238,7 +215,7 @@ void kvmppc_create_vcpu_debugfs(struct k
static char dbg_fname[50];
struct dentry *debugfs_file;
- snprintf(dbg_fname, sizeof(dbg_fname), "vm%u_vcpu%03u_timing",
+ snprintf(dbg_fname, sizeof(dbg_fname), "vm%u_vcpu%u_timing",
current->pid, id);
debugfs_file = debugfs_create_file(dbg_fname, 0666,
kvm_debugfs_dir, vcpu,
diff --git a/arch/powerpc/kvm/timing.h b/arch/powerpc/kvm/timing.h
--- a/arch/powerpc/kvm/timing.h
+++ b/arch/powerpc/kvm/timing.h
@@ -45,7 +45,7 @@ static inline void kvmppc_set_exit_type(
#endif /* CONFIG_KVM_EXIT_TIMING */
/* account the exit in kvm_stats */
-static inline void account_exit_stat(struct kvm_vcpu *vcpu, int type)
+static inline void kvmppc_account_exit_stat(struct kvm_vcpu *vcpu, int type)
{
/* type has to be known at build time for optimization */
BUILD_BUG_ON(__builtin_constant_p(type));
@@ -93,10 +93,10 @@ static inline void account_exit_stat(str
}
/* wrapper to set exit time and account for it in kvm_stats */
-static inline void account_exit(struct kvm_vcpu *vcpu, int type)
+static inline void kvmppc_account_exit(struct kvm_vcpu *vcpu, int type)
{
kvmppc_set_exit_type(vcpu, type);
- account_exit_stat(vcpu, type);
+ kvmppc_account_exit_stat(vcpu, type);
}
#endif /* __POWERPC_KVM_EXITTIMING_H__ */
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 10+ messages in thread