]>
Commit | Line | Data |
---|---|---|
7cb15094 GKH |
1 | From a0e3e70243f5b270bc3eca718f0a9fa5e6b8262e Mon Sep 17 00:00:00 2001 |
2 | From: Robert Richter <robert.richter@amd.com> | |
3 | Date: Fri, 3 Jun 2011 16:37:47 +0200 | |
4 | Subject: oprofile, x86: Fix nmi-unsafe callgraph support | |
5 | ||
6 | From: Robert Richter <robert.richter@amd.com> | |
7 | ||
8 | commit a0e3e70243f5b270bc3eca718f0a9fa5e6b8262e upstream. | |
9 | ||
10 | Current oprofile's x86 callgraph support may trigger page faults | |
11 | throwing the BUG_ON(in_nmi()) message below. This patch fixes this by | |
12 | using the same nmi-safe copy-from-user code as in perf. | |
13 | ||
14 | ------------[ cut here ]------------ | |
15 | kernel BUG at .../arch/x86/kernel/traps.c:436! | |
16 | invalid opcode: 0000 [#1] SMP | |
17 | last sysfs file: /sys/devices/pci0000:00/0000:00:0a.0/0000:07:00.0/0000:08:04.0/net/eth0/broadcast | |
18 | CPU 5 | |
19 | Modules linked in: | |
20 | ||
21 | Pid: 8611, comm: opcontrol Not tainted 2.6.39-00007-gfe47ae7 #1 Advanced Micro Device Anaheim/Anaheim | |
22 | RIP: 0010:[<ffffffff813e8e35>] [<ffffffff813e8e35>] do_nmi+0x22/0x1ee | |
23 | RSP: 0000:ffff88042fd47f28 EFLAGS: 00010002 | |
24 | RAX: ffff88042c0a7fd8 RBX: 0000000000000001 RCX: 00000000c0000101 | |
25 | RDX: 00000000ffff8804 RSI: ffffffffffffffff RDI: ffff88042fd47f58 | |
26 | RBP: ffff88042fd47f48 R08: 0000000000000004 R09: 0000000000001484 | |
27 | R10: 0000000000000001 R11: 0000000000000000 R12: ffff88042fd47f58 | |
28 | R13: 0000000000000000 R14: ffff88042fd47d98 R15: 0000000000000020 | |
29 | FS: 00007fca25e56700(0000) GS:ffff88042fd40000(0000) knlGS:0000000000000000 | |
30 | CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 | |
31 | CR2: 0000000000000074 CR3: 000000042d28b000 CR4: 00000000000006e0 | |
32 | DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 | |
33 | DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 | |
34 | Process opcontrol (pid: 8611, threadinfo ffff88042c0a6000, task ffff88042c532310) | |
35 | Stack: | |
36 | 0000000000000000 0000000000000001 ffff88042c0a7fd8 0000000000000000 | |
37 | ffff88042fd47de8 ffffffff813e897a 0000000000000020 ffff88042fd47d98 | |
38 | 0000000000000000 ffff88042c0a7fd8 ffff88042fd47de8 0000000000000074 | |
39 | Call Trace: | |
40 | <NMI> | |
41 | [<ffffffff813e897a>] nmi+0x1a/0x20 | |
42 | [<ffffffff813f08ab>] ? bad_to_user+0x25/0x771 | |
43 | <<EOE>> | |
44 | Code: ff 59 5b 41 5c 41 5d c9 c3 55 65 48 8b 04 25 88 b5 00 00 48 89 e5 41 55 41 54 49 89 fc 53 48 83 ec 08 f6 80 47 e0 ff ff 04 74 04 <0f> 0b eb fe 81 80 44 e0 ff ff 00 00 01 04 65 ff 04 25 c4 0f 01 | |
45 | RIP [<ffffffff813e8e35>] do_nmi+0x22/0x1ee | |
46 | RSP <ffff88042fd47f28> | |
47 | ---[ end trace ed6752185092104b ]--- | |
48 | Kernel panic - not syncing: Fatal exception in interrupt | |
49 | Pid: 8611, comm: opcontrol Tainted: G D 2.6.39-00007-gfe47ae7 #1 | |
50 | Call Trace: | |
51 | <NMI> [<ffffffff813e5e0a>] panic+0x8c/0x188 | |
52 | [<ffffffff813e915c>] oops_end+0x81/0x8e | |
53 | [<ffffffff8100403d>] die+0x55/0x5e | |
54 | [<ffffffff813e8c45>] do_trap+0x11c/0x12b | |
55 | [<ffffffff810023c8>] do_invalid_op+0x91/0x9a | |
56 | [<ffffffff813e8e35>] ? do_nmi+0x22/0x1ee | |
57 | [<ffffffff8131e6fa>] ? oprofile_add_sample+0x83/0x95 | |
58 | [<ffffffff81321670>] ? op_amd_check_ctrs+0x4f/0x2cf | |
59 | [<ffffffff813ee4d5>] invalid_op+0x15/0x20 | |
60 | [<ffffffff813e8e35>] ? do_nmi+0x22/0x1ee | |
61 | [<ffffffff813e8e7a>] ? do_nmi+0x67/0x1ee | |
62 | [<ffffffff813e897a>] nmi+0x1a/0x20 | |
63 | [<ffffffff813f08ab>] ? bad_to_user+0x25/0x771 | |
64 | <<EOE>> | |
65 | ||
66 | Cc: John Lumby <johnlumby@hotmail.com> | |
67 | Cc: Maynard Johnson <maynardj@us.ibm.com> | |
68 | Signed-off-by: Robert Richter <robert.richter@amd.com> | |
69 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
70 | ||
71 | --- | |
72 | arch/x86/oprofile/backtrace.c | 56 +++++++++++++++++++++++++++++++++++------- | |
73 | 1 file changed, 47 insertions(+), 9 deletions(-) | |
74 | ||
75 | --- a/arch/x86/oprofile/backtrace.c | |
76 | +++ b/arch/x86/oprofile/backtrace.c | |
77 | @@ -11,10 +11,12 @@ | |
78 | #include <linux/oprofile.h> | |
79 | #include <linux/sched.h> | |
80 | #include <linux/mm.h> | |
81 | +#include <linux/compat.h> | |
82 | +#include <linux/highmem.h> | |
83 | + | |
84 | #include <asm/ptrace.h> | |
85 | #include <asm/uaccess.h> | |
86 | #include <asm/stacktrace.h> | |
87 | -#include <linux/compat.h> | |
88 | ||
89 | static int backtrace_stack(void *data, char *name) | |
90 | { | |
91 | @@ -36,17 +38,53 @@ static struct stacktrace_ops backtrace_o | |
92 | .walk_stack = print_context_stack, | |
93 | }; | |
94 | ||
95 | +/* from arch/x86/kernel/cpu/perf_event.c: */ | |
96 | + | |
97 | +/* | |
98 | + * best effort, GUP based copy_from_user() that assumes IRQ or NMI context | |
99 | + */ | |
100 | +static unsigned long | |
101 | +copy_from_user_nmi(void *to, const void __user *from, unsigned long n) | |
102 | +{ | |
103 | + unsigned long offset, addr = (unsigned long)from; | |
104 | + unsigned long size, len = 0; | |
105 | + struct page *page; | |
106 | + void *map; | |
107 | + int ret; | |
108 | + | |
109 | + do { | |
110 | + ret = __get_user_pages_fast(addr, 1, 0, &page); | |
111 | + if (!ret) | |
112 | + break; | |
113 | + | |
114 | + offset = addr & (PAGE_SIZE - 1); | |
115 | + size = min(PAGE_SIZE - offset, n - len); | |
116 | + | |
117 | + map = kmap_atomic(page); | |
118 | + memcpy(to, map+offset, size); | |
119 | + kunmap_atomic(map); | |
120 | + put_page(page); | |
121 | + | |
122 | + len += size; | |
123 | + to += size; | |
124 | + addr += size; | |
125 | + | |
126 | + } while (len < n); | |
127 | + | |
128 | + return len; | |
129 | +} | |
130 | + | |
131 | #ifdef CONFIG_COMPAT | |
132 | static struct stack_frame_ia32 * | |
133 | dump_user_backtrace_32(struct stack_frame_ia32 *head) | |
134 | { | |
135 | + /* Also check accessibility of one struct frame_head beyond: */ | |
136 | struct stack_frame_ia32 bufhead[2]; | |
137 | struct stack_frame_ia32 *fp; | |
138 | + unsigned long bytes; | |
139 | ||
140 | - /* Also check accessibility of one struct frame_head beyond */ | |
141 | - if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | |
142 | - return NULL; | |
143 | - if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | |
144 | + bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); | |
145 | + if (bytes != sizeof(bufhead)) | |
146 | return NULL; | |
147 | ||
148 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); | |
149 | @@ -87,12 +125,12 @@ x86_backtrace_32(struct pt_regs * const | |
150 | ||
151 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) | |
152 | { | |
153 | + /* Also check accessibility of one struct frame_head beyond: */ | |
154 | struct stack_frame bufhead[2]; | |
155 | + unsigned long bytes; | |
156 | ||
157 | - /* Also check accessibility of one struct stack_frame beyond */ | |
158 | - if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | |
159 | - return NULL; | |
160 | - if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | |
161 | + bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); | |
162 | + if (bytes != sizeof(bufhead)) | |
163 | return NULL; | |
164 | ||
165 | oprofile_add_trace(bufhead[0].return_address); |