diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 55 |
1 files changed, 45 insertions, 10 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 60398a0d947c..db5bdc8addf8 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/kdebug.h> | 21 | #include <linux/kdebug.h> |
22 | #include <linux/sched.h> | 22 | #include <linux/sched.h> |
23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
24 | #include <linux/slab.h> | ||
24 | #include <linux/highmem.h> | 25 | #include <linux/highmem.h> |
25 | #include <linux/cpu.h> | 26 | #include <linux/cpu.h> |
26 | #include <linux/bitops.h> | 27 | #include <linux/bitops.h> |
@@ -28,6 +29,7 @@ | |||
28 | #include <asm/apic.h> | 29 | #include <asm/apic.h> |
29 | #include <asm/stacktrace.h> | 30 | #include <asm/stacktrace.h> |
30 | #include <asm/nmi.h> | 31 | #include <asm/nmi.h> |
32 | #include <asm/compat.h> | ||
31 | 33 | ||
32 | static u64 perf_event_mask __read_mostly; | 34 | static u64 perf_event_mask __read_mostly; |
33 | 35 | ||
@@ -158,7 +160,7 @@ struct x86_pmu { | |||
158 | struct perf_event *event); | 160 | struct perf_event *event); |
159 | struct event_constraint *event_constraints; | 161 | struct event_constraint *event_constraints; |
160 | 162 | ||
161 | void (*cpu_prepare)(int cpu); | 163 | int (*cpu_prepare)(int cpu); |
162 | void (*cpu_starting)(int cpu); | 164 | void (*cpu_starting)(int cpu); |
163 | void (*cpu_dying)(int cpu); | 165 | void (*cpu_dying)(int cpu); |
164 | void (*cpu_dead)(int cpu); | 166 | void (*cpu_dead)(int cpu); |
@@ -1333,11 +1335,12 @@ static int __cpuinit | |||
1333 | x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) | 1335 | x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) |
1334 | { | 1336 | { |
1335 | unsigned int cpu = (long)hcpu; | 1337 | unsigned int cpu = (long)hcpu; |
1338 | int ret = NOTIFY_OK; | ||
1336 | 1339 | ||
1337 | switch (action & ~CPU_TASKS_FROZEN) { | 1340 | switch (action & ~CPU_TASKS_FROZEN) { |
1338 | case CPU_UP_PREPARE: | 1341 | case CPU_UP_PREPARE: |
1339 | if (x86_pmu.cpu_prepare) | 1342 | if (x86_pmu.cpu_prepare) |
1340 | x86_pmu.cpu_prepare(cpu); | 1343 | ret = x86_pmu.cpu_prepare(cpu); |
1341 | break; | 1344 | break; |
1342 | 1345 | ||
1343 | case CPU_STARTING: | 1346 | case CPU_STARTING: |
@@ -1350,6 +1353,7 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) | |||
1350 | x86_pmu.cpu_dying(cpu); | 1353 | x86_pmu.cpu_dying(cpu); |
1351 | break; | 1354 | break; |
1352 | 1355 | ||
1356 | case CPU_UP_CANCELED: | ||
1353 | case CPU_DEAD: | 1357 | case CPU_DEAD: |
1354 | if (x86_pmu.cpu_dead) | 1358 | if (x86_pmu.cpu_dead) |
1355 | x86_pmu.cpu_dead(cpu); | 1359 | x86_pmu.cpu_dead(cpu); |
@@ -1359,7 +1363,7 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) | |||
1359 | break; | 1363 | break; |
1360 | } | 1364 | } |
1361 | 1365 | ||
1362 | return NOTIFY_OK; | 1366 | return ret; |
1363 | } | 1367 | } |
1364 | 1368 | ||
1365 | static void __init pmu_check_apic(void) | 1369 | static void __init pmu_check_apic(void) |
@@ -1628,14 +1632,42 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n) | |||
1628 | return len; | 1632 | return len; |
1629 | } | 1633 | } |
1630 | 1634 | ||
1631 | static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) | 1635 | #ifdef CONFIG_COMPAT |
1636 | static inline int | ||
1637 | perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry) | ||
1632 | { | 1638 | { |
1633 | unsigned long bytes; | 1639 | /* 32-bit process in 64-bit kernel. */ |
1640 | struct stack_frame_ia32 frame; | ||
1641 | const void __user *fp; | ||
1634 | 1642 | ||
1635 | bytes = copy_from_user_nmi(frame, fp, sizeof(*frame)); | 1643 | if (!test_thread_flag(TIF_IA32)) |
1644 | return 0; | ||
1645 | |||
1646 | fp = compat_ptr(regs->bp); | ||
1647 | while (entry->nr < PERF_MAX_STACK_DEPTH) { | ||
1648 | unsigned long bytes; | ||
1649 | frame.next_frame = 0; | ||
1650 | frame.return_address = 0; | ||
1651 | |||
1652 | bytes = copy_from_user_nmi(&frame, fp, sizeof(frame)); | ||
1653 | if (bytes != sizeof(frame)) | ||
1654 | break; | ||
1655 | |||
1656 | if (fp < compat_ptr(regs->sp)) | ||
1657 | break; | ||
1636 | 1658 | ||
1637 | return bytes == sizeof(*frame); | 1659 | callchain_store(entry, frame.return_address); |
1660 | fp = compat_ptr(frame.next_frame); | ||
1661 | } | ||
1662 | return 1; | ||
1663 | } | ||
1664 | #else | ||
1665 | static inline int | ||
1666 | perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry) | ||
1667 | { | ||
1668 | return 0; | ||
1638 | } | 1669 | } |
1670 | #endif | ||
1639 | 1671 | ||
1640 | static void | 1672 | static void |
1641 | perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) | 1673 | perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) |
@@ -1651,11 +1683,16 @@ perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) | |||
1651 | callchain_store(entry, PERF_CONTEXT_USER); | 1683 | callchain_store(entry, PERF_CONTEXT_USER); |
1652 | callchain_store(entry, regs->ip); | 1684 | callchain_store(entry, regs->ip); |
1653 | 1685 | ||
1686 | if (perf_callchain_user32(regs, entry)) | ||
1687 | return; | ||
1688 | |||
1654 | while (entry->nr < PERF_MAX_STACK_DEPTH) { | 1689 | while (entry->nr < PERF_MAX_STACK_DEPTH) { |
1690 | unsigned long bytes; | ||
1655 | frame.next_frame = NULL; | 1691 | frame.next_frame = NULL; |
1656 | frame.return_address = 0; | 1692 | frame.return_address = 0; |
1657 | 1693 | ||
1658 | if (!copy_stack_frame(fp, &frame)) | 1694 | bytes = copy_from_user_nmi(&frame, fp, sizeof(frame)); |
1695 | if (bytes != sizeof(frame)) | ||
1659 | break; | 1696 | break; |
1660 | 1697 | ||
1661 | if ((unsigned long)fp < regs->sp) | 1698 | if ((unsigned long)fp < regs->sp) |
@@ -1702,7 +1739,6 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) | |||
1702 | return entry; | 1739 | return entry; |
1703 | } | 1740 | } |
1704 | 1741 | ||
1705 | #ifdef CONFIG_EVENT_TRACING | ||
1706 | void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int skip) | 1742 | void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int skip) |
1707 | { | 1743 | { |
1708 | regs->ip = ip; | 1744 | regs->ip = ip; |
@@ -1714,4 +1750,3 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski | |||
1714 | regs->cs = __KERNEL_CS; | 1750 | regs->cs = __KERNEL_CS; |
1715 | local_save_flags(regs->flags); | 1751 | local_save_flags(regs->flags); |
1716 | } | 1752 | } |
1717 | #endif | ||