diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 6d5e7cfd97e7..e8c68a5091df 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/kdebug.h> | 19 | #include <linux/kdebug.h> |
20 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/highmem.h> | ||
22 | 23 | ||
23 | #include <asm/apic.h> | 24 | #include <asm/apic.h> |
24 | #include <asm/stacktrace.h> | 25 | #include <asm/stacktrace.h> |
@@ -1617,20 +1618,48 @@ perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) | |||
1617 | entry->kernel = entry->nr - nr; | 1618 | entry->kernel = entry->nr - nr; |
1618 | } | 1619 | } |
1619 | 1620 | ||
1620 | static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) | 1621 | /* |
1622 | * best effort, GUP based copy_from_user() that assumes IRQ or NMI context | ||
1623 | */ | ||
1624 | static unsigned long | ||
1625 | copy_from_user_nmi(void *to, const void __user *from, unsigned long n) | ||
1621 | { | 1626 | { |
1627 | unsigned long offset, addr = (unsigned long)from; | ||
1628 | int type = in_nmi() ? KM_NMI : KM_IRQ0; | ||
1629 | unsigned long size, len = 0; | ||
1630 | struct page *page; | ||
1631 | void *map; | ||
1622 | int ret; | 1632 | int ret; |
1623 | 1633 | ||
1624 | if (!access_ok(VERIFY_READ, fp, sizeof(*frame))) | 1634 | do { |
1625 | return 0; | 1635 | ret = __get_user_pages_fast(addr, 1, 0, &page); |
1636 | if (!ret) | ||
1637 | break; | ||
1626 | 1638 | ||
1627 | ret = 1; | 1639 | offset = addr & (PAGE_SIZE - 1); |
1628 | pagefault_disable(); | 1640 | size = min(PAGE_SIZE - offset, n - len); |
1629 | if (__copy_from_user_inatomic(frame, fp, sizeof(*frame))) | ||
1630 | ret = 0; | ||
1631 | pagefault_enable(); | ||
1632 | 1641 | ||
1633 | return ret; | 1642 | map = kmap_atomic(page, type); |
1643 | memcpy(to, map+offset, size); | ||
1644 | kunmap_atomic(map, type); | ||
1645 | put_page(page); | ||
1646 | |||
1647 | len += size; | ||
1648 | to += size; | ||
1649 | addr += size; | ||
1650 | |||
1651 | } while (len < n); | ||
1652 | |||
1653 | return len; | ||
1654 | } | ||
1655 | |||
1656 | static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) | ||
1657 | { | ||
1658 | unsigned long bytes; | ||
1659 | |||
1660 | bytes = copy_from_user_nmi(frame, fp, sizeof(*frame)); | ||
1661 | |||
1662 | return bytes == sizeof(*frame); | ||
1634 | } | 1663 | } |
1635 | 1664 | ||
1636 | static void | 1665 | static void |
@@ -1643,7 +1672,7 @@ perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) | |||
1643 | if (!user_mode(regs)) | 1672 | if (!user_mode(regs)) |
1644 | regs = task_pt_regs(current); | 1673 | regs = task_pt_regs(current); |
1645 | 1674 | ||
1646 | fp = (void __user *)regs->bp; | 1675 | fp = (void __user *)regs->bp; |
1647 | 1676 | ||
1648 | callchain_store(entry, regs->ip); | 1677 | callchain_store(entry, regs->ip); |
1649 | 1678 | ||