[patch 31/38] x86/calldepth: Add ret/call counting for debug

Thomas Gleixner
2022-07-16T23:19:35Z

Add a debuigfs mechanism to validate the accounting, e.g. vs. call/ret
balance and to gather statistics about the stuffing to call ratio.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
arch/x86/include/asm/nospec-branch.h | 32 +++++++++++++++++++--
arch/x86/kernel/callthunks.c | 51 +++++++++++++++++++++++++++++++++++
arch/x86/lib/retpoline.S | 7 ++++
3 files changed, 86 insertions(+), 4 deletions(-)

--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -77,6 +77,23 @@
*/

#ifdef CONFIG_CALL_DEPTH_TRACKING
+
+#ifdef CONFIG_CALL_THUNKS_DEBUG
+# define CALL_THUNKS_DEBUG_INC_CALLS \
+ incq %gs:__x86_call_count;
+# define CALL_THUNKS_DEBUG_INC_RETS \
+ incq %gs:__x86_ret_count;
+# define CALL_THUNKS_DEBUG_INC_STUFFS \
+ incq %gs:__x86_stuffs_count;
+# define CALL_THUNKS_DEBUG_INC_CTXSW \
+ incq %gs:__x86_ctxsw_count;
+#else
+# define CALL_THUNKS_DEBUG_INC_CALLS
+# define CALL_THUNKS_DEBUG_INC_RETS
+# define CALL_THUNKS_DEBUG_INC_STUFFS
+# define CALL_THUNKS_DEBUG_INC_CTXSW
+#endif
+
#define CREDIT_CALL_DEPTH \
movq $-1, PER_CPU_VAR(__x86_call_depth);

@@ -88,10 +105,12 @@
#define RESET_CALL_DEPTH_FROM_CALL \
mov $0xfc, %rax; \
shl $56, %rax; \
- movq %rax, PER_CPU_VAR(__x86_call_depth);
+ movq %rax, PER_CPU_VAR(__x86_call_depth); \
+ CALL_THUNKS_DEBUG_INC_CALLS

#define INCREMENT_CALL_DEPTH \
- sarq $5, %gs:__x86_call_depth
+ sarq $5, %gs:__x86_call_depth; \
+ CALL_THUNKS_DEBUG_INC_CALLS
#else
#define CREDIT_CALL_DEPTH
#define RESET_CALL_DEPTH
@@ -127,7 +146,8 @@
dec reg; \
jnz 771b; \
\
- CREDIT_CALL_DEPTH
+ CREDIT_CALL_DEPTH \
+ CALL_THUNKS_DEBUG_INC_CTXSW

#ifdef __ASSEMBLY__

@@ -274,6 +294,12 @@ static inline void x86_set_skl_return_th
}

DECLARE_PER_CPU(u64, __x86_call_depth);
+#ifdef CONFIG_CALL_THUNKS_DEBUG
+DECLARE_PER_CPU(u64, __x86_call_count);
+DECLARE_PER_CPU(u64, __x86_ret_count);
+DECLARE_PER_CPU(u64, __x86_stuffs_count);
+DECLARE_PER_CPU(u64, __x86_ctxsw_count);
+#endif
#else
static inline void x86_set_skl_return_thunk(void) {}
#endif
--- a/arch/x86/kernel/callthunks.c
+++ b/arch/x86/kernel/callthunks.c
@@ -3,6 +3,7 @@
#define pr_fmt(fmt) "callthunks: " fmt

#include <linux/btree.h>
+#include <linux/debugfs.h>
#include <linux/memory.h>
#include <linux/moduleloader.h>
#include <linux/set_memory.h>
@@ -32,6 +33,13 @@ static int __init debug_thunks(char *str
return 1;
}
__setup("debug-callthunks", debug_thunks);
+
+DEFINE_PER_CPU(u64, __x86_call_count);
+DEFINE_PER_CPU(u64, __x86_ret_count);
+DEFINE_PER_CPU(u64, __x86_stuffs_count);
+DEFINE_PER_CPU(u64, __x86_ctxsw_count);
+EXPORT_SYMBOL_GPL(__x86_ctxsw_count);
+
#else
#define prdbg(fmt, args...) do { } while(0)
#endif
@@ -530,3 +538,46 @@ void callthunks_module_free(struct modul
mutex_unlock(&text_mutex);
}
#endif /* CONFIG_MODULES */
+
+#if defined(CONFIG_CALL_THUNKS_DEBUG) && defined(CONFIG_DEBUG_FS)
+static int callthunks_debug_show(struct seq_file *m, void *p)
+{
+ unsigned long cpu = (unsigned long)m->private;
+
+ seq_printf(m, "C: %16llu R: %16llu S: %16llu X: %16llu\n,",
+ per_cpu(__x86_call_count, cpu),
+ per_cpu(__x86_ret_count, cpu),
+ per_cpu(__x86_stuffs_count, cpu),
+ per_cpu(__x86_ctxsw_count, cpu));
+ return 0;
+}
+
+static int callthunks_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, callthunks_debug_show, inode->i_private);
+}
+
+static const struct file_operations dfs_ops = {
+ .open = callthunks_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init callthunks_debugfs_init(void)
+{
+ struct dentry *dir;
+ unsigned long cpu;
+
+ dir = debugfs_create_dir("callthunks", NULL);
+ for_each_possible_cpu(cpu) {
+ void *arg = (void *)cpu;
+ char name [10];
+
+ sprintf(name, "cpu%lu", cpu);
+ debugfs_create_file(name, 0644, dir, arg, &dfs_ops);
+ }
+ return 0;
+}
+__initcall(callthunks_debugfs_init);
+#endif
--- a/arch/x86/lib/retpoline.S
+++ b/arch/x86/lib/retpoline.S
@@ -202,13 +202,18 @@ EXPORT_SYMBOL(__x86_return_thunk)
.align 64
SYM_FUNC_START(__x86_return_skl)
ANNOTATE_NOENDBR
- /* Keep the hotpath in a 16byte I-fetch */
+ /*
+ * Keep the hotpath in a 16byte I-fetch for the non-debug
+ * case.
+ */
+ CALL_THUNKS_DEBUG_INC_RETS
shlq $5, PER_CPU_VAR(__x86_call_depth)
jz 1f
ANNOTATE_UNRET_SAFE
ret
int3
1:
+ CALL_THUNKS_DEBUG_INC_STUFFS
.rept 16
ANNOTATE_INTRA_FUNCTION_CALL
call 2f