162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Kernel-based Virtual Machine driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2016 Red Hat, Inc. and/or its affiliates. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kvm_host.h> 1062306a36Sopenharmony_ci#include <linux/debugfs.h> 1162306a36Sopenharmony_ci#include "lapic.h" 1262306a36Sopenharmony_ci#include "mmu.h" 1362306a36Sopenharmony_ci#include "mmu/mmu_internal.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int vcpu_get_timer_advance_ns(void *data, u64 *val) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct kvm_vcpu *vcpu = (struct kvm_vcpu *) data; 1862306a36Sopenharmony_ci *val = vcpu->arch.apic->lapic_timer.timer_advance_ns; 1962306a36Sopenharmony_ci return 0; 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(vcpu_timer_advance_ns_fops, vcpu_get_timer_advance_ns, NULL, "%llu\n"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int vcpu_get_guest_mode(void *data, u64 *val) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct kvm_vcpu *vcpu = (struct kvm_vcpu *) data; 2762306a36Sopenharmony_ci *val = vcpu->stat.guest_mode; 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(vcpu_guest_mode_fops, vcpu_get_guest_mode, NULL, "%lld\n"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int vcpu_get_tsc_offset(void *data, u64 *val) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct kvm_vcpu *vcpu = (struct kvm_vcpu *) data; 3662306a36Sopenharmony_ci *val = vcpu->arch.tsc_offset; 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(vcpu_tsc_offset_fops, vcpu_get_tsc_offset, NULL, "%lld\n"); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int vcpu_get_tsc_scaling_ratio(void *data, u64 *val) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct kvm_vcpu *vcpu = (struct kvm_vcpu *) data; 4562306a36Sopenharmony_ci *val = vcpu->arch.tsc_scaling_ratio; 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(vcpu_tsc_scaling_fops, vcpu_get_tsc_scaling_ratio, NULL, "%llu\n"); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int vcpu_get_tsc_scaling_frac_bits(void *data, u64 *val) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci *val = kvm_caps.tsc_scaling_ratio_frac_bits; 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(vcpu_tsc_scaling_frac_fops, vcpu_get_tsc_scaling_frac_bits, NULL, "%llu\n"); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_civoid kvm_arch_create_vcpu_debugfs(struct kvm_vcpu *vcpu, struct dentry *debugfs_dentry) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci debugfs_create_file("guest_mode", 0444, debugfs_dentry, vcpu, 6262306a36Sopenharmony_ci &vcpu_guest_mode_fops); 6362306a36Sopenharmony_ci debugfs_create_file("tsc-offset", 0444, debugfs_dentry, vcpu, 6462306a36Sopenharmony_ci &vcpu_tsc_offset_fops); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (lapic_in_kernel(vcpu)) 6762306a36Sopenharmony_ci debugfs_create_file("lapic_timer_advance_ns", 0444, 6862306a36Sopenharmony_ci debugfs_dentry, vcpu, 6962306a36Sopenharmony_ci &vcpu_timer_advance_ns_fops); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (kvm_caps.has_tsc_control) { 7262306a36Sopenharmony_ci debugfs_create_file("tsc-scaling-ratio", 0444, 7362306a36Sopenharmony_ci debugfs_dentry, vcpu, 7462306a36Sopenharmony_ci &vcpu_tsc_scaling_fops); 7562306a36Sopenharmony_ci debugfs_create_file("tsc-scaling-ratio-frac-bits", 0444, 7662306a36Sopenharmony_ci debugfs_dentry, vcpu, 7762306a36Sopenharmony_ci &vcpu_tsc_scaling_frac_fops); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * This covers statistics <1024 (11=log(1024)+1), which should be enough to 8362306a36Sopenharmony_ci * cover RMAP_RECYCLE_THRESHOLD. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci#define RMAP_LOG_SIZE 11 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic const char *kvm_lpage_str[KVM_NR_PAGE_SIZES] = { "4K", "2M", "1G" }; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int kvm_mmu_rmaps_stat_show(struct seq_file *m, void *v) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct kvm_rmap_head *rmap; 9262306a36Sopenharmony_ci struct kvm *kvm = m->private; 9362306a36Sopenharmony_ci struct kvm_memory_slot *slot; 9462306a36Sopenharmony_ci struct kvm_memslots *slots; 9562306a36Sopenharmony_ci unsigned int lpage_size, index; 9662306a36Sopenharmony_ci /* Still small enough to be on the stack */ 9762306a36Sopenharmony_ci unsigned int *log[KVM_NR_PAGE_SIZES], *cur; 9862306a36Sopenharmony_ci int i, j, k, l, ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!kvm_memslots_have_rmaps(kvm)) 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = -ENOMEM; 10462306a36Sopenharmony_ci memset(log, 0, sizeof(log)); 10562306a36Sopenharmony_ci for (i = 0; i < KVM_NR_PAGE_SIZES; i++) { 10662306a36Sopenharmony_ci log[i] = kcalloc(RMAP_LOG_SIZE, sizeof(unsigned int), GFP_KERNEL); 10762306a36Sopenharmony_ci if (!log[i]) 10862306a36Sopenharmony_ci goto out; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci mutex_lock(&kvm->slots_lock); 11262306a36Sopenharmony_ci write_lock(&kvm->mmu_lock); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) { 11562306a36Sopenharmony_ci int bkt; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci slots = __kvm_memslots(kvm, i); 11862306a36Sopenharmony_ci kvm_for_each_memslot(slot, bkt, slots) 11962306a36Sopenharmony_ci for (k = 0; k < KVM_NR_PAGE_SIZES; k++) { 12062306a36Sopenharmony_ci rmap = slot->arch.rmap[k]; 12162306a36Sopenharmony_ci lpage_size = kvm_mmu_slot_lpages(slot, k + 1); 12262306a36Sopenharmony_ci cur = log[k]; 12362306a36Sopenharmony_ci for (l = 0; l < lpage_size; l++) { 12462306a36Sopenharmony_ci index = ffs(pte_list_count(&rmap[l])); 12562306a36Sopenharmony_ci if (WARN_ON_ONCE(index >= RMAP_LOG_SIZE)) 12662306a36Sopenharmony_ci index = RMAP_LOG_SIZE - 1; 12762306a36Sopenharmony_ci cur[index]++; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci write_unlock(&kvm->mmu_lock); 13362306a36Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* index=0 counts no rmap; index=1 counts 1 rmap */ 13662306a36Sopenharmony_ci seq_printf(m, "Rmap_Count:\t0\t1\t"); 13762306a36Sopenharmony_ci for (i = 2; i < RMAP_LOG_SIZE; i++) { 13862306a36Sopenharmony_ci j = 1 << (i - 1); 13962306a36Sopenharmony_ci k = (1 << i) - 1; 14062306a36Sopenharmony_ci seq_printf(m, "%d-%d\t", j, k); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci seq_printf(m, "\n"); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci for (i = 0; i < KVM_NR_PAGE_SIZES; i++) { 14562306a36Sopenharmony_ci seq_printf(m, "Level=%s:\t", kvm_lpage_str[i]); 14662306a36Sopenharmony_ci cur = log[i]; 14762306a36Sopenharmony_ci for (j = 0; j < RMAP_LOG_SIZE; j++) 14862306a36Sopenharmony_ci seq_printf(m, "%d\t", cur[j]); 14962306a36Sopenharmony_ci seq_printf(m, "\n"); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ret = 0; 15362306a36Sopenharmony_ciout: 15462306a36Sopenharmony_ci for (i = 0; i < KVM_NR_PAGE_SIZES; i++) 15562306a36Sopenharmony_ci kfree(log[i]); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int kvm_mmu_rmaps_stat_open(struct inode *inode, struct file *file) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct kvm *kvm = inode->i_private; 16362306a36Sopenharmony_ci int r; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!kvm_get_kvm_safe(kvm)) 16662306a36Sopenharmony_ci return -ENOENT; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci r = single_open(file, kvm_mmu_rmaps_stat_show, kvm); 16962306a36Sopenharmony_ci if (r < 0) 17062306a36Sopenharmony_ci kvm_put_kvm(kvm); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return r; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int kvm_mmu_rmaps_stat_release(struct inode *inode, struct file *file) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct kvm *kvm = inode->i_private; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci kvm_put_kvm(kvm); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return single_release(inode, file); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic const struct file_operations mmu_rmaps_stat_fops = { 18562306a36Sopenharmony_ci .open = kvm_mmu_rmaps_stat_open, 18662306a36Sopenharmony_ci .read = seq_read, 18762306a36Sopenharmony_ci .llseek = seq_lseek, 18862306a36Sopenharmony_ci .release = kvm_mmu_rmaps_stat_release, 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ciint kvm_arch_create_vm_debugfs(struct kvm *kvm) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci debugfs_create_file("mmu_rmaps_stat", 0644, kvm->debugfs_dentry, kvm, 19462306a36Sopenharmony_ci &mmu_rmaps_stat_fops); 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 197