diff options
Diffstat (limited to 'kernel/perf_counter.c')
-rw-r--r-- | kernel/perf_counter.c | 53 |
1 files changed, 41 insertions, 12 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index 0422fd9bf627..d76e3112d386 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -75,8 +75,10 @@ list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx) | |||
75 | */ | 75 | */ |
76 | if (counter->group_leader == counter) | 76 | if (counter->group_leader == counter) |
77 | list_add_tail(&counter->list_entry, &ctx->counter_list); | 77 | list_add_tail(&counter->list_entry, &ctx->counter_list); |
78 | else | 78 | else { |
79 | list_add_tail(&counter->list_entry, &group_leader->sibling_list); | 79 | list_add_tail(&counter->list_entry, &group_leader->sibling_list); |
80 | group_leader->nr_siblings++; | ||
81 | } | ||
80 | 82 | ||
81 | list_add_rcu(&counter->event_entry, &ctx->event_list); | 83 | list_add_rcu(&counter->event_entry, &ctx->event_list); |
82 | } | 84 | } |
@@ -89,6 +91,9 @@ list_del_counter(struct perf_counter *counter, struct perf_counter_context *ctx) | |||
89 | list_del_init(&counter->list_entry); | 91 | list_del_init(&counter->list_entry); |
90 | list_del_rcu(&counter->event_entry); | 92 | list_del_rcu(&counter->event_entry); |
91 | 93 | ||
94 | if (counter->group_leader != counter) | ||
95 | counter->group_leader->nr_siblings--; | ||
96 | |||
92 | /* | 97 | /* |
93 | * If this was a group counter with sibling counters then | 98 | * If this was a group counter with sibling counters then |
94 | * upgrade the siblings to singleton counters by adding them | 99 | * upgrade the siblings to singleton counters by adding them |
@@ -381,9 +386,11 @@ static int is_software_only_group(struct perf_counter *leader) | |||
381 | 386 | ||
382 | if (!is_software_counter(leader)) | 387 | if (!is_software_counter(leader)) |
383 | return 0; | 388 | return 0; |
389 | |||
384 | list_for_each_entry(counter, &leader->sibling_list, list_entry) | 390 | list_for_each_entry(counter, &leader->sibling_list, list_entry) |
385 | if (!is_software_counter(counter)) | 391 | if (!is_software_counter(counter)) |
386 | return 0; | 392 | return 0; |
393 | |||
387 | return 1; | 394 | return 1; |
388 | } | 395 | } |
389 | 396 | ||
@@ -1480,6 +1487,9 @@ static void perf_output_copy(struct perf_output_handle *handle, | |||
1480 | handle->offset = offset; | 1487 | handle->offset = offset; |
1481 | } | 1488 | } |
1482 | 1489 | ||
1490 | #define perf_output_put(handle, x) \ | ||
1491 | perf_output_copy((handle), &(x), sizeof(x)) | ||
1492 | |||
1483 | static void perf_output_end(struct perf_output_handle *handle, int nmi) | 1493 | static void perf_output_end(struct perf_output_handle *handle, int nmi) |
1484 | { | 1494 | { |
1485 | if (handle->wakeup) { | 1495 | if (handle->wakeup) { |
@@ -1514,34 +1524,53 @@ out: | |||
1514 | static void perf_output_simple(struct perf_counter *counter, | 1524 | static void perf_output_simple(struct perf_counter *counter, |
1515 | int nmi, struct pt_regs *regs) | 1525 | int nmi, struct pt_regs *regs) |
1516 | { | 1526 | { |
1517 | u64 entry; | 1527 | struct { |
1528 | struct perf_event_header header; | ||
1529 | u64 ip; | ||
1530 | } event; | ||
1518 | 1531 | ||
1519 | entry = instruction_pointer(regs); | 1532 | event.header.type = PERF_EVENT_IP; |
1533 | event.header.size = sizeof(event); | ||
1534 | event.ip = instruction_pointer(regs); | ||
1520 | 1535 | ||
1521 | perf_output_write(counter, nmi, &entry, sizeof(entry)); | 1536 | perf_output_write(counter, nmi, &event, sizeof(event)); |
1522 | } | 1537 | } |
1523 | 1538 | ||
1524 | struct group_entry { | ||
1525 | u64 event; | ||
1526 | u64 counter; | ||
1527 | }; | ||
1528 | |||
1529 | static void perf_output_group(struct perf_counter *counter, int nmi) | 1539 | static void perf_output_group(struct perf_counter *counter, int nmi) |
1530 | { | 1540 | { |
1541 | struct perf_output_handle handle; | ||
1542 | struct perf_event_header header; | ||
1531 | struct perf_counter *leader, *sub; | 1543 | struct perf_counter *leader, *sub; |
1544 | unsigned int size; | ||
1545 | struct { | ||
1546 | u64 event; | ||
1547 | u64 counter; | ||
1548 | } entry; | ||
1549 | int ret; | ||
1550 | |||
1551 | size = sizeof(header) + counter->nr_siblings * sizeof(entry); | ||
1552 | |||
1553 | ret = perf_output_begin(&handle, counter, size); | ||
1554 | if (ret) | ||
1555 | return; | ||
1556 | |||
1557 | header.type = PERF_EVENT_GROUP; | ||
1558 | header.size = size; | ||
1559 | |||
1560 | perf_output_put(&handle, header); | ||
1532 | 1561 | ||
1533 | leader = counter->group_leader; | 1562 | leader = counter->group_leader; |
1534 | list_for_each_entry(sub, &leader->sibling_list, list_entry) { | 1563 | list_for_each_entry(sub, &leader->sibling_list, list_entry) { |
1535 | struct group_entry entry; | ||
1536 | |||
1537 | if (sub != counter) | 1564 | if (sub != counter) |
1538 | sub->hw_ops->read(sub); | 1565 | sub->hw_ops->read(sub); |
1539 | 1566 | ||
1540 | entry.event = sub->hw_event.config; | 1567 | entry.event = sub->hw_event.config; |
1541 | entry.counter = atomic64_read(&sub->count); | 1568 | entry.counter = atomic64_read(&sub->count); |
1542 | 1569 | ||
1543 | perf_output_write(counter, nmi, &entry, sizeof(entry)); | 1570 | perf_output_put(&handle, entry); |
1544 | } | 1571 | } |
1572 | |||
1573 | perf_output_end(&handle, nmi); | ||
1545 | } | 1574 | } |
1546 | 1575 | ||
1547 | void perf_counter_output(struct perf_counter *counter, | 1576 | void perf_counter_output(struct perf_counter *counter, |