aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/trace/trace_functions.c117
1 files changed, 96 insertions, 21 deletions
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index a8e0c7666164..973db52eb070 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -261,37 +261,74 @@ static struct tracer function_trace __tracer_data =
261}; 261};
262 262
263#ifdef CONFIG_DYNAMIC_FTRACE 263#ifdef CONFIG_DYNAMIC_FTRACE
264static int update_count(void **data) 264static void update_traceon_count(void **data, int on)
265{ 265{
266 unsigned long *count = (long *)data; 266 long *count = (long *)data;
267 long old_count = *count;
267 268
268 if (!*count) 269 /*
269 return 0; 270 * Tracing gets disabled (or enabled) once per count.
271 * This function can be called at the same time on mulitple CPUs.
272 * It is fine if both disable (or enable) tracing, as disabling
273 * (or enabling) the second time doesn't do anything as the
274 * state of the tracer is already disabled (or enabled).
275 * What needs to be synchronized in this case is that the count
276 * only gets decremented once, even if the tracer is disabled
277 * (or enabled) twice, as the second one is really a nop.
278 *
279 * The memory barriers guarantee that we only decrement the
280 * counter once. First the count is read to a local variable
281 * and a read barrier is used to make sure that it is loaded
282 * before checking if the tracer is in the state we want.
283 * If the tracer is not in the state we want, then the count
284 * is guaranteed to be the old count.
285 *
286 * Next the tracer is set to the state we want (disabled or enabled)
287 * then a write memory barrier is used to make sure that
288 * the new state is visible before changing the counter by
289 * one minus the old counter. This guarantees that another CPU
290 * executing this code will see the new state before seeing
291 * the new counter value, and would not do anthing if the new
292 * counter is seen.
293 *
294 * Note, there is no synchronization between this and a user
295 * setting the tracing_on file. But we currently don't care
296 * about that.
297 */
298 if (!old_count)
299 return;
270 300
271 if (*count != -1) 301 /* Make sure we see count before checking tracing state */
272 (*count)--; 302 smp_rmb();
273 303
274 return 1; 304 if (on == !!tracing_is_on())
305 return;
306
307 if (on)
308 tracing_on();
309 else
310 tracing_off();
311
312 /* unlimited? */
313 if (old_count == -1)
314 return;
315
316 /* Make sure tracing state is visible before updating count */
317 smp_wmb();
318
319 *count = old_count - 1;
275} 320}
276 321
277static void 322static void
278ftrace_traceon_count(unsigned long ip, unsigned long parent_ip, void **data) 323ftrace_traceon_count(unsigned long ip, unsigned long parent_ip, void **data)
279{ 324{
280 if (tracing_is_on()) 325 update_traceon_count(data, 1);
281 return;
282
283 if (update_count(data))
284 tracing_on();
285} 326}
286 327
287static void 328static void
288ftrace_traceoff_count(unsigned long ip, unsigned long parent_ip, void **data) 329ftrace_traceoff_count(unsigned long ip, unsigned long parent_ip, void **data)
289{ 330{
290 if (!tracing_is_on()) 331 update_traceon_count(data, 0);
291 return;
292
293 if (update_count(data))
294 tracing_off();
295} 332}
296 333
297static void 334static void
@@ -330,11 +367,49 @@ ftrace_stacktrace(unsigned long ip, unsigned long parent_ip, void **data)
330static void 367static void
331ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip, void **data) 368ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip, void **data)
332{ 369{
333 if (!tracing_is_on()) 370 long *count = (long *)data;
334 return; 371 long old_count;
372 long new_count;
335 373
336 if (update_count(data)) 374 /*
337 trace_dump_stack(STACK_SKIP); 375 * Stack traces should only execute the number of times the
376 * user specified in the counter.
377 */
378 do {
379
380 if (!tracing_is_on())
381 return;
382
383 old_count = *count;
384
385 if (!old_count)
386 return;
387
388 /* unlimited? */
389 if (old_count == -1) {
390 trace_dump_stack(STACK_SKIP);
391 return;
392 }
393
394 new_count = old_count - 1;
395 new_count = cmpxchg(count, old_count, new_count);
396 if (new_count == old_count)
397 trace_dump_stack(STACK_SKIP);
398
399 } while (new_count != old_count);
400}
401
402static int update_count(void **data)
403{
404 unsigned long *count = (long *)data;
405
406 if (!*count)
407 return 0;
408
409 if (*count != -1)
410 (*count)--;
411
412 return 1;
338} 413}
339 414
340static void 415static void