Skip to contentSkip to conversation

bug fix mmap read in large PEBS

PATCH Superseded [email protected] 3 participants 13 messages Dec 18, 2017 – Dec 19, 2017

Subsystems (2)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

cover bug fix mmap read in large PEBS

From: Kan Liang <[email protected]>

There is bug when mmap read event->count with large PEBS enabled. Here is an example.
#./read_count
0x71f0
0x122c0
0x1000000001c54
0x100000001257d
0x200000000bdc5

The bug is caused by two issues.
- In x86_perf_event_update, the calculation of event->count does not take the auto-reload values into account.
- In x86_pmu_read, it doesn't count the undrained values in large PEBS buffers.

The issue is introduced with the auto-reload mechanism enabled by commit 851559e35fd5 ("perf/x86/intel: Use the PEBS auto reload mechanism when possible")

The source code of read_count is as below.

struct cpu {
        int fd;
        struct perf_event_mmap_page *buf;
};

int perf_open(struct cpu *ctx, int cpu)

{
        struct perf_event_attr attr = {
                .type = PERF_TYPE_HARDWARE,
                .size = sizeof(struct perf_event_attr),
                .sample_period = 100000,
                .config = 0,
                .sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU,
                .precise_ip = 3,
                .mmap = 1,
                .comm = 1,
                .task = 1,
                .mmap2 = 1,
                .sample_id_all = 1,
                .comm_exec = 1,
        };
        ctx->buf = NULL;
        ctx->fd = syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
        if (ctx->fd < 0) {
                perror("perf_event_open");
                return -1;
        }

        ctx->buf = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ, MAP_SHARED, ctx->fd, 0);
        if (ctx->buf == MAP_FAILED) {
                close(ctx->fd);
                perror("mmap on perf fd");
                return -1;
        }
        return 0;
}

void perf_close(struct cpu *ctx)

{
        close(ctx->fd);
        if (ctx->buf)
                munmap(ctx->buf, pagesize);
}

int main(int ac, char **av)

{
        struct cpu ctx;
        u64 count;

        perf_open(&ctx, 0);

        while (1) {
                sleep(5);

                if (read(ctx.fd, &count, 8) != 8) {
                        perror("counter read");
                        break;
                }
                printf("0x%llx\n", count);

        }
        perf_close(&ctx);
}

Kan Liang (4):
perf/x86/intel: pass auto-reload information to event update perf/x86/intel: fix event update for auto-reload perf/x86: introduce read function for x86_pmu perf/x86/intel: drain PEBS buffer in event read

 arch/x86/events/core.c       | 26 ++++++++++++++++++++++----
 arch/x86/events/intel/core.c | 18 ++++++++++++++----
 arch/x86/events/intel/ds.c   | 18 +++++++++++++++++-
 arch/x86/events/intel/knc.c  |  2 +-
 arch/x86/events/intel/p4.c   |  2 +-
 arch/x86/events/perf_event.h |  9 +++++++--
 6 files changed, 62 insertions(+), 13 deletions(-)
Signature · 1 line
2.7.4
#1 lore raw

PATCH 1/4 perf/x86/intel: pass auto-reload information to event update

From: Kan Liang <[email protected]>

There is auto-reload mechanism enabled for PEBS events in fixed period mode. When calculating the event->count, the reload value also need to be taken into account.

Pass the auto-reload value and times to event update. They will be used later.

No functional change.

---
 arch/x86/events/core.c       | 9 +++++----
 arch/x86/events/intel/core.c | 9 +++++----
 arch/x86/events/intel/ds.c   | 2 +-
 arch/x86/events/intel/knc.c  | 2 +-
 arch/x86/events/intel/p4.c   | 2 +-
 arch/x86/events/perf_event.h | 6 ++++--
 6 files changed, 17 insertions(+), 13 deletions(-)
