|
@@ -63,6 +63,9 @@
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
#include <trace/events/kvm.h>
|
|
|
|
|
|
+/* Worst case buffer size needed for holding an integer. */
|
|
|
+#define ITOA_MAX_LEN 12
|
|
|
+
|
|
|
MODULE_AUTHOR("Qumranet");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
@@ -100,6 +103,9 @@ static __read_mostly struct preempt_ops kvm_preempt_ops;
|
|
|
struct dentry *kvm_debugfs_dir;
|
|
|
EXPORT_SYMBOL_GPL(kvm_debugfs_dir);
|
|
|
|
|
|
+static int kvm_debugfs_num_entries;
|
|
|
+static const struct file_operations *stat_fops_per_vm[];
|
|
|
+
|
|
|
static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
|
|
|
unsigned long arg);
|
|
|
#ifdef CONFIG_KVM_COMPAT
|
|
@@ -542,6 +548,58 @@ static void kvm_free_memslots(struct kvm *kvm, struct kvm_memslots *slots)
|
|
|
kvfree(slots);
|
|
|
}
|
|
|
|
|
|
+static void kvm_destroy_vm_debugfs(struct kvm *kvm)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!kvm->debugfs_dentry)
|
|
|
+ return;
|
|
|
+
|
|
|
+ debugfs_remove_recursive(kvm->debugfs_dentry);
|
|
|
+
|
|
|
+ for (i = 0; i < kvm_debugfs_num_entries; i++)
|
|
|
+ kfree(kvm->debugfs_stat_data[i]);
|
|
|
+ kfree(kvm->debugfs_stat_data);
|
|
|
+}
|
|
|
+
|
|
|
+static int kvm_create_vm_debugfs(struct kvm *kvm, int fd)
|
|
|
+{
|
|
|
+ char dir_name[ITOA_MAX_LEN * 2];
|
|
|
+ struct kvm_stat_data *stat_data;
|
|
|
+ struct kvm_stats_debugfs_item *p;
|
|
|
+
|
|
|
+ if (!debugfs_initialized())
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ snprintf(dir_name, sizeof(dir_name), "%d-%d", task_pid_nr(current), fd);
|
|
|
+ kvm->debugfs_dentry = debugfs_create_dir(dir_name,
|
|
|
+ kvm_debugfs_dir);
|
|
|
+ if (!kvm->debugfs_dentry)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ kvm->debugfs_stat_data = kcalloc(kvm_debugfs_num_entries,
|
|
|
+ sizeof(*kvm->debugfs_stat_data),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!kvm->debugfs_stat_data)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ for (p = debugfs_entries; p->name; p++) {
|
|
|
+ stat_data = kzalloc(sizeof(*stat_data), GFP_KERNEL);
|
|
|
+ if (!stat_data)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ stat_data->kvm = kvm;
|
|
|
+ stat_data->offset = p->offset;
|
|
|
+ kvm->debugfs_stat_data[p - debugfs_entries] = stat_data;
|
|
|
+ if (!debugfs_create_file(p->name, 0444,
|
|
|
+ kvm->debugfs_dentry,
|
|
|
+ stat_data,
|
|
|
+ stat_fops_per_vm[p->kind]))
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct kvm *kvm_create_vm(unsigned long type)
|
|
|
{
|
|
|
int r, i;
|
|
@@ -647,6 +705,7 @@ static void kvm_destroy_vm(struct kvm *kvm)
|
|
|
int i;
|
|
|
struct mm_struct *mm = kvm->mm;
|
|
|
|
|
|
+ kvm_destroy_vm_debugfs(kvm);
|
|
|
kvm_arch_sync_events(kvm);
|
|
|
spin_lock(&kvm_lock);
|
|
|
list_del(&kvm->vm_list);
|
|
@@ -2999,8 +3058,15 @@ static int kvm_dev_ioctl_create_vm(unsigned long type)
|
|
|
}
|
|
|
#endif
|
|
|
r = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, O_RDWR | O_CLOEXEC);
|
|
|
- if (r < 0)
|
|
|
+ if (r < 0) {
|
|
|
kvm_put_kvm(kvm);
|
|
|
+ return r;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (kvm_create_vm_debugfs(kvm, r) < 0) {
|
|
|
+ kvm_put_kvm(kvm);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
|
|
|
return r;
|
|
|
}
|
|
@@ -3425,15 +3491,114 @@ static struct notifier_block kvm_cpu_notifier = {
|
|
|
.notifier_call = kvm_cpu_hotplug,
|
|
|
};
|
|
|
|
|
|
+static int kvm_debugfs_open(struct inode *inode, struct file *file,
|
|
|
+ int (*get)(void *, u64 *), int (*set)(void *, u64),
|
|
|
+ const char *fmt)
|
|
|
+{
|
|
|
+ struct kvm_stat_data *stat_data = (struct kvm_stat_data *)
|
|
|
+ inode->i_private;
|
|
|
+
|
|
|
+ /* The debugfs files are a reference to the kvm struct which
|
|
|
+ * is still valid when kvm_destroy_vm is called.
|
|
|
+ * To avoid the race between open and the removal of the debugfs
|
|
|
+ * directory we test against the users count.
|
|
|
+ */
|
|
|
+ if (!atomic_add_unless(&stat_data->kvm->users_count, 1, 0))
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ if (simple_attr_open(inode, file, get, set, fmt)) {
|
|
|
+ kvm_put_kvm(stat_data->kvm);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int kvm_debugfs_release(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ struct kvm_stat_data *stat_data = (struct kvm_stat_data *)
|
|
|
+ inode->i_private;
|
|
|
+
|
|
|
+ simple_attr_release(inode, file);
|
|
|
+ kvm_put_kvm(stat_data->kvm);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int vm_stat_get_per_vm(void *data, u64 *val)
|
|
|
+{
|
|
|
+ struct kvm_stat_data *stat_data = (struct kvm_stat_data *)data;
|
|
|
+
|
|
|
+ *val = *(u32 *)((void *)stat_data->kvm + stat_data->offset);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int vm_stat_get_per_vm_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ __simple_attr_check_format("%llu\n", 0ull);
|
|
|
+ return kvm_debugfs_open(inode, file, vm_stat_get_per_vm,
|
|
|
+ NULL, "%llu\n");
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations vm_stat_get_per_vm_fops = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = vm_stat_get_per_vm_open,
|
|
|
+ .release = kvm_debugfs_release,
|
|
|
+ .read = simple_attr_read,
|
|
|
+ .write = simple_attr_write,
|
|
|
+ .llseek = generic_file_llseek,
|
|
|
+};
|
|
|
+
|
|
|
+static int vcpu_stat_get_per_vm(void *data, u64 *val)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct kvm_stat_data *stat_data = (struct kvm_stat_data *)data;
|
|
|
+ struct kvm_vcpu *vcpu;
|
|
|
+
|
|
|
+ *val = 0;
|
|
|
+
|
|
|
+ kvm_for_each_vcpu(i, vcpu, stat_data->kvm)
|
|
|
+ *val += *(u32 *)((void *)vcpu + stat_data->offset);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int vcpu_stat_get_per_vm_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ __simple_attr_check_format("%llu\n", 0ull);
|
|
|
+ return kvm_debugfs_open(inode, file, vcpu_stat_get_per_vm,
|
|
|
+ NULL, "%llu\n");
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations vcpu_stat_get_per_vm_fops = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = vcpu_stat_get_per_vm_open,
|
|
|
+ .release = kvm_debugfs_release,
|
|
|
+ .read = simple_attr_read,
|
|
|
+ .write = simple_attr_write,
|
|
|
+ .llseek = generic_file_llseek,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct file_operations *stat_fops_per_vm[] = {
|
|
|
+ [KVM_STAT_VCPU] = &vcpu_stat_get_per_vm_fops,
|
|
|
+ [KVM_STAT_VM] = &vm_stat_get_per_vm_fops,
|
|
|
+};
|
|
|
+
|
|
|
static int vm_stat_get(void *_offset, u64 *val)
|
|
|
{
|
|
|
unsigned offset = (long)_offset;
|
|
|
struct kvm *kvm;
|
|
|
+ struct kvm_stat_data stat_tmp = {.offset = offset};
|
|
|
+ u64 tmp_val;
|
|
|
|
|
|
*val = 0;
|
|
|
spin_lock(&kvm_lock);
|
|
|
- list_for_each_entry(kvm, &vm_list, vm_list)
|
|
|
- *val += *(u32 *)((void *)kvm + offset);
|
|
|
+ list_for_each_entry(kvm, &vm_list, vm_list) {
|
|
|
+ stat_tmp.kvm = kvm;
|
|
|
+ vm_stat_get_per_vm((void *)&stat_tmp, &tmp_val);
|
|
|
+ *val += tmp_val;
|
|
|
+ }
|
|
|
spin_unlock(&kvm_lock);
|
|
|
return 0;
|
|
|
}
|
|
@@ -3444,15 +3609,16 @@ static int vcpu_stat_get(void *_offset, u64 *val)
|
|
|
{
|
|
|
unsigned offset = (long)_offset;
|
|
|
struct kvm *kvm;
|
|
|
- struct kvm_vcpu *vcpu;
|
|
|
- int i;
|
|
|
+ struct kvm_stat_data stat_tmp = {.offset = offset};
|
|
|
+ u64 tmp_val;
|
|
|
|
|
|
*val = 0;
|
|
|
spin_lock(&kvm_lock);
|
|
|
- list_for_each_entry(kvm, &vm_list, vm_list)
|
|
|
- kvm_for_each_vcpu(i, vcpu, kvm)
|
|
|
- *val += *(u32 *)((void *)vcpu + offset);
|
|
|
-
|
|
|
+ list_for_each_entry(kvm, &vm_list, vm_list) {
|
|
|
+ stat_tmp.kvm = kvm;
|
|
|
+ vcpu_stat_get_per_vm((void *)&stat_tmp, &tmp_val);
|
|
|
+ *val += tmp_val;
|
|
|
+ }
|
|
|
spin_unlock(&kvm_lock);
|
|
|
return 0;
|
|
|
}
|
|
@@ -3473,7 +3639,8 @@ static int kvm_init_debug(void)
|
|
|
if (kvm_debugfs_dir == NULL)
|
|
|
goto out;
|
|
|
|
|
|
- for (p = debugfs_entries; p->name; ++p) {
|
|
|
+ kvm_debugfs_num_entries = 0;
|
|
|
+ for (p = debugfs_entries; p->name; ++p, kvm_debugfs_num_entries++) {
|
|
|
if (!debugfs_create_file(p->name, 0444, kvm_debugfs_dir,
|
|
|
(void *)(long)p->offset,
|
|
|
stat_fops[p->kind]))
|