Skip to content

Commit b6c295d

Browse files
paulusmackagraf
authored andcommitted
KVM: PPC: Book3S HV: Accumulate timing information for real-mode code
This reads the timebase at various points in the real-mode guest entry/exit code and uses that to accumulate total, minimum and maximum time spent in those parts of the code. Currently these times are accumulated per vcpu in 5 parts of the code: * rm_entry - time taken from the start of kvmppc_hv_entry() until just before entering the guest. * rm_intr - time from when we take a hypervisor interrupt in the guest until we either re-enter the guest or decide to exit to the host. This includes time spent handling hcalls in real mode. * rm_exit - time from when we decide to exit the guest until the return from kvmppc_hv_entry(). * guest - time spend in the guest * cede - time spent napping in real mode due to an H_CEDE hcall while other threads in the same vcore are active. These times are exposed in debugfs in a directory per vcpu that contains a file called "timings". This file contains one line for each of the 5 timings above, with the name followed by a colon and 4 numbers, which are the count (number of times the code has been executed), the total time, the minimum time, and the maximum time, all in nanoseconds. The overhead of the extra code amounts to about 30ns for an hcall that is handled in real mode (e.g. H_SET_DABR), which is about 25%. Since production environments may not wish to incur this overhead, the new code is conditional on a new config symbol, CONFIG_KVM_BOOK3S_HV_EXIT_TIMING. Signed-off-by: Paul Mackerras <[email protected]> Signed-off-by: Alexander Graf <[email protected]>
1 parent e23a808 commit b6c295d

File tree

7 files changed

+346
-2
lines changed

7 files changed

+346
-2
lines changed

arch/powerpc/include/asm/kvm_host.h

+21
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,14 @@ struct kvmppc_slb {
369369
u8 base_page_size; /* MMU_PAGE_xxx */
370370
};
371371

372+
/* Struct used to accumulate timing information in HV real mode code */
373+
struct kvmhv_tb_accumulator {
374+
u64 seqcount; /* used to synchronize access, also count * 2 */
375+
u64 tb_total; /* total time in timebase ticks */
376+
u64 tb_min; /* min time */
377+
u64 tb_max; /* max time */
378+
};
379+
372380
# ifdef CONFIG_PPC_FSL_BOOK3E
373381
#define KVMPPC_BOOKE_IAC_NUM 2
374382
#define KVMPPC_BOOKE_DAC_NUM 2
@@ -657,6 +665,19 @@ struct kvm_vcpu_arch {
657665

658666
u32 emul_inst;
659667
#endif
668+
669+
#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING
670+
struct kvmhv_tb_accumulator *cur_activity; /* What we're timing */
671+
u64 cur_tb_start; /* when it started */
672+
struct kvmhv_tb_accumulator rm_entry; /* real-mode entry code */
673+
struct kvmhv_tb_accumulator rm_intr; /* real-mode intr handling */
674+
struct kvmhv_tb_accumulator rm_exit; /* real-mode exit code */
675+
struct kvmhv_tb_accumulator guest_time; /* guest execution */
676+
struct kvmhv_tb_accumulator cede_time; /* time napping inside guest */
677+
678+
struct dentry *debugfs_dir;
679+
struct dentry *debugfs_timings;
680+
#endif /* CONFIG_KVM_BOOK3S_HV_EXIT_TIMING */
660681
};
661682

662683
#define VCPU_FPR(vcpu, i) (vcpu)->arch.fp.fpr[i][TS_FPROFFSET]

arch/powerpc/include/asm/time.h

+3
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,8 @@ extern void secondary_cpu_time_init(void);
211211

212212
DECLARE_PER_CPU(u64, decrementers_next_tb);
213213

214+
/* Convert timebase ticks to nanoseconds */
215+
unsigned long long tb_to_ns(unsigned long long tb_ticks);
216+
214217
#endif /* __KERNEL__ */
215218
#endif /* __POWERPC_TIME_H */

arch/powerpc/kernel/asm-offsets.c

