KVM: s390,perf: Detect if perf samples belong to KVM host or guest
authorHeinz Graalfs <graalfs@linux.vnet.ibm.com>
Wed, 12 Jun 2013 11:54:56 +0000 (13:54 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 17 Jun 2013 15:10:23 +0000 (17:10 +0200)
This patch is based on an original patch of David Hildenbrand.

The perf core implementation calls architecture specific code in order
to ask for specific information for a particular sample:

perf_instruction_pointer()
When perf core code asks for the instruction pointer, architecture
specific code must detect if a KVM guest was running when the sample
was taken. A sample can be associated with a  KVM guest when the PSW
supervisor state bit is set and the PSW instruction pointer part
contains the address of 'sie_exit'.
A KVM guest's instruction pointer information is then retrieved via
gpsw entry pointed to by the sie control-block.

perf_misc_flags()
perf code code calls this function in order to associate the kernel
vs. user state infomation with a particular sample. Architecture
specific code must also first detectif a KVM guest was running
at the time the sample was taken.

Signed-off-by: Heinz Graalfs <graalfs@linux.vnet.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/s390/include/asm/kvm_host.h
arch/s390/include/asm/perf_event.h
arch/s390/kernel/entry64.S
arch/s390/kernel/perf_event.c
arch/s390/kernel/s390_ksyms.c

index d3ffd7eded3cc3efec6b8d559a4f71288030b333..43390699bd39131d4af4c4c62f182e2491f280aa 100644 (file)
@@ -275,4 +275,5 @@ struct kvm_arch{
 };
 
 extern int sie64a(struct kvm_s390_sie_block *, u64 *);
+extern char sie_exit;
 #endif
index 5f0173a31693b19700b8afa92b60db199bfcb749..1141fb3e7b21e6ad2e915835eb571301982cf29e 100644 (file)
 /* Per-CPU flags for PMU states */
 #define PMU_F_RESERVED                 0x1000
 #define PMU_F_ENABLED                  0x2000
+
+#ifdef CONFIG_64BIT
+
+/* Perf callbacks */
+struct pt_regs;
+extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
+extern unsigned long perf_misc_flags(struct pt_regs *regs);
+#define perf_misc_flags(regs) perf_misc_flags(regs)
+
+#endif /* CONFIG_64BIT */
index 51d99acc16fd0236444b86607e1f41e8404e63f2..b094ced7a1821cf0d369e035b42c07eb9e5cde2c 100644 (file)
@@ -964,6 +964,7 @@ sie_done:
 # See also HANDLE_SIE_INTERCEPT
 rewind_pad:
        nop     0
+       .globl sie_exit
 sie_exit:
        lg      %r14,__SF_EMPTY+8(%r15)         # load guest register save area
        stmg    %r0,%r13,0(%r14)                # save guest gprs 0-13
index f58f37f66824492c09acfc0c54c59c170be3e2a8..a6fc037671b11d0a2e96226256f9e36cd8e4d65d 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/kernel.h>
 #include <linux/perf_event.h>
+#include <linux/kvm_host.h>
 #include <linux/percpu.h>
 #include <linux/export.h>
 #include <asm/irq.h>
@@ -39,6 +40,57 @@ int perf_num_counters(void)
 }
 EXPORT_SYMBOL(perf_num_counters);
 
+static struct kvm_s390_sie_block *sie_block(struct pt_regs *regs)
+{
+       struct stack_frame *stack = (struct stack_frame *) regs->gprs[15];
+
+       if (!stack)
+               return NULL;
+
+       return (struct kvm_s390_sie_block *) stack->empty1[0];
+}
+
+static bool is_in_guest(struct pt_regs *regs)
+{
+       unsigned long ip = instruction_pointer(regs);
+
+       if (user_mode(regs))
+               return false;
+
+       return ip == (unsigned long) &sie_exit;
+}
+
+static unsigned long guest_is_user_mode(struct pt_regs *regs)
+{
+       return sie_block(regs)->gpsw.mask & PSW_MASK_PSTATE;
+}
+
+static unsigned long instruction_pointer_guest(struct pt_regs *regs)
+{
+       return sie_block(regs)->gpsw.addr & PSW_ADDR_INSN;
+}
+
+unsigned long perf_instruction_pointer(struct pt_regs *regs)
+{
+       return is_in_guest(regs) ? instruction_pointer_guest(regs)
+                                : instruction_pointer(regs);
+}
+
+static unsigned long perf_misc_guest_flags(struct pt_regs *regs)
+{
+       return guest_is_user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER
+                                       : PERF_RECORD_MISC_GUEST_KERNEL;
+}
+
+unsigned long perf_misc_flags(struct pt_regs *regs)
+{
+       if (is_in_guest(regs))
+               return perf_misc_guest_flags(regs);
+
+       return user_mode(regs) ? PERF_RECORD_MISC_USER
+                              : PERF_RECORD_MISC_KERNEL;
+}
+
 void perf_event_print_debug(void)
 {
        struct cpumf_ctr_info cf_info;
index 9bdbcef1da9ecd6ac0136973d59537252f6d3bf2..3bac589844a747c22136810d0f63600139111e3a 100644 (file)
@@ -7,6 +7,7 @@ EXPORT_SYMBOL(_mcount);
 #endif
 #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
 EXPORT_SYMBOL(sie64a);
+EXPORT_SYMBOL(sie_exit);
 #endif
 EXPORT_SYMBOL(memcpy);
 EXPORT_SYMBOL(memset);