KVM: PPC: Book3S HV: Create debugfs file for each guest's HPT
authorPaul Mackerras <paulus@samba.org>
Sat, 28 Mar 2015 03:21:01 +0000 (14:21 +1100)
committerAlexander Graf <agraf@suse.de>
Tue, 21 Apr 2015 13:21:31 +0000 (15:21 +0200)
This creates a debugfs directory for each HV guest (assuming debugfs
is enabled in the kernel config), and within that directory, a file
by which the contents of the guest's HPT (hashed page table) can be
read.  The directory is named vmnnnn, where nnnn is the PID of the
process that created the guest.  The file is named "htab".  This is
intended to help in debugging problems in the host's management
of guest memory.

The contents of the file consist of a series of lines like this:

  3f48 4000d032bf003505 0000000bd7ff1196 00000003b5c71196

The first field is the index of the entry in the HPT, the second and
third are the HPT entry, so the third entry contains the real page
number that is mapped by the entry if the entry's valid bit is set.
The fourth field is the guest's view of the second doubleword of the
entry, so it contains the guest physical address.  (The format of the
second through fourth fields are described in the Power ISA and also
in arch/powerpc/include/asm/mmu-hash64.h.)

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
arch/powerpc/include/asm/kvm_book3s_64.h
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/kvm/book3s_64_mmu_hv.c
arch/powerpc/kvm/book3s_hv.c
virt/kvm/kvm_main.c

index 0789a0f..869c53f 100644 (file)
@@ -436,6 +436,8 @@ static inline struct kvm_memslots *kvm_memslots_raw(struct kvm *kvm)
        return rcu_dereference_raw_notrace(kvm->memslots);
 }
 
+extern void kvmppc_mmu_debugfs_init(struct kvm *kvm);
+
 #endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
 
 #endif /* __ASM_KVM_BOOK3S_64_H__ */
index 015773f..f1d0bbc 100644 (file)
@@ -238,6 +238,8 @@ struct kvm_arch {
        atomic_t hpte_mod_interest;
        cpumask_t need_tlb_flush;
        int hpt_cma_alloc;
+       struct dentry *debugfs_dir;
+       struct dentry *htab_dentry;
 #endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
 #ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
        struct mutex hpt_mutex;
index 6c6825a..d6fe308 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/srcu.h>
 #include <linux/anon_inodes.h>
 #include <linux/file.h>
+#include <linux/debugfs.h>
 
 #include <asm/tlbflush.h>
 #include <asm/kvm_ppc.h>
@@ -1490,6 +1491,141 @@ int kvm_vm_ioctl_get_htab_fd(struct kvm *kvm, struct kvm_get_htab_fd *ghf)
        return ret;
 }
 