arch/x86/events/core.c +5 -4
index 140d332..35552ea 100644
@@ -64,7 +64,8 @@ u64 __read_mostly hw_cache_extra_regs
  * Can only be executed on the CPU where the event is active.
  * Returns the delta events processed.
  */
-u64 x86_perf_event_update(struct perf_event *event)
+u64 x86_perf_event_update(struct perf_event *event,
+			  u64 reload_val, int reload_times)
 {
 	struct hw_perf_event *hwc = &event->hw;
 	int shift = 64 - x86_pmu.cntval_bits;
@@ -1357,7 +1358,7 @@ void x86_pmu_stop(struct perf_event *event, int flags)
 		 * Drain the remaining delta count out of a event
 		 * that we are disabling:
 		 */
-		x86_perf_event_update(event);
+		x86_perf_event_update(event, 0, 0);
 		hwc->state |= PERF_HES_UPTODATE;
 	}
 }
@@ -1456,7 +1457,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs)
 
 		event = cpuc->events[idx];
 
-		val = x86_perf_event_update(event);
+		val = x86_perf_event_update(event, 0, 0);
 		if (val & (1ULL << (x86_pmu.cntval_bits - 1)))
 			continue;
 
@@ -1884,7 +1885,7 @@ early_initcall(init_hw_perf_events);
 
 static inline void x86_pmu_read(struct perf_event *event)
 {
-	x86_perf_event_update(event);
+	x86_perf_event_update(event, 0, 0);
 }
 
 /*
arch/x86/events/intel/core.c +5 -4
index 09c26a4..1f7edaf 100644
@@ -1965,7 +1965,7 @@ static void intel_pmu_nhm_workaround(void)
 	for (i = 0; i < 4; i++) {
 		event = cpuc->events[i];
 		if (event)
-			x86_perf_event_update(event);
+			x86_perf_event_update(event, 0, 0);
 	}
 
 	for (i = 0; i < 4; i++) {
@@ -2135,9 +2135,10 @@ static void intel_pmu_add_event(struct perf_event *event)
  * Save and restart an expired event. Called by NMI contexts,
  * so it has to be careful about preempting normal event ops:
  */
