Unlike normal hardware PMCs, the 24x7 counters in Power8 are stored in
memory and accessed via a hypervisor call (HCALL). A major aspect of the
HCALL is that it allows retireving _several_ counters at once (unlike
regular PMCs, which are read one at a time). By reading several counters
at once, we can get a more consistent snapshot of the system.
This patchset extends the transaction interface to accomplish submitting
several events to the PMU and have the PMU read them all at once. User is
expected to submit the set of events they want to read as an "event group".
In the kernel, we submit each event to the PMU using the following logic
(from Peter Zijlstra).
pmu->start_txn(pmu, PMU_TXN_READ);
leader->read();
for_each_sibling()
sibling->read();
pmu->commit_txn();
where:
- the ->read()s queue events to be submitted to the hypervisor, and,
- the ->commit_txn() issues the HCALL, retrieves the result and
updates the event count.
Architectures/PMUs that don't need/implement PMU_TXN_READ type of transactions,
simply ignore the ->start_txn() and ->commit_txn() and continue to read the
counters one at a time in the ->read() call.
Compile/touch tested on x86. Need help testing on s390 and Sparc.
Thanks to Peter Zijlstra for his input/code.
Changelog[v4]
- Ensure all the transactions operations happen on the same CPU so PMUs
can use per-CPU buffers for the transaction.
- Add lockdep assert and fix a locking issue in perf_read_group().
Changelog [v3]
- Simple changes/reorg of patchset to split/rename functions
- [Peter Zijlstra] Save the transaction flags in ->start_txn() and
drop the flags parameter from ->commit_txn() and ->cancel_txn().
- [Peter Zijlstra] The nop txn interfaces don't need to disable/enable
PMU for PERF_PMU_TXN_READ transactions.
Changelog [v2]
- Use the transaction interface unconditionally to avoid special-case
code. Architectures/PMUs that don't need the READ transaction types
simply ignore the ->start_txn() and ->commit_txn() calls.
Peter Zijlstra (Intel) (1):
perf: Rename perf_event_read_{one,group}, perf_read_hw
Sukadev Bhattiprolu (9):
perf: Add a flags parameter to pmu txn interfaces
perf: Split perf_event_read() and perf_event_count()
perf: Define perf_event_aggregate()
perf: Unroll perf_event_read_value() in perf_read_group()
perf: Add return value for perf_event_read().
perf: Add group parameter to perf_event_read()
perf: Add return value to __perf_event_read()
Define PERF_PMU_TXN_READ interface
powerpc/perf/hv-24x7: Use PMU_TXN_READ interface
arch/powerpc/perf/core-book3s.c | 25 +++++-
arch/powerpc/perf/hv-24x7.c | 166 ++++++++++++++++++++++++++++++++++++-
arch/s390/kernel/perf_cpum_cf.c | 24 +++++-
arch/sparc/kernel/perf_event.c | 19 ++++-
arch/x86/kernel/cpu/perf_event.c | 27 +++++-
arch/x86/kernel/cpu/perf_event.h | 1 +
include/linux/perf_event.h | 15 +++-
kernel/events/core.c | 167 +++++++++++++++++++++++++++++++-------
8 files changed, 403 insertions(+), 41 deletions(-)
--
1.7.9.5
Move the part of perf_event_read_value() that aggregates the event
counts and event times into a new function, perf_event_aggregate().
This would allow us to call perf_event_aggregate() independently.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
Changelog[v4]
[Peter Zijlstra] Add missing lockdep_assert(). Rename
perf_event_compute() (to perf_event_aggregate()).
Changelog[v3]
Rather than move perf_event_read() into callers and then
rename, just move the computations into a separate function
(redesign to address comment from Peter Zijlstra).
---
kernel/events/core.c | 39 ++++++++++++++++++++++++++-------------
1 file changed, 26 insertions(+), 13 deletions(-)
perf_event_read() does two things:
- call the PMU to read/update the counter value, and
- compute the total count of the event and its children
Not all callers need both. perf_event_reset() for instance needs the
first piece but doesn't need the second. Similarly, when we implement
the ability to read a group of events using the transaction interface,
we would need the two pieces done independently.
Break up perf_event_read() and have it just read/update the counter
and have the callers compute the total count if necessary.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
kernel/events/core.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
Currently, the PMU interface allows reading only one counter at a time.
But some PMUs like the 24x7 counters in Power, support reading several
counters at once. To leveage this functionality, extend the transaction
interface to support a "transaction type".
The first type, PERF_PMU_TXN_ADD, refers to the existing transactions,
i.e. used to _schedule_ all the events on the PMU as a group. A second
transaction type, PERF_PMU_TXN_READ, will be used in a follow-on patch,
by the 24x7 counters to read several counters at once.
Extend the transaction interfaces to the PMU to accept a 'txn_flags'
parameter and use this parameter to ignore any transactions that are
not of type PERF_PMU_TXN_ADD.
Thanks to Peter Zijlstra for his input.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
Changelog[v4]
- [Peter Zijlstra] Fix an copy-paste error in power_pmu_cancel_txn().
- [Peter Zijlstra] Use __this_cpu_read() and __this_cpu_write().
Changelog[v3]
- [Peter Zijlstra] Ensure the nop_txn interfaces disable/enable
PMU only for TXN_ADD transactions.
- [Peter Zijlstra] Cache the flags parameter in ->start_txn() and
drop the flags parameter from ->commit_txn() and ->cancel_txn().
---
arch/powerpc/perf/core-book3s.c | 25 ++++++++++++++++++++++++-
arch/s390/kernel/perf_cpum_cf.c | 24 +++++++++++++++++++++++-
arch/sparc/kernel/perf_event.c | 19 ++++++++++++++++++-
arch/x86/kernel/cpu/perf_event.c | 27 +++++++++++++++++++++++++--
arch/x86/kernel/cpu/perf_event.h | 1 +
include/linux/perf_event.h | 14 +++++++++++---
kernel/events/core.c | 31 ++++++++++++++++++++++++++++---
7 files changed, 130 insertions(+), 11 deletions(-)
@@ -604,9 +620,15 @@ static void cpumf_pmu_cancel_txn(struct pmu *pmu)*/staticintcpumf_pmu_commit_txn(structpmu*pmu){+inttxn_flags;structcpu_hw_events*cpuhw=this_cpu_ptr(&cpu_hw_events);u64state;+txn_flags=cpuhw->txn_flags;+cpuhw->txn_flags=0;+if(txn_flags&~PERF_PMU_TXN_ADD)+return0;+/* check if the updated state can be scheduled */state=cpuhw->state&~((1<<CPUMF_LCCTL_ENABLE_SHIFT)-1);state>>=CPUMF_LCCTL_ENABLE_SHIFT;
Unroll the calls to perf_event_read_value() in perf_read_group()
so we can later optimize out parts we don't need for group events.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
kernel/events/core.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
Define a new PERF_PMU_TXN_READ interface to read a group of counters
at once.
pmu->start_txn() // Initialize before first event
for each event in group
pmu->read(event); // Queue each event to be read
pmu->commit_txn() // Read/update all queued counters
Note that we use this interface with all PMUs. PMUs that implement this
interface use the ->read() operation to _queue_ the counters to be read
and use ->commit_txn() to actually read all the queued counters at once.
PMUs that don't implement PERF_PMU_TXN_READ ignore ->start_txn() and
->commit_txn() and continue to read counters one at a time.
Thanks to input from Peter Zijlstra.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
Changelog[v4]
[Peter Zijlstra] Add lockdep_assert_held() in perf_event_read_group().
Make sure the entire transaction happens on the same CPU.
---
include/linux/perf_event.h | 1 +
kernel/events/core.c | 38 +++++++++++++++++++++++++++++++-------
2 files changed, 32 insertions(+), 7 deletions(-)
The 24x7 counters in Powerpc allow monitoring a large number of counters
simultaneously. They also allow reading several counters in a single
HCALL so we can get a more consistent snapshot of the system.
Use the PMU's transaction interface to monitor and read several event
counters at once. The idea is that users can group several 24x7 events
into a single group of events. We use the following logic to submit
the group of events to the PMU and read the values:
pmu->start_txn() // Initialize before first event
for each event in group
pmu->read(event); // Queue each event to be read
pmu->commit_txn() // Read/update all queuedcounters
The ->commit_txn() also updates the event counts in the respective
perf_event objects. The perf subsystem can then directly get the
event counts from the perf_event and can avoid submitting a new
->read() request to the PMU.
Thanks to input from Peter Zijlstra.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
Changelog[v3]
[Peter Zijlstra] Save the transaction state in ->start_txn() and
remove the flags parameter from ->commit_txn() and ->cancel_txn().
---
arch/powerpc/perf/hv-24x7.c | 166 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 164 insertions(+), 2 deletions(-)
@@ -1257,6 +1305,117 @@ static int h_24x7_event_add(struct perf_event *event, int flags)return0;}+/*+*24x7countersonlysupportREADtransactions.Theyare+*alwayscountinganddontneed/supportADDtransactions.+*Cachetheflags,butotherwiseignoretransactionsthat+*arenotPERF_PMU_TXN_READ.+*/+staticvoidh_24x7_event_start_txn(structpmu*pmu,intflags)+{+structhv_24x7_request_buffer*request_buffer;+structhv_24x7_data_result_buffer*result_buffer;++/* We should not be called if we are already in a txn */+WARN_ON_ONCE(__this_cpu_read(hv_24x7_txn_flags));++__this_cpu_write(hv_24x7_txn_flags,flags);+if(flags&~PERF_PMU_TXN_READ)+return;++request_buffer=(void*)get_cpu_var(hv_24x7_reqb);+result_buffer=(void*)get_cpu_var(hv_24x7_resb);++init_24x7_request(request_buffer,result_buffer);++put_cpu_var(hv_24x7_resb);+put_cpu_var(hv_24x7_reqb);+}++/*+*Cleanuptransactionstate.+*+*NOTE:Ignorestateofrequestandresultbuffersfornow.+*Wewillinitializethemduringthenextread/txn.+*/+staticvoidreset_txn(void)+{+__this_cpu_write(hv_24x7_txn_flags,0);+__this_cpu_write(hv_24x7_txn_err,0);+}++/*+*24x7countersonlysupportREADtransactions.Theyarealwayscounting+*anddontneed/supportADDtransactions.Clear->txn_flagsbutotherwise+*ignoretransactionsthatarenotoftypePERF_PMU_TXN_READ.+*+*ForREADtransactions,submitallpending24x7requests(i.erequests+*thatwerequeuedbyh_24x7_event_read()),tothehypervisorandupdate+*theeventcounts.+*/+staticinth_24x7_event_commit_txn(structpmu*pmu)+{+structhv_24x7_request_buffer*request_buffer;+structhv_24x7_data_result_buffer*result_buffer;+structhv_24x7_result*resb;+structperf_event*event;+u64count;+inti,ret,txn_flags;+structhv_24x7_hw*h24x7hw;++txn_flags=__this_cpu_read(hv_24x7_txn_flags);+WARN_ON_ONCE(!txn_flags);++ret=0;+if(txn_flags&~PERF_PMU_TXN_READ)+gotoout;++ret=__this_cpu_read(hv_24x7_txn_err);+if(ret)+gotoout;++request_buffer=(void*)get_cpu_var(hv_24x7_reqb);+result_buffer=(void*)get_cpu_var(hv_24x7_resb);++ret=make_24x7_request(request_buffer,result_buffer);+if(ret){+log_24x7_hcall(request_buffer,result_buffer,ret);+gotoput_reqb;+}++h24x7hw=&get_cpu_var(hv_24x7_hw);++/* Update event counts from hcall */+for(i=0;i<request_buffer->num_requests;i++){+resb=&result_buffer->results[i];+count=be64_to_cpu(resb->elements[0].element_data[0]);+event=h24x7hw->events[i];+h24x7hw->events[i]=NULL;+update_event_count(event,count);+}++put_cpu_var(hv_24x7_hw);++put_reqb:+put_cpu_var(hv_24x7_resb);+put_cpu_var(hv_24x7_reqb);+out:+reset_txn();+returnret;+}++/*+*24x7countersonlysupportREADtransactions.Theyarealwayscounting+*anddontneed/supportADDtransactions.However,regardlessoftype+*oftransaction,allweneedtodoiscleanup,sowedon'thavetocheck+*thetypeoftransaction.+*/+staticvoidh_24x7_event_cancel_txn(structpmu*pmu)+{+WARN_ON_ONCE(!__this_cpu_read(hv_24x7_txn_flags));+reset_txn();+}+staticstructpmuh_24x7_pmu={.task_ctx_nr=perf_invalid_context,
Add a 'group' parameter to perf_event_read(). It will be used (set
to true) in a follow-on patch to update event times of the group.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
kernel/events/core.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
Add a return value to __perf_event_read(). The return value will be
needed later in perf_read_group() implements ability to read several
counters in a PERF_PMU_TXN_READ transaction.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
kernel/events/core.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
Add a return value to perf_event_read(). The return value will be
needed later in perf_read_group() implements ability to read several
counters in a PERF_PMU_TXN_READ transaction.
Signed-off-by: Sukadev Bhattiprolu <redacted>
---
kernel/events/core.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
From: Peter Zijlstra <peterz@infradead.org> Date: 2015-08-06 12:10:43
On Sun, Jul 26, 2015 at 10:40:37PM -0700, Sukadev Bhattiprolu wrote:
quoted hunk
@@ -3743,7 +3762,13 @@ static u64 perf_event_aggregate(struct perf_event *event, u64 *enabled, lockdep_assert_held(&event->child_mutex); list_for_each_entry(child, &event->child_list, child_list) {+#if 0+ /*+ * TODO: Do we need this read() for group events on PMUs that+ * don't implement PERF_PMU_TXN_READ transactions?+ */ (void)perf_event_read(child, false);+#endif total += perf_event_count(child); *enabled += child->total_time_enabled; *running += child->total_time_running;
Aw gawd, I've been an idiot!!
I just realized this is a _CHILD_ loop, not a _SIBLING_ loop !!
We need to flip the loops in perf_read_group(), find attached two
patches that go on top of 1,2,4.
After this you can add the perf_event_read() return value (just fold
patches 6,8) after which you can do patch 10 (which has a broken
Subject fwiw).
From: Peter Zijlstra <peterz@infradead.org> Date: 2015-08-12 08:45:58
On Tue, Aug 11, 2015 at 09:14:00PM -0700, Sukadev Bhattiprolu wrote:
| +static void __perf_read_group_add(struct perf_event *leader, u64 read_format, u64 *values)
| {
| + struct perf_event *sub;
| + int n = 1; /* skip @nr */
This n = 1 is to skip over the values[0] = 1 + nr_siblings in the
caller.
Anyway, in __perf_read_group_add() we always start with n = 1, however
...
|
| + perf_event_read(leader, true);
| +
| + /*
| + * Since we co-schedule groups, {enabled,running} times of siblings
| + * will be identical to those of the leader, so we only publish one
| + * set.
| + */
| + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
| + values[n++] += leader->total_time_enabled +
| + atomic64_read(leader->child_total_time_enabled);
| + }
| +}
|
| +static int perf_read_group(struct perf_event *event,
| + u64 read_format, char __user *buf)
| +{
| + struct perf_event *leader = event->group_leader, *child;
| + struct perf_event_context *ctx = leader->ctx;
| + int ret = leader->read_size;
| + u64 *values;
|
| + lockdep_assert_held(&ctx->mutex);
|
| + values = kzalloc(event->read_size);
| + if (!values)
| + return -ENOMEM;
|
| + values[0] = 1 + leader->nr_siblings;
|
| + /*
| + * By locking the child_mutex of the leader we effectively
| + * lock the child list of all siblings.. XXX explain how.
| + */
| + mutex_lock(&leader->child_mutex);
|
| + __perf_read_group_add(leader, read_format, values);
... we don't copy_to_user() here,
| + list_for_each_entry(child, &leader->child_list, child_list)
| + __perf_read_group_add(child, read_format, values);
so won't we overwrite the values[], if we always start at n = 1
in __perf_read_group_add()?
yes and no, we have to re-iterate the same values for each child as they
all have the same group, but we add the time and count fields, we do not
overwrite. The _add() suffix was supposed to be a hint ;-)
Where previously we would iterate the group and for each member
iterate/sum all the child values together before copying the value out,
we now, because we need to read groups together, need to first iterate
the child list and sum whole groups.
Peter Zijlstra [peterz@infradead.org] wrote:
| On Tue, Aug 11, 2015 at 09:14:00PM -0700, Sukadev Bhattiprolu wrote:
| > | +static void __perf_read_group_add(struct perf_event *leader, u64 read_format, u64 *values)
| > | {
| > | + struct perf_event *sub;
| > | + int n = 1; /* skip @nr */
| >
| > This n = 1 is to skip over the values[0] = 1 + nr_siblings in the
| > caller.
| >
| > Anyway, in __perf_read_group_add() we always start with n = 1, however
| > ...
| > |
| > | + perf_event_read(leader, true);
| > | +
| > | + /*
| > | + * Since we co-schedule groups, {enabled,running} times of siblings
| > | + * will be identical to those of the leader, so we only publish one
| > | + * set.
| > | + */
| > | + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
| > | + values[n++] += leader->total_time_enabled +
| > | + atomic64_read(leader->child_total_time_enabled);
|
| Note how this is an in-place addition,
Ah, yes, Sorry I missed that. It make sense now and my tests seem to
be running fine.
|
| > | + }
| > |
| > | + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
| > | + values[n++] += leader->total_time_running +
| > | + atomic64_read(leader->child_total_time_running);
|
| and here,
|
| > | + }
| > |
| > | + /*
| > | + * Write {count,id} tuples for every sibling.
| > | + */
| > | + values[n++] += perf_event_count(leader);
|
| and here,
|
|
| > | if (read_format & PERF_FORMAT_ID)
| > | values[n++] = primary_event_id(leader);
|
| and this will always assign the same value.
|
| > | + list_for_each_entry(sub, &leader->sibling_list, group_entry) {
| > | + values[n++] += perf_event_count(sub);
| > | + if (read_format & PERF_FORMAT_ID)
| > | + values[n++] = primary_event_id(sub);
|
| Same for these, therefore,
|
| > | + }
| > | +}
| > |
| > | +static int perf_read_group(struct perf_event *event,
| > | + u64 read_format, char __user *buf)
| > | +{
| > | + struct perf_event *leader = event->group_leader, *child;
| > | + struct perf_event_context *ctx = leader->ctx;
| > | + int ret = leader->read_size;
One other question, We return leader->read_size but allocate/copy_to_user
the sibling's event->read_size. We consistently use read_format from the
'event' being read, rather than its 'group_leader', so we are ok in terms
of what we copy into values[] for each event in the group.
But, can the leader's read_format (and hence its read_size) differ from
its sibling's read_size? If so, in the current code, we return the event's
read_size but in the new code, we return the leader's read_size.
| > | + u64 *values;
| > |
| > | + lockdep_assert_held(&ctx->mutex);
| > |
| > | + values = kzalloc(event->read_size);
| > | + if (!values)
| > | + return -ENOMEM;
| > |
| > | + values[0] = 1 + leader->nr_siblings;
| > |
| > | + /*
| > | + * By locking the child_mutex of the leader we effectively
| > | + * lock the child list of all siblings.. XXX explain how.
| > | + */
| > | + mutex_lock(&leader->child_mutex);
| > |
| > | + __perf_read_group_add(leader, read_format, values);
| >
| > ... we don't copy_to_user() here,
| >
| > | + list_for_each_entry(child, &leader->child_list, child_list)
| > | + __perf_read_group_add(child, read_format, values);
| >
| > so won't we overwrite the values[], if we always start at n = 1
| > in __perf_read_group_add()?
|
| yes and no, we have to re-iterate the same values for each child as they
| all have the same group, but we add the time and count fields, we do not
| overwrite. The _add() suffix was supposed to be a hint ;-)
|
| > | + mutex_unlock(&leader->child_mutex);
| > | +
| > | + if (copy_to_user(buf, values, event->read_size))
| > | + ret = -EFAULT;
| > | +
| > | + kfree(values);
| > |
| > | return ret;
| > | }
|
| Where previously we would iterate the group and for each member
| iterate/sum all the child values together before copying the value out,
| we now, because we need to read groups together, need to first iterate
| the child list and sum whole groups.
One other question, We return leader->read_size but allocate/copy_to_user
the sibling's event->read_size. We consistently use read_format from the
'event' being read, rather than its 'group_leader', so we are ok in terms
of what we copy into values[] for each event in the group.
But, can the leader's read_format (and hence its read_size) differ from
its sibling's read_size? If so, in the current code, we return the event's
read_size but in the new code, we return the leader's read_size.
Hmm, good spotting that. I'm fairly sure I didn't do that on purpose.
I think we should use event->read_size there too and have the lot
consistent. I don't think we require read_format to be uniform across
siblings.