diff options
Diffstat (limited to 'drivers/oprofile/buffer_sync.c')
-rw-r--r-- | drivers/oprofile/buffer_sync.c | 117 |
1 files changed, 40 insertions, 77 deletions
diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index b55cd23ffdef..737bd9484822 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c | |||
@@ -268,18 +268,6 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) | |||
268 | return cookie; | 268 | return cookie; |
269 | } | 269 | } |
270 | 270 | ||
271 | static void increment_tail(struct oprofile_cpu_buffer *b) | ||
272 | { | ||
273 | unsigned long new_tail = b->tail_pos + 1; | ||
274 | |||
275 | rmb(); /* be sure fifo pointers are synchromized */ | ||
276 | |||
277 | if (new_tail < b->buffer_size) | ||
278 | b->tail_pos = new_tail; | ||
279 | else | ||
280 | b->tail_pos = 0; | ||
281 | } | ||
282 | |||
283 | static unsigned long last_cookie = INVALID_COOKIE; | 271 | static unsigned long last_cookie = INVALID_COOKIE; |
284 | 272 | ||
285 | static void add_cpu_switch(int i) | 273 | static void add_cpu_switch(int i) |
@@ -331,28 +319,25 @@ static void add_trace_begin(void) | |||
331 | 319 | ||
332 | #define IBS_FETCH_CODE_SIZE 2 | 320 | #define IBS_FETCH_CODE_SIZE 2 |
333 | #define IBS_OP_CODE_SIZE 5 | 321 | #define IBS_OP_CODE_SIZE 5 |
334 | #define IBS_EIP(offset) \ | ||
335 | (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) | ||
336 | #define IBS_EVENT(offset) \ | ||
337 | (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) | ||
338 | 322 | ||
339 | /* | 323 | /* |
340 | * Add IBS fetch and op entries to event buffer | 324 | * Add IBS fetch and op entries to event buffer |
341 | */ | 325 | */ |
342 | static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, | 326 | static void add_ibs_begin(int cpu, int code, struct mm_struct *mm) |
343 | struct mm_struct *mm) | ||
344 | { | 327 | { |
345 | unsigned long rip; | 328 | unsigned long rip; |
346 | int i, count; | 329 | int i, count; |
347 | unsigned long ibs_cookie = 0; | 330 | unsigned long ibs_cookie = 0; |
348 | off_t offset; | 331 | off_t offset; |
332 | struct op_sample *sample; | ||
349 | 333 | ||
350 | increment_tail(cpu_buf); /* move to RIP entry */ | 334 | sample = cpu_buffer_read_entry(cpu); |
351 | 335 | if (!sample) | |
352 | rip = IBS_EIP(cpu_buf->tail_pos); | 336 | goto Error; |
337 | rip = sample->eip; | ||
353 | 338 | ||
354 | #ifdef __LP64__ | 339 | #ifdef __LP64__ |
355 | rip += IBS_EVENT(cpu_buf->tail_pos) << 32; | 340 | rip += sample->event << 32; |
356 | #endif | 341 | #endif |
357 | 342 | ||
358 | if (mm) { | 343 | if (mm) { |
@@ -376,8 +361,8 @@ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, | |||
376 | add_event_entry(offset); /* Offset from Dcookie */ | 361 | add_event_entry(offset); /* Offset from Dcookie */ |
377 | 362 | ||
378 | /* we send the Dcookie offset, but send the raw Linear Add also*/ | 363 | /* we send the Dcookie offset, but send the raw Linear Add also*/ |
379 | add_event_entry(IBS_EIP(cpu_buf->tail_pos)); | 364 | add_event_entry(sample->eip); |
380 | add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); | 365 | add_event_entry(sample->event); |
381 | 366 | ||
382 | if (code == IBS_FETCH_CODE) | 367 | if (code == IBS_FETCH_CODE) |
383 | count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ | 368 | count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ |
@@ -385,10 +370,17 @@ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, | |||
385 | count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ | 370 | count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ |
386 | 371 | ||
387 | for (i = 0; i < count; i++) { | 372 | for (i = 0; i < count; i++) { |
388 | increment_tail(cpu_buf); | 373 | sample = cpu_buffer_read_entry(cpu); |
389 | add_event_entry(IBS_EIP(cpu_buf->tail_pos)); | 374 | if (!sample) |
390 | add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); | 375 | goto Error; |
376 | add_event_entry(sample->eip); | ||
377 | add_event_entry(sample->event); | ||
391 | } | 378 | } |
379 | |||
380 | return; | ||
381 | |||
382 | Error: | ||
383 | return; | ||
392 | } | 384 | } |
393 | 385 | ||
394 | #endif | 386 | #endif |
@@ -466,33 +458,6 @@ static inline int is_code(unsigned long val) | |||
466 | } | 458 | } |
467 | 459 | ||
468 | 460 | ||
469 | /* "acquire" as many cpu buffer slots as we can */ | ||
470 | static unsigned long get_slots(struct oprofile_cpu_buffer *b) | ||
471 | { | ||
472 | unsigned long head = b->head_pos; | ||
473 | unsigned long tail = b->tail_pos; | ||
474 | |||
475 | /* | ||
476 | * Subtle. This resets the persistent last_task | ||
477 | * and in_kernel values used for switching notes. | ||
478 | * BUT, there is a small window between reading | ||
479 | * head_pos, and this call, that means samples | ||
480 | * can appear at the new head position, but not | ||
481 | * be prefixed with the notes for switching | ||
482 | * kernel mode or a task switch. This small hole | ||
483 | * can lead to mis-attribution or samples where | ||
484 | * we don't know if it's in the kernel or not, | ||
485 | * at the start of an event buffer. | ||
486 | */ | ||
487 | cpu_buffer_reset(b); | ||
488 | |||
489 | if (head >= tail) | ||
490 | return head - tail; | ||
491 | |||
492 | return head + (b->buffer_size - tail); | ||
493 | } | ||
494 | |||
495 | |||
496 | /* Move tasks along towards death. Any tasks on dead_tasks | 461 | /* Move tasks along towards death. Any tasks on dead_tasks |
497 | * will definitely have no remaining references in any | 462 | * will definitely have no remaining references in any |
498 | * CPU buffers at this point, because we use two lists, | 463 | * CPU buffers at this point, because we use two lists, |
@@ -559,61 +524,61 @@ typedef enum { | |||
559 | */ | 524 | */ |
560 | void sync_buffer(int cpu) | 525 | void sync_buffer(int cpu) |
561 | { | 526 | { |
562 | struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); | ||
563 | struct mm_struct *mm = NULL; | 527 | struct mm_struct *mm = NULL; |
528 | struct mm_struct *oldmm; | ||
564 | struct task_struct *new; | 529 | struct task_struct *new; |
565 | unsigned long cookie = 0; | 530 | unsigned long cookie = 0; |
566 | int in_kernel = 1; | 531 | int in_kernel = 1; |
567 | sync_buffer_state state = sb_buffer_start; | 532 | sync_buffer_state state = sb_buffer_start; |
568 | #ifndef CONFIG_OPROFILE_IBS | ||
569 | unsigned int i; | 533 | unsigned int i; |
570 | unsigned long available; | 534 | unsigned long available; |
571 | #endif | ||
572 | 535 | ||
573 | mutex_lock(&buffer_mutex); | 536 | mutex_lock(&buffer_mutex); |
574 | 537 | ||
575 | add_cpu_switch(cpu); | 538 | add_cpu_switch(cpu); |
576 | 539 | ||
577 | /* Remember, only we can modify tail_pos */ | 540 | cpu_buffer_reset(cpu); |
578 | 541 | available = cpu_buffer_entries(cpu); | |
579 | #ifndef CONFIG_OPROFILE_IBS | ||
580 | available = get_slots(cpu_buf); | ||
581 | 542 | ||
582 | for (i = 0; i < available; ++i) { | 543 | for (i = 0; i < available; ++i) { |
583 | #else | 544 | struct op_sample *s = cpu_buffer_read_entry(cpu); |
584 | while (get_slots(cpu_buf)) { | 545 | if (!s) |
585 | #endif | 546 | break; |
586 | struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos]; | ||
587 | 547 | ||
588 | if (is_code(s->eip)) { | 548 | if (is_code(s->eip)) { |
589 | if (s->event <= CPU_IS_KERNEL) { | 549 | switch (s->event) { |
550 | case 0: | ||
551 | case CPU_IS_KERNEL: | ||
590 | /* kernel/userspace switch */ | 552 | /* kernel/userspace switch */ |
591 | in_kernel = s->event; | 553 | in_kernel = s->event; |
592 | if (state == sb_buffer_start) | 554 | if (state == sb_buffer_start) |
593 | state = sb_sample_start; | 555 | state = sb_sample_start; |
594 | add_kernel_ctx_switch(s->event); | 556 | add_kernel_ctx_switch(s->event); |
595 | } else if (s->event == CPU_TRACE_BEGIN) { | 557 | break; |
558 | case CPU_TRACE_BEGIN: | ||
596 | state = sb_bt_start; | 559 | state = sb_bt_start; |
597 | add_trace_begin(); | 560 | add_trace_begin(); |
561 | break; | ||
598 | #ifdef CONFIG_OPROFILE_IBS | 562 | #ifdef CONFIG_OPROFILE_IBS |
599 | } else if (s->event == IBS_FETCH_BEGIN) { | 563 | case IBS_FETCH_BEGIN: |
600 | state = sb_bt_start; | 564 | state = sb_bt_start; |
601 | add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm); | 565 | add_ibs_begin(cpu, IBS_FETCH_CODE, mm); |
602 | } else if (s->event == IBS_OP_BEGIN) { | 566 | break; |
567 | case IBS_OP_BEGIN: | ||
603 | state = sb_bt_start; | 568 | state = sb_bt_start; |
604 | add_ibs_begin(cpu_buf, IBS_OP_CODE, mm); | 569 | add_ibs_begin(cpu, IBS_OP_CODE, mm); |
570 | break; | ||
605 | #endif | 571 | #endif |
606 | } else { | 572 | default: |
607 | struct mm_struct *oldmm = mm; | ||
608 | |||
609 | /* userspace context switch */ | 573 | /* userspace context switch */ |
574 | oldmm = mm; | ||
610 | new = (struct task_struct *)s->event; | 575 | new = (struct task_struct *)s->event; |
611 | |||
612 | release_mm(oldmm); | 576 | release_mm(oldmm); |
613 | mm = take_tasks_mm(new); | 577 | mm = take_tasks_mm(new); |
614 | if (mm != oldmm) | 578 | if (mm != oldmm) |
615 | cookie = get_exec_dcookie(mm); | 579 | cookie = get_exec_dcookie(mm); |
616 | add_user_ctx_switch(new, cookie); | 580 | add_user_ctx_switch(new, cookie); |
581 | break; | ||
617 | } | 582 | } |
618 | } else if (state >= sb_bt_start && | 583 | } else if (state >= sb_bt_start && |
619 | !add_sample(mm, s, in_kernel)) { | 584 | !add_sample(mm, s, in_kernel)) { |
@@ -622,8 +587,6 @@ void sync_buffer(int cpu) | |||
622 | atomic_inc(&oprofile_stats.bt_lost_no_mapping); | 587 | atomic_inc(&oprofile_stats.bt_lost_no_mapping); |
623 | } | 588 | } |
624 | } | 589 | } |
625 | |||
626 | increment_tail(cpu_buf); | ||
627 | } | 590 | } |
628 | release_mm(mm); | 591 | release_mm(mm); |
629 | 592 | ||