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; |