+13
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,19 @@ int main(void)
458458
DEFINE(VCPU_SPRG1, offsetof(struct kvm_vcpu, arch.shregs.sprg1));
459459
DEFINE(VCPU_SPRG2, offsetof(struct kvm_vcpu, arch.shregs.sprg2));
460460
DEFINE(VCPU_SPRG3, offsetof(struct kvm_vcpu, arch.shregs.sprg3));
461+
#endif
462+
#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING
463+
DEFINE(VCPU_TB_RMENTRY, offsetof(struct kvm_vcpu, arch.rm_entry));
464+
DEFINE(VCPU_TB_RMINTR, offsetof(struct kvm_vcpu, arch.rm_intr));
465+
DEFINE(VCPU_TB_RMEXIT, offsetof(struct kvm_vcpu, arch.rm_exit));
466+
DEFINE(VCPU_TB_GUEST, offsetof(struct kvm_vcpu, arch.guest_time));
467+
DEFINE(VCPU_TB_CEDE, offsetof(struct kvm_vcpu, arch.cede_time));
468+
DEFINE(VCPU_CUR_ACTIVITY, offsetof(struct kvm_vcpu, arch.cur_activity));
469+
DEFINE(VCPU_ACTIVITY_START, offsetof(struct kvm_vcpu, arch.cur_tb_start));
470+
DEFINE(TAS_SEQCOUNT, offsetof(struct kvmhv_tb_accumulator, seqcount));
471+
DEFINE(TAS_TOTAL, offsetof(struct kvmhv_tb_accumulator, tb_total));
472+
DEFINE(TAS_MIN, offsetof(struct kvmhv_tb_accumulator, tb_min));
473+
DEFINE(TAS_MAX, offsetof(struct kvmhv_tb_accumulator, tb_max));
461474
#endif
462475
DEFINE(VCPU_SHARED_SPRG3, offsetof(struct kvm_vcpu_arch_shared, sprg3));
463476
DEFINE(VCPU_SHARED_SPRG4, offsetof(struct kvm_vcpu_arch_shared, sprg4));

arch/powerpc/kernel/time.c

+6
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,12 @@ void arch_suspend_enable_irqs(void)
608608
}
609609
#endif
610610

