diff options
Diffstat (limited to 'drivers/oprofile/buffer_sync.c')
-rw-r--r-- | drivers/oprofile/buffer_sync.c | 229 |
1 files changed, 81 insertions, 148 deletions
diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index b55cd23ffdef..ac014cb27915 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c | |||
@@ -1,11 +1,12 @@ | |||
1 | /** | 1 | /** |
2 | * @file buffer_sync.c | 2 | * @file buffer_sync.c |
3 | * | 3 | * |
4 | * @remark Copyright 2002 OProfile authors | 4 | * @remark Copyright 2002-2009 OProfile authors |
5 | * @remark Read the file COPYING | 5 | * @remark Read the file COPYING |
6 | * | 6 | * |
7 | * @author John Levon <levon@movementarian.org> | 7 | * @author John Levon <levon@movementarian.org> |
8 | * @author Barry Kasindorf | 8 | * @author Barry Kasindorf |
9 | * @author Robert Richter <robert.richter@amd.com> | ||
9 | * | 10 | * |
10 | * This is the core of the buffer management. Each | 11 | * This is the core of the buffer management. Each |
11 | * CPU buffer is processed and entered into the | 12 | * CPU buffer is processed and entered into the |
@@ -268,18 +269,6 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) | |||
268 | return cookie; | 269 | return cookie; |
269 | } | 270 | } |
270 | 271 | ||
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; | 272 | static unsigned long last_cookie = INVALID_COOKIE; |
284 | 273 | ||
285 | static void add_cpu_switch(int i) | 274 | static void add_cpu_switch(int i) |
@@ -327,84 +316,73 @@ static void add_trace_begin(void) | |||
327 | add_event_entry(TRACE_BEGIN_CODE); | 316 | add_event_entry(TRACE_BEGIN_CODE); |
328 | } | 317 | } |
329 | 318 | ||
330 | #ifdef CONFIG_OPROFILE_IBS | 319 | static void add_data(struct op_entry *entry, struct mm_struct *mm) |
331 | |||
332 | #define IBS_FETCH_CODE_SIZE 2 | ||
333 | #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 | |||
339 | /* | ||
340 | * Add IBS fetch and op entries to event buffer | ||
341 | */ | ||
342 | static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, | ||
343 | struct mm_struct *mm) | ||
344 | { | 320 | { |
345 | unsigned long rip; | 321 | unsigned long code, pc, val; |
346 | int i, count; | 322 | unsigned long cookie; |
347 | unsigned long ibs_cookie = 0; | ||
348 | off_t offset; | 323 | off_t offset; |
349 | 324 | ||
350 | increment_tail(cpu_buf); /* move to RIP entry */ | 325 | if (!op_cpu_buffer_get_data(entry, &code)) |
351 | 326 | return; | |
352 | rip = IBS_EIP(cpu_buf->tail_pos); | 327 | if (!op_cpu_buffer_get_data(entry, &pc)) |
353 | 328 | return; | |
354 | #ifdef __LP64__ | 329 | if (!op_cpu_buffer_get_size(entry)) |
355 | rip += IBS_EVENT(cpu_buf->tail_pos) << 32; | 330 | return; |
356 | #endif | ||
357 | 331 | ||
358 | if (mm) { | 332 | if (mm) { |
359 | ibs_cookie = lookup_dcookie(mm, rip, &offset); | 333 | cookie = lookup_dcookie(mm, pc, &offset); |
360 | 334 | ||
361 | if (ibs_cookie == NO_COOKIE) | 335 | if (cookie == NO_COOKIE) |
362 | offset = rip; | 336 | offset = pc; |
363 | if (ibs_cookie == INVALID_COOKIE) { | 337 | if (cookie == INVALID_COOKIE) { |
364 | atomic_inc(&oprofile_stats.sample_lost_no_mapping); | 338 | atomic_inc(&oprofile_stats.sample_lost_no_mapping); |
365 | offset = rip; | 339 | offset = pc; |
366 | } | 340 | } |
367 | if (ibs_cookie != last_cookie) { | 341 | if (cookie != last_cookie) { |
368 | add_cookie_switch(ibs_cookie); | 342 | add_cookie_switch(cookie); |
369 | last_cookie = ibs_cookie; | 343 | last_cookie = cookie; |
370 | } | 344 | } |
371 | } else | 345 | } else |
372 | offset = rip; | 346 | offset = pc; |
373 | 347 | ||
374 | add_event_entry(ESCAPE_CODE); | 348 | add_event_entry(ESCAPE_CODE); |
375 | add_event_entry(code); | 349 | add_event_entry(code); |
376 | add_event_entry(offset); /* Offset from Dcookie */ | 350 | add_event_entry(offset); /* Offset from Dcookie */ |
377 | 351 | ||
378 | /* we send the Dcookie offset, but send the raw Linear Add also*/ | 352 | while (op_cpu_buffer_get_data(entry, &val)) |
379 | add_event_entry(IBS_EIP(cpu_buf->tail_pos)); | 353 | add_event_entry(val); |
380 | add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); | ||
381 | |||
382 | if (code == IBS_FETCH_CODE) | ||
383 | count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ | ||
384 | else | ||
385 | count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ | ||
386 | |||
387 | for (i = 0; i < count; i++) { | ||
388 | increment_tail(cpu_buf); | ||
389 | add_event_entry(IBS_EIP(cpu_buf->tail_pos)); | ||
390 | add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); | ||
391 | } | ||
392 | } | 354 | } |
393 | 355 | ||
394 | #endif | 356 | static inline void add_sample_entry(unsigned long offset, unsigned long event) |
395 | |||
396 | static void add_sample_entry(unsigned long offset, unsigned long event) | ||
397 | { | 357 | { |
398 | add_event_entry(offset); | 358 | add_event_entry(offset); |
399 | add_event_entry(event); | 359 | add_event_entry(event); |
400 | } | 360 | } |
401 | 361 | ||
402 | 362 | ||
403 | static int add_us_sample(struct mm_struct *mm, struct op_sample *s) | 363 | /* |
364 | * Add a sample to the global event buffer. If possible the | ||
365 | * sample is converted into a persistent dentry/offset pair | ||
366 | * for later lookup from userspace. Return 0 on failure. | ||
367 | */ | ||
368 | static int | ||
369 | add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) | ||
404 | { | 370 | { |
405 | unsigned long cookie; | 371 | unsigned long cookie; |
406 | off_t offset; | 372 | off_t offset; |
407 | 373 | ||
374 | if (in_kernel) { | ||
375 | add_sample_entry(s->eip, s->event); | ||
376 | return 1; | ||
377 | } | ||
378 | |||
379 | /* add userspace sample */ | ||
380 | |||
381 | if (!mm) { | ||
382 | atomic_inc(&oprofile_stats.sample_lost_no_mm); | ||
383 | return 0; | ||
384 | } | ||
385 | |||
408 | cookie = lookup_dcookie(mm, s->eip, &offset); | 386 | cookie = lookup_dcookie(mm, s->eip, &offset); |
409 | 387 | ||
410 | if (cookie == INVALID_COOKIE) { | 388 | if (cookie == INVALID_COOKIE) { |
@@ -423,25 +401,6 @@ static int add_us_sample(struct mm_struct *mm, struct op_sample *s) | |||
423 | } | 401 | } |
424 | 402 | ||
425 | 403 | ||
426 | /* Add a sample to the global event buffer. If possible the | ||
427 | * sample is converted into a persistent dentry/offset pair | ||
428 | * for later lookup from userspace. | ||
429 | */ | ||
430 | static int | ||
431 | add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) | ||
432 | { | ||
433 | if (in_kernel) { | ||
434 | add_sample_entry(s->eip, s->event); | ||
435 | return 1; | ||
436 | } else if (mm) { | ||
437 | return add_us_sample(mm, s); | ||
438 | } else { | ||
439 | atomic_inc(&oprofile_stats.sample_lost_no_mm); | ||
440 | } | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | |||
445 | static void release_mm(struct mm_struct *mm) | 404 | static void release_mm(struct mm_struct *mm) |
446 | { | 405 | { |
447 | if (!mm) | 406 | if (!mm) |
@@ -466,33 +425,6 @@ static inline int is_code(unsigned long val) | |||
466 | } | 425 | } |
467 | 426 | ||
468 | 427 | ||
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 | 428 | /* Move tasks along towards death. Any tasks on dead_tasks |
497 | * will definitely have no remaining references in any | 429 | * will definitely have no remaining references in any |
498 | * CPU buffers at this point, because we use two lists, | 430 | * CPU buffers at this point, because we use two lists, |
@@ -559,71 +491,72 @@ typedef enum { | |||
559 | */ | 491 | */ |
560 | void sync_buffer(int cpu) | 492 | void sync_buffer(int cpu) |
561 | { | 493 | { |
562 | struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); | ||
563 | struct mm_struct *mm = NULL; | 494 | struct mm_struct *mm = NULL; |
495 | struct mm_struct *oldmm; | ||
496 | unsigned long val; | ||
564 | struct task_struct *new; | 497 | struct task_struct *new; |
565 | unsigned long cookie = 0; | 498 | unsigned long cookie = 0; |
566 | int in_kernel = 1; | 499 | int in_kernel = 1; |
567 | sync_buffer_state state = sb_buffer_start; | 500 | sync_buffer_state state = sb_buffer_start; |
568 | #ifndef CONFIG_OPROFILE_IBS | ||
569 | unsigned int i; | 501 | unsigned int i; |
570 | unsigned long available; | 502 | unsigned long available; |
571 | #endif | 503 | unsigned long flags; |
504 | struct op_entry entry; | ||
505 | struct op_sample *sample; | ||
572 | 506 | ||
573 | mutex_lock(&buffer_mutex); | 507 | mutex_lock(&buffer_mutex); |
574 | 508 | ||
575 | add_cpu_switch(cpu); | 509 | add_cpu_switch(cpu); |
576 | 510 | ||
577 | /* Remember, only we can modify tail_pos */ | 511 | op_cpu_buffer_reset(cpu); |
578 | 512 | available = op_cpu_buffer_entries(cpu); | |
579 | #ifndef CONFIG_OPROFILE_IBS | ||
580 | available = get_slots(cpu_buf); | ||
581 | 513 | ||
582 | for (i = 0; i < available; ++i) { | 514 | for (i = 0; i < available; ++i) { |
583 | #else | 515 | sample = op_cpu_buffer_read_entry(&entry, cpu); |
584 | while (get_slots(cpu_buf)) { | 516 | if (!sample) |
585 | #endif | 517 | break; |
586 | struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos]; | ||
587 | 518 | ||
588 | if (is_code(s->eip)) { | 519 | if (is_code(sample->eip)) { |
589 | if (s->event <= CPU_IS_KERNEL) { | 520 | flags = sample->event; |
521 | if (flags & TRACE_BEGIN) { | ||
522 | state = sb_bt_start; | ||
523 | add_trace_begin(); | ||
524 | } | ||
525 | if (flags & KERNEL_CTX_SWITCH) { | ||
590 | /* kernel/userspace switch */ | 526 | /* kernel/userspace switch */ |
591 | in_kernel = s->event; | 527 | in_kernel = flags & IS_KERNEL; |
592 | if (state == sb_buffer_start) | 528 | if (state == sb_buffer_start) |
593 | state = sb_sample_start; | 529 | state = sb_sample_start; |
594 | add_kernel_ctx_switch(s->event); | 530 | add_kernel_ctx_switch(flags & IS_KERNEL); |
595 | } else if (s->event == CPU_TRACE_BEGIN) { | 531 | } |
596 | state = sb_bt_start; | 532 | if (flags & USER_CTX_SWITCH |
597 | add_trace_begin(); | 533 | && op_cpu_buffer_get_data(&entry, &val)) { |
598 | #ifdef CONFIG_OPROFILE_IBS | ||
599 | } else if (s->event == IBS_FETCH_BEGIN) { | ||
600 | state = sb_bt_start; | ||
601 | add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm); | ||
602 | } else if (s->event == IBS_OP_BEGIN) { | ||
603 | state = sb_bt_start; | ||
604 | add_ibs_begin(cpu_buf, IBS_OP_CODE, mm); | ||
605 | #endif | ||
606 | } else { | ||
607 | struct mm_struct *oldmm = mm; | ||
608 | |||
609 | /* userspace context switch */ | 534 | /* userspace context switch */ |
610 | new = (struct task_struct *)s->event; | 535 | new = (struct task_struct *)val; |
611 | 536 | oldmm = mm; | |
612 | release_mm(oldmm); | 537 | release_mm(oldmm); |
613 | mm = take_tasks_mm(new); | 538 | mm = take_tasks_mm(new); |
614 | if (mm != oldmm) | 539 | if (mm != oldmm) |
615 | cookie = get_exec_dcookie(mm); | 540 | cookie = get_exec_dcookie(mm); |
616 | add_user_ctx_switch(new, cookie); | 541 | add_user_ctx_switch(new, cookie); |
617 | } | 542 | } |
618 | } else if (state >= sb_bt_start && | 543 | if (op_cpu_buffer_get_size(&entry)) |
619 | !add_sample(mm, s, in_kernel)) { | 544 | add_data(&entry, mm); |
620 | if (state == sb_bt_start) { | 545 | continue; |
621 | state = sb_bt_ignore; | ||
622 | atomic_inc(&oprofile_stats.bt_lost_no_mapping); | ||
623 | } | ||
624 | } | 546 | } |
625 | 547 | ||
626 | increment_tail(cpu_buf); | 548 | if (state < sb_bt_start) |
549 | /* ignore sample */ | ||
550 | continue; | ||
551 | |||
552 | if (add_sample(mm, sample, in_kernel)) | ||
553 | continue; | ||
554 | |||
555 | /* ignore backtraces if failed to add a sample */ | ||
556 | if (state == sb_bt_start) { | ||
557 | state = sb_bt_ignore; | ||
558 | atomic_inc(&oprofile_stats.bt_lost_no_mapping); | ||
559 | } | ||
627 | } | 560 | } |
628 | release_mm(mm); | 561 | release_mm(mm); |
629 | 562 | ||