-int intel_pmu_save_and_restart(struct perf_event *event)
+int intel_pmu_save_and_restart(struct perf_event *event,
+			       u64 reload_val, int reload_times)
 {
-	x86_perf_event_update(event);
+	x86_perf_event_update(event, reload_val, reload_times);
 	/*
 	 * For a checkpointed counter always reset back to 0.  This
 	 * avoids a situation where the counter overflows, aborts the
@@ -2299,7 +2300,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
 		if (!test_bit(bit, cpuc->active_mask))
 			continue;
 
-		if (!intel_pmu_save_and_restart(event))
+		if (!intel_pmu_save_and_restart(event, 0, 0))
 			continue;
 
 		perf_sample_data_init(&data, 0, event->hw.last_period);
arch/x86/events/intel/ds.c +1 -1
index 3674a4b..0b693b7 100644
@@ -1260,7 +1260,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
 	struct pt_regs regs;
 	void *at = get_next_pebs_record_by_bit(base, top, bit);
 
-	if (!intel_pmu_save_and_restart(event) &&
+	if (!intel_pmu_save_and_restart(event, 0, 0) &&
 	    !(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD))
 		return;
 
arch/x86/events/intel/knc.c +1 -1
index 618001c..b42a49d 100644
@@ -247,7 +247,7 @@ static int knc_pmu_handle_irq(struct pt_regs *regs)
 		if (!test_bit(bit, cpuc->active_mask))
 			continue;
 
-		if (!intel_pmu_save_and_restart(event))
+		if (!intel_pmu_save_and_restart(event, 0, 0))
 			continue;
 
 		perf_sample_data_init(&data, 0, event->hw.last_period);
arch/x86/events/intel/p4.c +1 -1
index d32c0ee..672bf5e 100644
@@ -1024,7 +1024,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
 		/* it might be unflagged overflow */
 		overflow = p4_pmu_clear_cccr_ovf(hwc);
 
-		val = x86_perf_event_update(event);
+		val = x86_perf_event_update(event, 0, 0);
 		if (!overflow && (val & (1ULL << (x86_pmu.cntval_bits - 1))))
 			continue;
 
arch/x86/events/perf_event.h +4 -2
index f7aaadf..fabb8b3 100644
@@ -738,7 +738,8 @@ extern u64 __read_mostly hw_cache_extra_regs
 				[PERF_COUNT_HW_CACHE_OP_MAX]
 				[PERF_COUNT_HW_CACHE_RESULT_MAX];
 
-u64 x86_perf_event_update(struct perf_event *event);
+u64 x86_perf_event_update(struct perf_event *event,
+			  u64 reload_val, int reload_times);
 
 static inline unsigned int x86_pmu_config_addr(int index)
 {
@@ -871,7 +872,8 @@ static inline bool intel_pmu_has_bts(struct perf_event *event)
 	return false;
 }
 
-int intel_pmu_save_and_restart(struct perf_event *event);
+int intel_pmu_save_and_restart(struct perf_event *event,
+			       u64 reload_val, int reload_times);
 
 struct event_constraint *
 x86_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
Signature · 1 line
2.7.4
#2 lore raw

PATCH 2/4 perf/x86/intel: fix event update for auto-reload

In reply to [email protected]

From: Kan Liang <[email protected]>

There is bug when mmap read event->count with large PEBS enabled. Here is an example.
#./read_count
0x71f0
0x122c0
0x1000000001c54
0x100000001257d
0x200000000bdc5

There is auto-reload mechanism enabled for PEBS events in fixed period mode. But the calculation of event->count does not take the auto-reload values into account. Anyone who read the event->count will get wrong result, e.g x86_pmu_read. Also, the calculation of hwc->period_left is wrong either. It impacts the accuracy of period for the first record in PEBS multiple records.

The issue is introduced with the auto-reload mechanism enabled by commit 851559e35fd5 ("perf/x86/intel: Use the PEBS auto reload mechanism when possible")

For the auto-reload before the last time, it went through the whole period (reload value) every time. So period * times should be added into the event->count.
For the last load, the elapsed delta (event-)time need to be corrected by adding the period (reload value). Because the start point is -period.

---
 arch/x86/events/core.c     | 14 ++++++++++++++
 arch/x86/events/intel/ds.c |  8 +++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)