+struct debugfs_htab_state {
+       struct kvm      *kvm;
+       struct mutex    mutex;
+       unsigned long   hpt_index;
+       int             chars_left;
+       int             buf_index;
+       char            buf[64];
+};
+
+static int debugfs_htab_open(struct inode *inode, struct file *file)
+{
+       struct kvm *kvm = inode->i_private;
+       struct debugfs_htab_state *p;
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       kvm_get_kvm(kvm);
+       p->kvm = kvm;
+       mutex_init(&p->mutex);
+       file->private_data = p;
+
+       return nonseekable_open(inode, file);
+}
+
+static int debugfs_htab_release(struct inode *inode, struct file *file)
+{
+       struct debugfs_htab_state *p = file->private_data;
+
+       kvm_put_kvm(p->kvm);
+       kfree(p);
+       return 0;
+}
+
+static ssize_t debugfs_htab_read(struct file *file, char __user *buf,
+                                size_t len, loff_t *ppos)
+{
+       struct debugfs_htab_state *p = file->private_data;
+       ssize_t ret, r;
+       unsigned long i, n;
+       unsigned long v, hr, gr;
+       struct kvm *kvm;
+       __be64 *hptp;
+
+       ret = mutex_lock_interruptible(&p->mutex);
+       if (ret)
+               return ret;
+
+       if (p->chars_left) {
+               n = p->chars_left;
+               if (n > len)
+                       n = len;
+               r = copy_to_user(buf, p->buf + p->buf_index, n);
+               n -= r;
+               p->chars_left -= n;
+               p->buf_index += n;
+               buf += n;
+               len -= n;
+               ret = n;
+               if (r) {
+                       if (!n)
+                               ret = -EFAULT;
+                       goto out;
+               }
+       }
+
+       kvm = p->kvm;
+       i = p->hpt_index;
+       hptp = (__be64 *)(kvm->arch.hpt_virt + (i * HPTE_SIZE));
+       for (; len != 0 && i < kvm->arch.hpt_npte; ++i, hptp += 2) {
+               if (!(be64_to_cpu(hptp[0]) & (HPTE_V_VALID | HPTE_V_ABSENT)))
+                       continue;
+
+               /* lock the HPTE so it's stable and read it */
+               preempt_disable();
+               while (!try_lock_hpte(hptp, HPTE_V_HVLOCK))
+                       cpu_relax();
+               v = be64_to_cpu(hptp[0]) & ~HPTE_V_HVLOCK;
+               hr = be64_to_cpu(hptp[1]);
+               gr = kvm->arch.revmap[i].guest_rpte;
+               unlock_hpte(hptp, v);
+               preempt_enable();
+
+               if (!(v & (HPTE_V_VALID | HPTE_V_ABSENT)))
+                       continue;
+
+               n = scnprintf(p->buf, sizeof(p->buf),
+                             "%6lx %.16lx %.16lx %.16lx\n",
+                             i, v, hr, gr);
+               p->chars_left = n;
+               if (n > len)
+                       n = len;
+               r = copy_to_user(buf, p->buf, n);
+               n -= r;
+               p->chars_left -= n;
+               p->buf_index = n;
+               buf += n;
+               len -= n;
+               ret += n;
+               if (r) {
+                       if (!ret)
+                               ret = -EFAULT;
+                       goto out;
+               }
+       }
+       p->hpt_index = i;
+
+ out:
+       mutex_unlock(&p->mutex);
+       return ret;
+}
+
+ssize_t debugfs_htab_write(struct file *file, const char __user *buf,
+                          size_t len, loff_t *ppos)
+{
+       return -EACCES;
+}
+
+static const struct file_operations debugfs_htab_fops = {
+       .owner   = THIS_MODULE,
+       .open    = debugfs_htab_open,
+       .release = debugfs_htab_release,
+       .read    = debugfs_htab_read,
+       .write   = debugfs_htab_write,
+       .llseek  = generic_file_llseek,
+};
+
+void kvmppc_mmu_debugfs_init(struct kvm *kvm)
+{
+       kvm->arch.htab_dentry = debugfs_create_file("htab", 0400,
+                                                   kvm->arch.debugfs_dir, kvm,
+                                                   &debugfs_htab_fops);
+}
+
 void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_mmu *mmu = &vcpu->arch.mmu;
index dde14fd..08f8617 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/page-flags.h>
 #include <linux/srcu.h>
 #include <linux/miscdevice.h>
+#include <linux/debugfs.h>
 
 #include <asm/reg.h>
 #include <asm/cputable.h>
@@ -2319,6 +2320,7 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
 static int kvmppc_core_init_vm_hv(struct kvm *kvm)
 {
        unsigned long lpcr, lpid;
+       char buf[32];
 
        /* Allocate the guest's logical partition ID */
 
@@ -2359,6 +2361,14 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
         */
        kvm_hv_vm_activated();
 
+       /*
+        * Create a debugfs directory for the VM
+        */
+       snprintf(buf, sizeof(buf), "vm%d", current->pid);
+       kvm->arch.debugfs_dir = debugfs_create_dir(buf, kvm_debugfs_dir);
+       if (!IS_ERR_OR_NULL(kvm->arch.debugfs_dir))
+               kvmppc_mmu_debugfs_init(kvm);
+
        return 0;
 }
 
@@ -2379,6 +2389,8 @@ static void kvmppc_free_vcores(struct kvm *kvm)
 
 static void kvmppc_core_destroy_vm_hv(struct kvm *kvm)
 {
+       debugfs_remove_recursive(kvm->arch.debugfs_dir);
+
        kvm_hv_vm_deactivated();
 
        kvmppc_free_vcores(kvm);
index d3fc939..9097741 100644 (file)
@@ -89,6 +89,7 @@ EXPORT_SYMBOL_GPL(kvm_vcpu_cache);
 static __read_mostly struct preempt_ops kvm_preempt_ops;
 
 struct dentry *kvm_debugfs_dir;
+EXPORT_SYMBOL_GPL(kvm_debugfs_dir);
 
 static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
                           unsigned long arg);