From: Ashish Kalra Add a debugfs interface to report per-CPU RMPOPT status across all system RAM. To dump the per-CPU RMPOPT status for all system RAM: /sys/kernel/debug/rmpopt# cat rmpopt-table Memory @ 0GB: CPU(s): none Memory @ 1GB: CPU(s): none Memory @ 2GB: CPU(s): 0-1023 Memory @ 3GB: CPU(s): 0-1023 Memory @ 4GB: CPU(s): none Memory @ 5GB: CPU(s): 0-1023 Memory @ 6GB: CPU(s): 0-1023 Memory @ 7GB: CPU(s): 0-1023 ... Memory @1025GB: CPU(s): 0-1023 Memory @1026GB: CPU(s): 0-1023 Memory @1027GB: CPU(s): 0-1023 Memory @1028GB: CPU(s): 0-1023 Memory @1029GB: CPU(s): 0-1023 Memory @1030GB: CPU(s): 0-1023 Memory @1031GB: CPU(s): 0-1023 Memory @1032GB: CPU(s): 0-1023 Memory @1033GB: CPU(s): 0-1023 Memory @1034GB: CPU(s): 0-1023 Memory @1035GB: CPU(s): 0-1023 Memory @1036GB: CPU(s): 0-1023 Memory @1037GB: CPU(s): 0-1023 Memory @1038GB: CPU(s): none Suggested-by: Thomas Lendacky Signed-off-by: Ashish Kalra --- arch/x86/virt/svm/sev.c | 128 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/arch/x86/virt/svm/sev.c b/arch/x86/virt/svm/sev.c index db2d4c1f5dd7..fe45a333df92 100644 --- a/arch/x86/virt/svm/sev.c +++ b/arch/x86/virt/svm/sev.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -145,6 +147,15 @@ static DEFINE_SPINLOCK(snp_leaked_pages_list_lock); static unsigned long snp_nr_leaked_pages; +/* All users of rmpopt_report_cpumask must hold rmpopt_show_mutex. */ +static cpumask_t rmpopt_report_cpumask; +static struct dentry *rmpopt_debugfs; +static DEFINE_MUTEX(rmpopt_show_mutex); + +struct seq_paddr { + phys_addr_t next_seq_paddr; +}; + #undef pr_fmt #define pr_fmt(fmt) "SEV-SNP: " fmt @@ -587,6 +598,8 @@ static void rmpopt_cleanup(void) cancel_delayed_work_sync(&rmpopt_delayed_work); destroy_workqueue(rmpopt_wq); + debugfs_remove_recursive(rmpopt_debugfs); + rmpopt_debugfs = NULL; cpus_read_lock(); @@ -635,6 +648,10 @@ static inline bool __rmpopt(u64 pa_start, u64 op_type) : "a" (pa_start), "c" (op_type) : "memory", "cc"); + if (op_type == RMPOPT_FUNC_REPORT_STATUS) + assign_cpu(smp_processor_id(), &rmpopt_report_cpumask, + optimized); + return optimized; } @@ -654,6 +671,115 @@ static void rmpopt_smp(void *val) rmpopt((u64)val); } +/* + * 'val' is a system physical address. + */ +static void rmpopt_report_status(void *val) +{ + u64 pa_start = ALIGN_DOWN((u64)val, SZ_1G); + u64 op_type = RMPOPT_FUNC_REPORT_STATUS; + + __rmpopt(pa_start, op_type); +} + +/* + * start() can be called multiple times if allocated buffer has overflowed + * and bigger buffer is allocated. + */ +static void *rmpopt_table_seq_start(struct seq_file *seq, loff_t *pos) +{ + phys_addr_t end_paddr = rmpopt_pa_end; + struct seq_paddr *p = seq->private; + + if (*pos == 0) { + p->next_seq_paddr = rmpopt_pa_start; + if (p->next_seq_paddr >= end_paddr) + return NULL; + return &p->next_seq_paddr; + } + + if (p->next_seq_paddr >= end_paddr) + return NULL; + + return &p->next_seq_paddr; +} + +static void *rmpopt_table_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + phys_addr_t end_paddr = rmpopt_pa_end; + phys_addr_t *curr_paddr = v; + + (*pos)++; + *curr_paddr += SZ_1G; + if (*curr_paddr >= end_paddr) + return NULL; + + return curr_paddr; +} + +static void rmpopt_table_seq_stop(struct seq_file *seq, void *v) +{ +} + +static int rmpopt_table_seq_show(struct seq_file *seq, void *v) +{ + phys_addr_t *curr_paddr = v; + + guard(mutex)(&rmpopt_show_mutex); + + seq_printf(seq, "Memory @%3lluGB: ", + *curr_paddr >> (get_order(SZ_1G) + PAGE_SHIFT)); + + /* + * Query all online CPUs rather than just rmpopt_cpumask (primary + * threads only). The RMPOPT instruction only needs to run on one + * thread per core for the optimization to take effect, but debugfs + * reporting requires the RMPOPT status across all CPUs. + * Performance is not a concern for this diagnostic interface. + * + * This is safe because RMPOPT_BASE MSR is per-core and + * snp_prepare() ensures all CPUs are online when the MSR is + * programmed during snp_setup_rmpopt(). + */ + cpumask_clear(&rmpopt_report_cpumask); + on_each_cpu_mask(cpu_online_mask, rmpopt_report_status, + (void *)*curr_paddr, true); + + if (cpumask_empty(&rmpopt_report_cpumask)) + seq_puts(seq, "CPU(s): none\n"); + else + seq_printf(seq, "CPU(s): %*pbl\n", cpumask_pr_args(&rmpopt_report_cpumask)); + + return 0; +} + +static const struct seq_operations rmpopt_table_seq_ops = { + .start = rmpopt_table_seq_start, + .next = rmpopt_table_seq_next, + .stop = rmpopt_table_seq_stop, + .show = rmpopt_table_seq_show +}; + +static int rmpopt_table_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &rmpopt_table_seq_ops, sizeof(struct seq_paddr)); +} + +static const struct file_operations rmpopt_table_fops = { + .open = rmpopt_table_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +static void rmpopt_debugfs_setup(void) +{ + rmpopt_debugfs = debugfs_create_dir("rmpopt", arch_debugfs_dir); + + debugfs_create_file("rmpopt-table", 0400, rmpopt_debugfs, + NULL, &rmpopt_table_fops); +} + /* * RMPOPT optimizations skip RMP checks at 1GB granularity if this * range of memory does not contain any SNP guest memory. @@ -852,6 +978,8 @@ void snp_setup_rmpopt(void) * optimizations on all physical memory. */ queue_delayed_work(rmpopt_wq, &rmpopt_delayed_work, 0); + + rmpopt_debugfs_setup(); } EXPORT_SYMBOL_FOR_MODULES(snp_setup_rmpopt, "ccp"); -- 2.43.0