aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/perf_counter.h11
-rw-r--r--kernel/perf_counter.c53
2 files changed, 52 insertions, 12 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h
index 48212c15b7d6..c256635377d4 100644
--- a/include/linux/perf_counter.h
+++ b/include/linux/perf_counter.h
@@ -156,6 +156,16 @@ struct perf_counter_mmap_page {
156 __u32 data_head; /* head in the data section */ 156 __u32 data_head; /* head in the data section */
157}; 157};
158 158
159struct perf_event_header {
160 __u32 type;
161 __u32 size;
162};
163
164enum perf_event_type {
165 PERF_EVENT_IP = 0,
166 PERF_EVENT_GROUP = 1,
167};
168
159#ifdef __KERNEL__ 169#ifdef __KERNEL__
160/* 170/*
161 * Kernel-internal data types and definitions: 171 * Kernel-internal data types and definitions:
@@ -260,6 +270,7 @@ struct perf_counter {
260 struct list_head list_entry; 270 struct list_head list_entry;
261 struct list_head event_entry; 271 struct list_head event_entry;
262 struct list_head sibling_list; 272 struct list_head sibling_list;
273 int nr_siblings;
263 struct perf_counter *group_leader; 274 struct perf_counter *group_leader;
264 const struct hw_perf_counter_ops *hw_ops; 275 const struct hw_perf_counter_ops *hw_ops;
265 276
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
1483static void perf_output_end(struct perf_output_handle *handle, int nmi) 1493static 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:
1514static void perf_output_simple(struct perf_counter *counter, 1524static 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
1524struct group_entry {
1525 u64 event;
1526 u64 counter;
1527};
1528
1529static void perf_output_group(struct perf_counter *counter, int nmi) 1539static 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
1547void perf_counter_output(struct perf_counter *counter, 1576void perf_counter_output(struct perf_counter *counter,