diff options
-rw-r--r-- | arch/x86/include/asm/perf_event.h | 15 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 31 | ||||
-rw-r--r-- | include/linux/perf_event.h | 21 | ||||
-rw-r--r-- | kernel/perf_event.c | 23 |
4 files changed, 77 insertions, 13 deletions
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index f6d43dbfd8e7..254883d0c7e0 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h | |||
@@ -135,17 +135,10 @@ extern void perf_events_lapic_init(void); | |||
135 | */ | 135 | */ |
136 | #define PERF_EFLAGS_EXACT (1UL << 3) | 136 | #define PERF_EFLAGS_EXACT (1UL << 3) |
137 | 137 | ||
138 | #define perf_misc_flags(regs) \ | 138 | struct pt_regs; |
139 | ({ int misc = 0; \ | 139 | extern unsigned long perf_instruction_pointer(struct pt_regs *regs); |
140 | if (user_mode(regs)) \ | 140 | extern unsigned long perf_misc_flags(struct pt_regs *regs); |
141 | misc |= PERF_RECORD_MISC_USER; \ | 141 | #define perf_misc_flags(regs) perf_misc_flags(regs) |
142 | else \ | ||
143 | misc |= PERF_RECORD_MISC_KERNEL; \ | ||
144 | if (regs->flags & PERF_EFLAGS_EXACT) \ | ||
145 | misc |= PERF_RECORD_MISC_EXACT; \ | ||
146 | misc; }) | ||
147 | |||
148 | #define perf_instruction_pointer(regs) ((regs)->ip) | ||
149 | 142 | ||
150 | #else | 143 | #else |
151 | static inline void init_hw_perf_events(void) { } | 144 | static inline void init_hw_perf_events(void) { } |
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 626154a9f535..2ea78abf69d9 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -1720,6 +1720,11 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) | |||
1720 | { | 1720 | { |
1721 | struct perf_callchain_entry *entry; | 1721 | struct perf_callchain_entry *entry; |
1722 | 1722 | ||
1723 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
1724 | /* TODO: We don't support guest os callchain now */ | ||
1725 | return NULL; | ||
1726 | } | ||
1727 | |||
1723 | if (in_nmi()) | 1728 | if (in_nmi()) |
1724 | entry = &__get_cpu_var(pmc_nmi_entry); | 1729 | entry = &__get_cpu_var(pmc_nmi_entry); |
1725 | else | 1730 | else |
@@ -1743,3 +1748,29 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski | |||
1743 | regs->cs = __KERNEL_CS; | 1748 | regs->cs = __KERNEL_CS; |
1744 | local_save_flags(regs->flags); | 1749 | local_save_flags(regs->flags); |
1745 | } | 1750 | } |
1751 | |||
1752 | unsigned long perf_instruction_pointer(struct pt_regs *regs) | ||
1753 | { | ||
1754 | unsigned long ip; | ||
1755 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) | ||
1756 | ip = perf_guest_cbs->get_guest_ip(); | ||
1757 | else | ||
1758 | ip = instruction_pointer(regs); | ||
1759 | return ip; | ||
1760 | } | ||
1761 | |||
1762 | unsigned long perf_misc_flags(struct pt_regs *regs) | ||
1763 | { | ||
1764 | int misc = 0; | ||
1765 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
1766 | misc |= perf_guest_cbs->is_user_mode() ? | ||
1767 | PERF_RECORD_MISC_GUEST_USER : | ||
1768 | PERF_RECORD_MISC_GUEST_KERNEL; | ||
1769 | } else | ||
1770 | misc |= user_mode(regs) ? PERF_RECORD_MISC_USER : | ||
1771 | PERF_RECORD_MISC_KERNEL; | ||
1772 | if (regs->flags & PERF_EFLAGS_EXACT) | ||
1773 | misc |= PERF_RECORD_MISC_EXACT; | ||
1774 | |||
1775 | return misc; | ||
1776 | } | ||
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index bf896d0b2e9c..24de5f181a41 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -288,11 +288,13 @@ struct perf_event_mmap_page { | |||
288 | __u64 data_tail; /* user-space written tail */ | 288 | __u64 data_tail; /* user-space written tail */ |
289 | }; | 289 | }; |
290 | 290 | ||
291 | #define PERF_RECORD_MISC_CPUMODE_MASK (3 << 0) | 291 | #define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) |
292 | #define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) | 292 | #define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) |
293 | #define PERF_RECORD_MISC_KERNEL (1 << 0) | 293 | #define PERF_RECORD_MISC_KERNEL (1 << 0) |
294 | #define PERF_RECORD_MISC_USER (2 << 0) | 294 | #define PERF_RECORD_MISC_USER (2 << 0) |
295 | #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) | 295 | #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) |
296 | #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) | ||
297 | #define PERF_RECORD_MISC_GUEST_USER (5 << 0) | ||
296 | 298 | ||
297 | #define PERF_RECORD_MISC_EXACT (1 << 14) | 299 | #define PERF_RECORD_MISC_EXACT (1 << 14) |
298 | /* | 300 | /* |
@@ -446,6 +448,12 @@ enum perf_callchain_context { | |||
446 | # include <asm/perf_event.h> | 448 | # include <asm/perf_event.h> |
447 | #endif | 449 | #endif |
448 | 450 | ||
451 | struct perf_guest_info_callbacks { | ||
452 | int (*is_in_guest) (void); | ||
453 | int (*is_user_mode) (void); | ||
454 | unsigned long (*get_guest_ip) (void); | ||
455 | }; | ||
456 | |||
449 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | 457 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
450 | #include <asm/hw_breakpoint.h> | 458 | #include <asm/hw_breakpoint.h> |
451 | #endif | 459 | #endif |
@@ -932,6 +940,12 @@ static inline void perf_event_mmap(struct vm_area_struct *vma) | |||
932 | __perf_event_mmap(vma); | 940 | __perf_event_mmap(vma); |
933 | } | 941 | } |
934 | 942 | ||
943 | extern struct perf_guest_info_callbacks *perf_guest_cbs; | ||
944 | extern int perf_register_guest_info_callbacks( | ||
945 | struct perf_guest_info_callbacks *); | ||
946 | extern int perf_unregister_guest_info_callbacks( | ||
947 | struct perf_guest_info_callbacks *); | ||
948 | |||
935 | extern void perf_event_comm(struct task_struct *tsk); | 949 | extern void perf_event_comm(struct task_struct *tsk); |
936 | extern void perf_event_fork(struct task_struct *tsk); | 950 | extern void perf_event_fork(struct task_struct *tsk); |
937 | 951 | ||
@@ -1001,6 +1015,11 @@ perf_sw_event(u32 event_id, u64 nr, int nmi, | |||
1001 | static inline void | 1015 | static inline void |
1002 | perf_bp_event(struct perf_event *event, void *data) { } | 1016 | perf_bp_event(struct perf_event *event, void *data) { } |
1003 | 1017 | ||
1018 | static inline int perf_register_guest_info_callbacks | ||
1019 | (struct perf_guest_info_callbacks *) {return 0; } | ||
1020 | static inline int perf_unregister_guest_info_callbacks | ||
1021 | (struct perf_guest_info_callbacks *) {return 0; } | ||
1022 | |||
1004 | static inline void perf_event_mmap(struct vm_area_struct *vma) { } | 1023 | static inline void perf_event_mmap(struct vm_area_struct *vma) { } |
1005 | static inline void perf_event_comm(struct task_struct *tsk) { } | 1024 | static inline void perf_event_comm(struct task_struct *tsk) { } |
1006 | static inline void perf_event_fork(struct task_struct *tsk) { } | 1025 | static inline void perf_event_fork(struct task_struct *tsk) { } |
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 07b7a435bf03..9dbe8cdaf145 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -2798,6 +2798,27 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski | |||
2798 | 2798 | ||
2799 | 2799 | ||
2800 | /* | 2800 | /* |
2801 | * We assume there is only KVM supporting the callbacks. | ||
2802 | * Later on, we might change it to a list if there is | ||
2803 | * another virtualization implementation supporting the callbacks. | ||
2804 | */ | ||
2805 | struct perf_guest_info_callbacks *perf_guest_cbs; | ||
2806 | |||
2807 | int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) | ||
2808 | { | ||
2809 | perf_guest_cbs = cbs; | ||
2810 | return 0; | ||
2811 | } | ||
2812 | EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks); | ||
2813 | |||
2814 | int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) | ||
2815 | { | ||
2816 | perf_guest_cbs = NULL; | ||
2817 | return 0; | ||
2818 | } | ||
2819 | EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); | ||
2820 | |||
2821 | /* | ||
2801 | * Output | 2822 | * Output |
2802 | */ | 2823 | */ |
2803 | static bool perf_output_space(struct perf_mmap_data *data, unsigned long tail, | 2824 | static bool perf_output_space(struct perf_mmap_data *data, unsigned long tail, |
@@ -3749,7 +3770,7 @@ void __perf_event_mmap(struct vm_area_struct *vma) | |||
3749 | .event_id = { | 3770 | .event_id = { |
3750 | .header = { | 3771 | .header = { |
3751 | .type = PERF_RECORD_MMAP, | 3772 | .type = PERF_RECORD_MMAP, |
3752 | .misc = 0, | 3773 | .misc = PERF_RECORD_MISC_USER, |
3753 | /* .size */ | 3774 | /* .size */ |
3754 | }, | 3775 | }, |
3755 | /* .pid */ | 3776 | /* .pid */ |