Skip to content

Commit e46db8d

Browse files
committed
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf fixes from Thomas Gleixner: "Two fixes for the perf subsystem: - Fix an inconsistency of RDPMC mm struct tagging across exec() which causes RDPMC to fault. - Correct the timestamp mechanics across IOC_DISABLE/ENABLE which causes incorrect timestamps and total time calculations" * 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: perf/core: Fix time on IOC_ENABLE perf/x86: Fix RDPMC vs. mm_struct tracking
2 parents 9dae41a + 9b231d9 commit e46db8d

File tree

3 files changed

+48
-19
lines changed

3 files changed

+48
-19
lines changed

arch/x86/events/core.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,7 +2114,7 @@ static void refresh_pce(void *ignored)
21142114
load_mm_cr4(this_cpu_read(cpu_tlbstate.loaded_mm));
21152115
}
21162116

2117-
static void x86_pmu_event_mapped(struct perf_event *event)
2117+
static void x86_pmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
21182118
{
21192119
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
21202120
return;
@@ -2129,22 +2129,20 @@ static void x86_pmu_event_mapped(struct perf_event *event)
21292129
* For now, this can't happen because all callers hold mmap_sem
21302130
* for write. If this changes, we'll need a different solution.
21312131
*/
2132-
lockdep_assert_held_exclusive(&current->mm->mmap_sem);
2132+
lockdep_assert_held_exclusive(&mm->mmap_sem);
21332133

2134-
if (atomic_inc_return(&current->mm->context.perf_rdpmc_allowed) == 1)
2135-
on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
2134+
if (atomic_inc_return(&mm->context.perf_rdpmc_allowed) == 1)
2135+
on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
21362136
}
21372137

2138-
static void x86_pmu_event_unmapped(struct perf_event *event)
2138+
static void x86_pmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
21392139
{
2140-
if (!current->mm)
2141-
return;
21422140

21432141
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
21442142
return;
21452143

2146-
if (atomic_dec_and_test(&current->mm->context.perf_rdpmc_allowed))
2147-
on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
2144+
if (atomic_dec_and_test(&mm->context.perf_rdpmc_allowed))
2145+
on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
21482146
}
21492147

21502148
static int x86_pmu_event_idx(struct perf_event *event)

include/linux/perf_event.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@ struct pmu {
310310
* Notification that the event was mapped or unmapped. Called
311311
* in the context of the mapping task.
312312
*/
313-
void (*event_mapped) (struct perf_event *event); /*optional*/
314-
void (*event_unmapped) (struct perf_event *event); /*optional*/
313+
void (*event_mapped) (struct perf_event *event, struct mm_struct *mm); /* optional */
314+
void (*event_unmapped) (struct perf_event *event, struct mm_struct *mm); /* optional */
315315

316316
/*
317317
* Flags for ->add()/->del()/ ->start()/->stop(). There are

kernel/events/core.c

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,16 +2217,46 @@ static int group_can_go_on(struct perf_event *event,
22172217
return can_add_hw;
22182218
}
22192219

2220+
/*
2221+
* Complement to update_event_times(). This computes the tstamp_* values to
2222+
* continue 'enabled' state from @now, and effectively discards the time
2223+
* between the prior tstamp_stopped and now (as we were in the OFF state, or
2224+
* just switched (context) time base).
2225+
*
2226+
* This further assumes '@event->state == INACTIVE' (we just came from OFF) and
2227+
* cannot have been scheduled in yet. And going into INACTIVE state means
2228+
* '@event->tstamp_stopped = @now'.
2229+
*
2230+
* Thus given the rules of update_event_times():
2231+
*
2232+
* total_time_enabled = tstamp_stopped - tstamp_enabled
2233+
* total_time_running = tstamp_stopped - tstamp_running
2234+
*
2235+
* We can insert 'tstamp_stopped == now' and reverse them to compute new
2236+
* tstamp_* values.
2237+
*/
2238+
static void __perf_event_enable_time(struct perf_event *event, u64 now)
2239+
{
2240+
WARN_ON_ONCE(event->state != PERF_EVENT_STATE_INACTIVE);
2241+
2242+
event->tstamp_stopped = now;
2243+
event->tstamp_enabled = now - event->total_time_enabled;
2244+
event->tstamp_running = now - event->total_time_running;
2245+
}
2246+
22202247
static void add_event_to_ctx(struct perf_event *event,
22212248
struct perf_event_context *ctx)
22222249
{
22232250
u64 tstamp = perf_event_time(event);
22242251

22252252
list_add_event(event, ctx);
22262253
perf_group_attach(event);
2227-
event->tstamp_enabled = tstamp;
2228-
event->tstamp_running = tstamp;
2229-
event->tstamp_stopped = tstamp;
2254+
/*
2255+
* We can be called with event->state == STATE_OFF when we create with
2256+
* .disabled = 1. In that case the IOC_ENABLE will call this function.
2257+
*/
2258+
if (event->state == PERF_EVENT_STATE_INACTIVE)
2259+
__perf_event_enable_time(event, tstamp);
22302260
}
22312261

22322262
static void ctx_sched_out(struct perf_event_context *ctx,
@@ -2471,10 +2501,11 @@ static void __perf_event_mark_enabled(struct perf_event *event)
24712501
u64 tstamp = perf_event_time(event);
24722502

24732503
event->state = PERF_EVENT_STATE_INACTIVE;
2474-
event->tstamp_enabled = tstamp - event->total_time_enabled;
2504+
__perf_event_enable_time(event, tstamp);
24752505
list_for_each_entry(sub, &event->sibling_list, group_entry) {
2506+
/* XXX should not be > INACTIVE if event isn't */
24762507
if (sub->state >= PERF_EVENT_STATE_INACTIVE)
2477-
sub->tstamp_enabled = tstamp - sub->total_time_enabled;
2508+
__perf_event_enable_time(sub, tstamp);
24782509
}
24792510
}
24802511

@@ -5090,7 +5121,7 @@ static void perf_mmap_open(struct vm_area_struct *vma)
50905121
atomic_inc(&event->rb->aux_mmap_count);
50915122

50925123
if (event->pmu->event_mapped)
5093-
event->pmu->event_mapped(event);
5124+
event->pmu->event_mapped(event, vma->vm_mm);
50945125
}
50955126

50965127
static void perf_pmu_output_stop(struct perf_event *event);
@@ -5113,7 +5144,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
51135144
unsigned long size = perf_data_size(rb);
51145145

51155146
if (event->pmu->event_unmapped)
5116-
event->pmu->event_unmapped(event);
5147+
event->pmu->event_unmapped(event, vma->vm_mm);
51175148

51185149
/*
51195150
* rb->aux_mmap_count will always drop before rb->mmap_count and
@@ -5411,7 +5442,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
54115442
vma->vm_ops = &perf_mmap_vmops;
54125443

54135444
if (event->pmu->event_mapped)
5414-
event->pmu->event_mapped(event);
5445+
event->pmu->event_mapped(event, vma->vm_mm);
54155446

54165447
return ret;
54175448
}

0 commit comments

Comments
 (0)