perf_event_open: Add support for counting REF_CPU_CYCLES
Using thread's tsc count instead of performance counter Refs: #1025 Change-Id: I1d7a18f1c52f1d52087002d31818638a6b206014
This commit is contained in:
committed by
Masamichi Takagi
parent
bc06d68d84
commit
309145587f
218
kernel/syscall.c
218
kernel/syscall.c
@@ -3767,10 +3767,48 @@ unsigned long perf_event_read_value(struct mc_perf_event *event)
|
||||
unsigned long rtn_count = 0;
|
||||
unsigned long pmc_count = 0;
|
||||
int counter_id = event->counter_id;
|
||||
struct thread *thread = cpu_local_var(current);
|
||||
unsigned long cur_user_tsc, cur_system_tsc;
|
||||
|
||||
if (event->stopped_user_tsc) {
|
||||
cur_user_tsc = event->stopped_user_tsc;
|
||||
}
|
||||
else {
|
||||
cur_user_tsc = thread->user_tsc;
|
||||
}
|
||||
|
||||
if (event->stopped_system_tsc) {
|
||||
cur_system_tsc = event->stopped_system_tsc;
|
||||
}
|
||||
else {
|
||||
cur_system_tsc = thread->system_tsc;
|
||||
}
|
||||
|
||||
/* -- For use_invariant_tsc --
|
||||
* Add sum of counts in the previous start-stop periods to
|
||||
* the current count in the start-read period
|
||||
*/
|
||||
if(event->pid == 0) {
|
||||
pmc_count = ihk_mc_perfctr_value(counter_id,
|
||||
event->attr.sample_freq);
|
||||
if (event->use_invariant_tsc) {
|
||||
if (!event->attr.exclude_user) {
|
||||
pmc_count += cur_user_tsc -
|
||||
event->base_user_tsc +
|
||||
event->user_accum_count;
|
||||
}
|
||||
if (!event->attr.exclude_kernel) {
|
||||
/* Add sum of counts in the previous
|
||||
* start-stop periods to the current count
|
||||
* in the start-read period
|
||||
*/
|
||||
pmc_count += cur_system_tsc -
|
||||
event->base_system_tsc +
|
||||
event->system_accum_count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
pmc_count = ihk_mc_perfctr_read(counter_id) +
|
||||
event->attr.sample_freq;
|
||||
}
|
||||
}
|
||||
|
||||
rtn_count += event->count + pmc_count;
|
||||
@@ -3787,14 +3825,14 @@ perf_event_read_group(struct mc_perf_event *event, unsigned long read_format, ch
|
||||
struct mc_perf_event *leader = event->group_leader, *sub;
|
||||
int n = 0, size = 0, ret;
|
||||
unsigned long count;
|
||||
unsigned long values[5];
|
||||
long long values[5];
|
||||
|
||||
count = perf_event_read_value(leader);
|
||||
|
||||
values[n++] = 1 + leader->nr_siblings;
|
||||
values[n++] = count;
|
||||
|
||||
size = n * sizeof(unsigned long);
|
||||
size = n * sizeof(long long);
|
||||
|
||||
if (copy_to_user(buf, values, size))
|
||||
return -EFAULT;
|
||||
@@ -3805,7 +3843,7 @@ perf_event_read_group(struct mc_perf_event *event, unsigned long read_format, ch
|
||||
n = 0;
|
||||
values[n++] = perf_event_read_value(sub);
|
||||
|
||||
size = n * sizeof(unsigned long);
|
||||
size = n * sizeof(long long);
|
||||
|
||||
if (copy_to_user(buf + ret, values, size)) {
|
||||
return -EFAULT;
|
||||
@@ -3854,25 +3892,75 @@ void perf_start(struct mc_perf_event *event)
|
||||
int counter_id;
|
||||
unsigned long counter_mask = 0;
|
||||
struct mc_perf_event *leader = event->group_leader, *sub;
|
||||
struct thread *thread = cpu_local_var(current);
|
||||
|
||||
/* -- For use_invariant_tsc --
|
||||
* Record sum of counts the previous start-stop periods
|
||||
* into accum_count,
|
||||
* because only the count at the last start is recorded
|
||||
*/
|
||||
counter_id = leader->counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id)) {
|
||||
perf_counter_set(leader);
|
||||
counter_mask |= 1UL << counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id) &&
|
||||
leader->state == PERF_EVENT_STATE_INACTIVE) {
|
||||
if (leader->use_invariant_tsc) {
|
||||
if (leader->stopped_user_tsc) {
|
||||
leader->user_accum_count +=
|
||||
leader->stopped_user_tsc -
|
||||
leader->base_user_tsc;
|
||||
leader->stopped_user_tsc = 0;
|
||||
}
|
||||
leader->base_user_tsc = thread->user_tsc;
|
||||
|
||||
if (leader->stopped_system_tsc) {
|
||||
leader->system_accum_count +=
|
||||
leader->stopped_system_tsc -
|
||||
leader->base_system_tsc;
|
||||
leader->stopped_system_tsc = 0;
|
||||
}
|
||||
leader->base_system_tsc = thread->system_tsc;
|
||||
}
|
||||
else {
|
||||
perf_counter_set(leader);
|
||||
counter_mask |= 1UL << counter_id;
|
||||
}
|
||||
|
||||
leader->state = PERF_EVENT_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
list_for_each_entry(sub, &leader->sibling_list, group_entry) {
|
||||
counter_id = sub->counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id)) {
|
||||
perf_counter_set(sub);
|
||||
counter_mask |= 1UL << counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id) &&
|
||||
sub->state == PERF_EVENT_STATE_INACTIVE) {
|
||||
if (sub->use_invariant_tsc) {
|
||||
if (sub->stopped_user_tsc) {
|
||||
sub->user_accum_count +=
|
||||
sub->stopped_user_tsc -
|
||||
sub->base_user_tsc;
|
||||
sub->stopped_user_tsc = 0;
|
||||
}
|
||||
sub->base_user_tsc = thread->user_tsc;
|
||||
|
||||
if (sub->stopped_system_tsc) {
|
||||
sub->system_accum_count +=
|
||||
sub->stopped_system_tsc -
|
||||
sub->base_system_tsc;
|
||||
sub->stopped_system_tsc = 0;
|
||||
}
|
||||
sub->base_system_tsc = thread->system_tsc;
|
||||
}
|
||||
else {
|
||||
perf_counter_set(sub);
|
||||
counter_mask |= 1UL << counter_id;
|
||||
}
|
||||
|
||||
sub->state = PERF_EVENT_STATE_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if (counter_mask) {
|
||||
ihk_mc_perfctr_start(counter_mask);
|
||||
cpu_local_var(current)->proc->perf_status = PP_COUNT;
|
||||
}
|
||||
thread->proc->perf_status = PP_COUNT;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3880,16 +3968,62 @@ perf_reset(struct mc_perf_event *event)
|
||||
{
|
||||
int counter_id;
|
||||
struct mc_perf_event *leader = event->group_leader, *sub;
|
||||
struct thread *thread = cpu_local_var(current);
|
||||
|
||||
counter_id = leader->counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id)) {
|
||||
ihk_mc_perfctr_reset(counter_id);
|
||||
/* Let perf_event_read_value return zero when stopped */
|
||||
if (leader->use_invariant_tsc) {
|
||||
if (leader->stopped_user_tsc) {
|
||||
leader->base_user_tsc =
|
||||
leader->stopped_user_tsc;
|
||||
}
|
||||
else {
|
||||
leader->base_user_tsc = thread->user_tsc;
|
||||
}
|
||||
leader->user_accum_count = 0;
|
||||
|
||||
if (leader->stopped_system_tsc) {
|
||||
leader->base_system_tsc =
|
||||
leader->stopped_system_tsc;
|
||||
}
|
||||
else {
|
||||
leader->base_system_tsc = thread->system_tsc;
|
||||
}
|
||||
leader->system_accum_count = 0;
|
||||
}
|
||||
else {
|
||||
ihk_mc_perfctr_reset(counter_id);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(sub, &leader->sibling_list, group_entry) {
|
||||
counter_id = sub->counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id)) {
|
||||
ihk_mc_perfctr_reset(counter_id);
|
||||
/* Let perf_event_read_value return zero when stopped */
|
||||
if (sub->use_invariant_tsc) {
|
||||
if (sub->stopped_user_tsc) {
|
||||
sub->base_user_tsc =
|
||||
sub->stopped_user_tsc;
|
||||
}
|
||||
else {
|
||||
sub->base_user_tsc = thread->user_tsc;
|
||||
}
|
||||
sub->user_accum_count = 0;
|
||||
|
||||
if (sub->stopped_system_tsc) {
|
||||
sub->base_system_tsc =
|
||||
sub->stopped_system_tsc;
|
||||
}
|
||||
else {
|
||||
sub->base_system_tsc =
|
||||
thread->system_tsc;
|
||||
}
|
||||
sub->system_accum_count = 0;
|
||||
}
|
||||
else {
|
||||
ihk_mc_perfctr_reset(counter_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3900,24 +4034,52 @@ perf_stop(struct mc_perf_event *event)
|
||||
int counter_id;
|
||||
unsigned long counter_mask = 0;
|
||||
struct mc_perf_event *leader = event->group_leader, *sub;
|
||||
struct thread *thread = cpu_local_var(current);
|
||||
|
||||
counter_id = leader->counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id)) {
|
||||
counter_mask |= 1UL << counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id) &&
|
||||
leader->state == PERF_EVENT_STATE_ACTIVE) {
|
||||
if (leader->use_invariant_tsc) {
|
||||
if (leader->stopped_user_tsc == 0) {
|
||||
leader->stopped_user_tsc = thread->user_tsc;
|
||||
}
|
||||
if (leader->stopped_system_tsc == 0) {
|
||||
leader->stopped_system_tsc = thread->system_tsc;
|
||||
}
|
||||
}
|
||||
else {
|
||||
counter_mask |= 1UL << counter_id;
|
||||
}
|
||||
|
||||
leader->state = PERF_EVENT_STATE_INACTIVE;
|
||||
}
|
||||
|
||||
list_for_each_entry(sub, &leader->sibling_list, group_entry) {
|
||||
counter_id = sub->counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id)) {
|
||||
counter_mask |= 1UL << counter_id;
|
||||
if (ihk_mc_perf_counter_mask_check(1UL << counter_id) &&
|
||||
sub->state == PERF_EVENT_STATE_ACTIVE) {
|
||||
if (sub->use_invariant_tsc) {
|
||||
if (sub->stopped_user_tsc == 0) {
|
||||
sub->stopped_user_tsc =
|
||||
thread->user_tsc;
|
||||
}
|
||||
if (sub->stopped_system_tsc == 0) {
|
||||
sub->stopped_system_tsc =
|
||||
thread->system_tsc;
|
||||
}
|
||||
}
|
||||
else {
|
||||
counter_mask |= 1UL << counter_id;
|
||||
}
|
||||
sub->state = PERF_EVENT_STATE_INACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if (counter_mask) {
|
||||
ihk_mc_perfctr_stop(counter_mask, 0);
|
||||
cpu_local_var(current)->proc->monitoring_event = NULL;
|
||||
cpu_local_var(current)->proc->perf_status = PP_NONE;
|
||||
}
|
||||
cpu_local_var(current)->proc->monitoring_event = NULL;
|
||||
cpu_local_var(current)->proc->perf_status = PP_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -3964,8 +4126,7 @@ perf_ioctl(struct mckfd *sfd, ihk_mc_user_context_t *ctx)
|
||||
break;
|
||||
case PERF_EVENT_IOC_RESET:
|
||||
// TODO: reset other process
|
||||
ihk_mc_perfctr_set(counter_id, event->attr.sample_freq * -1);
|
||||
event->count = 0L;
|
||||
perf_reset(event);
|
||||
break;
|
||||
case PERF_EVENT_IOC_REFRESH:
|
||||
// TODO: refresh other process
|
||||
@@ -4131,6 +4292,19 @@ static int mc_perf_event_alloc(struct mc_perf_event **out,
|
||||
event->child_count_total = 0;
|
||||
event->parent = NULL;
|
||||
|
||||
if (attr->type == PERF_TYPE_HARDWARE &&
|
||||
attr->config == PERF_COUNT_HW_REF_CPU_CYCLES) {
|
||||
event->use_invariant_tsc = 1;
|
||||
|
||||
/*
|
||||
* REF_CPU_CYCLES is counted by thread's tsc.
|
||||
* Always support.
|
||||
*/
|
||||
*out = event;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (attr->type) {
|
||||
case PERF_TYPE_HARDWARE :
|
||||
val = ihk_mc_hw_event_map(attr->config);
|
||||
|
||||
Reference in New Issue
Block a user