diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2009-04-03 10:43:44 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-04-07 07:36:25 -0400 |
commit | 01f6569ece6915616f6cae1d7d8b46ab8da9c1bd (patch) | |
tree | 7737a8d64ddaaebf5a2d3a801932e1732d3aca68 /arch/x86/kernel/ds_selftest.c | |
parent | 84f201139245c30777ff858e71b8d7e134b8c3ed (diff) |
x86, ds: selftest each cpu
Perform debug store selftests on each cpu.
Cover both the normal and the _noirq variant of the debug store interface.
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Cc: roland@redhat.com
Cc: eranian@googlemail.com
Cc: oleg@redhat.com
Cc: juan.villacis@intel.com
Cc: ak@linux.jf.intel.com
LKML-Reference: <20090403144559.394583000@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/ds_selftest.c')
-rw-r--r-- | arch/x86/kernel/ds_selftest.c | 182 |
1 files changed, 135 insertions, 47 deletions
diff --git a/arch/x86/kernel/ds_selftest.c b/arch/x86/kernel/ds_selftest.c index cccc19a38f6d..599a96300628 100644 --- a/arch/x86/kernel/ds_selftest.c +++ b/arch/x86/kernel/ds_selftest.c | |||
@@ -11,13 +11,21 @@ | |||
11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
12 | #include <linux/string.h> | 12 | #include <linux/string.h> |
13 | #include <linux/smp.h> | 13 | #include <linux/smp.h> |
14 | #include <linux/cpu.h> | ||
14 | 15 | ||
15 | #include <asm/ds.h> | 16 | #include <asm/ds.h> |
16 | 17 | ||
17 | 18 | ||
18 | #define BUFFER_SIZE 1021 /* Intentionally chose an odd size. */ | 19 | #define BUFFER_SIZE 521 /* Intentionally chose an odd size. */ |
19 | 20 | ||
20 | 21 | ||
22 | struct ds_selftest_bts_conf { | ||
23 | struct bts_tracer *tracer; | ||
24 | int error; | ||
25 | int (*suspend)(struct bts_tracer *); | ||
26 | int (*resume)(struct bts_tracer *); | ||
27 | }; | ||
28 | |||
21 | static int ds_selftest_bts_consistency(const struct bts_trace *trace) | 29 | static int ds_selftest_bts_consistency(const struct bts_trace *trace) |
22 | { | 30 | { |
23 | int error = 0; | 31 | int error = 0; |
@@ -125,36 +133,32 @@ static int ds_selftest_bts_read(struct bts_tracer *tracer, | |||
125 | return 0; | 133 | return 0; |
126 | } | 134 | } |
127 | 135 | ||
128 | int ds_selftest_bts(void) | 136 | static void ds_selftest_bts_cpu(void *arg) |
129 | { | 137 | { |
138 | struct ds_selftest_bts_conf *conf = arg; | ||
130 | const struct bts_trace *trace; | 139 | const struct bts_trace *trace; |
131 | struct bts_tracer *tracer; | ||
132 | int error = 0; | ||
133 | void *top; | 140 | void *top; |
134 | unsigned char buffer[BUFFER_SIZE]; | ||
135 | 141 | ||
136 | printk(KERN_INFO "[ds] bts selftest..."); | 142 | if (IS_ERR(conf->tracer)) { |
137 | 143 | conf->error = PTR_ERR(conf->tracer); | |
138 | tracer = ds_request_bts_cpu(smp_processor_id(), buffer, BUFFER_SIZE, | 144 | conf->tracer = NULL; |
139 | NULL, (size_t)-1, BTS_KERNEL); | ||
140 | if (IS_ERR(tracer)) { | ||
141 | error = PTR_ERR(tracer); | ||
142 | tracer = NULL; | ||
143 | 145 | ||
144 | printk(KERN_CONT | 146 | printk(KERN_CONT |
145 | "initialization failed (err: %d)...", error); | 147 | "initialization failed (err: %d)...", conf->error); |
146 | goto out; | 148 | return; |
147 | } | 149 | } |
148 | 150 | ||
149 | /* The return should already give us enough trace. */ | 151 | /* We should meanwhile have enough trace. */ |
150 | ds_suspend_bts(tracer); | 152 | conf->error = conf->suspend(conf->tracer); |
153 | if (conf->error < 0) | ||
154 | return; | ||
151 | 155 | ||
152 | /* Let's see if we can access the trace. */ | 156 | /* Let's see if we can access the trace. */ |
153 | trace = ds_read_bts(tracer); | 157 | trace = ds_read_bts(conf->tracer); |
154 | 158 | ||
155 | error = ds_selftest_bts_consistency(trace); | 159 | conf->error = ds_selftest_bts_consistency(trace); |
156 | if (error < 0) | 160 | if (conf->error < 0) |
157 | goto out; | 161 | return; |
158 | 162 | ||
159 | /* If everything went well, we should have a few trace entries. */ | 163 | /* If everything went well, we should have a few trace entries. */ |
160 | if (trace->ds.top == trace->ds.begin) { | 164 | if (trace->ds.top == trace->ds.begin) { |
@@ -168,10 +172,11 @@ int ds_selftest_bts(void) | |||
168 | } | 172 | } |
169 | 173 | ||
170 | /* Let's try to read the trace we collected. */ | 174 | /* Let's try to read the trace we collected. */ |
171 | error = ds_selftest_bts_read(tracer, trace, | 175 | conf->error = |
176 | ds_selftest_bts_read(conf->tracer, trace, | ||
172 | trace->ds.begin, trace->ds.top); | 177 | trace->ds.begin, trace->ds.top); |
173 | if (error < 0) | 178 | if (conf->error < 0) |
174 | goto out; | 179 | return; |
175 | 180 | ||
176 | /* | 181 | /* |
177 | * Let's read the trace again. | 182 | * Let's read the trace again. |
@@ -179,26 +184,31 @@ int ds_selftest_bts(void) | |||
179 | */ | 184 | */ |
180 | top = trace->ds.top; | 185 | top = trace->ds.top; |
181 | 186 | ||
182 | trace = ds_read_bts(tracer); | 187 | trace = ds_read_bts(conf->tracer); |
183 | error = ds_selftest_bts_consistency(trace); | 188 | conf->error = ds_selftest_bts_consistency(trace); |
184 | if (error < 0) | 189 | if (conf->error < 0) |
185 | goto out; | 190 | return; |
186 | 191 | ||
187 | if (top != trace->ds.top) { | 192 | if (top != trace->ds.top) { |
188 | printk(KERN_CONT "suspend not working..."); | 193 | printk(KERN_CONT "suspend not working..."); |
189 | error = -1; | 194 | conf->error = -1; |
190 | goto out; | 195 | return; |
191 | } | 196 | } |
192 | 197 | ||
193 | /* Let's collect some more trace - see if resume is working. */ | 198 | /* Let's collect some more trace - see if resume is working. */ |
194 | ds_resume_bts(tracer); | 199 | conf->error = conf->resume(conf->tracer); |
195 | ds_suspend_bts(tracer); | 200 | if (conf->error < 0) |
201 | return; | ||
202 | |||
203 | conf->error = conf->suspend(conf->tracer); | ||
204 | if (conf->error < 0) | ||
205 | return; | ||
196 | 206 | ||
197 | trace = ds_read_bts(tracer); | 207 | trace = ds_read_bts(conf->tracer); |
198 | 208 | ||
199 | error = ds_selftest_bts_consistency(trace); | 209 | conf->error = ds_selftest_bts_consistency(trace); |
200 | if (error < 0) | 210 | if (conf->error < 0) |
201 | goto out; | 211 | return; |
202 | 212 | ||
203 | if (trace->ds.top == top) { | 213 | if (trace->ds.top == top) { |
204 | /* | 214 | /* |
@@ -210,35 +220,113 @@ int ds_selftest_bts(void) | |||
210 | printk(KERN_CONT | 220 | printk(KERN_CONT |
211 | "no resume progress/overflow..."); | 221 | "no resume progress/overflow..."); |
212 | 222 | ||
213 | error = ds_selftest_bts_read(tracer, trace, | 223 | conf->error = |
224 | ds_selftest_bts_read(conf->tracer, trace, | ||
214 | trace->ds.begin, trace->ds.end); | 225 | trace->ds.begin, trace->ds.end); |
215 | } else if (trace->ds.top < top) { | 226 | } else if (trace->ds.top < top) { |
216 | /* | 227 | /* |
217 | * We had a buffer overflow - the entire buffer should | 228 | * We had a buffer overflow - the entire buffer should |
218 | * contain trace records. | 229 | * contain trace records. |
219 | */ | 230 | */ |
220 | error = ds_selftest_bts_read(tracer, trace, | 231 | conf->error = |
232 | ds_selftest_bts_read(conf->tracer, trace, | ||
221 | trace->ds.begin, trace->ds.end); | 233 | trace->ds.begin, trace->ds.end); |
222 | } else { | 234 | } else { |
223 | /* | 235 | /* |
224 | * It is quite likely that the buffer did not overflow. | 236 | * It is quite likely that the buffer did not overflow. |
225 | * Let's just check the delta trace. | 237 | * Let's just check the delta trace. |
226 | */ | 238 | */ |
227 | error = ds_selftest_bts_read(tracer, trace, | 239 | conf->error = |
228 | top, trace->ds.top); | 240 | ds_selftest_bts_read(conf->tracer, trace, top, |
241 | trace->ds.top); | ||
229 | } | 242 | } |
230 | if (error < 0) | 243 | if (conf->error < 0) |
231 | goto out; | 244 | return; |
232 | 245 | ||
233 | error = 0; | 246 | conf->error = 0; |
247 | } | ||
234 | 248 | ||
235 | /* The final test: release the tracer while tracing is suspended. */ | 249 | static int ds_suspend_bts_wrap(struct bts_tracer *tracer) |
236 | out: | 250 | { |
237 | ds_release_bts(tracer); | 251 | ds_suspend_bts(tracer); |
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int ds_resume_bts_wrap(struct bts_tracer *tracer) | ||
256 | { | ||
257 | ds_resume_bts(tracer); | ||
258 | return 0; | ||
259 | } | ||
238 | 260 | ||
239 | printk(KERN_CONT "%s.\n", (error ? "failed" : "passed")); | 261 | static void ds_release_bts_noirq_wrap(void *tracer) |
262 | { | ||
263 | (void)ds_release_bts_noirq(tracer); | ||
264 | } | ||
240 | 265 | ||
241 | return error; | 266 | static int ds_selftest_bts_bad_release_noirq(int cpu, |
267 | struct bts_tracer *tracer) | ||
268 | { | ||
269 | int error = -EPERM; | ||
270 | |||
271 | /* Try to release the tracer on the wrong cpu. */ | ||
272 | get_cpu(); | ||
273 | if (cpu != smp_processor_id()) { | ||
274 | error = ds_release_bts_noirq(tracer); | ||
275 | if (error != -EPERM) | ||
276 | printk(KERN_CONT "release on wrong cpu..."); | ||
277 | } | ||
278 | put_cpu(); | ||
279 | |||
280 | return error ? 0 : -1; | ||
281 | } | ||
282 | |||
283 | int ds_selftest_bts(void) | ||
284 | { | ||
285 | struct ds_selftest_bts_conf conf; | ||
286 | unsigned char buffer[BUFFER_SIZE]; | ||
287 | int cpu; | ||
288 | |||
289 | printk(KERN_INFO "[ds] bts selftest..."); | ||
290 | conf.error = 0; | ||
291 | |||
292 | get_online_cpus(); | ||
293 | for_each_online_cpu(cpu) { | ||
294 | conf.suspend = ds_suspend_bts_wrap; | ||
295 | conf.resume = ds_resume_bts_wrap; | ||
296 | conf.tracer = | ||
297 | ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, | ||
298 | NULL, (size_t)-1, BTS_KERNEL); | ||
299 | ds_selftest_bts_cpu(&conf); | ||
300 | ds_release_bts(conf.tracer); | ||
301 | if (conf.error < 0) | ||
302 | goto out; | ||
303 | |||
304 | conf.suspend = ds_suspend_bts_noirq; | ||
305 | conf.resume = ds_resume_bts_noirq; | ||
306 | conf.tracer = | ||
307 | ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, | ||
308 | NULL, (size_t)-1, BTS_KERNEL); | ||
309 | smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1); | ||
310 | if (conf.error >= 0) { | ||
311 | conf.error = | ||
312 | ds_selftest_bts_bad_release_noirq(cpu, | ||
313 | conf.tracer); | ||
314 | /* We must not release the tracer twice. */ | ||
315 | if (conf.error < 0) | ||
316 | conf.tracer = NULL; | ||
317 | } | ||
318 | smp_call_function_single(cpu, ds_release_bts_noirq_wrap, | ||
319 | conf.tracer, 1); | ||
320 | if (conf.error < 0) | ||
321 | goto out; | ||
322 | } | ||
323 | |||
324 | conf.error = 0; | ||
325 | out: | ||
326 | put_online_cpus(); | ||
327 | printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed")); | ||
328 | |||
329 | return conf.error; | ||
242 | } | 330 | } |
243 | 331 | ||
244 | int ds_selftest_pebs(void) | 332 | int ds_selftest_pebs(void) |