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
This commit is contained in:
commit
e46db8d2ef
@ -2114,7 +2114,7 @@ static void refresh_pce(void *ignored)
|
|||||||
load_mm_cr4(this_cpu_read(cpu_tlbstate.loaded_mm));
|
load_mm_cr4(this_cpu_read(cpu_tlbstate.loaded_mm));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void x86_pmu_event_mapped(struct perf_event *event)
|
static void x86_pmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
|
||||||
{
|
{
|
||||||
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
|
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
|
||||||
return;
|
return;
|
||||||
@ -2129,22 +2129,20 @@ static void x86_pmu_event_mapped(struct perf_event *event)
|
|||||||
* For now, this can't happen because all callers hold mmap_sem
|
* For now, this can't happen because all callers hold mmap_sem
|
||||||
* for write. If this changes, we'll need a different solution.
|
* for write. If this changes, we'll need a different solution.
|
||||||
*/
|
*/
|
||||||
lockdep_assert_held_exclusive(¤t->mm->mmap_sem);
|
lockdep_assert_held_exclusive(&mm->mmap_sem);
|
||||||
|
|
||||||
if (atomic_inc_return(¤t->mm->context.perf_rdpmc_allowed) == 1)
|
if (atomic_inc_return(&mm->context.perf_rdpmc_allowed) == 1)
|
||||||
on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
|
on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void x86_pmu_event_unmapped(struct perf_event *event)
|
static void x86_pmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
|
||||||
{
|
{
|
||||||
if (!current->mm)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
|
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (atomic_dec_and_test(¤t->mm->context.perf_rdpmc_allowed))
|
if (atomic_dec_and_test(&mm->context.perf_rdpmc_allowed))
|
||||||
on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
|
on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int x86_pmu_event_idx(struct perf_event *event)
|
static int x86_pmu_event_idx(struct perf_event *event)
|
||||||
|
@ -310,8 +310,8 @@ struct pmu {
|
|||||||
* Notification that the event was mapped or unmapped. Called
|
* Notification that the event was mapped or unmapped. Called
|
||||||
* in the context of the mapping task.
|
* in the context of the mapping task.
|
||||||
*/
|
*/
|
||||||
void (*event_mapped) (struct perf_event *event); /*optional*/
|
void (*event_mapped) (struct perf_event *event, struct mm_struct *mm); /* optional */
|
||||||
void (*event_unmapped) (struct perf_event *event); /*optional*/
|
void (*event_unmapped) (struct perf_event *event, struct mm_struct *mm); /* optional */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flags for ->add()/->del()/ ->start()/->stop(). There are
|
* Flags for ->add()/->del()/ ->start()/->stop(). There are
|
||||||
|
@ -2217,6 +2217,33 @@ static int group_can_go_on(struct perf_event *event,
|
|||||||
return can_add_hw;
|
return can_add_hw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Complement to update_event_times(). This computes the tstamp_* values to
|
||||||
|
* continue 'enabled' state from @now, and effectively discards the time
|
||||||
|
* between the prior tstamp_stopped and now (as we were in the OFF state, or
|
||||||
|
* just switched (context) time base).
|
||||||
|
*
|
||||||
|
* This further assumes '@event->state == INACTIVE' (we just came from OFF) and
|
||||||
|
* cannot have been scheduled in yet. And going into INACTIVE state means
|
||||||
|
* '@event->tstamp_stopped = @now'.
|
||||||
|
*
|
||||||
|
* Thus given the rules of update_event_times():
|
||||||
|
*
|
||||||
|
* total_time_enabled = tstamp_stopped - tstamp_enabled
|
||||||
|
* total_time_running = tstamp_stopped - tstamp_running
|
||||||
|
*
|
||||||
|
* We can insert 'tstamp_stopped == now' and reverse them to compute new
|
||||||
|
* tstamp_* values.
|
||||||
|
*/
|
||||||
|
static void __perf_event_enable_time(struct perf_event *event, u64 now)
|
||||||
|
{
|
||||||
|
WARN_ON_ONCE(event->state != PERF_EVENT_STATE_INACTIVE);
|
||||||
|
|
||||||
|
event->tstamp_stopped = now;
|
||||||
|
event->tstamp_enabled = now - event->total_time_enabled;
|
||||||
|
event->tstamp_running = now - event->total_time_running;
|
||||||
|
}
|
||||||
|
|
||||||
static void add_event_to_ctx(struct perf_event *event,
|
static void add_event_to_ctx(struct perf_event *event,
|
||||||
struct perf_event_context *ctx)
|
struct perf_event_context *ctx)
|
||||||
{
|
{
|
||||||
@ -2224,9 +2251,12 @@ static void add_event_to_ctx(struct perf_event *event,
|
|||||||
|
|
||||||
list_add_event(event, ctx);
|
list_add_event(event, ctx);
|
||||||
perf_group_attach(event);
|
perf_group_attach(event);
|
||||||
event->tstamp_enabled = tstamp;
|
/*
|
||||||
event->tstamp_running = tstamp;
|
* We can be called with event->state == STATE_OFF when we create with
|
||||||
event->tstamp_stopped = tstamp;
|
* .disabled = 1. In that case the IOC_ENABLE will call this function.
|
||||||
|
*/
|
||||||
|
if (event->state == PERF_EVENT_STATE_INACTIVE)
|
||||||
|
__perf_event_enable_time(event, tstamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ctx_sched_out(struct perf_event_context *ctx,
|
static void ctx_sched_out(struct perf_event_context *ctx,
|
||||||
@ -2471,10 +2501,11 @@ static void __perf_event_mark_enabled(struct perf_event *event)
|
|||||||
u64 tstamp = perf_event_time(event);
|
u64 tstamp = perf_event_time(event);
|
||||||
|
|
||||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||||
event->tstamp_enabled = tstamp - event->total_time_enabled;
|
__perf_event_enable_time(event, tstamp);
|
||||||
list_for_each_entry(sub, &event->sibling_list, group_entry) {
|
list_for_each_entry(sub, &event->sibling_list, group_entry) {
|
||||||
|
/* XXX should not be > INACTIVE if event isn't */
|
||||||
if (sub->state >= PERF_EVENT_STATE_INACTIVE)
|
if (sub->state >= PERF_EVENT_STATE_INACTIVE)
|
||||||
sub->tstamp_enabled = tstamp - sub->total_time_enabled;
|
__perf_event_enable_time(sub, tstamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5090,7 +5121,7 @@ static void perf_mmap_open(struct vm_area_struct *vma)
|
|||||||
atomic_inc(&event->rb->aux_mmap_count);
|
atomic_inc(&event->rb->aux_mmap_count);
|
||||||
|
|
||||||
if (event->pmu->event_mapped)
|
if (event->pmu->event_mapped)
|
||||||
event->pmu->event_mapped(event);
|
event->pmu->event_mapped(event, vma->vm_mm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void perf_pmu_output_stop(struct perf_event *event);
|
static void perf_pmu_output_stop(struct perf_event *event);
|
||||||
@ -5113,7 +5144,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
|
|||||||
unsigned long size = perf_data_size(rb);
|
unsigned long size = perf_data_size(rb);
|
||||||
|
|
||||||
if (event->pmu->event_unmapped)
|
if (event->pmu->event_unmapped)
|
||||||
event->pmu->event_unmapped(event);
|
event->pmu->event_unmapped(event, vma->vm_mm);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rb->aux_mmap_count will always drop before rb->mmap_count and
|
* rb->aux_mmap_count will always drop before rb->mmap_count and
|
||||||
@ -5411,7 +5442,7 @@ aux_unlock:
|
|||||||
vma->vm_ops = &perf_mmap_vmops;
|
vma->vm_ops = &perf_mmap_vmops;
|
||||||
|
|
||||||
if (event->pmu->event_mapped)
|
if (event->pmu->event_mapped)
|
||||||
event->pmu->event_mapped(event);
|
event->pmu->event_mapped(event, vma->vm_mm);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user