diff options
author | Steven Rostedt <srostedt@redhat.com> | 2008-05-12 15:20:45 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2008-05-23 14:41:35 -0400 |
commit | 93a588f459da134be6ab17c4104e28441beb0d22 (patch) | |
tree | 30eb991743d7ce5698f070204d666eca98519213 | |
parent | 1d4db00a5e30c7b8f8dc2a1b19e886fd942be143 (diff) |
ftrace: change buffers to producer consumer
This patch changes the way the CPU trace buffers are handled.
Instead of always starting from the trace page head, the logic
is changed to a producer consumer logic. This allows for the
buffers to be drained while they are alive.
Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | kernel/trace/trace.c | 103 | ||||
-rw-r--r-- | kernel/trace/trace.h | 6 |
2 files changed, 65 insertions, 44 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6580e7ed04be..777b859e1c2e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -176,10 +176,9 @@ flip_trace(struct trace_array_cpu *tr1, struct trace_array_cpu *tr2) | |||
176 | 176 | ||
177 | INIT_LIST_HEAD(&flip_pages); | 177 | INIT_LIST_HEAD(&flip_pages); |
178 | 178 | ||
179 | tr1->trace_current = NULL; | 179 | memcpy(&tr1->trace_head_idx, &tr2->trace_head_idx, |
180 | memcpy(&tr1->trace_current_idx, &tr2->trace_current_idx, | ||
181 | sizeof(struct trace_array_cpu) - | 180 | sizeof(struct trace_array_cpu) - |
182 | offsetof(struct trace_array_cpu, trace_current_idx)); | 181 | offsetof(struct trace_array_cpu, trace_head_idx)); |
183 | 182 | ||
184 | check_pages(tr1); | 183 | check_pages(tr1); |
185 | check_pages(tr2); | 184 | check_pages(tr2); |
@@ -228,7 +227,6 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) | |||
228 | tracing_reset(max_tr.data[i]); | 227 | tracing_reset(max_tr.data[i]); |
229 | 228 | ||
230 | flip_trace(max_tr.data[cpu], data); | 229 | flip_trace(max_tr.data[cpu], data); |
231 | |||
232 | tracing_reset(data); | 230 | tracing_reset(data); |
233 | 231 | ||
234 | __update_max_tr(tr, tsk, cpu); | 232 | __update_max_tr(tr, tsk, cpu); |
@@ -343,9 +341,9 @@ void unregister_tracer(struct tracer *type) | |||
343 | notrace void tracing_reset(struct trace_array_cpu *data) | 341 | notrace void tracing_reset(struct trace_array_cpu *data) |
344 | { | 342 | { |
345 | data->trace_idx = 0; | 343 | data->trace_idx = 0; |
346 | data->trace_current = head_page(data); | 344 | data->trace_head = data->trace_tail = head_page(data); |
347 | data->trace_current_idx = 0; | 345 | data->trace_head_idx = 0; |
348 | data->time_offset = 0; | 346 | data->trace_tail_idx = 0; |
349 | } | 347 | } |
350 | 348 | ||
351 | #ifdef CONFIG_FTRACE | 349 | #ifdef CONFIG_FTRACE |
@@ -470,38 +468,65 @@ notrace void tracing_record_cmdline(struct task_struct *tsk) | |||
470 | trace_save_cmdline(tsk); | 468 | trace_save_cmdline(tsk); |
471 | } | 469 | } |
472 | 470 | ||
471 | static inline notrace struct list_head * | ||
472 | trace_next_list(struct trace_array_cpu *data, struct list_head *next) | ||
473 | { | ||
474 | /* | ||
475 | * Roundrobin - but skip the head (which is not a real page): | ||
476 | */ | ||
477 | next = next->next; | ||
478 | if (unlikely(next == &data->trace_pages)) | ||
479 | next = next->next; | ||
480 | BUG_ON(next == &data->trace_pages); | ||
481 | |||
482 | return next; | ||
483 | } | ||
484 | |||
485 | static inline notrace void * | ||
486 | trace_next_page(struct trace_array_cpu *data, void *addr) | ||
487 | { | ||
488 | struct list_head *next; | ||
489 | struct page *page; | ||
490 | |||
491 | page = virt_to_page(addr); | ||
492 | |||
493 | next = trace_next_list(data, &page->lru); | ||
494 | page = list_entry(next, struct page, lru); | ||
495 | |||
496 | return page_address(page); | ||
497 | } | ||
498 | |||
473 | static inline notrace struct trace_entry * | 499 | static inline notrace struct trace_entry * |
474 | tracing_get_trace_entry(struct trace_array *tr, struct trace_array_cpu *data) | 500 | tracing_get_trace_entry(struct trace_array *tr, struct trace_array_cpu *data) |
475 | { | 501 | { |
476 | unsigned long idx, idx_next; | 502 | unsigned long idx, idx_next; |
477 | struct trace_entry *entry; | 503 | struct trace_entry *entry; |
478 | struct list_head *next; | ||
479 | struct page *page; | ||
480 | 504 | ||
481 | data->trace_idx++; | 505 | data->trace_idx++; |
482 | idx = data->trace_current_idx; | 506 | idx = data->trace_head_idx; |
483 | idx_next = idx + 1; | 507 | idx_next = idx + 1; |
484 | 508 | ||
485 | BUG_ON(idx * TRACE_ENTRY_SIZE >= PAGE_SIZE); | 509 | BUG_ON(idx * TRACE_ENTRY_SIZE >= PAGE_SIZE); |
486 | 510 | ||
487 | entry = data->trace_current + idx * TRACE_ENTRY_SIZE; | 511 | entry = data->trace_head + idx * TRACE_ENTRY_SIZE; |
488 | 512 | ||
489 | if (unlikely(idx_next >= ENTRIES_PER_PAGE)) { | 513 | if (unlikely(idx_next >= ENTRIES_PER_PAGE)) { |
490 | page = virt_to_page(data->trace_current); | 514 | data->trace_head = trace_next_page(data, data->trace_head); |
491 | /* | ||
492 | * Roundrobin - but skip the head (which is not a real page): | ||
493 | */ | ||
494 | next = page->lru.next; | ||
495 | if (unlikely(next == &data->trace_pages)) | ||
496 | next = next->next; | ||
497 | BUG_ON(next == &data->trace_pages); | ||
498 | |||
499 | page = list_entry(next, struct page, lru); | ||
500 | data->trace_current = page_address(page); | ||
501 | idx_next = 0; | 515 | idx_next = 0; |
502 | } | 516 | } |
503 | 517 | ||
504 | data->trace_current_idx = idx_next; | 518 | if (data->trace_head == data->trace_tail && |
519 | idx_next == data->trace_tail_idx) { | ||
520 | /* overrun */ | ||
521 | data->trace_tail_idx++; | ||
522 | if (data->trace_tail_idx >= ENTRIES_PER_PAGE) { | ||
523 | data->trace_tail = | ||
524 | trace_next_page(data, data->trace_tail); | ||
525 | data->trace_tail_idx = 0; | ||
526 | } | ||
527 | } | ||
528 | |||
529 | data->trace_head_idx = idx_next; | ||
505 | 530 | ||
506 | return entry; | 531 | return entry; |
507 | } | 532 | } |
@@ -571,21 +596,11 @@ trace_entry_idx(struct trace_array *tr, struct trace_array_cpu *data, | |||
571 | return NULL; | 596 | return NULL; |
572 | 597 | ||
573 | if (!iter->next_page[cpu]) { | 598 | if (!iter->next_page[cpu]) { |
574 | /* | 599 | /* Initialize the iterator for this cpu trace buffer */ |
575 | * Initialize. If the count of elements in | 600 | WARN_ON(!data->trace_tail); |
576 | * this buffer is greater than the max entries | 601 | page = virt_to_page(data->trace_tail); |
577 | * we had an underrun. Which means we looped around. | 602 | iter->next_page[cpu] = &page->lru; |
578 | * We can simply use the current pointer as our | 603 | iter->next_page_idx[cpu] = data->trace_tail_idx; |
579 | * starting point. | ||
580 | */ | ||
581 | if (data->trace_idx >= tr->entries) { | ||
582 | page = virt_to_page(data->trace_current); | ||
583 | iter->next_page[cpu] = &page->lru; | ||
584 | iter->next_page_idx[cpu] = data->trace_current_idx; | ||
585 | } else { | ||
586 | iter->next_page[cpu] = data->trace_pages.next; | ||
587 | iter->next_page_idx[cpu] = 0; | ||
588 | } | ||
589 | } | 604 | } |
590 | 605 | ||
591 | page = list_entry(iter->next_page[cpu], struct page, lru); | 606 | page = list_entry(iter->next_page[cpu], struct page, lru); |
@@ -593,6 +608,12 @@ trace_entry_idx(struct trace_array *tr, struct trace_array_cpu *data, | |||
593 | 608 | ||
594 | array = page_address(page); | 609 | array = page_address(page); |
595 | 610 | ||
611 | /* Still possible to catch up to the tail */ | ||
612 | if (iter->next_idx[cpu] && array == data->trace_tail && | ||
613 | iter->next_page_idx[cpu] == data->trace_tail_idx) | ||
614 | return NULL; | ||
615 | |||
616 | WARN_ON(iter->next_page_idx[cpu] >= ENTRIES_PER_PAGE); | ||
596 | return &array[iter->next_page_idx[cpu]]; | 617 | return &array[iter->next_page_idx[cpu]]; |
597 | } | 618 | } |
598 | 619 | ||
@@ -638,10 +659,8 @@ static void *find_next_entry_inc(struct trace_iterator *iter) | |||
638 | 659 | ||
639 | iter->next_page_idx[next_cpu] = 0; | 660 | iter->next_page_idx[next_cpu] = 0; |
640 | iter->next_page[next_cpu] = | 661 | iter->next_page[next_cpu] = |
641 | iter->next_page[next_cpu]->next; | 662 | trace_next_list(data, iter->next_page[next_cpu]); |
642 | if (iter->next_page[next_cpu] == &data->trace_pages) | 663 | |
643 | iter->next_page[next_cpu] = | ||
644 | data->trace_pages.next; | ||
645 | } | 664 | } |
646 | } | 665 | } |
647 | iter->prev_ent = iter->ent; | 666 | iter->prev_ent = iter->ent; |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 5df8ff2b84a7..0ce127455b4b 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -53,13 +53,15 @@ struct trace_entry { | |||
53 | * the trace, etc.) | 53 | * the trace, etc.) |
54 | */ | 54 | */ |
55 | struct trace_array_cpu { | 55 | struct trace_array_cpu { |
56 | void *trace_current; | ||
57 | struct list_head trace_pages; | 56 | struct list_head trace_pages; |
58 | atomic_t disabled; | 57 | atomic_t disabled; |
59 | cycle_t time_offset; | 58 | cycle_t time_offset; |
60 | 59 | ||
61 | /* these fields get copied into max-trace: */ | 60 | /* these fields get copied into max-trace: */ |
62 | unsigned trace_current_idx; | 61 | unsigned trace_head_idx; |
62 | unsigned trace_tail_idx; | ||
63 | void *trace_head; /* producer */ | ||
64 | void *trace_tail; /* consumer */ | ||
63 | unsigned long trace_idx; | 65 | unsigned long trace_idx; |
64 | unsigned long saved_latency; | 66 | unsigned long saved_latency; |
65 | unsigned long critical_start; | 67 | unsigned long critical_start; |