arch/x86/events/core.c +14 -0
index 35552ea..f74e21d 100644
@@ -100,6 +100,20 @@ u64 x86_perf_event_update(struct perf_event *event,
 	 * of the count.
 	 */
 	delta = (new_raw_count << shift) - (prev_raw_count << shift);
+
+	/*
+	 * Take auto-reload into account
+	 * For the auto-reload before the last time, it went through the
+	 * whole period (reload_val) every time.
+	 * Just simply add period * times to the event.
+	 *
+	 * For the last load, the elapsed delta (event-)time need to be
+	 * corrected by adding the period. Because the start point is -period.
+	 */
+	if (reload_times > 0) {
+		delta += (reload_val << shift);
+		local64_add(reload_val * (reload_times - 1), &event->count);
+	}
 	delta >>= shift;
 
 	local64_add(delta, &event->count);
arch/x86/events/intel/ds.c +7 -1
index 0b693b7..f0f6026 100644
@@ -1256,11 +1256,17 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
 				   void *base, void *top,
 				   int bit, int count)
 {
+	struct hw_perf_event *hwc = &event->hw;
 	struct perf_sample_data data;
 	struct pt_regs regs;
 	void *at = get_next_pebs_record_by_bit(base, top, bit);
 
-	if (!intel_pmu_save_and_restart(event, 0, 0) &&
+	/*
+	 * Now, auto-reload is only enabled in fixed period mode.
+	 * The reload value is always hwc->sample_period.
+	 * May need to change it, if auto-reload is enabled in freq mode later.
+	 */
+	if (!intel_pmu_save_and_restart(event, hwc->sample_period, count - 1) &&
 	    !(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD))
 		return;
 
Signature · 1 line
2.7.4
maintainer
#3 lore raw

Re: [PATCH 2/4] perf/x86/intel: fix event update for auto-reload

Quoted 42 lines · On Mon, Dec 18, 2017 at 03:34:49AM -0800, [email protected] wrote:
 arch/x86/events/core.c     | 14 ++++++++++++++
 arch/x86/events/intel/ds.c |  8 +++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)
arch/x86/events/core.c +14 -0
index 35552ea..f74e21d 100644
@@ -100,6 +100,20 @@ u64 x86_perf_event_update(struct perf_event *event,
 	 * of the count.
 	 */
 	delta = (new_raw_count << shift) - (prev_raw_count << shift);
+
+	/*
+	 * Take auto-reload into account
+	 * For the auto-reload before the last time, it went through the
+	 * whole period (reload_val) every time.
+	 * Just simply add period * times to the event.
+	 *
+	 * For the last load, the elapsed delta (event-)time need to be
+	 * corrected by adding the period. Because the start point is -period.
+	 */
+	if (reload_times > 0) {
+		delta += (reload_val << shift);
+		local64_add(reload_val * (reload_times - 1), &event->count);
+	}
 	delta >>= shift;
 
 	local64_add(delta, &event->count);
arch/x86/events/intel/ds.c +1 -0
index 0b693b7..f0f6026 100644
@@ -1256,11 +1256,17 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
 				   void *base, void *top,
 				   int bit, int count)
 {
+	struct hw_perf_event *hwc = &event->hw;
 	struct perf_sample_data data;
 	struct pt_regs regs;
 	void *at = get_next_pebs_record_by_bit(base, top, bit);
 

arch/x86/events/intel/ds.c static void __intel_pmu_pebs_event(struct perf_event *event, · line 1272

Quoted 10 lines
arch/x86/events/intel/ds.c +6 -1
-	if (!intel_pmu_save_and_restart(event, 0, 0) &&
+	/*
+	 * Now, auto-reload is only enabled in fixed period mode.
+	 * The reload value is always hwc->sample_period.
+	 * May need to change it, if auto-reload is enabled in freq mode later.
+	 */
+	if (!intel_pmu_save_and_restart(event, hwc->sample_period, count - 1) &&
 	    !(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD))
 		return;
 

This all looks very wrong... In auto reload we should never call intel_pmu_save_and_restore() in the first place I think.

Things like x86_perf_event_update() and x86_perf_event_set_period() simply _cannot_ do the right thing when we auto reload the counter.

author
#4 lore raw

Re: [PATCH 2/4] perf/x86/intel: fix event update for auto-reload

Quoted 57 lines · Peter Zijlstra
Quoted 42 lines · On Mon, Dec 18, 2017 at 03:34:49AM -0800, [email protected] wrote:
  arch/x86/events/core.c     | 14 ++++++++++++++
  arch/x86/events/intel/ds.c |  8 +++++++-
  2 files changed, 21 insertions(+), 1 deletion(-)
arch/x86/events/core.c +14 -0
index 35552ea..f74e21d 100644
@@ -100,6 +100,20 @@ u64 x86_perf_event_update(struct perf_event *event,
  	 * of the count.
  	 */
  	delta = (new_raw_count << shift) - (prev_raw_count << shift);
+
+	/*
+	 * Take auto-reload into account
+	 * For the auto-reload before the last time, it went through the
+	 * whole period (reload_val) every time.
+	 * Just simply add period * times to the event.
+	 *
+	 * For the last load, the elapsed delta (event-)time need to be
+	 * corrected by adding the period. Because the start point is -period.
+	 */
+	if (reload_times > 0) {
+		delta += (reload_val << shift);
+		local64_add(reload_val * (reload_times - 1), &event->count);
+	}
  	delta >>= shift;
 
  	local64_add(delta, &event->count);
arch/x86/events/intel/ds.c +1 -0
index 0b693b7..f0f6026 100644
@@ -1256,11 +1256,17 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
  				   void *base, void *top,
  				   int bit, int count)
  {
+	struct hw_perf_event *hwc = &event->hw;
  	struct perf_sample_data data;
  	struct pt_regs regs;
  	void *at = get_next_pebs_record_by_bit(base, top, bit);
 

arch/x86/events/intel/ds.c static void __intel_pmu_pebs_event(struct perf_event *event, · line 1272

Quoted 10 lines
arch/x86/events/intel/ds.c +6 -1
-	if (!intel_pmu_save_and_restart(event, 0, 0) &&
+	/*
+	 * Now, auto-reload is only enabled in fixed period mode.
+	 * The reload value is always hwc->sample_period.
+	 * May need to change it, if auto-reload is enabled in freq mode later.
+	 */
+	if (!intel_pmu_save_and_restart(event, hwc->sample_period, count - 1) &&
  	    !(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD))
  		return;
 

This all looks very wrong... In auto reload we should never call intel_pmu_save_and_restore() in the first place I think.

Quoted 3 lines

Things like x86_perf_event_update() and x86_perf_event_set_period() simply _cannot_ do the right thing when we auto reload the counter.

I think it should be OK to call it in first place.
For x86_perf_event_update(), the reload_times will tell if it's auto reload. Both period_left and event->count are carefully recalculated for auto reload. For x86_perf_event_set_period(), there is nothing special needed for auto reload. The period is fixed. The period_left from x86_perf_event_update() is already handled.

BTW: It should be 'count' not 'count - 1' which pass to intel_pmu_save_and_restart(). I just found the issue. I will fix it in V2 with other improvements if there are any.

Thanks,
Kan

author
#5 lore raw

Re: [PATCH 2/4] perf/x86/intel: fix event update for auto-reload

Quoted 61 lines · Kan
Quoted 58 lines · Peter Zijlstra
Quoted 47 lines · On Mon, Dec 18, 2017 at 03:34:49AM -0800, [email protected] wrote:

  arch/x86/events/core.c     | 14 ++++++++++++++
  arch/x86/events/intel/ds.c |  8 +++++++-

  2 files changed, 21 insertions(+), 1 deletion(-)
arch/x86/events/core.c +14 -1
index 35552ea..f74e21d 100644
@@ -100,6 +100,20 @@ u64 x86_perf_event_update(struct perf_event *event,
        * of the count.
        */
       delta = (new_raw_count << shift) - (prev_raw_count << shift);
+
+    /*
+     * Take auto-reload into account
+     * For the auto-reload before the last time, it went through the
+     * whole period (reload_val) every time.
+     * Just simply add period * times to the event.
+     *
+     * For the last load, the elapsed delta (event-)time need to be
+     * corrected by adding the period. Because the start point is 
-period.
+     */
+    if (reload_times > 0) {
+        delta += (reload_val << shift);
+        local64_add(reload_val * (reload_times - 1), &event->count);
+    }
       delta >>= shift;
       local64_add(delta, &event->count);
arch/x86/events/intel/ds.c +5 -1
index 0b693b7..f0f6026 100644
@@ -1256,11 +1256,17 @@ static void __intel_pmu_pebs_event(struct 
 perf_event *event,
                      void *base, void *top,
                      int bit, int count)
   {
+    struct hw_perf_event *hwc = &event->hw;
       struct perf_sample_data data;
       struct pt_regs regs;
       void *at = get_next_pebs_record_by_bit(base, top, bit);
-    if (!intel_pmu_save_and_restart(event, 0, 0) &&
+    /*
+     * Now, auto-reload is only enabled in fixed period mode.
+     * The reload value is always hwc->sample_period.
+     * May need to change it, if auto-reload is enabled in freq mode 
Quoted 6 lines

later.
+     */
+    if (!intel_pmu_save_and_restart(event, hwc->sample_period, count
- 1) &&
          !(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD)) return;

This all looks very wrong... In auto reload we should never call intel_pmu_save_and_restore() in the first place I think.

Quoted 6 lines
Quoted 3 lines

Things like x86_perf_event_update() and x86_perf_event_set_period() simply _cannot_ do the right thing when we auto reload the counter.

I think it should be OK to call it in first place.
For x86_perf_event_update(), the reload_times will tell if it's auto reload. Both period_left and event->count are carefully recalculated for auto reload.

Think a bit more. You are right. We cannot rely on count to tell us if it's auto reload. The count could also be 1 if auto reload is enabled.

I will fix it in V2.

Thanks,
Kan

Quoted 11 lines

For x86_perf_event_set_period(), there is nothing special needed for auto reload. The period is fixed. The period_left from x86_perf_event_update() is already handled.

BTW: It should be 'count' not 'count - 1' which pass to intel_pmu_save_and_restart(). I just found the issue. I will fix it in V2 with other improvements if there are any.

Thanks,
Kan

maintainer
#6 lore raw

Re: [PATCH 2/4] perf/x86/intel: fix event update for auto-reload

In reply to Liang, Kan
Quoted 11 lines · Kan
Quoted 6 lines

This all looks very wrong... In auto reload we should never call intel_pmu_save_and_restore() in the first place I think.

Things like x86_perf_event_update() and x86_perf_event_set_period() simply _cannot_ do the right thing when we auto reload the counter.

I think it should be OK to call it in first place.
For x86_perf_event_update(), the reload_times will tell if it's auto reload. Both period_left and event->count are carefully recalculated for auto reload.

How does prev_count make sense when we've already reloaded a bunch of times?

Quoted 3 lines

For x86_perf_event_set_period(), there is nothing special needed for auto reload. The period is fixed. The period_left from x86_perf_event_update() is already handled.

Hurm.. I see. But rather than make an ever bigger trainwreck of things, I'd rather you just write a special purpose intel_pmu_save_and_restart() just for AUTO_RELOAD.

#7 lore raw

Re: [PATCH 2/4] perf/x86/intel: fix event update for auto-reload

Quoted 8 lines · Peter Zijlstra
Quoted 7 lines · Kan
Quoted 6 lines

This all looks very wrong... In auto reload we should never call intel_pmu_save_and_restore() in the first place I think.

Things like x86_perf_event_update() and x86_perf_event_set_period() simply _cannot_ do the right thing when we auto reload the counter.

Quoted 7 lines
Quoted 4 lines

I think it should be OK to call it in first place.
For x86_perf_event_update(), the reload_times will tell if it's auto reload. Both period_left and event->count are carefully recalculated for auto reload.

How does prev_count make sense when we've already reloaded a bunch of times?

We can figure it out how often there was a reload based on the PEBS index.

-Andi

author
#8 lore raw

Re: [PATCH 2/4] perf/x86/intel: fix event update for auto-reload

In reply to Peter Zijlstra
Quoted 8 lines · Peter Zijlstra
Quoted 7 lines · Kan
Quoted 6 lines

This all looks very wrong... In auto reload we should never call intel_pmu_save_and_restore() in the first place I think.

Things like x86_perf_event_update() and x86_perf_event_set_period() simply _cannot_ do the right thing when we auto reload the counter.

Quoted 7 lines
Quoted 4 lines

I think it should be OK to call it in first place.
For x86_perf_event_update(), the reload_times will tell if it's auto reload. Both period_left and event->count are carefully recalculated for auto reload.

How does prev_count make sense when we've already reloaded a bunch of times?

Same as non-auto-reload, it's the 'left' (unfinished) period from last time.
The period for the first record should always be the 'left' period no matter on which case.
For auto-reload, it doesn't need to increase the prev_count with the reload. Because for later records, the period should be exactly the same as the reload value.

To calculate the event->count,
For auto-reload, the event->count = prev_count + (reload times - 1) * reload value + gap between PMI trigger and PMI handler.

For non-auto-reload, the event->count = prev_count + gap between PMI trigger and PMI handler.

The 'prev_count' is same for both auto-reload and non-auto-reload.

The gap is a little bit tricky for auto-reload. Because it starts from -reload_value. But for non-auto-reload, it starts from 0. "delta += (reload_val << shift);" is used to correct it.

Quoted 8 lines
Quoted 4 lines

For x86_perf_event_set_period(), there is nothing special needed for auto reload. The period is fixed. The period_left from x86_perf_event_update() is already handled.

Hurm.. I see. But rather than make an ever bigger trainwreck of things, I'd rather you just write a special purpose intel_pmu_save_and_restart() just for AUTO_RELOAD.

OK. I will do it in V2.

Thanks,
Kan

#9 lore raw

PATCH 3/4 perf/x86: introduce read function for x86_pmu

In reply to [email protected]

From: Kan Liang <[email protected]>

Large PEBS need to be specially handled in event count read.

---
 arch/x86/events/core.c       | 3 +++
 arch/x86/events/perf_event.h | 1 +
 2 files changed, 4 insertions(+)
arch/x86/events/core.c +3 -0
index f74e21d..c781497 100644
@@ -1899,6 +1899,9 @@ early_initcall(init_hw_perf_events);
 
 static inline void x86_pmu_read(struct perf_event *event)
 {
+	if (x86_pmu.read)
+		return x86_pmu.read(event);
+
 	x86_perf_event_update(event, 0, 0);
 }
 
arch/x86/events/perf_event.h +1 -0
index fabb8b3..3a7a2e7 100644
@@ -534,6 +534,7 @@ struct x86_pmu {
 	void		(*disable)(struct perf_event *);
 	void		(*add)(struct perf_event *);
 	void		(*del)(struct perf_event *);
+	void		(*read)(struct perf_event *event);
 	int		(*hw_config)(struct perf_event *event);
 	int		(*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign);
 	unsigned	eventsel;
Signature · 1 line
2.7.4
#10 lore raw

PATCH 4/4 perf/x86/intel: drain PEBS buffer in event read

In reply to [email protected]

From: Kan Liang <[email protected]>

When the PEBS interrupt threshold is larger than one, there is no way to get exact auto-reload times and value needed for event update unless flush the PEBS buffer.

Drain the PEBS buffer in event read when large PEBS is enabled.

For the threshold is one, even auto-reload is enabled, it doesn't need to be specially handled. Because auto-reload is only effect when event overflow. There is no overflow in event read.

---
 arch/x86/events/intel/core.c |  9 +++++++++
 arch/x86/events/intel/ds.c   | 10 ++++++++++
 arch/x86/events/perf_event.h |  2 ++
 3 files changed, 21 insertions(+)
arch/x86/events/intel/core.c +9 -0
index 1f7edaf..2411ef0 100644
@@ -2060,6 +2060,14 @@ static void intel_pmu_del_event(struct perf_event *event)
 		intel_pmu_pebs_del(event);
 }
 
+static void intel_pmu_read_event(struct perf_event *event)
+{
+	if (event->attr.precise_ip)
+		return intel_pmu_pebs_read(event);
+
+	x86_perf_event_update(event, 0, 0);
+}
+
 static void intel_pmu_enable_fixed(struct hw_perf_event *hwc)
 {
 	int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
@@ -3496,6 +3504,7 @@ static __initconst const struct x86_pmu intel_pmu = {
 	.disable		= intel_pmu_disable_event,
 	.add			= intel_pmu_add_event,
 	.del			= intel_pmu_del_event,
+	.read			= intel_pmu_read_event,
 	.hw_config		= intel_pmu_hw_config,
 	.schedule_events	= x86_schedule_events,
 	.eventsel		= MSR_ARCH_PERFMON_EVENTSEL0,
arch/x86/events/intel/ds.c +10 -0
index f0f6026..2e0c215 100644
@@ -926,6 +926,16 @@ void intel_pmu_pebs_del(struct perf_event *event)
 	pebs_update_state(needed_cb, cpuc, event->ctx->pmu);
 }
 
+void intel_pmu_pebs_read(struct perf_event *event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	if (pebs_needs_sched_cb(cpuc))
+		return intel_pmu_drain_pebs_buffer();
+
+	x86_perf_event_update(event, 0, 0);
+}
+
 void intel_pmu_pebs_disable(struct perf_event *event)
 {
 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
arch/x86/events/perf_event.h +2 -0
index 3a7a2e7..34faf48 100644
@@ -930,6 +930,8 @@ void intel_pmu_pebs_add(struct perf_event *event);
 
 void intel_pmu_pebs_del(struct perf_event *event);
 
+void intel_pmu_pebs_read(struct perf_event *event);
+
 void intel_pmu_pebs_enable(struct perf_event *event);
 
 void intel_pmu_pebs_disable(struct perf_event *event);
Signature · 1 line
2.7.4
maintainer
#11 lore raw

Re: [PATCH 4/4] perf/x86/intel: drain PEBS buffer in event read

Quoted 5 lines · On Mon, Dec 18, 2017 at 03:34:51AM -0800, [email protected] wrote:
arch/x86/events/intel/ds.c +0 -0
@@ -926,6 +926,16 @@ void intel_pmu_pebs_del(struct perf_event *event)
 	pebs_update_state(needed_cb, cpuc, event->ctx->pmu);
 }

arch/x86/events/intel/ds.c void intel_pmu_pebs_del(struct perf_event *event) · line 937

Quoted 10 lines
arch/x86/events/intel/ds.c +9 -0
 
+void intel_pmu_pebs_read(struct perf_event *event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	if (pebs_needs_sched_cb(cpuc))
+		return intel_pmu_drain_pebs_buffer();
+
+	x86_perf_event_update(event, 0, 0);
+}

This is completely broken.. what if @event isn't a pebs event, but we do have an auto-reloading pebs event configured?

author
#12 lore raw

Re: [PATCH 4/4] perf/x86/intel: drain PEBS buffer in event read

Quoted 7 lines · Peter Zijlstra
Quoted 6 lines · On Mon, Dec 18, 2017 at 03:34:51AM -0800, [email protected] wrote:
arch/x86/events/intel/ds.c +0 -0
@@ -926,6 +926,16 @@ void intel_pmu_pebs_del(struct perf_event *event)
  	pebs_update_state(needed_cb, cpuc, event->ctx->pmu);
  }
 
Quoted 13 lines

arch/x86/events/intel/ds.c void intel_pmu_pebs_del(struct perf_event *event) · line 937

Quoted 9 lines
arch/x86/events/intel/ds.c +9 -0
+void intel_pmu_pebs_read(struct perf_event *event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	if (pebs_needs_sched_cb(cpuc))
+		return intel_pmu_drain_pebs_buffer();
+
+	x86_perf_event_update(event, 0, 0);
+}

This is completely broken.. what if @event isn't a pebs event, but we do have an auto-reloading pebs event configured?

precise_ip will be checked before intel_pmu_pebs_read is called. So @event must be a pebs event.

@@ -2060,6 +2060,14 @@ static void intel_pmu_del_event(struct perf_event *event)
intel_pmu_pebs_del(event);
}

+static void intel_pmu_read_event(struct perf_event *event) +{
+ if (event->attr.precise_ip)
+ return intel_pmu_pebs_read(event);
+
+ x86_perf_event_update(event, 0, 0);
+}