611+
unsigned long long tb_to_ns(unsigned long long ticks)
612+
{
613+
return mulhdu(ticks, tb_to_ns_scale) << tb_to_ns_shift;
614+
}
615+
EXPORT_SYMBOL_GPL(tb_to_ns);
616+
611617
/*
612618
* Scheduler clock - returns current time in nanosec units.
613619
*

arch/powerpc/kvm/Kconfig

+14
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,20 @@ config KVM_BOOK3S_64_PR
110110
processor, including emulating 32-bit processors on a 64-bit
111111
host.
112112

113+
config KVM_BOOK3S_HV_EXIT_TIMING
114+
bool "Detailed timing for hypervisor real-mode code"
115+
depends on KVM_BOOK3S_HV_POSSIBLE && DEBUG_FS
116+
---help---
117+
Calculate time taken for each vcpu in the real-mode guest entry,
118+
exit, and interrupt handling code, plus time spent in the guest
119+
and in nap mode due to idle (cede) while other threads are still
120+
in the guest. The total, minimum and maximum times in nanoseconds
121+
together with the number of executions are reported in debugfs in
122+
kvm/vm#/vcpu#/timings. The overhead is of the order of 30 - 40
123+
ns per exit on POWER8.
124+
125+
If unsure, say N.
126+
113127
config KVM_BOOKE_HV
114128
bool
115129

arch/powerpc/kvm/book3s_hv.c

+150
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,154 @@ static struct kvmppc_vcore *kvmppc_vcore_create(struct kvm *kvm, int core)
14231423
return vcore;
14241424
}
14251425

1426+
#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING
1427+
static struct debugfs_timings_element {
1428+
const char *name;
1429+
size_t offset;
1430+
} timings[] = {
1431+
{"rm_entry", offsetof(struct kvm_vcpu, arch.rm_entry)},
1432+
{"rm_intr", offsetof(struct kvm_vcpu, arch.rm_intr)},
1433+
{"rm_exit", offsetof(struct kvm_vcpu, arch.rm_exit)},
1434+
{"guest", offsetof(struct kvm_vcpu, arch.guest_time)},
1435+
{"cede", offsetof(struct kvm_vcpu, arch.cede_time)},
1436+
};
1437+
1438+
#define N_TIMINGS (sizeof(timings) / sizeof(timings[0]))
1439+
1440+
struct debugfs_timings_state {
1441+
struct kvm_vcpu *vcpu;
1442+
unsigned int buflen;
1443+
char buf[N_TIMINGS * 100];
1444+
};
1445+
1446+
static int debugfs_timings_open(struct inode *inode, struct file *file)
1447+
{
1448+
struct kvm_vcpu *vcpu = inode->i_private;
1449+
struct debugfs_timings_state *p;
1450+
1451+
p = kzalloc(sizeof(*p), GFP_KERNEL);
1452+
if (!p)
1453+
return -ENOMEM;
1454+
1455+
kvm_get_kvm(vcpu->kvm);
1456+
p->vcpu = vcpu;
1457+
file->private_data = p;
1458+
1459+
return nonseekable_open(inode, file);
1460+
}
1461+
1462+
static int debugfs_timings_release(struct inode *inode, struct file *file)
1463+
{
1464+
struct debugfs_timings_state *p = file->private_data;
1465+
1466+
kvm_put_kvm(p->vcpu->kvm);
1467+
kfree(p);
1468+
return 0;
1469+
}
1470+
1471+
static ssize_t debugfs_timings_read(struct file *file, char __user *buf,
1472+
size_t len, loff_t *ppos)
1473+
{
1474+
struct debugfs_timings_state *p = file->private_data;
1475+
struct kvm_vcpu *vcpu = p->vcpu;
1476+
char *s, *buf_end;
1477+
struct kvmhv_tb_accumulator tb;
1478+
u64 count;
1479+
loff_t pos;
1480+
ssize_t n;
1481+
int i, loops;
1482+
bool ok;
1483+
1484+
if (!p->buflen) {
1485+
s = p->buf;
1486+
buf_end = s + sizeof(p->buf);
1487+
for (i = 0; i < N_TIMINGS; ++i) {
1488+
struct kvmhv_tb_accumulator *acc;
1489+
1490+
acc = (struct kvmhv_tb_accumulator *)
1491+
((unsigned long)vcpu + timings[i].offset);
1492+
ok = false;
1493+
for (loops = 0; loops < 1000; ++loops) {
1494+
count = acc->seqcount;
1495+
if (!(count & 1)) {
1496+
smp_rmb();
1497+
tb = *acc;
1498+
smp_rmb();
1499+
if (count == acc->seqcount) {
1500+
ok = true;
1501+
break;
1502+
}
1503+
}
1504+
udelay(1);
1505+
}
1506+
if (!ok)
1507+
snprintf(s, buf_end - s, "%s: stuck\n",
1508+
timings[i].name);
1509+
else
1510+
snprintf(s, buf_end - s,
1511+
"%s: %llu %llu %llu %llu\n",
1512+
timings[i].name, count / 2,
1513+
tb_to_ns(tb.tb_total),
1514+
tb_to_ns(tb.tb_min),
1515+
tb_to_ns(tb.tb_max));
1516+
s += strlen(s);
1517+
}
1518+
p->buflen = s - p->buf;
1519+
}
1520+
1521+
pos = *ppos;
1522+
if (pos >= p->buflen)
1523+
return 0;
1524+
if (len > p->buflen - pos)
1525+
len = p->buflen - pos;
1526+
n = copy_to_user(buf, p->buf + pos, len);
1527+
if (n) {
1528+
if (n == len)
1529+
return -EFAULT;
1530+
len -= n;
1531+
}
1532+
*ppos = pos + len;
1533+
return len;
1534+
}
1535+
1536+
static ssize_t debugfs_timings_write(struct file *file, const char __user *buf,
1537+
size_t len, loff_t *ppos)
1538+
{
1539+
return -EACCES;
1540+
}
1541+
1542+
static const struct file_operations debugfs_timings_ops = {
1543+
.owner = THIS_MODULE,
1544+
.open = debugfs_timings_open,
1545+
.release = debugfs_timings_release,
1546+
.read = debugfs_timings_read,
1547+
.write = debugfs_timings_write,
1548+
.llseek = generic_file_llseek,
1549+
};
1550+
1551+
/* Create a debugfs directory for the vcpu */
1552+
static void debugfs_vcpu_init(struct kvm_vcpu *vcpu, unsigned int id)
1553+
{
1554+
char buf[16];
1555+
struct kvm *kvm = vcpu->kvm;
1556+
1557+
snprintf(buf, sizeof(buf), "vcpu%u", id);
1558+
if (IS_ERR_OR_NULL(kvm->arch.debugfs_dir))
1559+
return;
1560+
vcpu->arch.debugfs_dir = debugfs_create_dir(buf, kvm->arch.debugfs_dir);
1561+
if (IS_ERR_OR_NULL(vcpu->arch.debugfs_dir))
1562+
return;
1563+
vcpu->arch.debugfs_timings =
1564+
debugfs_create_file("timings", 0444, vcpu->arch.debugfs_dir,
1565+
vcpu, &debugfs_timings_ops);
1566+
}
1567+
1568+
#else /* CONFIG_KVM_BOOK3S_HV_EXIT_TIMING */
1569+
static void debugfs_vcpu_init(struct kvm_vcpu *vcpu, unsigned int id)
1570+
{
1571+
}
1572+
#endif /* CONFIG_KVM_BOOK3S_HV_EXIT_TIMING */
1573+
14261574
static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm,
14271575
unsigned int id)
14281576
{
@@ -1492,6 +1640,8 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm,
14921640
vcpu->arch.cpu_type = KVM_CPU_3S_64;
14931641
kvmppc_sanity_check(vcpu);
14941642

1643+
debugfs_vcpu_init(vcpu, id);
1644+
14951645
return vcpu;
14961646

14971647
free_vcpu:

0 commit comments

Comments
 (0)