[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC LINUX PATCH v1 1/3] perf kvm: introduce a hypervisor_callchain callback



`perf kvm` currently assumes that it can construct a stacktrace by
looking up stack pointer addresses in the current kernel's address
space.
That only works if the hypervisor is the same as the kernel (i.e. KVM),
but doesn't work if the hypervisor is separate from the kernel (Xen,
with Linux as Dom0).

Introduce a callback to enable Xen to retrieve the stacktrace from Xen
instead when a sample is inside the hypervisor (domid == DOMID_XEN
instead of DOMID_SELF).

The callback can replace the registers with the guest kernel's registers
upon return when domid == DOMID_SELF, so that we can continue with the
kernel stacktrace.

Both KVM and Xen define this as NULL, a followup commit will implement
the callback for Xen (KVM doesn't need a callback implementation).

No functional change.

Signed-off-by: Edwin Török <edwin.torok@xxxxxxxxx>
---
 arch/x86/xen/pmu.c         |  2 ++
 include/linux/perf_event.h | 12 ++++++++++++
 kernel/events/core.c       |  5 +++++
 virt/kvm/kvm_main.c        |  1 +
 4 files changed, 20 insertions(+)

diff --git a/arch/x86/xen/pmu.c b/arch/x86/xen/pmu.c
index 246d67dab510..b92dc739fdfb 100644
--- a/arch/x86/xen/pmu.c
+++ b/arch/x86/xen/pmu.c
@@ -466,6 +466,7 @@ static unsigned long xen_get_guest_ip(void)
 static struct perf_guest_info_callbacks xen_guest_cbs = {
        .state                  = xen_guest_state,
        .get_ip                 = xen_get_guest_ip,
+       .hypervisor_callchain   = NULL
 };
 
 /* Convert registers from Xen's format to Linux' */
@@ -489,6 +490,7 @@ static void xen_convert_regs(const struct xen_pmu_regs 
*xen_regs,
        }
 }
 
+
 irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id)
 {
        int err, ret = IRQ_NONE;
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 90c782749b05..d82aeaddadb8 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -29,10 +29,14 @@
 #define PERF_GUEST_ACTIVE      0x01
 #define PERF_GUEST_USER        0x02
 
+struct perf_callchain_entry_ctx;
+
 struct perf_guest_info_callbacks {
        unsigned int                    (*state)(void);
        unsigned long                   (*get_ip)(void);
        unsigned int                    (*handle_intel_pt_intr)(void);
+       void                            (*hypervisor_callchain)(struct 
perf_callchain_entry_ctx *pc,
+                                                               struct pt_regs 
*regs);
 };
 
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
@@ -1514,6 +1518,7 @@ extern struct perf_guest_info_callbacks __rcu 
*perf_guest_cbs;
 DECLARE_STATIC_CALL(__perf_guest_state, *perf_guest_cbs->state);
 DECLARE_STATIC_CALL(__perf_guest_get_ip, *perf_guest_cbs->get_ip);
 DECLARE_STATIC_CALL(__perf_guest_handle_intel_pt_intr, 
*perf_guest_cbs->handle_intel_pt_intr);
+DECLARE_STATIC_CALL(__perf_hypervisor_callchain, 
*perf_guest_cbs->hypervisor_callchain);
 
 static inline unsigned int perf_guest_state(void)
 {
@@ -1527,12 +1532,19 @@ static inline unsigned int 
perf_guest_handle_intel_pt_intr(void)
 {
        return static_call(__perf_guest_handle_intel_pt_intr)();
 }
+static inline void
+perf_hypervisor_callchain(struct perf_callchain_entry_ctx *entry,
+                         struct pt_regs *regs)
+{
+       static_call(__perf_hypervisor_callchain)(entry, regs);
+}
 extern void perf_register_guest_info_callbacks(struct 
perf_guest_info_callbacks *cbs);
 extern void perf_unregister_guest_info_callbacks(struct 
perf_guest_info_callbacks *cbs);
 #else
 static inline unsigned int perf_guest_state(void)               { return 0; }
 static inline unsigned long perf_guest_get_ip(void)             { return 0; }
 static inline unsigned int perf_guest_handle_intel_pt_intr(void) { return 0; }
+static inline void perf_hypervisor_callchain(struct perf_callchain_entry_ctx 
*) { return; }
 #endif /* CONFIG_GUEST_PERF_EVENTS */
 
 extern void perf_event_exec(void);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 3a33d9c1b1b2..a8535294018b 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6917,6 +6917,7 @@ struct perf_guest_info_callbacks __rcu *perf_guest_cbs;
 DEFINE_STATIC_CALL_RET0(__perf_guest_state, *perf_guest_cbs->state);
 DEFINE_STATIC_CALL_RET0(__perf_guest_get_ip, *perf_guest_cbs->get_ip);
 DEFINE_STATIC_CALL_RET0(__perf_guest_handle_intel_pt_intr, 
*perf_guest_cbs->handle_intel_pt_intr);
+DEFINE_STATIC_CALL_NULL(__perf_hypervisor_callchain, 
*perf_guest_cbs->hypervisor_callchain);
 
 void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
 {
@@ -6931,6 +6932,9 @@ void perf_register_guest_info_callbacks(struct 
perf_guest_info_callbacks *cbs)
        if (cbs->handle_intel_pt_intr)
                static_call_update(__perf_guest_handle_intel_pt_intr,
                                   cbs->handle_intel_pt_intr);
+       if (cbs->hypervisor_callchain)
+               static_call_update(__perf_hypervisor_callchain,
+                                  cbs->hypervisor_callchain);
 }
 EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks);
 
@@ -6944,6 +6948,7 @@ void perf_unregister_guest_info_callbacks(struct 
perf_guest_info_callbacks *cbs)
        static_call_update(__perf_guest_get_ip, (void *)&__static_call_return0);
        static_call_update(__perf_guest_handle_intel_pt_intr,
                           (void *)&__static_call_return0);
+       static_call_update(__perf_hypervisor_callchain, (void 
*)&__static_call_return0);
        synchronize_rcu();
 }
 EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 44c228bcd699..20a03dd9cc42 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -6038,6 +6038,7 @@ static struct perf_guest_info_callbacks kvm_guest_cbs = {
        .state                  = kvm_guest_state,
        .get_ip                 = kvm_guest_get_ip,
        .handle_intel_pt_intr   = NULL,
+       .hypervisor_callchain   = NULL
 };
 
 void kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void))

base-commit: dbcb8d8e4163e46066f43e2bd9a6779e594ec900
-- 
2.47.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.