diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2009-02-25 00:13:16 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-25 07:40:58 -0500 |
commit | d7350c3f45694104e820041969c8185c5f99e57c (patch) | |
tree | 7aaed9e59f6d33c82e0b62c21f9d1bda9f094b4e /kernel/trace | |
parent | b04cc6b1f6398b0e0b60d37e27ce51b4899672ec (diff) |
tracing/core: make the read callbacks reentrants
Now that several per-cpu files can be read or spliced at the
same, we want the read/splice callbacks for tracing files to be
reentrants.
Until now, a single global mutex (trace_types_lock) serialized
the access to tracing_read_pipe(), tracing_splice_read_pipe(),
and the seq helpers.
Ie: it means that if a user tries to read trace_pipe0 and
trace_pipe1 at the same time, the access to the function
tracing_read_pipe() is contended and one reader must wait for
the other to finish its read call.
The trace_type_lock mutex is mostly here to serialize the access
to the global current tracer (current_trace), which can be
changed concurrently. Although the iter struct keeps a private
pointer to this tracer, its callbacks can be changed by another
function.
The method used here is to not keep anymore private reference to
the tracer inside the iterator but to make a copy of it inside
the iterator. Then it checks on subsequents read calls if the
tracer has changed. This is not costly because the current
tracer is not expected to be changed often, so we use a branch
prediction for that.
Moreover, we add a private mutex to the iterator (there is one
iterator per file descriptor) to serialize the accesses in case
of multiple consumers per file descriptor (which would be a
silly idea from the user). Note that this is not to protect the
ring buffer, since the ring buffer already serializes the
readers accesses. This is to prevent from traces weirdness in
case of concurrent consumers. But these mutexes can be dropped
anyway, that would not result in any crash. Just tell me what
you think about it.
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace.c | 101 | ||||
-rw-r--r-- | kernel/trace/trace.h | 3 |
2 files changed, 85 insertions, 19 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index aa58b7bc847c..d8d899f3bd6f 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -1294,20 +1294,32 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos) | |||
1294 | return ent; | 1294 | return ent; |
1295 | } | 1295 | } |
1296 | 1296 | ||
1297 | /* | ||
1298 | * No necessary locking here. The worst thing which can | ||
1299 | * happen is loosing events consumed at the same time | ||
1300 | * by a trace_pipe reader. | ||
1301 | * Other than that, we don't risk to crash the ring buffer | ||
1302 | * because it serializes the readers. | ||
1303 | * | ||
1304 | * The current tracer is copied to avoid a global locking | ||
1305 | * all around. | ||
1306 | */ | ||
1297 | static void *s_start(struct seq_file *m, loff_t *pos) | 1307 | static void *s_start(struct seq_file *m, loff_t *pos) |
1298 | { | 1308 | { |
1299 | struct trace_iterator *iter = m->private; | 1309 | struct trace_iterator *iter = m->private; |
1310 | static struct tracer *old_tracer; | ||
1300 | int cpu_file = iter->cpu_file; | 1311 | int cpu_file = iter->cpu_file; |
1301 | void *p = NULL; | 1312 | void *p = NULL; |
1302 | loff_t l = 0; | 1313 | loff_t l = 0; |
1303 | int cpu; | 1314 | int cpu; |
1304 | 1315 | ||
1316 | /* copy the tracer to avoid using a global lock all around */ | ||
1305 | mutex_lock(&trace_types_lock); | 1317 | mutex_lock(&trace_types_lock); |
1306 | 1318 | if (unlikely(old_tracer != current_trace && current_trace)) { | |
1307 | if (!current_trace || current_trace != iter->trace) { | 1319 | old_tracer = current_trace; |
1308 | mutex_unlock(&trace_types_lock); | 1320 | *iter->trace = *current_trace; |
1309 | return NULL; | ||
1310 | } | 1321 | } |
1322 | mutex_unlock(&trace_types_lock); | ||
1311 | 1323 | ||
1312 | atomic_inc(&trace_record_cmdline_disabled); | 1324 | atomic_inc(&trace_record_cmdline_disabled); |
1313 | 1325 | ||
@@ -1341,7 +1353,6 @@ static void *s_start(struct seq_file *m, loff_t *pos) | |||
1341 | static void s_stop(struct seq_file *m, void *p) | 1353 | static void s_stop(struct seq_file *m, void *p) |
1342 | { | 1354 | { |
1343 | atomic_dec(&trace_record_cmdline_disabled); | 1355 | atomic_dec(&trace_record_cmdline_disabled); |
1344 | mutex_unlock(&trace_types_lock); | ||
1345 | } | 1356 | } |
1346 | 1357 | ||
1347 | static void print_lat_help_header(struct seq_file *m) | 1358 | static void print_lat_help_header(struct seq_file *m) |
@@ -1691,13 +1702,25 @@ __tracing_open(struct inode *inode, struct file *file, int *ret) | |||
1691 | goto out; | 1702 | goto out; |
1692 | } | 1703 | } |
1693 | 1704 | ||
1705 | /* | ||
1706 | * We make a copy of the current tracer to avoid concurrent | ||
1707 | * changes on it while we are reading. | ||
1708 | */ | ||
1694 | mutex_lock(&trace_types_lock); | 1709 | mutex_lock(&trace_types_lock); |
1710 | iter->trace = kzalloc(sizeof(*iter->trace), GFP_KERNEL); | ||
1711 | if (!iter->trace) { | ||
1712 | *ret = -ENOMEM; | ||
1713 | goto fail; | ||
1714 | } | ||
1715 | if (current_trace) | ||
1716 | *iter->trace = *current_trace; | ||
1717 | |||
1695 | if (current_trace && current_trace->print_max) | 1718 | if (current_trace && current_trace->print_max) |
1696 | iter->tr = &max_tr; | 1719 | iter->tr = &max_tr; |
1697 | else | 1720 | else |
1698 | iter->tr = &global_trace; | 1721 | iter->tr = &global_trace; |
1699 | iter->trace = current_trace; | ||
1700 | iter->pos = -1; | 1722 | iter->pos = -1; |
1723 | mutex_init(&iter->mutex); | ||
1701 | iter->cpu_file = cpu_file; | 1724 | iter->cpu_file = cpu_file; |
1702 | 1725 | ||
1703 | /* Notify the tracer early; before we stop tracing. */ | 1726 | /* Notify the tracer early; before we stop tracing. */ |
@@ -1747,8 +1770,9 @@ __tracing_open(struct inode *inode, struct file *file, int *ret) | |||
1747 | if (iter->buffer_iter[cpu]) | 1770 | if (iter->buffer_iter[cpu]) |
1748 | ring_buffer_read_finish(iter->buffer_iter[cpu]); | 1771 | ring_buffer_read_finish(iter->buffer_iter[cpu]); |
1749 | } | 1772 | } |
1750 | fail: | 1773 | fail: |
1751 | mutex_unlock(&trace_types_lock); | 1774 | mutex_unlock(&trace_types_lock); |
1775 | kfree(iter->trace); | ||
1752 | kfree(iter); | 1776 | kfree(iter); |
1753 | 1777 | ||
1754 | return ERR_PTR(-ENOMEM); | 1778 | return ERR_PTR(-ENOMEM); |
@@ -1783,6 +1807,8 @@ static int tracing_release(struct inode *inode, struct file *file) | |||
1783 | mutex_unlock(&trace_types_lock); | 1807 | mutex_unlock(&trace_types_lock); |
1784 | 1808 | ||
1785 | seq_release(inode, file); | 1809 | seq_release(inode, file); |
1810 | mutex_destroy(&iter->mutex); | ||
1811 | kfree(iter->trace); | ||
1786 | kfree(iter); | 1812 | kfree(iter); |
1787 | return 0; | 1813 | return 0; |
1788 | } | 1814 | } |
@@ -2392,10 +2418,21 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) | |||
2392 | goto out; | 2418 | goto out; |
2393 | } | 2419 | } |
2394 | 2420 | ||
2421 | /* | ||
2422 | * We make a copy of the current tracer to avoid concurrent | ||
2423 | * changes on it while we are reading. | ||
2424 | */ | ||
2425 | iter->trace = kmalloc(sizeof(*iter->trace), GFP_KERNEL); | ||
2426 | if (!iter->trace) { | ||
2427 | ret = -ENOMEM; | ||
2428 | goto fail; | ||
2429 | } | ||
2430 | if (current_trace) | ||
2431 | *iter->trace = *current_trace; | ||
2432 | |||
2395 | if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) { | 2433 | if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) { |
2396 | kfree(iter); | ||
2397 | ret = -ENOMEM; | 2434 | ret = -ENOMEM; |
2398 | goto out; | 2435 | goto fail; |
2399 | } | 2436 | } |
2400 | 2437 | ||
2401 | /* trace pipe does not show start of buffer */ | 2438 | /* trace pipe does not show start of buffer */ |
@@ -2403,7 +2440,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) | |||
2403 | 2440 | ||
2404 | iter->cpu_file = cpu_file; | 2441 | iter->cpu_file = cpu_file; |
2405 | iter->tr = &global_trace; | 2442 | iter->tr = &global_trace; |
2406 | iter->trace = current_trace; | 2443 | mutex_init(&iter->mutex); |
2407 | filp->private_data = iter; | 2444 | filp->private_data = iter; |
2408 | 2445 | ||
2409 | if (iter->trace->pipe_open) | 2446 | if (iter->trace->pipe_open) |
@@ -2412,6 +2449,12 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) | |||
2412 | out: | 2449 | out: |
2413 | mutex_unlock(&trace_types_lock); | 2450 | mutex_unlock(&trace_types_lock); |
2414 | return ret; | 2451 | return ret; |
2452 | |||
2453 | fail: | ||
2454 | kfree(iter->trace); | ||
2455 | kfree(iter); | ||
2456 | mutex_unlock(&trace_types_lock); | ||
2457 | return ret; | ||
2415 | } | 2458 | } |
2416 | 2459 | ||
2417 | static int tracing_release_pipe(struct inode *inode, struct file *file) | 2460 | static int tracing_release_pipe(struct inode *inode, struct file *file) |
@@ -2428,6 +2471,8 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) | |||
2428 | mutex_unlock(&trace_types_lock); | 2471 | mutex_unlock(&trace_types_lock); |
2429 | 2472 | ||
2430 | free_cpumask_var(iter->started); | 2473 | free_cpumask_var(iter->started); |
2474 | mutex_destroy(&iter->mutex); | ||
2475 | kfree(iter->trace); | ||
2431 | kfree(iter); | 2476 | kfree(iter); |
2432 | 2477 | ||
2433 | return 0; | 2478 | return 0; |
@@ -2497,18 +2542,15 @@ static int tracing_wait_pipe(struct file *filp) | |||
2497 | return -EAGAIN; | 2542 | return -EAGAIN; |
2498 | } | 2543 | } |
2499 | 2544 | ||
2500 | mutex_unlock(&trace_types_lock); | 2545 | mutex_unlock(&iter->mutex); |
2501 | 2546 | ||
2502 | iter->trace->wait_pipe(iter); | 2547 | iter->trace->wait_pipe(iter); |
2503 | 2548 | ||
2504 | mutex_lock(&trace_types_lock); | 2549 | mutex_lock(&iter->mutex); |
2505 | 2550 | ||
2506 | if (signal_pending(current)) | 2551 | if (signal_pending(current)) |
2507 | return -EINTR; | 2552 | return -EINTR; |
2508 | 2553 | ||
2509 | if (iter->trace != current_trace) | ||
2510 | return 0; | ||
2511 | |||
2512 | /* | 2554 | /* |
2513 | * We block until we read something and tracing is disabled. | 2555 | * We block until we read something and tracing is disabled. |
2514 | * We still block if tracing is disabled, but we have never | 2556 | * We still block if tracing is disabled, but we have never |
@@ -2533,6 +2575,7 @@ tracing_read_pipe(struct file *filp, char __user *ubuf, | |||
2533 | size_t cnt, loff_t *ppos) | 2575 | size_t cnt, loff_t *ppos) |
2534 | { | 2576 | { |
2535 | struct trace_iterator *iter = filp->private_data; | 2577 | struct trace_iterator *iter = filp->private_data; |
2578 | static struct tracer *old_tracer; | ||
2536 | ssize_t sret; | 2579 | ssize_t sret; |
2537 | 2580 | ||
2538 | /* return any leftover data */ | 2581 | /* return any leftover data */ |
@@ -2542,7 +2585,20 @@ tracing_read_pipe(struct file *filp, char __user *ubuf, | |||
2542 | 2585 | ||
2543 | trace_seq_reset(&iter->seq); | 2586 | trace_seq_reset(&iter->seq); |
2544 | 2587 | ||
2588 | /* copy the tracer to avoid using a global lock all around */ | ||
2545 | mutex_lock(&trace_types_lock); | 2589 | mutex_lock(&trace_types_lock); |
2590 | if (unlikely(old_tracer != current_trace && current_trace)) { | ||
2591 | old_tracer = current_trace; | ||
2592 | *iter->trace = *current_trace; | ||
2593 | } | ||
2594 | mutex_unlock(&trace_types_lock); | ||
2595 | |||
2596 | /* | ||
2597 | * Avoid more than one consumer on a single file descriptor | ||
2598 | * This is just a matter of traces coherency, the ring buffer itself | ||
2599 | * is protected. | ||
2600 | */ | ||
2601 | mutex_lock(&iter->mutex); | ||
2546 | if (iter->trace->read) { | 2602 | if (iter->trace->read) { |
2547 | sret = iter->trace->read(iter, filp, ubuf, cnt, ppos); | 2603 | sret = iter->trace->read(iter, filp, ubuf, cnt, ppos); |
2548 | if (sret) | 2604 | if (sret) |
@@ -2599,7 +2655,7 @@ waitagain: | |||
2599 | goto waitagain; | 2655 | goto waitagain; |
2600 | 2656 | ||
2601 | out: | 2657 | out: |
2602 | mutex_unlock(&trace_types_lock); | 2658 | mutex_unlock(&iter->mutex); |
2603 | 2659 | ||
2604 | return sret; | 2660 | return sret; |
2605 | } | 2661 | } |
@@ -2676,11 +2732,20 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, | |||
2676 | .ops = &tracing_pipe_buf_ops, | 2732 | .ops = &tracing_pipe_buf_ops, |
2677 | .spd_release = tracing_spd_release_pipe, | 2733 | .spd_release = tracing_spd_release_pipe, |
2678 | }; | 2734 | }; |
2735 | static struct tracer *old_tracer; | ||
2679 | ssize_t ret; | 2736 | ssize_t ret; |
2680 | size_t rem; | 2737 | size_t rem; |
2681 | unsigned int i; | 2738 | unsigned int i; |
2682 | 2739 | ||
2740 | /* copy the tracer to avoid using a global lock all around */ | ||
2683 | mutex_lock(&trace_types_lock); | 2741 | mutex_lock(&trace_types_lock); |
2742 | if (unlikely(old_tracer != current_trace && current_trace)) { | ||
2743 | old_tracer = current_trace; | ||
2744 | *iter->trace = *current_trace; | ||
2745 | } | ||
2746 | mutex_unlock(&trace_types_lock); | ||
2747 | |||
2748 | mutex_lock(&iter->mutex); | ||
2684 | 2749 | ||
2685 | if (iter->trace->splice_read) { | 2750 | if (iter->trace->splice_read) { |
2686 | ret = iter->trace->splice_read(iter, filp, | 2751 | ret = iter->trace->splice_read(iter, filp, |
@@ -2720,14 +2785,14 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, | |||
2720 | trace_seq_reset(&iter->seq); | 2785 | trace_seq_reset(&iter->seq); |
2721 | } | 2786 | } |
2722 | 2787 | ||
2723 | mutex_unlock(&trace_types_lock); | 2788 | mutex_unlock(&iter->mutex); |
2724 | 2789 | ||
2725 | spd.nr_pages = i; | 2790 | spd.nr_pages = i; |
2726 | 2791 | ||
2727 | return splice_to_pipe(pipe, &spd); | 2792 | return splice_to_pipe(pipe, &spd); |
2728 | 2793 | ||
2729 | out_err: | 2794 | out_err: |
2730 | mutex_unlock(&trace_types_lock); | 2795 | mutex_unlock(&iter->mutex); |
2731 | 2796 | ||
2732 | return ret; | 2797 | return ret; |
2733 | } | 2798 | } |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 508235a39da6..632191770aac 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -405,8 +405,9 @@ struct trace_iterator { | |||
405 | struct trace_array *tr; | 405 | struct trace_array *tr; |
406 | struct tracer *trace; | 406 | struct tracer *trace; |
407 | void *private; | 407 | void *private; |
408 | struct ring_buffer_iter *buffer_iter[NR_CPUS]; | ||
409 | int cpu_file; | 408 | int cpu_file; |
409 | struct mutex mutex; | ||
410 | struct ring_buffer_iter *buffer_iter[NR_CPUS]; | ||
410 | 411 | ||
411 | /* The below is zeroed out in pipe_read */ | 412 | /* The below is zeroed out in pipe_read */ |
412 | struct trace_seq seq; | 413 | struct trace_seq seq; |