aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kvm/book3s_hv.c
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2015-03-27 23:21:02 -0400
committerAlexander Graf <agraf@suse.de>2015-04-21 09:21:31 -0400
commitb6c295df3131c6fa25f8f29625ee0609506150ad (patch)
treebb9a89ae934bcefcb16f62de9f432493852b480a /arch/powerpc/kvm/book3s_hv.c
parente23a808b1681d398a983ebc51179efc51c4a1eaf (diff)
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 <paulus@samba.org> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'arch/powerpc/kvm/book3s_hv.c')
-rw-r--r--arch/powerpc/kvm/book3s_hv.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 08f8617f4046..64a02d4c737c 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -1423,6 +1423,154 @@ static struct kvmppc_vcore *kvmppc_vcore_create(struct kvm *kvm, int core)
1423 return vcore; 1423 return vcore;
1424} 1424}
1425 1425
1426#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING
1427static 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
1440struct debugfs_timings_state {
1441 struct kvm_vcpu *vcpu;
1442 unsigned int buflen;
1443 char buf[N_TIMINGS * 100];
1444};
1445
1446static 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
1462static 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
1471static 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
1536static 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
1542static 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 */
1552static 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 */
1569static void debugfs_vcpu_init(struct kvm_vcpu *vcpu, unsigned int id)
1570{
1571}
1572#endif /* CONFIG_KVM_BOOK3S_HV_EXIT_TIMING */
1573
1426static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm, 1574static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm,
1427 unsigned int id) 1575 unsigned int id)
1428{ 1576{
@@ -1492,6 +1640,8 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm,
1492 vcpu->arch.cpu_type = KVM_CPU_3S_64; 1640 vcpu->arch.cpu_type = KVM_CPU_3S_64;
1493 kvmppc_sanity_check(vcpu); 1641 kvmppc_sanity_check(vcpu);
1494 1642
1643 debugfs_vcpu_init(vcpu, id);
1644
1495 return vcpu; 1645 return vcpu;
1496 1646
1497free_vcpu: 1647free_vcpu: