Hi Peter, 

I am trying to understand this. Pardon me if any question is silly. 

I am not sure I fully understand the motivation here. I guess we
see problem when there are two (or more) independent hardware PMUs 
per cpu? Then on a given cpu, there are two (or more) 
perf_cpu_context, but only one task context? 

If this is correct (I really doubt...), I guess perf_rotate_context()
is the problem? And if this is still correct, this patch may not help,
as we are doing rotation for each perf_cpu_pmu_context? (or rotation 
per perf_event_context is the next step?). 

Or step back a little... I see two big changes:

1. struct perf_ctx_context is now per cpu (instead of per pmu per cpu);
2. one perf_event_ctxp per task_struct (instead of 2).  

I think #1 is a bigger change than #2. Is this correct? 


Of course, I could be totally lost. I will continue reading the code 
tomorrow. 

Could you please help me understand it better? 

Thanks,
Song

> On Oct 10, 2018, at 3:45 AM, Peter Zijlstra <[email protected]> wrote:
> 
> Hi all,
> 
> There have been various issues and limitations with the way perf uses
> (task) contexts to track events. Most notable is the single hardware PMU
> task context, which has resulted in a number of yucky things (both
> proposed and merged).
> Notably:
> 
> - HW breakpoint PMU
> - ARM big.little PMU
> - Intel Branch Monitoring PMU
> 
> Since we now track the events in RB trees, we can 'simply' add a pmu
> order to them and have them grouped that way, reducing to a single
> context. Of course, reality never quite works out that simple, and below
> ends up adding an intermediate data structure to bridge the context ->
> pmu mapping.
> 
> Something a little like:
> 
>              ,------------------------[1:n]---------------------.
>              V                                                  V
>    perf_event_context <-[1:n]-> perf_event_pmu_context <--- perf_event
>              ^                      ^     |                     |
>              `--------[1:n]---------'     `-[n:1]-> pmu <-[1:n]-'
> 
> This patch builds (provided you disable CGROUP_PERF), boots and survives
> perf-top without the machine catching fire.
> 
> There's still a fair bit of loose ends (look for XXX), but I think this
> is the direction we should be going.
> 
> Comments?
> 
> Not-Quite-Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
> ---
> arch/powerpc/perf/core-book3s.c |    4 
> arch/x86/events/core.c          |    4 
> arch/x86/events/intel/core.c    |    6 
> arch/x86/events/intel/ds.c      |    6 
> arch/x86/events/intel/lbr.c     |   16 
> arch/x86/events/perf_event.h    |    6 
> include/linux/perf_event.h      |   80 +-
> include/linux/sched.h           |    2 
> kernel/events/core.c            | 1412 
> ++++++++++++++++++++--------------------
> 9 files changed, 815 insertions(+), 721 deletions(-)
> 
> --- a/arch/powerpc/perf/core-book3s.c
> +++ b/arch/powerpc/perf/core-book3s.c
> @@ -125,7 +125,7 @@ static unsigned long ebb_switch_in(bool
> 
> static inline void power_pmu_bhrb_enable(struct perf_event *event) {}
> static inline void power_pmu_bhrb_disable(struct perf_event *event) {}
> -static void power_pmu_sched_task(struct perf_event_context *ctx, bool 
> sched_in) {}
> +static void power_pmu_sched_task(struct perf_event_pmu_context *pmu_ctx, 
> bool sched_in) {}
> static inline void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) {}
> static void pmao_restore_workaround(bool ebb) { }
> #endif /* CONFIG_PPC32 */
> @@ -395,7 +395,7 @@ static void power_pmu_bhrb_disable(struc
> /* Called from ctxsw to prevent one process's branch entries to
>  * mingle with the other process's entries during context switch.
>  */
> -static void power_pmu_sched_task(struct perf_event_context *ctx, bool 
> sched_in)
> +static void power_pmu_sched_task(struct perf_event_pmu_context *pmu_ctx, 
> bool sched_in)
> {
>       if (!ppmu->bhrb_nr)
>               return;
> --- a/arch/x86/events/core.c
> +++ b/arch/x86/events/core.c
> @@ -2286,10 +2286,10 @@ static const struct attribute_group *x86
>       NULL,
> };
> 
> -static void x86_pmu_sched_task(struct perf_event_context *ctx, bool sched_in)
> +static void x86_pmu_sched_task(struct perf_event_pmu_context *pmu_ctx, bool 
> sched_in)
> {
>       if (x86_pmu.sched_task)
> -             x86_pmu.sched_task(ctx, sched_in);
> +             x86_pmu.sched_task(pmu_ctx, sched_in);
> }
> 
> void perf_check_microcode(void)
> --- a/arch/x86/events/intel/core.c
> +++ b/arch/x86/events/intel/core.c
> @@ -3537,11 +3537,11 @@ static void intel_pmu_cpu_dying(int cpu)
>               disable_counter_freeze();
> }
> 
> -static void intel_pmu_sched_task(struct perf_event_context *ctx,
> +static void intel_pmu_sched_task(struct perf_event_pmu_context *pmu_ctx,
>                                bool sched_in)
> {
> -     intel_pmu_pebs_sched_task(ctx, sched_in);
> -     intel_pmu_lbr_sched_task(ctx, sched_in);
> +     intel_pmu_pebs_sched_task(pmu_ctx, sched_in);
> +     intel_pmu_lbr_sched_task(pmu_ctx, sched_in);
> }
> 
> PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63");
> --- a/arch/x86/events/intel/ds.c
> +++ b/arch/x86/events/intel/ds.c
> @@ -885,7 +885,7 @@ static inline bool pebs_needs_sched_cb(s
>       return cpuc->n_pebs && (cpuc->n_pebs == cpuc->n_large_pebs);
> }
> 
> -void intel_pmu_pebs_sched_task(struct perf_event_context *ctx, bool sched_in)
> +void intel_pmu_pebs_sched_task(struct perf_event_pmu_context *pmu_ctx, bool 
> sched_in)
> {
>       struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
> 
> @@ -947,7 +947,7 @@ void intel_pmu_pebs_add(struct perf_even
>       if (hwc->flags & PERF_X86_EVENT_LARGE_PEBS)
>               cpuc->n_large_pebs++;
> 
> -     pebs_update_state(needed_cb, cpuc, event->ctx->pmu);
> +     pebs_update_state(needed_cb, cpuc, event->pmu);
> }
> 
> void intel_pmu_pebs_enable(struct perf_event *event)
> @@ -991,7 +991,7 @@ void intel_pmu_pebs_del(struct perf_even
>       if (hwc->flags & PERF_X86_EVENT_LARGE_PEBS)
>               cpuc->n_large_pebs--;
> 
> -     pebs_update_state(needed_cb, cpuc, event->ctx->pmu);
> +     pebs_update_state(needed_cb, cpuc, event->pmu);
> }
> 
> void intel_pmu_pebs_disable(struct perf_event *event)
> --- a/arch/x86/events/intel/lbr.c
> +++ b/arch/x86/events/intel/lbr.c
> @@ -417,7 +417,7 @@ static void __intel_pmu_lbr_save(struct
>       cpuc->last_log_id = ++task_ctx->log_id;
> }
> 
> -void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in)
> +void intel_pmu_lbr_sched_task(struct perf_event_pmu_context *pmu_ctx, bool 
> sched_in)
> {
>       struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
>       struct x86_perf_task_context *task_ctx;
> @@ -430,7 +430,7 @@ void intel_pmu_lbr_sched_task(struct per
>        * the task was scheduled out, restore the stack. Otherwise flush
>        * the LBR stack.
>        */
> -     task_ctx = ctx ? ctx->task_ctx_data : NULL;
> +     task_ctx = pmu_ctx ? pmu_ctx->task_ctx_data : NULL;
>       if (task_ctx) {
>               if (sched_in)
>                       __intel_pmu_lbr_restore(task_ctx);
> @@ -464,8 +464,8 @@ void intel_pmu_lbr_add(struct perf_event
> 
>       cpuc->br_sel = event->hw.branch_reg.reg;
> 
> -     if (branch_user_callstack(cpuc->br_sel) && event->ctx->task_ctx_data) {
> -             task_ctx = event->ctx->task_ctx_data;
> +     if (branch_user_callstack(cpuc->br_sel) && 
> event->pmu_ctx->task_ctx_data) {
> +             task_ctx = event->pmu_ctx->task_ctx_data;
>               task_ctx->lbr_callstack_users++;
>       }
> 
> @@ -488,7 +488,7 @@ void intel_pmu_lbr_add(struct perf_event
>        * be 'new'. Conversely, a new event can get installed through the
>        * context switch path for the first time.
>        */
> -     perf_sched_cb_inc(event->ctx->pmu);
> +     perf_sched_cb_inc(event->pmu);
>       if (!cpuc->lbr_users++ && !event->total_time_running)
>               intel_pmu_lbr_reset();
> }
> @@ -502,14 +502,14 @@ void intel_pmu_lbr_del(struct perf_event
>               return;
> 
>       if (branch_user_callstack(cpuc->br_sel) &&
> -         event->ctx->task_ctx_data) {
> -             task_ctx = event->ctx->task_ctx_data;
> +         event->pmu_ctx->task_ctx_data) {
> +             task_ctx = event->pmu_ctx->task_ctx_data;
>               task_ctx->lbr_callstack_users--;
>       }
> 
>       cpuc->lbr_users--;
>       WARN_ON_ONCE(cpuc->lbr_users < 0);
> -     perf_sched_cb_dec(event->ctx->pmu);
> +     perf_sched_cb_dec(event->pmu);
> }
> 
> void intel_pmu_lbr_enable_all(bool pmi)
> --- a/arch/x86/events/perf_event.h
> +++ b/arch/x86/events/perf_event.h
> @@ -589,7 +589,7 @@ struct x86_pmu {
>       void            (*cpu_dead)(int cpu);
> 
>       void            (*check_microcode)(void);
> -     void            (*sched_task)(struct perf_event_context *ctx,
> +     void            (*sched_task)(struct perf_event_pmu_context *pmu_ctx,
>                                     bool sched_in);
> 
>       /*
> @@ -930,13 +930,13 @@ void intel_pmu_pebs_enable_all(void);
> 
> void intel_pmu_pebs_disable_all(void);
> 
> -void intel_pmu_pebs_sched_task(struct perf_event_context *ctx, bool 
> sched_in);
> +void intel_pmu_pebs_sched_task(struct perf_event_pmu_context *pmu_ctx, bool 
> sched_in);
> 
> void intel_pmu_auto_reload_read(struct perf_event *event);
> 
> void intel_ds_init(void);
> 
> -void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in);
> +void intel_pmu_lbr_sched_task(struct perf_event_pmu_context *pmu_ctx, bool 
> sched_in);
> 
> u64 lbr_from_signext_quirk_wr(u64 val);
> 
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -227,6 +227,7 @@ struct hw_perf_event {
> };
> 
> struct perf_event;
> +struct perf_event_pmu_context;
> 
> /*
>  * Common implementation detail of pmu::{start,commit,cancel}_txn
> @@ -263,7 +264,9 @@ struct pmu {
>       int                             capabilities;
> 
>       int * __percpu                  pmu_disable_count;
> -     struct perf_cpu_context * __percpu pmu_cpu_context;
> +     struct perf_cpu_pmu_context * __percpu cpu_pmu_context;
> +
> +
>       atomic_t                        exclusive_cnt; /* < 0: cpu; > 0: tsk */
>       int                             task_ctx_nr;
>       int                             hrtimer_interval_ms;
> @@ -398,7 +401,7 @@ struct pmu {
>       /*
>        * context-switches callback
>        */
> -     void (*sched_task)              (struct perf_event_context *ctx,
> +     void (*sched_task)              (struct perf_event_pmu_context *ctx,
>                                       bool sched_in);
>       /*
>        * PMU specific data size
> @@ -619,6 +622,7 @@ struct perf_event {
>       struct hw_perf_event            hw;
> 
>       struct perf_event_context       *ctx;
> +     struct perf_event_pmu_context   *pmu_ctx;
>       atomic_long_t                   refcount;
> 
>       /*
> @@ -698,6 +702,41 @@ struct perf_event {
> #endif /* CONFIG_PERF_EVENTS */
> };
> 
> +/*
> + *           ,------------------------[1:n]---------------------.
> + *           V                                                  V
> + * perf_event_context <-[1:n]-> perf_event_pmu_context <--- perf_event
> + *           ^                      ^     |                     |
> + *           `--------[1:n]---------'     `-[n:1]-> pmu <-[1:n]-'
> + *
> + *
> + * XXX destroy epc when empty
> + *   refcount, !rcu
> + *
> + * XXX epc locking
> + *
> + *   event->pmu_ctx          ctx->mutex && inactive
> + *   ctx->pmu_ctx_list               ctx->mutex && ctx->lock
> + *
> + */
> +struct perf_event_pmu_context {
> +     struct pmu                      *pmu;
> +     struct perf_event_context       *ctx;
> +
> +     struct list_head                pmu_ctx_entry;
> +
> +     struct list_head                pinned_active;
> +     struct list_head                flexible_active;
> +
> +     unsigned int                    embedded : 1;
> +
> +     unsigned int                    nr_events;
> +     unsigned int                    nr_active;
> +
> +     atomic_t                        refcount; /* event <-> epc */
> +
> +     void                            *task_ctx_data; /* pmu specific data */
> +};
> 
> struct perf_event_groups {
>       struct rb_root  tree;
> @@ -710,7 +749,6 @@ struct perf_event_groups {
>  * Used as a container for task events and CPU events as well:
>  */
> struct perf_event_context {
> -     struct pmu                      *pmu;
>       /*
>        * Protect the states of the events in the list,
>        * nr_active, and the list:
> @@ -723,20 +761,21 @@ struct perf_event_context {
>        */
>       struct mutex                    mutex;
> 
> -     struct list_head                active_ctx_list;
> +     struct list_head                pmu_ctx_list;
> +
>       struct perf_event_groups        pinned_groups;
>       struct perf_event_groups        flexible_groups;
>       struct list_head                event_list;
> 
> -     struct list_head                pinned_active;
> -     struct list_head                flexible_active;
> -
>       int                             nr_events;
>       int                             nr_active;
>       int                             is_active;
> +
> +     int                             nr_task_data;
>       int                             nr_stat;
>       int                             nr_freq;
>       int                             rotate_disable;
> +
>       atomic_t                        refcount;
>       struct task_struct              *task;
> 
> @@ -757,7 +796,6 @@ struct perf_event_context {
> #ifdef CONFIG_CGROUP_PERF
>       int                             nr_cgroups;      /* cgroup evts */
> #endif
> -     void                            *task_ctx_data; /* pmu specific data */
>       struct rcu_head                 rcu_head;
> };
> 
> @@ -767,12 +805,13 @@ struct perf_event_context {
>  */
> #define PERF_NR_CONTEXTS      4
> 
> -/**
> - * struct perf_event_cpu_context - per cpu event context structure
> - */
> -struct perf_cpu_context {
> -     struct perf_event_context       ctx;
> -     struct perf_event_context       *task_ctx;
> +struct perf_cpu_pmu_context {
> +     struct perf_event_pmu_context   epc;
> +     struct perf_event_pmu_context   *task_epc;
> +
> +     struct list_head                sched_cb_entry;
> +     int                             sched_cb_usage;
> +
>       int                             active_oncpu;
>       int                             exclusive;
> 
> @@ -780,15 +819,20 @@ struct perf_cpu_context {
>       struct hrtimer                  hrtimer;
>       ktime_t                         hrtimer_interval;
>       unsigned int                    hrtimer_active;
> +};
> +
> +/**
> + * struct perf_event_cpu_context - per cpu event context structure
> + */
> +struct perf_cpu_context {
> +     struct perf_event_context       ctx;
> +     struct perf_event_context       *task_ctx;
> 
> #ifdef CONFIG_CGROUP_PERF
>       struct perf_cgroup              *cgrp;
>       struct list_head                cgrp_cpuctx_entry;
> #endif
> 
> -     struct list_head                sched_cb_entry;
> -     int                             sched_cb_usage;
> -
>       int                             online;
> };
> 
> @@ -1022,7 +1066,7 @@ static inline int is_software_event(stru
>  */
> static inline int in_software_context(struct perf_event *event)
> {
> -     return event->ctx->pmu->task_ctx_nr == perf_sw_context;
> +     return event->pmu_ctx->pmu->task_ctx_nr == perf_sw_context;
> }
> 
> extern struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX];
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1000,7 +1000,7 @@ struct task_struct {
>       struct futex_pi_state           *pi_state_cache;
> #endif
> #ifdef CONFIG_PERF_EVENTS
> -     struct perf_event_context       *perf_event_ctxp[perf_nr_task_contexts];
> +     struct perf_event_context       *perf_event_ctxp;
>       struct mutex                    perf_event_mutex;
>       struct list_head                perf_event_list;
> #endif
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -143,12 +143,6 @@ static int cpu_function_call(int cpu, re
>       return data.ret;
> }
> 
> -static inline struct perf_cpu_context *
> -__get_cpu_context(struct perf_event_context *ctx)
> -{
> -     return this_cpu_ptr(ctx->pmu->pmu_cpu_context);
> -}
> -
> static void perf_ctx_lock(struct perf_cpu_context *cpuctx,
>                         struct perf_event_context *ctx)
> {
> @@ -172,6 +166,8 @@ static bool is_kernel_event(struct perf_
>       return READ_ONCE(event->owner) == TASK_TOMBSTONE;
> }
> 
> +static DEFINE_PER_CPU(struct perf_cpu_context, cpu_context);
> +
> /*
>  * On task ctx scheduling...
>  *
> @@ -205,7 +201,7 @@ static int event_function(void *info)
>       struct event_function_struct *efs = info;
>       struct perf_event *event = efs->event;
>       struct perf_event_context *ctx = event->ctx;
> -     struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
>       struct perf_event_context *task_ctx = cpuctx->task_ctx;
>       int ret = 0;
> 
> @@ -302,7 +298,7 @@ static void event_function_call(struct p
> static void event_function_local(struct perf_event *event, event_f func, void 
> *data)
> {
>       struct perf_event_context *ctx = event->ctx;
> -     struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
>       struct task_struct *task = READ_ONCE(ctx->task);
>       struct perf_event_context *task_ctx = NULL;
> 
> @@ -376,7 +372,6 @@ static DEFINE_MUTEX(perf_sched_mutex);
> static atomic_t perf_sched_count;
> 
> static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
> -static DEFINE_PER_CPU(int, perf_sched_cb_usages);
> static DEFINE_PER_CPU(struct pmu_event_list, pmu_sb_events);
> 
> static atomic_t nr_mmap_events __read_mostly;
> @@ -430,7 +425,7 @@ static void update_perf_cpu_limits(void)
>       WRITE_ONCE(perf_sample_allowed_ns, tmp);
> }
> 
> -static bool perf_rotate_context(struct perf_cpu_context *cpuctx);
> +static bool perf_rotate_context(struct perf_cpu_pmu_context *cpc);
> 
> int perf_proc_update_handler(struct ctl_table *table, int write,
>               void __user *buffer, size_t *lenp,
> @@ -555,13 +550,6 @@ void perf_sample_event_took(u64 sample_l
> 
> static atomic64_t perf_event_id;
> 
> -static void cpu_ctx_sched_out(struct perf_cpu_context *cpuctx,
> -                           enum event_type_t event_type);
> -
> -static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx,
> -                          enum event_type_t event_type,
> -                          struct task_struct *task);
> -
> static void update_context_time(struct perf_event_context *ctx);
> static u64 perf_event_time(struct perf_event *event);
> 
> @@ -810,7 +798,7 @@ static void perf_cgroup_switch(struct ta
>               perf_pmu_disable(cpuctx->ctx.pmu);
> 
>               if (mode & PERF_CGROUP_SWOUT) {
> -                     cpu_ctx_sched_out(cpuctx, EVENT_ALL);
> +                     ctx_sched_out(&cpuctx->ctx, EVENT_ALL);
>                       /*
>                        * must not be done before ctxswout due
>                        * to event_filter_match() in event_sched_out()
> @@ -827,9 +815,8 @@ static void perf_cgroup_switch(struct ta
>                        * we pass the cpuctx->ctx to perf_cgroup_from_task()
>                        * because cgorup events are only per-cpu
>                        */
> -                     cpuctx->cgrp = perf_cgroup_from_task(task,
> -                                                          &cpuctx->ctx);
> -                     cpu_ctx_sched_in(cpuctx, EVENT_ALL, task);
> +                     cpuctx->cgrp = perf_cgroup_from_task(task, 
> &cpuctx->ctx);
> +                     ctx_sched_in(&cpuctx->ctx, EVENT_ALL, task);
>               }
>               perf_pmu_enable(cpuctx->ctx.pmu);
>               perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
> @@ -1063,34 +1050,30 @@ list_update_cgroup_event(struct perf_eve
>  */
> static enum hrtimer_restart perf_mux_hrtimer_handler(struct hrtimer *hr)
> {
> -     struct perf_cpu_context *cpuctx;
> +     struct perf_cpu_pmu_context *cpc;
>       bool rotations;
> 
>       lockdep_assert_irqs_disabled();
> 
> -     cpuctx = container_of(hr, struct perf_cpu_context, hrtimer);
> -     rotations = perf_rotate_context(cpuctx);
> +     cpc = container_of(hr, struct perf_cpu_pmu_context, hrtimer);
> +     rotations = perf_rotate_context(cpc);
> 
> -     raw_spin_lock(&cpuctx->hrtimer_lock);
> +     raw_spin_lock(&cpc->hrtimer_lock);
>       if (rotations)
> -             hrtimer_forward_now(hr, cpuctx->hrtimer_interval);
> +             hrtimer_forward_now(hr, cpc->hrtimer_interval);
>       else
> -             cpuctx->hrtimer_active = 0;
> -     raw_spin_unlock(&cpuctx->hrtimer_lock);
> +             cpc->hrtimer_active = 0;
> +     raw_spin_unlock(&cpc->hrtimer_lock);
> 
>       return rotations ? HRTIMER_RESTART : HRTIMER_NORESTART;
> }
> 
> -static void __perf_mux_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
> +static void __perf_mux_hrtimer_init(struct perf_cpu_pmu_context *cpc, int 
> cpu)
> {
> -     struct hrtimer *timer = &cpuctx->hrtimer;
> -     struct pmu *pmu = cpuctx->ctx.pmu;
> +     struct hrtimer *timer = &cpc->hrtimer;
> +     struct pmu *pmu = cpc->epc.pmu;
>       u64 interval;
> 
> -     /* no multiplexing needed for SW PMU */
> -     if (pmu->task_ctx_nr == perf_sw_context)
> -             return;
> -
>       /*
>        * check default is sane, if not set then force to
>        * default interval (1/tick)
> @@ -1099,30 +1082,25 @@ static void __perf_mux_hrtimer_init(stru
>       if (interval < 1)
>               interval = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;
> 
> -     cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);
> +     cpc->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);
> 
> -     raw_spin_lock_init(&cpuctx->hrtimer_lock);
> +     raw_spin_lock_init(&cpc->hrtimer_lock);
>       hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
>       timer->function = perf_mux_hrtimer_handler;
> }
> 
> -static int perf_mux_hrtimer_restart(struct perf_cpu_context *cpuctx)
> +static int perf_mux_hrtimer_restart(struct perf_cpu_pmu_context *cpc)
> {
> -     struct hrtimer *timer = &cpuctx->hrtimer;
> -     struct pmu *pmu = cpuctx->ctx.pmu;
> +     struct hrtimer *timer = &cpc->hrtimer;
>       unsigned long flags;
> 
> -     /* not for SW PMU */
> -     if (pmu->task_ctx_nr == perf_sw_context)
> -             return 0;
> -
> -     raw_spin_lock_irqsave(&cpuctx->hrtimer_lock, flags);
> -     if (!cpuctx->hrtimer_active) {
> -             cpuctx->hrtimer_active = 1;
> -             hrtimer_forward_now(timer, cpuctx->hrtimer_interval);
> +     raw_spin_lock_irqsave(&cpc->hrtimer_lock, flags);
> +     if (!cpc->hrtimer_active) {
> +             cpc->hrtimer_active = 1;
> +             hrtimer_forward_now(timer, cpc->hrtimer_interval);
>               hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
>       }
> -     raw_spin_unlock_irqrestore(&cpuctx->hrtimer_lock, flags);
> +     raw_spin_unlock_irqrestore(&cpc->hrtimer_lock, flags);
> 
>       return 0;
> }
> @@ -1141,32 +1119,25 @@ void perf_pmu_enable(struct pmu *pmu)
>               pmu->pmu_enable(pmu);
> }
> 
> -static DEFINE_PER_CPU(struct list_head, active_ctx_list);
> -
> -/*
> - * perf_event_ctx_activate(), perf_event_ctx_deactivate(), and
> - * perf_event_task_tick() are fully serialized because they're strictly cpu
> - * affine and perf_event_ctx{activate,deactivate} are called with IRQs
> - * disabled, while perf_event_task_tick is called from IRQ context.
> - */
> -static void perf_event_ctx_activate(struct perf_event_context *ctx)
> +void perf_assert_pmu_disabled(struct pmu *pmu)
> {
> -     struct list_head *head = this_cpu_ptr(&active_ctx_list);
> -
> -     lockdep_assert_irqs_disabled();
> +     WARN_ON_ONCE(*this_cpu_ptr(pmu->pmu_disable_count) == 0);
> +}
> 
> -     WARN_ON(!list_empty(&ctx->active_ctx_list));
> +void perf_ctx_disable(struct perf_event_context *ctx)
> +{
> +     struct perf_event_pmu_context *pmu_ctx;
> 
> -     list_add(&ctx->active_ctx_list, head);
> +     list_for_each_entry(pmu_ctx, &ctx->pmu_ctx_list, pmu_ctx_entry)
> +             perf_pmu_disable(pmu_ctx->pmu);
> }
> 
> -static void perf_event_ctx_deactivate(struct perf_event_context *ctx)
> +void perf_ctx_enable(struct perf_event_context *ctx)
> {
> -     lockdep_assert_irqs_disabled();
> +     struct perf_event_pmu_context *pmu_ctx;
> 
> -     WARN_ON(list_empty(&ctx->active_ctx_list));
> -
> -     list_del_init(&ctx->active_ctx_list);
> +     list_for_each_entry(pmu_ctx, &ctx->pmu_ctx_list, pmu_ctx_entry)
> +             perf_pmu_enable(pmu_ctx->pmu);
> }
> 
> static void get_ctx(struct perf_event_context *ctx)
> @@ -1179,7 +1150,6 @@ static void free_ctx(struct rcu_head *he
>       struct perf_event_context *ctx;
> 
>       ctx = container_of(head, struct perf_event_context, rcu_head);
> -     kfree(ctx->task_ctx_data);
>       kfree(ctx);
> }
> 
> @@ -1363,7 +1333,7 @@ static u64 primary_event_id(struct perf_
>  * the context could get moved to another task.
>  */
> static struct perf_event_context *
> -perf_lock_task_context(struct task_struct *task, int ctxn, unsigned long 
> *flags)
> +perf_lock_task_context(struct task_struct *task, unsigned long *flags)
> {
>       struct perf_event_context *ctx;
> 
> @@ -1379,7 +1349,7 @@ perf_lock_task_context(struct task_struc
>        */
>       local_irq_save(*flags);
>       rcu_read_lock();
> -     ctx = rcu_dereference(task->perf_event_ctxp[ctxn]);
> +     ctx = rcu_dereference(task->perf_event_ctxp);
>       if (ctx) {
>               /*
>                * If this context is a clone of another, it might
> @@ -1392,7 +1362,7 @@ perf_lock_task_context(struct task_struc
>                * can't get swapped on us any more.
>                */
>               raw_spin_lock(&ctx->lock);
> -             if (ctx != rcu_dereference(task->perf_event_ctxp[ctxn])) {
> +             if (ctx != rcu_dereference(task->perf_event_ctxp)) {
>                       raw_spin_unlock(&ctx->lock);
>                       rcu_read_unlock();
>                       local_irq_restore(*flags);
> @@ -1419,12 +1389,12 @@ perf_lock_task_context(struct task_struc
>  * reference count so that the context can't get freed.
>  */
> static struct perf_event_context *
> -perf_pin_task_context(struct task_struct *task, int ctxn)
> +perf_pin_task_context(struct task_struct *task)
> {
>       struct perf_event_context *ctx;
>       unsigned long flags;
> 
> -     ctx = perf_lock_task_context(task, ctxn, &flags);
> +     ctx = perf_lock_task_context(task, &flags);
>       if (ctx) {
>               ++ctx->pin_count;
>               raw_spin_unlock_irqrestore(&ctx->lock, flags);
> @@ -1528,6 +1498,11 @@ perf_event_groups_less(struct perf_event
>       if (left->cpu > right->cpu)
>               return false;
> 
> +     if (left->pmu_ctx->pmu < right->pmu_ctx->pmu)
> +             return true;
> +     if (left->pmu_ctx->pmu > right->pmu_ctx->pmu)
> +             return false;
> +
>       if (left->group_index < right->group_index)
>               return true;
>       if (left->group_index > right->group_index)
> @@ -1610,7 +1585,7 @@ del_event_from_groups(struct perf_event
>  * Get the leftmost event in the @cpu subtree.
>  */
> static struct perf_event *
> -perf_event_groups_first(struct perf_event_groups *groups, int cpu)
> +perf_event_groups_first(struct perf_event_groups *groups, int cpu, struct 
> pmu *pmu)
> {
>       struct perf_event *node_event = NULL, *match = NULL;
>       struct rb_node *node = groups->tree.rb_node;
> @@ -1623,8 +1598,19 @@ perf_event_groups_first(struct perf_even
>               } else if (cpu > node_event->cpu) {
>                       node = node->rb_right;
>               } else {
> -                     match = node_event;
> -                     node = node->rb_left;
> +                     if (pmu) {
> +                             if (pmu < node_event->pmu_ctx->pmu) {
> +                                     node = node->rb_left;
> +                             } else if (pmu > node_event->pmu_ctx->pmu) {
> +                                     node = node->rb_right;
> +                             } else  {
> +                                     match = node_event;
> +                                     node = node->rb_left;
> +                             }
> +                     } else {
> +                             match = node_event;
> +                             node = node->rb_left;
> +                     }
>               }
>       }
> 
> @@ -1635,13 +1621,17 @@ perf_event_groups_first(struct perf_even
>  * Like rb_entry_next_safe() for the @cpu subtree.
>  */
> static struct perf_event *
> -perf_event_groups_next(struct perf_event *event)
> +perf_event_groups_next(struct perf_event *event, struct pmu *pmu)
> {
>       struct perf_event *next;
> 
>       next = rb_entry_safe(rb_next(&event->group_node), typeof(*event), 
> group_node);
> -     if (next && next->cpu == event->cpu)
> +     if (next && next->cpu == event->cpu) {
> +             if (pmu && next->pmu_ctx->pmu != pmu)
> +                     return NULL;
> +
>               return next;
> +     }
> 
>       return NULL;
> }
> @@ -1687,6 +1677,8 @@ list_add_event(struct perf_event *event,
>               ctx->nr_stat++;
> 
>       ctx->generation++;
> +
> +     event->pmu_ctx->nr_events++;
> }
> 
> /*
> @@ -1883,6 +1875,8 @@ list_del_event(struct perf_event *event,
>               perf_event_set_state(event, PERF_EVENT_STATE_OFF);
> 
>       ctx->generation++;
> +
> +     event->pmu_ctx->nr_events--;
> }
> 
> static void perf_group_detach(struct perf_event *event)
> @@ -1926,8 +1920,9 @@ static void perf_group_detach(struct per
>                       add_event_to_groups(sibling, event->ctx);
> 
>                       if (sibling->state == PERF_EVENT_STATE_ACTIVE) {
> +                             struct perf_event_pmu_context *pmu_ctx = 
> event->pmu_ctx;
>                               struct list_head *list = sibling->attr.pinned ?
> -                                     &ctx->pinned_active : 
> &ctx->flexible_active;
> +                                     &pmu_ctx->pinned_active : 
> &pmu_ctx->flexible_active;
> 
>                               list_add_tail(&sibling->active_list, list);
>                       }
> @@ -1983,12 +1978,14 @@ event_filter_match(struct perf_event *ev
> }
> 
> static void
> -event_sched_out(struct perf_event *event,
> -               struct perf_cpu_context *cpuctx,
> -               struct perf_event_context *ctx)
> +event_sched_out(struct perf_event *event, struct perf_event_context *ctx)
> {
> +     struct perf_event_pmu_context *epc = event->pmu_ctx;
> +     struct perf_cpu_pmu_context *cpc = 
> this_cpu_ptr(epc->pmu->cpu_pmu_context);
>       enum perf_event_state state = PERF_EVENT_STATE_INACTIVE;
> 
> +     // XXX cpc serialization, probably per-cpu IRQ disabled
> +
>       WARN_ON_ONCE(event->ctx != ctx);
>       lockdep_assert_held(&ctx->lock);
> 
> @@ -2014,41 +2011,35 @@ event_sched_out(struct perf_event *event
>       perf_event_set_state(event, state);
> 
>       if (!is_software_event(event))
> -             cpuctx->active_oncpu--;
> +             cpc->active_oncpu--;
>       if (!--ctx->nr_active)
> -             perf_event_ctx_deactivate(ctx);
> +             ;
> +     event->pmu_ctx->nr_active--;
>       if (event->attr.freq && event->attr.sample_freq)
>               ctx->nr_freq--;
> -     if (event->attr.exclusive || !cpuctx->active_oncpu)
> -             cpuctx->exclusive = 0;
> +     if (event->attr.exclusive || !cpc->active_oncpu)
> +             cpc->exclusive = 0;
> 
>       perf_pmu_enable(event->pmu);
> }
> 
> static void
> -group_sched_out(struct perf_event *group_event,
> -             struct perf_cpu_context *cpuctx,
> -             struct perf_event_context *ctx)
> +group_sched_out(struct perf_event *group_event, struct perf_event_context 
> *ctx)
> {
>       struct perf_event *event;
> 
>       if (group_event->state != PERF_EVENT_STATE_ACTIVE)
>               return;
> 
> -     perf_pmu_disable(ctx->pmu);
> +     perf_assert_pmu_disabled(group_event->pmu_ctx->pmu);
> 
> -     event_sched_out(group_event, cpuctx, ctx);
> +     event_sched_out(group_event, ctx);
> 
>       /*
>        * Schedule out siblings (if any):
>        */
>       for_each_sibling_event(event, group_event)
> -             event_sched_out(event, cpuctx, ctx);
> -
> -     perf_pmu_enable(ctx->pmu);
> -
> -     if (group_event->attr.exclusive)
> -             cpuctx->exclusive = 0;
> +             event_sched_out(event, ctx);
> }
> 
> #define DETACH_GROUP  0x01UL
> @@ -2072,7 +2063,7 @@ __perf_remove_from_context(struct perf_e
>               update_cgrp_time_from_cpuctx(cpuctx);
>       }
> 
> -     event_sched_out(event, cpuctx, ctx);
> +     event_sched_out(event, ctx);
>       if (flags & DETACH_GROUP)
>               perf_group_detach(event);
>       list_del_event(event, ctx);
> @@ -2139,12 +2130,16 @@ static void __perf_event_disable(struct
>               update_cgrp_time_from_event(event);
>       }
> 
> +     perf_pmu_disable(event->pmu_ctx->pmu);
> +
>       if (event == event->group_leader)
> -             group_sched_out(event, cpuctx, ctx);
> +             group_sched_out(event, ctx);
>       else
> -             event_sched_out(event, cpuctx, ctx);
> +             event_sched_out(event, ctx);
> 
>       perf_event_set_state(event, PERF_EVENT_STATE_OFF);
> +
> +     perf_pmu_enable(event->pmu_ctx->pmu);
> }
> 
> /*
> @@ -2240,10 +2235,10 @@ static void perf_log_throttle(struct per
> static void perf_log_itrace_start(struct perf_event *event);
> 
> static int
> -event_sched_in(struct perf_event *event,
> -              struct perf_cpu_context *cpuctx,
> -              struct perf_event_context *ctx)
> +event_sched_in(struct perf_event *event, struct perf_event_context *ctx)
> {
> +     struct perf_event_pmu_context *epc = event->pmu_ctx;
> +     struct perf_cpu_pmu_context *cpc = 
> this_cpu_ptr(epc->pmu->cpu_pmu_context);
>       int ret = 0;
> 
>       lockdep_assert_held(&ctx->lock);
> @@ -2284,14 +2279,15 @@ event_sched_in(struct perf_event *event,
>       }
> 
>       if (!is_software_event(event))
> -             cpuctx->active_oncpu++;
> +             cpc->active_oncpu++;
>       if (!ctx->nr_active++)
> -             perf_event_ctx_activate(ctx);
> +             ;
> +     event->pmu_ctx->nr_active++;
>       if (event->attr.freq && event->attr.sample_freq)
>               ctx->nr_freq++;
> 
>       if (event->attr.exclusive)
> -             cpuctx->exclusive = 1;
> +             cpc->exclusive = 1;
> 
> out:
>       perf_pmu_enable(event->pmu);
> @@ -2300,21 +2296,19 @@ event_sched_in(struct perf_event *event,
> }
> 
> static int
> -group_sched_in(struct perf_event *group_event,
> -            struct perf_cpu_context *cpuctx,
> -            struct perf_event_context *ctx)
> +group_sched_in(struct perf_event *group_event, struct perf_event_context 
> *ctx)
> {
>       struct perf_event *event, *partial_group = NULL;
> -     struct pmu *pmu = ctx->pmu;
> +     struct pmu *pmu = group_event->pmu_ctx->pmu;
> 
>       if (group_event->state == PERF_EVENT_STATE_OFF)
>               return 0;
> 
>       pmu->start_txn(pmu, PERF_PMU_TXN_ADD);
> 
> -     if (event_sched_in(group_event, cpuctx, ctx)) {
> +     if (event_sched_in(group_event, ctx)) {
>               pmu->cancel_txn(pmu);
> -             perf_mux_hrtimer_restart(cpuctx);
> +             perf_mux_hrtimer_restart(this_cpu_ptr(pmu->cpu_pmu_context));
>               return -EAGAIN;
>       }
> 
> @@ -2322,7 +2316,7 @@ group_sched_in(struct perf_event *group_
>        * Schedule in siblings as one group (if any):
>        */
>       for_each_sibling_event(event, group_event) {
> -             if (event_sched_in(event, cpuctx, ctx)) {
> +             if (event_sched_in(event, ctx)) {
>                       partial_group = event;
>                       goto group_error;
>               }
> @@ -2341,13 +2335,13 @@ group_sched_in(struct perf_event *group_
>               if (event == partial_group)
>                       break;
> 
> -             event_sched_out(event, cpuctx, ctx);
> +             event_sched_out(event, ctx);
>       }
> -     event_sched_out(group_event, cpuctx, ctx);
> +     event_sched_out(group_event, ctx);
> 
>       pmu->cancel_txn(pmu);
> 
> -     perf_mux_hrtimer_restart(cpuctx);
> +     perf_mux_hrtimer_restart(this_cpu_ptr(pmu->cpu_pmu_context));
> 
>       return -EAGAIN;
> }
> @@ -2355,10 +2349,11 @@ group_sched_in(struct perf_event *group_
> /*
>  * Work out whether we can put this event group on the CPU now.
>  */
> -static int group_can_go_on(struct perf_event *event,
> -                        struct perf_cpu_context *cpuctx,
> -                        int can_add_hw)
> +static int group_can_go_on(struct perf_event *event, int can_add_hw)
> {
> +     struct perf_event_pmu_context *epc = event->pmu_ctx;
> +     struct perf_cpu_pmu_context *cpc = 
> this_cpu_ptr(epc->pmu->cpu_pmu_context);
> +
>       /*
>        * Groups consisting entirely of software events can always go on.
>        */
> @@ -2368,13 +2363,13 @@ static int group_can_go_on(struct perf_e
>        * If an exclusive group is already on, no other hardware
>        * events can go on.
>        */
> -     if (cpuctx->exclusive)
> +     if (cpc->exclusive)
>               return 0;
>       /*
>        * If this group is exclusive and there are already
>        * events on the CPU, it can't go on.
>        */
> -     if (event->attr.exclusive && cpuctx->active_oncpu)
> +     if (event->attr.exclusive && cpc->active_oncpu)
>               return 0;
>       /*
>        * Otherwise, try to add it if all previous groups were able
> @@ -2391,37 +2386,36 @@ static void add_event_to_ctx(struct perf
> }
> 
> static void ctx_sched_out(struct perf_event_context *ctx,
> -                       struct perf_cpu_context *cpuctx,
>                         enum event_type_t event_type);
> static void
> ctx_sched_in(struct perf_event_context *ctx,
> -          struct perf_cpu_context *cpuctx,
>            enum event_type_t event_type,
>            struct task_struct *task);
> 
> -static void task_ctx_sched_out(struct perf_cpu_context *cpuctx,
> -                            struct perf_event_context *ctx,
> +static void task_ctx_sched_out(struct perf_event_context *ctx,
>                              enum event_type_t event_type)
> {
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
> +
>       if (!cpuctx->task_ctx)
>               return;
> 
>       if (WARN_ON_ONCE(ctx != cpuctx->task_ctx))
>               return;
> 
> -     ctx_sched_out(ctx, cpuctx, event_type);
> +     ctx_sched_out(ctx, event_type);
> }
> 
> static void perf_event_sched_in(struct perf_cpu_context *cpuctx,
>                               struct perf_event_context *ctx,
>                               struct task_struct *task)
> {
> -     cpu_ctx_sched_in(cpuctx, EVENT_PINNED, task);
> +     ctx_sched_in(&cpuctx->ctx, EVENT_PINNED, task);
>       if (ctx)
> -             ctx_sched_in(ctx, cpuctx, EVENT_PINNED, task);
> -     cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE, task);
> +             ctx_sched_in(ctx, EVENT_PINNED, task);
> +     ctx_sched_in(&cpuctx->ctx, EVENT_FLEXIBLE, task);
>       if (ctx)
> -             ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE, task);
> +             ctx_sched_in(ctx, EVENT_FLEXIBLE, task);
> }
> 
> /*
> @@ -2438,12 +2432,12 @@ static void perf_event_sched_in(struct p
>  * This can be called after a batch operation on task events, in which case
>  * event_type is a bit mask of the types of events involved. For CPU events,
>  * event_type is only either EVENT_PINNED or EVENT_FLEXIBLE.
> + *
>  */
> static void ctx_resched(struct perf_cpu_context *cpuctx,
>                       struct perf_event_context *task_ctx,
>                       enum event_type_t event_type)
> {
> -     enum event_type_t ctx_event_type;
>       bool cpu_event = !!(event_type & EVENT_CPU);
> 
>       /*
> @@ -2453,11 +2447,13 @@ static void ctx_resched(struct perf_cpu_
>       if (event_type & EVENT_PINNED)
>               event_type |= EVENT_FLEXIBLE;
> 
> -     ctx_event_type = event_type & EVENT_ALL;
> +     event_type &= EVENT_ALL;
> 
> -     perf_pmu_disable(cpuctx->ctx.pmu);
> -     if (task_ctx)
> -             task_ctx_sched_out(cpuctx, task_ctx, event_type);
> +     perf_ctx_disable(&cpuctx->ctx);
> +     if (task_ctx) {
> +             perf_ctx_disable(task_ctx);
> +             task_ctx_sched_out(task_ctx, event_type);
> +     }
> 
>       /*
>        * Decide which cpu ctx groups to schedule out based on the types
> @@ -2467,12 +2463,15 @@ static void ctx_resched(struct perf_cpu_
>        *  - otherwise, do nothing more.
>        */
>       if (cpu_event)
> -             cpu_ctx_sched_out(cpuctx, ctx_event_type);
> -     else if (ctx_event_type & EVENT_PINNED)
> -             cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
> +             ctx_sched_out(&cpuctx->ctx, event_type);
> +     else if (event_type & EVENT_PINNED)
> +             ctx_sched_out(&cpuctx->ctx, EVENT_FLEXIBLE);
> 
>       perf_event_sched_in(cpuctx, task_ctx, current);
> -     perf_pmu_enable(cpuctx->ctx.pmu);
> +
> +     perf_ctx_enable(&cpuctx->ctx);
> +     if (task_ctx)
> +             perf_ctx_enable(task_ctx);
> }
> 
> /*
> @@ -2485,7 +2484,7 @@ static int  __perf_install_in_context(vo
> {
>       struct perf_event *event = info;
>       struct perf_event_context *ctx = event->ctx;
> -     struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
>       struct perf_event_context *task_ctx = cpuctx->task_ctx;
>       bool reprogram = true;
>       int ret = 0;
> @@ -2527,7 +2526,7 @@ static int  __perf_install_in_context(vo
> #endif
> 
>       if (reprogram) {
> -             ctx_sched_out(ctx, cpuctx, EVENT_TIME);
> +             ctx_sched_out(ctx, EVENT_TIME);
>               add_event_to_ctx(event, ctx);
>               ctx_resched(cpuctx, task_ctx, get_event_type(event));
>       } else {
> @@ -2648,7 +2647,7 @@ static void __perf_event_enable(struct p
>               return;
> 
>       if (ctx->is_active)
> -             ctx_sched_out(ctx, cpuctx, EVENT_TIME);
> +             ctx_sched_out(ctx, EVENT_TIME);
> 
>       perf_event_set_state(event, PERF_EVENT_STATE_INACTIVE);
> 
> @@ -2656,7 +2655,7 @@ static void __perf_event_enable(struct p
>               return;
> 
>       if (!event_filter_match(event)) {
> -             ctx_sched_in(ctx, cpuctx, EVENT_TIME, current);
> +             ctx_sched_in(ctx, EVENT_TIME, current);
>               return;
>       }
> 
> @@ -2665,7 +2664,7 @@ static void __perf_event_enable(struct p
>        * then don't put it on unless the group is on.
>        */
>       if (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE) {
> -             ctx_sched_in(ctx, cpuctx, EVENT_TIME, current);
> +             ctx_sched_in(ctx, EVENT_TIME, current);
>               return;
>       }
> 
> @@ -2889,11 +2888,46 @@ static int perf_event_modify_attr(struct
>       }
> }
> 
> -static void ctx_sched_out(struct perf_event_context *ctx,
> -                       struct perf_cpu_context *cpuctx,
> -                       enum event_type_t event_type)
> +static void __pmu_ctx_sched_out(struct perf_event_pmu_context *pmu_ctx,
> +                             enum event_type_t event_type)
> {
> +     struct perf_event_context *ctx = pmu_ctx->ctx;
>       struct perf_event *event, *tmp;
> +     struct pmu *pmu = pmu_ctx->pmu;
> +
> +     if (ctx->task && !ctx->is_active) {
> +             struct perf_cpu_pmu_context *cpc;
> +
> +             cpc = this_cpu_ptr(pmu->cpu_pmu_context);
> +             WARN_ON_ONCE(cpc->task_epc != pmu_ctx);
> +             cpc->task_epc = NULL;
> +     }
> +
> +     if (!event_type)
> +             return;
> +
> +     perf_pmu_disable(pmu);
> +     if (event_type & EVENT_PINNED) {
> +             list_for_each_entry_safe(event, tmp,
> +                             &pmu_ctx->pinned_active,
> +                             active_list)
> +                     group_sched_out(event, ctx);
> +     }
> +
> +     if (event_type & EVENT_FLEXIBLE) {
> +             list_for_each_entry_safe(event, tmp,
> +                             &pmu_ctx->flexible_active,
> +                             active_list)
> +                     group_sched_out(event, ctx);
> +     }
> +     perf_pmu_enable(pmu);
> +}
> +
> +static void
> +ctx_sched_out(struct perf_event_context *ctx, enum event_type_t event_type)
> +{
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
> +     struct perf_event_pmu_context *pmu_ctx;
>       int is_active = ctx->is_active;
> 
>       lockdep_assert_held(&ctx->lock);
> @@ -2936,20 +2970,8 @@ static void ctx_sched_out(struct perf_ev
> 
>       is_active ^= ctx->is_active; /* changed bits */
> 
> -     if (!ctx->nr_active || !(is_active & EVENT_ALL))
> -             return;
> -
> -     perf_pmu_disable(ctx->pmu);
> -     if (is_active & EVENT_PINNED) {
> -             list_for_each_entry_safe(event, tmp, &ctx->pinned_active, 
> active_list)
> -                     group_sched_out(event, cpuctx, ctx);
> -     }
> -
> -     if (is_active & EVENT_FLEXIBLE) {
> -             list_for_each_entry_safe(event, tmp, &ctx->flexible_active, 
> active_list)
> -                     group_sched_out(event, cpuctx, ctx);
> -     }
> -     perf_pmu_enable(ctx->pmu);
> +     list_for_each_entry(pmu_ctx, &ctx->pmu_ctx_list, pmu_ctx_entry)
> +             __pmu_ctx_sched_out(pmu_ctx, is_active);
> }
> 
> /*
> @@ -3054,10 +3076,34 @@ static void perf_event_sync_stat(struct
>       }
> }
> 
> -static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
> -                                      struct task_struct *next)
> +static void perf_event_swap_task_ctx_data(struct perf_event_context 
> *prev_ctx,
> +                                       struct perf_event_context *next_ctx)
> +{
> +     struct perf_event_pmu_context *prev_epc, *next_epc;
> +
> +     if (!prev_ctx->nr_task_data)
> +             return;
> +
> +     prev_epc = list_first_entry(&prev_ctx->pmu_ctx_list,
> +                                 struct perf_event_pmu_context,
> +                                 pmu_ctx_entry);
> +     next_epc = list_first_entry(&next_ctx->pmu_ctx_list,
> +                                 struct perf_event_pmu_context,
> +                                 pmu_ctx_entry);
> +
> +     while (&prev_epc->pmu_ctx_entry != &prev_ctx->pmu_ctx_list &&
> +            &next_epc->pmu_ctx_entry != &next_ctx->pmu_ctx_list) {
> +
> +             WARN_ON_ONCE(prev_epc->pmu != next_epc->pmu);
> +
> +             swap(prev_epc->task_ctx_data, next_epc->task_ctx_data);
> +     }
> +}
> +
> +static void
> +perf_event_context_sched_out(struct task_struct *task, struct task_struct 
> *next)
> {
> -     struct perf_event_context *ctx = task->perf_event_ctxp[ctxn];
> +     struct perf_event_context *ctx = task->perf_event_ctxp;
>       struct perf_event_context *next_ctx;
>       struct perf_event_context *parent, *next_parent;
>       struct perf_cpu_context *cpuctx;
> @@ -3066,12 +3112,12 @@ static void perf_event_context_sched_out
>       if (likely(!ctx))
>               return;
> 
> -     cpuctx = __get_cpu_context(ctx);
> +     cpuctx = this_cpu_ptr(&cpu_context);
>       if (!cpuctx->task_ctx)
>               return;
> 
>       rcu_read_lock();
> -     next_ctx = next->perf_event_ctxp[ctxn];
> +     next_ctx = rcu_dereference(next->perf_event_ctxp);
>       if (!next_ctx)
>               goto unlock;
> 
> @@ -3098,7 +3144,7 @@ static void perf_event_context_sched_out
>                       WRITE_ONCE(ctx->task, next);
>                       WRITE_ONCE(next_ctx->task, task);
> 
> -                     swap(ctx->task_ctx_data, next_ctx->task_ctx_data);
> +                     perf_event_swap_task_ctx_data(ctx, next_ctx);
> 
>                       /*
>                        * RCU_INIT_POINTER here is safe because we've not
> @@ -3107,8 +3153,8 @@ static void perf_event_context_sched_out
>                        * since those values are always verified under
>                        * ctx->lock which we're now holding.
>                        */
> -                     RCU_INIT_POINTER(task->perf_event_ctxp[ctxn], next_ctx);
> -                     RCU_INIT_POINTER(next->perf_event_ctxp[ctxn], ctx);
> +                     RCU_INIT_POINTER(task->perf_event_ctxp, next_ctx);
> +                     RCU_INIT_POINTER(next->perf_event_ctxp, ctx);
> 
>                       do_switch = 0;
> 
> @@ -3122,31 +3168,34 @@ static void perf_event_context_sched_out
> 
>       if (do_switch) {
>               raw_spin_lock(&ctx->lock);
> -             task_ctx_sched_out(cpuctx, ctx, EVENT_ALL);
> +             task_ctx_sched_out(ctx, EVENT_ALL);
>               raw_spin_unlock(&ctx->lock);
>       }
> }
> 
> static DEFINE_PER_CPU(struct list_head, sched_cb_list);
> +static DEFINE_PER_CPU(int, perf_sched_cb_usages);
> 
> void perf_sched_cb_dec(struct pmu *pmu)
> {
> -     struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
> +     struct perf_cpu_pmu_context *cpc = this_cpu_ptr(pmu->cpu_pmu_context);
> 
>       this_cpu_dec(perf_sched_cb_usages);
> +     barrier();
> 
> -     if (!--cpuctx->sched_cb_usage)
> -             list_del(&cpuctx->sched_cb_entry);
> +     if (!--cpc->sched_cb_usage)
> +             list_del(&cpc->sched_cb_entry);
> }
> 
> 
> void perf_sched_cb_inc(struct pmu *pmu)
> {
> -     struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
> +     struct perf_cpu_pmu_context *cpc = this_cpu_ptr(pmu->cpu_pmu_context);
> 
> -     if (!cpuctx->sched_cb_usage++)
> -             list_add(&cpuctx->sched_cb_entry, this_cpu_ptr(&sched_cb_list));
> +     if (!cpc->sched_cb_usage++)
> +             list_add(&cpc->sched_cb_entry, this_cpu_ptr(&sched_cb_list));
> 
> +     barrier();
>       this_cpu_inc(perf_sched_cb_usages);
> }
> 
> @@ -3162,22 +3211,24 @@ static void perf_pmu_sched_task(struct t
>                               struct task_struct *next,
>                               bool sched_in)
> {
> -     struct perf_cpu_context *cpuctx;
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
> +     struct perf_cpu_pmu_context *cpc;
>       struct pmu *pmu;
> 
>       if (prev == next)
>               return;
> 
> -     list_for_each_entry(cpuctx, this_cpu_ptr(&sched_cb_list), 
> sched_cb_entry) {
> -             pmu = cpuctx->ctx.pmu; /* software PMUs will not have 
> sched_task */
> +     list_for_each_entry(cpc, this_cpu_ptr(&sched_cb_list), sched_cb_entry) {
> +             pmu = cpc->epc.pmu;
> 
> +             /* software PMUs will not have sched_task */
>               if (WARN_ON_ONCE(!pmu->sched_task))
>                       continue;
> 
>               perf_ctx_lock(cpuctx, cpuctx->task_ctx);
>               perf_pmu_disable(pmu);
> 
> -             pmu->sched_task(cpuctx->task_ctx, sched_in);
> +             pmu->sched_task(cpc->task_epc, sched_in);
> 
>               perf_pmu_enable(pmu);
>               perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
> @@ -3187,9 +3238,6 @@ static void perf_pmu_sched_task(struct t
> static void perf_event_switch(struct task_struct *task,
>                             struct task_struct *next_prev, bool sched_in);
> 
> -#define for_each_task_context_nr(ctxn)                                       
> \
> -     for ((ctxn) = 0; (ctxn) < perf_nr_task_contexts; (ctxn)++)
> -
> /*
>  * Called from scheduler to remove the events of the current task,
>  * with interrupts disabled.
> @@ -3204,16 +3252,13 @@ static void perf_event_switch(struct tas
> void __perf_event_task_sched_out(struct task_struct *task,
>                                struct task_struct *next)
> {
> -     int ctxn;
> -
>       if (__this_cpu_read(perf_sched_cb_usages))
>               perf_pmu_sched_task(task, next, false);
> 
>       if (atomic_read(&nr_switch_events))
>               perf_event_switch(task, next, false);
> 
> -     for_each_task_context_nr(ctxn)
> -             perf_event_context_sched_out(task, ctxn, next);
> +     perf_event_context_sched_out(task, next);
> 
>       /*
>        * if cgroup events exist on this CPU, then we need
> @@ -3224,27 +3269,19 @@ void __perf_event_task_sched_out(struct
>               perf_cgroup_sched_out(task, next);
> }
> 
> -/*
> - * Called with IRQs disabled
> - */
> -static void cpu_ctx_sched_out(struct perf_cpu_context *cpuctx,
> -                           enum event_type_t event_type)
> -{
> -     ctx_sched_out(&cpuctx->ctx, cpuctx, event_type);
> -}
> -
> -static int visit_groups_merge(struct perf_event_groups *groups, int cpu,
> -                           int (*func)(struct perf_event *, void *), void 
> *data)
> +static int
> +visit_groups_merge(struct perf_event_groups *groups, int cpu, struct pmu 
> *pmu,
> +                int (*func)(struct perf_event *, void *), void *data)
> {
>       struct perf_event **evt, *evt1, *evt2;
>       int ret;
> 
> -     evt1 = perf_event_groups_first(groups, -1);
> -     evt2 = perf_event_groups_first(groups, cpu);
> +     evt1 = perf_event_groups_first(groups, -1, pmu);
> +     evt2 = perf_event_groups_first(groups, cpu, pmu);
> 
>       while (evt1 || evt2) {
>               if (evt1 && evt2) {
> -                     if (evt1->group_index < evt2->group_index)
> +                     if (perf_event_groups_less(evt1, evt2))
>                               evt = &evt1;
>                       else
>                               evt = &evt2;
> @@ -3258,7 +3295,7 @@ static int visit_groups_merge(struct per
>               if (ret)
>                       return ret;
> 
> -             *evt = perf_event_groups_next(*evt);
> +             *evt = perf_event_groups_next(*evt, pmu);
>       }
> 
>       return 0;
> @@ -3266,91 +3303,106 @@ static int visit_groups_merge(struct per
> 
> struct sched_in_data {
>       struct perf_event_context *ctx;
> -     struct perf_cpu_context *cpuctx;
> +     struct perf_event_pmu_context *epc;
>       int can_add_hw;
> +
> +     int pinned; /* set for pinned semantics */
> +     int busy;   /* set to terminate on busy */
> };
> 
> -static int pinned_sched_in(struct perf_event *event, void *data)
> +static void __link_epc(struct perf_event_pmu_context *pmu_ctx)
> {
> -     struct sched_in_data *sid = data;
> +     struct perf_cpu_pmu_context *cpc;
> 
> -     if (event->state <= PERF_EVENT_STATE_OFF)
> -             return 0;
> -
> -     if (!event_filter_match(event))
> -             return 0;
> -
> -     if (group_can_go_on(event, sid->cpuctx, sid->can_add_hw)) {
> -             if (!group_sched_in(event, sid->cpuctx, sid->ctx))
> -                     list_add_tail(&event->active_list, 
> &sid->ctx->pinned_active);
> -     }
> -
> -     /*
> -      * If this pinned group hasn't been scheduled,
> -      * put it in error state.
> -      */
> -     if (event->state == PERF_EVENT_STATE_INACTIVE)
> -             perf_event_set_state(event, PERF_EVENT_STATE_ERROR);
> +     if (!pmu_ctx->ctx->task)
> +             return;
> 
> -     return 0;
> +     cpc = this_cpu_ptr(pmu_ctx->pmu->cpu_pmu_context);
> +     WARN_ON_ONCE(cpc->task_epc && cpc->task_epc != pmu_ctx);
> +     cpc->task_epc = pmu_ctx;
> }
> 
> -static int flexible_sched_in(struct perf_event *event, void *data)
> +static int merge_sched_in(struct perf_event *event, void *data)
> {
>       struct sched_in_data *sid = data;
> 
> +     if (sid->epc != event->pmu_ctx) {
> +             sid->epc = event->pmu_ctx;
> +             sid->can_add_hw = 1;
> +             __link_epc(event->pmu_ctx);
> +
> +             perf_assert_pmu_disabled(sid->epc->pmu);
> +     }
> +
>       if (event->state <= PERF_EVENT_STATE_OFF)
>               return 0;
> 
>       if (!event_filter_match(event))
>               return 0;
> 
> -     if (group_can_go_on(event, sid->cpuctx, sid->can_add_hw)) {
> -             if (!group_sched_in(event, sid->cpuctx, sid->ctx))
> -                     list_add_tail(&event->active_list, 
> &sid->ctx->flexible_active);
> -             else
> +     if (group_can_go_on(event, sid->can_add_hw)) {
> +             if (!group_sched_in(event, sid->ctx)) {
> +                     struct list_head *list;
> +
> +                     if (sid->pinned)
> +                             list = &sid->epc->pinned_active;
> +                     else
> +                             list = &sid->epc->flexible_active;
> +
> +                     list_add_tail(&event->active_list, list);
> +             }
> +     }
> +
> +     if (event->state == PERF_EVENT_STATE_INACTIVE) {
> +             if (sid->pinned) {
> +                     /*
> +                      * If this pinned group hasn't been scheduled,
> +                      * put it in error state.
> +                      */
> +                     perf_event_set_state(event, PERF_EVENT_STATE_ERROR);
> +             } else {
>                       sid->can_add_hw = 0;
> +                     return sid->busy;
> +             }
>       }
> 
>       return 0;
> }
> 
> static void
> -ctx_pinned_sched_in(struct perf_event_context *ctx,
> -                 struct perf_cpu_context *cpuctx)
> +ctx_pinned_sched_in(struct perf_event_context *ctx, struct pmu *pmu)
> {
>       struct sched_in_data sid = {
>               .ctx = ctx,
> -             .cpuctx = cpuctx,
> -             .can_add_hw = 1,
> +             .pinned = 1,
>       };
> 
> -     visit_groups_merge(&ctx->pinned_groups,
> -                        smp_processor_id(),
> -                        pinned_sched_in, &sid);
> +     visit_groups_merge(&ctx->pinned_groups, smp_processor_id(), pmu,
> +                        merge_sched_in, &sid);
> }
> 
> static void
> -ctx_flexible_sched_in(struct perf_event_context *ctx,
> -                   struct perf_cpu_context *cpuctx)
> +ctx_flexible_sched_in(struct perf_event_context *ctx, struct pmu *pmu)
> {
>       struct sched_in_data sid = {
>               .ctx = ctx,
> -             .cpuctx = cpuctx,
> -             .can_add_hw = 1,
> +             .busy = pmu ? -EBUSY : 0,
>       };
> 
> -     visit_groups_merge(&ctx->flexible_groups,
> -                        smp_processor_id(),
> -                        flexible_sched_in, &sid);
> +     visit_groups_merge(&ctx->flexible_groups, smp_processor_id(), pmu,
> +                        merge_sched_in, &sid);
> +}
> +
> +static void __pmu_ctx_sched_in(struct perf_event_context *ctx, struct pmu 
> *pmu)
> +{
> +     ctx_flexible_sched_in(ctx, pmu);
> }
> 
> static void
> -ctx_sched_in(struct perf_event_context *ctx,
> -          struct perf_cpu_context *cpuctx,
> -          enum event_type_t event_type,
> +ctx_sched_in(struct perf_event_context *ctx, enum event_type_t event_type,
>            struct task_struct *task)
> {
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
>       int is_active = ctx->is_active;
>       u64 now;
> 
> @@ -3373,6 +3425,7 @@ ctx_sched_in(struct perf_event_context *
>               /* start ctx time */
>               now = perf_clock();
>               ctx->timestamp = now;
> +             // XXX ctx->task =? task
>               perf_cgroup_set_timestamp(task, ctx);
>       }
> 
> @@ -3381,30 +3434,25 @@ ctx_sched_in(struct perf_event_context *
>        * in order to give them the best chance of going on.
>        */
>       if (is_active & EVENT_PINNED)
> -             ctx_pinned_sched_in(ctx, cpuctx);
> +             ctx_pinned_sched_in(ctx, NULL);
> 
>       /* Then walk through the lower prio flexible groups */
>       if (is_active & EVENT_FLEXIBLE)
> -             ctx_flexible_sched_in(ctx, cpuctx);
> +             ctx_flexible_sched_in(ctx, NULL);
> }
> 
> -static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx,
> -                          enum event_type_t event_type,
> -                          struct task_struct *task)
> +static void perf_event_context_sched_in(struct task_struct *task)
> {
> -     struct perf_event_context *ctx = &cpuctx->ctx;
> -
> -     ctx_sched_in(ctx, cpuctx, event_type, task);
> -}
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
> +     struct perf_event_context *ctx;
> 
> -static void perf_event_context_sched_in(struct perf_event_context *ctx,
> -                                     struct task_struct *task)
> -{
> -     struct perf_cpu_context *cpuctx;
> +     rcu_read_lock();
> +     ctx = rcu_dereference(task->perf_event_ctxp);
> +     if (!ctx)
> +             goto rcu_unlock;
> 
> -     cpuctx = __get_cpu_context(ctx);
>       if (cpuctx->task_ctx == ctx)
> -             return;
> +             goto rcu_unlock;
> 
>       perf_ctx_lock(cpuctx, ctx);
>       /*
> @@ -3414,7 +3462,7 @@ static void perf_event_context_sched_in(
>       if (!ctx->nr_events)
>               goto unlock;
> 
> -     perf_pmu_disable(ctx->pmu);
> +     perf_ctx_disable(ctx);
>       /*
>        * We want to keep the following priority order:
>        * cpu pinned (that don't need to move), task pinned,
> @@ -3423,13 +3471,21 @@ static void perf_event_context_sched_in(
>        * However, if task's ctx is not carrying any pinned
>        * events, no need to flip the cpuctx's events around.
>        */
> -     if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree))
> -             cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
> +     if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree)) {
> +             perf_ctx_disable(&cpuctx->ctx);
> +             ctx_sched_out(&cpuctx->ctx, EVENT_FLEXIBLE);
> +     }
> +
>       perf_event_sched_in(cpuctx, ctx, task);
> -     perf_pmu_enable(ctx->pmu);
> +
> +     if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree))
> +             perf_ctx_enable(&cpuctx->ctx);
> +     perf_ctx_enable(ctx);
> 
> unlock:
>       perf_ctx_unlock(cpuctx, ctx);
> +rcu_unlock:
> +     rcu_read_unlock();
> }
> 
> /*
> @@ -3446,9 +3502,6 @@ static void perf_event_context_sched_in(
> void __perf_event_task_sched_in(struct task_struct *prev,
>                               struct task_struct *task)
> {
> -     struct perf_event_context *ctx;
> -     int ctxn;
> -
>       /*
>        * If cgroup events exist on this CPU, then we need to check if we have
>        * to switch in PMU state; cgroup event are system-wide mode only.
> @@ -3459,13 +3512,7 @@ void __perf_event_task_sched_in(struct t
>       if (atomic_read(this_cpu_ptr(&perf_cgroup_events)))
>               perf_cgroup_sched_in(prev, task);
> 
> -     for_each_task_context_nr(ctxn) {
> -             ctx = task->perf_event_ctxp[ctxn];
> -             if (likely(!ctx))
> -                     continue;
> -
> -             perf_event_context_sched_in(ctx, task);
> -     }
> +     perf_event_context_sched_in(task);
> 
>       if (atomic_read(&nr_switch_events))
>               perf_event_switch(task, prev, true);
> @@ -3584,8 +3631,8 @@ static void perf_adjust_period(struct pe
>  * events. At the same time, make sure, having freq events does not change
>  * the rate of unthrottling as that would introduce bias.
>  */
> -static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx,
> -                                        int needs_unthr)
> +static void
> +perf_adjust_freq_unthr_context(struct perf_event_context *ctx, bool 
> unthrottle)
> {
>       struct perf_event *event;
>       struct hw_perf_event *hwc;
> @@ -3597,16 +3644,16 @@ static void perf_adjust_freq_unthr_conte
>        * - context have events in frequency mode (needs freq adjust)
>        * - there are events to unthrottle on this cpu
>        */
> -     if (!(ctx->nr_freq || needs_unthr))
> +     if (!(ctx->nr_freq || unthrottle))
>               return;
> 
>       raw_spin_lock(&ctx->lock);
> -     perf_pmu_disable(ctx->pmu);
> 
>       list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
>               if (event->state != PERF_EVENT_STATE_ACTIVE)
>                       continue;
> 
> +             // XXX use visit thingy to avoid the -1,cpu match
>               if (!event_filter_match(event))
>                       continue;
> 
> @@ -3647,7 +3694,6 @@ static void perf_adjust_freq_unthr_conte
>               perf_pmu_enable(event->pmu);
>       }
> 
> -     perf_pmu_enable(ctx->pmu);
>       raw_spin_unlock(&ctx->lock);
> }
> 
> @@ -3668,71 +3714,97 @@ static void rotate_ctx(struct perf_event
> }
> 
> static inline struct perf_event *
> -ctx_first_active(struct perf_event_context *ctx)
> +ctx_first_active(struct perf_event_pmu_context *pmu_ctx)
> {
> -     return list_first_entry_or_null(&ctx->flexible_active,
> +     return list_first_entry_or_null(&pmu_ctx->flexible_active,
>                                       struct perf_event, active_list);
> }
> 
> -static bool perf_rotate_context(struct perf_cpu_context *cpuctx)
> +/*
> + * XXX somewhat completely buggered; this is in cpu_pmu_context, but we need
> + * event_pmu_context for rotations. We also need event_pmu_context specific
> + * scheduling routines. ARGH
> + *
> + *  - fixed the cpu_pmu_context vs event_pmu_context thingy
> + *    (cpu_pmu_context embeds an event_pmu_context)
> + *
> + *  - need nr_events/nr_active in epc to do per epc rotation
> + *    (done)
> + *
> + *  - need cpu and task pmu ctx together...
> + *    (cpc->task_epc)
> + */
> +static bool perf_rotate_context(struct perf_cpu_pmu_context *cpc)
> {
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
> +     struct perf_event_pmu_context *cpu_epc, *task_epc = NULL;
>       struct perf_event *cpu_event = NULL, *task_event = NULL;
>       bool cpu_rotate = false, task_rotate = false;
>       struct perf_event_context *ctx = NULL;
> +     struct pmu *pmu;
> 
>       /*
>        * Since we run this from IRQ context, nobody can install new
>        * events, thus the event count values are stable.
>        */
> 
> -     if (cpuctx->ctx.nr_events) {
> -             if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active)
> -                     cpu_rotate = true;
> -     }
> +     cpu_epc = &cpc->epc;
> +     pmu = cpu_epc->pmu;
> 
> -     ctx = cpuctx->task_ctx;
> -     if (ctx && ctx->nr_events) {
> -             if (ctx->nr_events != ctx->nr_active)
> +     if (cpu_epc->nr_events && cpu_epc->nr_events != cpu_epc->nr_active)
> +             cpu_rotate = true;
> +
> +     task_epc = cpc->task_epc;
> +     if (task_epc) {
> +             WARN_ON_ONCE(task_epc->pmu != pmu);
> +             if (task_epc->nr_events && task_epc->nr_events != 
> task_epc->nr_active)
>                       task_rotate = true;
>       }
> 
>       if (!(cpu_rotate || task_rotate))
>               return false;
> 
> -     perf_ctx_lock(cpuctx, cpuctx->task_ctx);
> -     perf_pmu_disable(cpuctx->ctx.pmu);
> +     perf_ctx_lock(cpuctx, ctx);
> +     perf_pmu_disable(pmu);
> 
>       if (task_rotate)
> -             task_event = ctx_first_active(ctx);
> +             task_event = ctx_first_active(task_epc);
> +
>       if (cpu_rotate)
> -             cpu_event = ctx_first_active(&cpuctx->ctx);
> +             cpu_event = ctx_first_active(cpu_epc);
> 
>       /*
>        * As per the order given at ctx_resched() first 'pop' task flexible
>        * and then, if needed CPU flexible.
>        */
> -     if (task_event || (ctx && cpu_event))
> -             ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE);
> -     if (cpu_event)
> -             cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
> +     if (task_event || (task_epc && cpu_event)) {
> +             update_context_time(ctx);
> +             __pmu_ctx_sched_out(task_epc, EVENT_FLEXIBLE);
> +     }
> +
> +     if (cpu_event) {
> +             update_context_time(&cpuctx->ctx);
> +             __pmu_ctx_sched_out(cpu_epc, EVENT_FLEXIBLE);
> +             rotate_ctx(&cpuctx->ctx, cpu_event);
> +             __pmu_ctx_sched_in(&cpuctx->ctx, pmu);
> +     }
> 
>       if (task_event)
>               rotate_ctx(ctx, task_event);
> -     if (cpu_event)
> -             rotate_ctx(&cpuctx->ctx, cpu_event);
> 
> -     perf_event_sched_in(cpuctx, ctx, current);
> +     if (task_event || (task_epc && cpu_event))
> +             __pmu_ctx_sched_in(ctx, pmu);
> 
> -     perf_pmu_enable(cpuctx->ctx.pmu);
> -     perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
> +     perf_pmu_enable(pmu);
> +     perf_ctx_unlock(cpuctx, ctx);
> 
>       return true;
> }
> 
> void perf_event_task_tick(void)
> {
> -     struct list_head *head = this_cpu_ptr(&active_ctx_list);
> -     struct perf_event_context *ctx, *tmp;
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
> +     struct perf_event_context *ctx;
>       int throttled;
> 
>       lockdep_assert_irqs_disabled();
> @@ -3741,8 +3813,13 @@ void perf_event_task_tick(void)
>       throttled = __this_cpu_xchg(perf_throttled_count, 0);
>       tick_dep_clear_cpu(smp_processor_id(), TICK_DEP_BIT_PERF_EVENTS);
> 
> -     list_for_each_entry_safe(ctx, tmp, head, active_ctx_list)
> -             perf_adjust_freq_unthr_context(ctx, throttled);
> +     perf_adjust_freq_unthr_context(&cpuctx->ctx, !!throttled);
> +
> +     rcu_read_lock();
> +     ctx = rcu_dereference(current->perf_event_ctxp);
> +     if (ctx)
> +             perf_adjust_freq_unthr_context(ctx, !!throttled);
> +     rcu_read_unlock();
> }
> 
> static int event_enable_on_exec(struct perf_event *event,
> @@ -3764,9 +3841,9 @@ static int event_enable_on_exec(struct p
>  * Enable all of a task's events that have been marked enable-on-exec.
>  * This expects task == current.
>  */
> -static void perf_event_enable_on_exec(int ctxn)
> +static void perf_event_enable_on_exec(struct perf_event_context *ctx)
> {
> -     struct perf_event_context *ctx, *clone_ctx = NULL;
> +     struct perf_event_context *clone_ctx = NULL;
>       enum event_type_t event_type = 0;
>       struct perf_cpu_context *cpuctx;
>       struct perf_event *event;
> @@ -3774,13 +3851,16 @@ static void perf_event_enable_on_exec(in
>       int enabled = 0;
> 
>       local_irq_save(flags);
> -     ctx = current->perf_event_ctxp[ctxn];
> -     if (!ctx || !ctx->nr_events)
> +     if (WARN_ON_ONCE(current->perf_event_ctxp != ctx))
>               goto out;
> 
> -     cpuctx = __get_cpu_context(ctx);
> +     if (!ctx->nr_events)
> +             goto out;
> +
> +     cpuctx = this_cpu_ptr(&cpu_context);
>       perf_ctx_lock(cpuctx, ctx);
> -     ctx_sched_out(ctx, cpuctx, EVENT_TIME);
> +     ctx_sched_out(ctx, EVENT_TIME);
> +
>       list_for_each_entry(event, &ctx->event_list, event_entry) {
>               enabled |= event_enable_on_exec(event, ctx);
>               event_type |= get_event_type(event);
> @@ -3793,7 +3873,7 @@ static void perf_event_enable_on_exec(in
>               clone_ctx = unclone_ctx(ctx);
>               ctx_resched(cpuctx, ctx, event_type);
>       } else {
> -             ctx_sched_in(ctx, cpuctx, EVENT_TIME, current);
> +             ctx_sched_in(ctx, EVENT_TIME, current);
>       }
>       perf_ctx_unlock(cpuctx, ctx);
> 
> @@ -3835,7 +3915,7 @@ static void __perf_event_read(void *info
>       struct perf_read_data *data = info;
>       struct perf_event *sub, *event = data->event;
>       struct perf_event_context *ctx = event->ctx;
> -     struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
>       struct pmu *pmu = event->pmu;
> 
>       /*
> @@ -4050,17 +4130,25 @@ static void __perf_event_init_context(st
> {
>       raw_spin_lock_init(&ctx->lock);
>       mutex_init(&ctx->mutex);
> -     INIT_LIST_HEAD(&ctx->active_ctx_list);
> +     INIT_LIST_HEAD(&ctx->pmu_ctx_list);
>       perf_event_groups_init(&ctx->pinned_groups);
>       perf_event_groups_init(&ctx->flexible_groups);
>       INIT_LIST_HEAD(&ctx->event_list);
> -     INIT_LIST_HEAD(&ctx->pinned_active);
> -     INIT_LIST_HEAD(&ctx->flexible_active);
>       atomic_set(&ctx->refcount, 1);
> }
> 
> +static void
> +__perf_init_event_pmu_context(struct perf_event_pmu_context *epc, struct pmu 
> *pmu)
> +{
> +     epc->pmu = pmu;
> +     INIT_LIST_HEAD(&epc->pmu_ctx_entry);
> +     INIT_LIST_HEAD(&epc->pinned_active);
> +     INIT_LIST_HEAD(&epc->flexible_active);
> +     atomic_set(&epc->refcount, 1);
> +}
> +
> static struct perf_event_context *
> -alloc_perf_context(struct pmu *pmu, struct task_struct *task)
> +alloc_perf_context(struct task_struct *task)
> {
>       struct perf_event_context *ctx;
> 
> @@ -4073,7 +4161,6 @@ alloc_perf_context(struct pmu *pmu, stru
>               ctx->task = task;
>               get_task_struct(task);
>       }
> -     ctx->pmu = pmu;
> 
>       return ctx;
> }
> @@ -4102,22 +4189,19 @@ find_lively_task_by_vpid(pid_t vpid)
>  * Returns a matching context with refcount and pincount.
>  */
> static struct perf_event_context *
> -find_get_context(struct pmu *pmu, struct task_struct *task,
> -             struct perf_event *event)
> +find_get_context(struct task_struct *task, struct perf_event *event)
> {
>       struct perf_event_context *ctx, *clone_ctx = NULL;
>       struct perf_cpu_context *cpuctx;
> -     void *task_ctx_data = NULL;
>       unsigned long flags;
> -     int ctxn, err;
> -     int cpu = event->cpu;
> +     int err;
> 
>       if (!task) {
>               /* Must be root to operate on a CPU event: */
>               if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN))
>                       return ERR_PTR(-EACCES);
> 
> -             cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
> +             cpuctx = per_cpu_ptr(&cpu_context, event->cpu);
>               ctx = &cpuctx->ctx;
>               get_ctx(ctx);
>               ++ctx->pin_count;
> @@ -4126,43 +4210,22 @@ find_get_context(struct pmu *pmu, struct
>       }
> 
>       err = -EINVAL;
> -     ctxn = pmu->task_ctx_nr;
> -     if (ctxn < 0)
> -             goto errout;
> -
> -     if (event->attach_state & PERF_ATTACH_TASK_DATA) {
> -             task_ctx_data = kzalloc(pmu->task_ctx_size, GFP_KERNEL);
> -             if (!task_ctx_data) {
> -                     err = -ENOMEM;
> -                     goto errout;
> -             }
> -     }
> -
> retry:
> -     ctx = perf_lock_task_context(task, ctxn, &flags);
> +     ctx = perf_lock_task_context(task, &flags);
>       if (ctx) {
>               clone_ctx = unclone_ctx(ctx);
>               ++ctx->pin_count;
> 
> -             if (task_ctx_data && !ctx->task_ctx_data) {
> -                     ctx->task_ctx_data = task_ctx_data;
> -                     task_ctx_data = NULL;
> -             }
>               raw_spin_unlock_irqrestore(&ctx->lock, flags);
> 
>               if (clone_ctx)
>                       put_ctx(clone_ctx);
>       } else {
> -             ctx = alloc_perf_context(pmu, task);
> +             ctx = alloc_perf_context(task);
>               err = -ENOMEM;
>               if (!ctx)
>                       goto errout;
> 
> -             if (task_ctx_data) {
> -                     ctx->task_ctx_data = task_ctx_data;
> -                     task_ctx_data = NULL;
> -             }
> -
>               err = 0;
>               mutex_lock(&task->perf_event_mutex);
>               /*
> @@ -4171,12 +4234,12 @@ find_get_context(struct pmu *pmu, struct
>                */
>               if (task->flags & PF_EXITING)
>                       err = -ESRCH;
> -             else if (task->perf_event_ctxp[ctxn])
> +             else if (task->perf_event_ctxp)
>                       err = -EAGAIN;
>               else {
>                       get_ctx(ctx);
>                       ++ctx->pin_count;
> -                     rcu_assign_pointer(task->perf_event_ctxp[ctxn], ctx);
> +                     rcu_assign_pointer(task->perf_event_ctxp, ctx);
>               }
>               mutex_unlock(&task->perf_event_mutex);
> 
> @@ -4189,14 +4252,117 @@ find_get_context(struct pmu *pmu, struct
>               }
>       }
> 
> -     kfree(task_ctx_data);
>       return ctx;
> 
> errout:
> -     kfree(task_ctx_data);
>       return ERR_PTR(err);
> }
> 
> +struct perf_event_pmu_context *
> +find_get_pmu_context(struct pmu *pmu, struct perf_event_context *ctx,
> +                  struct perf_event *event)
> +{
> +     struct perf_event_pmu_context *new = NULL, *epc;
> +     void *task_ctx_data = NULL;
> +
> +     if (!ctx->task) {
> +             struct perf_cpu_pmu_context *cpc;
> +
> +             cpc = per_cpu_ptr(pmu->cpu_pmu_context, event->cpu);
> +             epc = &cpc->epc;
> +
> +             if (!epc->ctx) {
> +                     atomic_set(&epc->refcount, 1);
> +                     epc->embedded = 1;
> +                     raw_spin_lock_irq(&ctx->lock);
> +                     list_add(&epc->pmu_ctx_entry, &ctx->pmu_ctx_list);
> +                     epc->ctx = ctx;
> +                     raw_spin_unlock_irq(&ctx->lock);
> +             } else {
> +                     WARN_ON_ONCE(epc->ctx != ctx);
> +                     atomic_inc(&epc->refcount);
> +             }
> +
> +             return epc;
> +     }
> +
> +     new = kzalloc(sizeof(*epc), GFP_KERNEL);
> +     if (!new)
> +             return ERR_PTR(-ENOMEM);
> +
> +     if (event->attach_state & PERF_ATTACH_TASK_DATA) {
> +             task_ctx_data = kzalloc(pmu->task_ctx_size, GFP_KERNEL);
> +             if (!task_ctx_data) {
> +                     kfree(new);
> +                     return ERR_PTR(-ENOMEM);
> +             }
> +     }
> +
> +     __perf_init_event_pmu_context(new, pmu);
> +
> +     raw_spin_lock_irq(&ctx->lock);
> +     list_for_each_entry(epc, &ctx->pmu_ctx_list, pmu_ctx_entry) {
> +             if (epc->pmu == pmu) {
> +                     WARN_ON_ONCE(epc->ctx != ctx);
> +                     atomic_inc(&epc->refcount);
> +                     goto found_epc;
> +             }
> +     }
> +
> +     epc = new;
> +     new = NULL;
> +
> +     list_add(&epc->pmu_ctx_entry, &ctx->pmu_ctx_list);
> +     epc->ctx = ctx;
> +
> +found_epc:
> +     if (task_ctx_data && !epc->task_ctx_data) {
> +             epc->task_ctx_data = task_ctx_data;
> +             task_ctx_data = NULL;
> +             ctx->nr_task_data++;
> +     }
> +     raw_spin_unlock_irq(&ctx->lock);
> +
> +     kfree(task_ctx_data);
> +     kfree(new);
> +
> +     return epc;
> +}
> +
> +static void get_pmu_ctx(struct perf_event_pmu_context *epc)
> +{
> +     WARN_ON_ONCE(!atomic_inc_not_zero(&epc->refcount));
> +}
> +
> +static void put_pmu_ctx(struct perf_event_pmu_context *epc)
> +{
> +     unsigned long flags;
> +
> +     if (!atomic_dec_and_test(&epc->refcount))
> +             return;
> +
> +     if (epc->ctx) {
> +             struct perf_event_context *ctx = epc->ctx;
> +
> +             // XXX ctx->mutex
> +
> +             WARN_ON_ONCE(list_empty(&epc->pmu_ctx_entry));
> +             raw_spin_lock_irqsave(&ctx->lock, flags);
> +             list_del_init(&epc->pmu_ctx_entry);
> +             epc->ctx = NULL;
> +             raw_spin_unlock_irqrestore(&ctx->lock, flags);
> +     }
> +
> +     WARN_ON_ONCE(!list_empty(&epc->pinned_active));
> +     WARN_ON_ONCE(!list_empty(&epc->flexible_active));
> +
> +     if (epc->embedded)
> +             return;
> +
> +     kfree(epc->task_ctx_data);
> +     kfree(epc);
> +}
> +
> static void perf_event_free_filter(struct perf_event *event);
> static void perf_event_free_bpf_prog(struct perf_event *event);
> 
> @@ -4445,6 +4611,9 @@ static void _free_event(struct perf_even
>       if (event->destroy)
>               event->destroy(event);
> 
> +     if (event->pmu_ctx)
> +             put_pmu_ctx(event->pmu_ctx);
> +
>       if (event->ctx)
>               put_ctx(event->ctx);
> 
> @@ -4943,7 +5112,7 @@ static void __perf_event_period(struct p
> 
>       active = (event->state == PERF_EVENT_STATE_ACTIVE);
>       if (active) {
> -             perf_pmu_disable(ctx->pmu);
> +             perf_pmu_disable(event->pmu);
>               /*
>                * We could be throttled; unthrottle now to avoid the tick
>                * trying to unthrottle while we already re-started the event.
> @@ -4959,7 +5128,7 @@ static void __perf_event_period(struct p
> 
>       if (active) {
>               event->pmu->start(event, PERF_EF_RELOAD);
> -             perf_pmu_enable(ctx->pmu);
> +             perf_pmu_enable(event->pmu);
>       }
> }
> 
> @@ -6634,7 +6803,6 @@ perf_iterate_sb(perf_iterate_f output, v
>              struct perf_event_context *task_ctx)
> {
>       struct perf_event_context *ctx;
> -     int ctxn;
> 
>       rcu_read_lock();
>       preempt_disable();
> @@ -6651,11 +6819,9 @@ perf_iterate_sb(perf_iterate_f output, v
> 
>       perf_iterate_sb_cpu(output, data);
> 
> -     for_each_task_context_nr(ctxn) {
> -             ctx = rcu_dereference(current->perf_event_ctxp[ctxn]);
> -             if (ctx)
> -                     perf_iterate_ctx(ctx, output, data, false);
> -     }
> +     ctx = rcu_dereference(current->perf_event_ctxp);
> +     if (ctx)
> +             perf_iterate_ctx(ctx, output, data, false);
> done:
>       preempt_enable();
>       rcu_read_unlock();
> @@ -6696,18 +6862,12 @@ static void perf_event_addr_filters_exec
> void perf_event_exec(void)
> {
>       struct perf_event_context *ctx;
> -     int ctxn;
> 
>       rcu_read_lock();
> -     for_each_task_context_nr(ctxn) {
> -             ctx = current->perf_event_ctxp[ctxn];
> -             if (!ctx)
> -                     continue;
> -
> -             perf_event_enable_on_exec(ctxn);
> -
> -             perf_iterate_ctx(ctx, perf_event_addr_filters_exec, NULL,
> -                                true);
> +     ctx = rcu_dereference(current->perf_event_ctxp);
> +     if (ctx) {
> +             perf_event_enable_on_exec(ctx);
> +             perf_iterate_ctx(ctx, perf_event_addr_filters_exec, NULL, true);
>       }
>       rcu_read_unlock();
> }
> @@ -6749,8 +6909,7 @@ static void __perf_event_output_stop(str
> static int __perf_pmu_output_stop(void *info)
> {
>       struct perf_event *event = info;
> -     struct pmu *pmu = event->pmu;
> -     struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
>       struct remote_output ro = {
>               .rb     = event->rb,
>       };
> @@ -7398,7 +7557,6 @@ static void __perf_addr_filters_adjust(s
> static void perf_addr_filters_adjust(struct vm_area_struct *vma)
> {
>       struct perf_event_context *ctx;
> -     int ctxn;
> 
>       /*
>        * Data tracing isn't supported yet and as such there is no need
> @@ -7408,13 +7566,9 @@ static void perf_addr_filters_adjust(str
>               return;
> 
>       rcu_read_lock();
> -     for_each_task_context_nr(ctxn) {
> -             ctx = rcu_dereference(current->perf_event_ctxp[ctxn]);
> -             if (!ctx)
> -                     continue;
> -
> +     ctx = rcu_dereference(current->perf_event_ctxp);
> +     if (ctx)
>               perf_iterate_ctx(ctx, __perf_addr_filters_adjust, vma, true);
> -     }
>       rcu_read_unlock();
> }
> 
> @@ -8309,10 +8463,13 @@ void perf_tp_event(u16 event_type, u64 c
>               struct trace_entry *entry = record;
> 
>               rcu_read_lock();
> -             ctx = rcu_dereference(task->perf_event_ctxp[perf_sw_context]);
> +             ctx = rcu_dereference(task->perf_event_ctxp);
>               if (!ctx)
>                       goto unlock;
> 
> +             // XXX iterate groups instead, we should be able to
> +             // find the subtree for the perf_tracepoint pmu and CPU.
> +
>               list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
>                       if (event->cpu != smp_processor_id())
>                               continue;
> @@ -9404,25 +9561,6 @@ static int perf_event_idx_default(struct
>       return 0;
> }
> 
> -/*
> - * Ensures all contexts with the same task_ctx_nr have the same
> - * pmu_cpu_context too.
> - */
> -static struct perf_cpu_context __percpu *find_pmu_context(int ctxn)
> -{
> -     struct pmu *pmu;
> -
> -     if (ctxn < 0)
> -             return NULL;
> -
> -     list_for_each_entry(pmu, &pmus, entry) {
> -             if (pmu->task_ctx_nr == ctxn)
> -                     return pmu->pmu_cpu_context;
> -     }
> -
> -     return NULL;
> -}
> -
> static void free_pmu_context(struct pmu *pmu)
> {
>       /*
> @@ -9433,7 +9571,7 @@ static void free_pmu_context(struct pmu
>       if (pmu->task_ctx_nr > perf_invalid_context)
>               return;
> 
> -     free_percpu(pmu->pmu_cpu_context);
> +     free_percpu(pmu->cpu_pmu_context);
> }
> 
> /*
> @@ -9497,12 +9635,12 @@ perf_event_mux_interval_ms_store(struct
>       /* update all cpuctx for this PMU */
>       cpus_read_lock();
>       for_each_online_cpu(cpu) {
> -             struct perf_cpu_context *cpuctx;
> -             cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
> -             cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
> +             struct perf_cpu_pmu_context *cpc;
> +             cpc = per_cpu_ptr(pmu->cpu_pmu_context, cpu);
> +             cpc->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
> 
>               cpu_function_call(cpu,
> -                     (remote_function_f)perf_mux_hrtimer_restart, cpuctx);
> +                     (remote_function_f)perf_mux_hrtimer_restart, cpc);
>       }
>       cpus_read_unlock();
>       mutex_unlock(&mux_interval_mutex);
> @@ -9602,44 +9740,19 @@ int perf_pmu_register(struct pmu *pmu, c
>       }
> 
> skip_type:
> -     if (pmu->task_ctx_nr == perf_hw_context) {
> -             static int hw_context_taken = 0;
> -
> -             /*
> -              * Other than systems with heterogeneous CPUs, it never makes
> -              * sense for two PMUs to share perf_hw_context. PMUs which are
> -              * uncore must use perf_invalid_context.
> -              */
> -             if (WARN_ON_ONCE(hw_context_taken &&
> -                 !(pmu->capabilities & PERF_PMU_CAP_HETEROGENEOUS_CPUS)))
> -                     pmu->task_ctx_nr = perf_invalid_context;
> -
> -             hw_context_taken = 1;
> -     }
> -
> -     pmu->pmu_cpu_context = find_pmu_context(pmu->task_ctx_nr);
> -     if (pmu->pmu_cpu_context)
> -             goto got_cpu_context;
> -
>       ret = -ENOMEM;
> -     pmu->pmu_cpu_context = alloc_percpu(struct perf_cpu_context);
> -     if (!pmu->pmu_cpu_context)
> +     pmu->cpu_pmu_context = alloc_percpu(struct perf_cpu_pmu_context);
> +     if (!pmu->cpu_pmu_context)
>               goto free_dev;
> 
>       for_each_possible_cpu(cpu) {
> -             struct perf_cpu_context *cpuctx;
> +             struct perf_cpu_pmu_context *cpc;
> 
> -             cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
> -             __perf_event_init_context(&cpuctx->ctx);
> -             lockdep_set_class(&cpuctx->ctx.mutex, &cpuctx_mutex);
> -             lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock);
> -             cpuctx->ctx.pmu = pmu;
> -             cpuctx->online = cpumask_test_cpu(cpu, perf_online_mask);
> -
> -             __perf_mux_hrtimer_init(cpuctx, cpu);
> +             cpc = per_cpu_ptr(pmu->cpu_pmu_context, cpu);
> +             __perf_init_event_pmu_context(&cpc->epc, pmu);
> +             __perf_mux_hrtimer_init(cpc, cpu);
>       }
> 
> -got_cpu_context:
>       if (!pmu->start_txn) {
>               if (pmu->pmu_enable) {
>                       /*
> @@ -10349,37 +10462,6 @@ static int perf_event_set_clock(struct p
>       return 0;
> }
> 
> -/*
> - * Variation on perf_event_ctx_lock_nested(), except we take two context
> - * mutexes.
> - */
> -static struct perf_event_context *
> -__perf_event_ctx_lock_double(struct perf_event *group_leader,
> -                          struct perf_event_context *ctx)
> -{
> -     struct perf_event_context *gctx;
> -
> -again:
> -     rcu_read_lock();
> -     gctx = READ_ONCE(group_leader->ctx);
> -     if (!atomic_inc_not_zero(&gctx->refcount)) {
> -             rcu_read_unlock();
> -             goto again;
> -     }
> -     rcu_read_unlock();
> -
> -     mutex_lock_double(&gctx->mutex, &ctx->mutex);
> -
> -     if (group_leader->ctx != gctx) {
> -             mutex_unlock(&ctx->mutex);
> -             mutex_unlock(&gctx->mutex);
> -             put_ctx(gctx);
> -             goto again;
> -     }
> -
> -     return gctx;
> -}
> -
> /**
>  * sys_perf_event_open - open a performance event, associate it to a task/cpu
>  *
> @@ -10393,9 +10475,10 @@ SYSCALL_DEFINE5(perf_event_open,
>               pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)
> {
>       struct perf_event *group_leader = NULL, *output_event = NULL;
> +     struct perf_event_pmu_context *pmu_ctx;
>       struct perf_event *event, *sibling;
>       struct perf_event_attr attr;
> -     struct perf_event_context *ctx, *uninitialized_var(gctx);
> +     struct perf_event_context *ctx;
>       struct file *event_file = NULL;
>       struct fd group = {NULL, 0};
>       struct task_struct *task = NULL;
> @@ -10506,6 +10589,8 @@ SYSCALL_DEFINE5(perf_event_open,
>               goto err_cred;
>       }
> 
> +     // XXX premature; what if this is allowed, but we get moved to a PMU
> +     // that doesn't have this.
>       if (is_sampling_event(event)) {
>               if (event->pmu->capabilities & PERF_PMU_CAP_NO_INTERRUPT) {
>                       err = -EOPNOTSUPP;
> @@ -10525,50 +10610,45 @@ SYSCALL_DEFINE5(perf_event_open,
>                       goto err_alloc;
>       }
> 
> +     if (pmu->task_ctx_nr < 0 && task) {
> +             err = -EINVAL;
> +             goto err_alloc;
> +     }
> +
>       if (pmu->task_ctx_nr == perf_sw_context)
>               event->event_caps |= PERF_EV_CAP_SOFTWARE;
> 
> -     if (group_leader) {
> -             if (is_software_event(event) &&
> -                 !in_software_context(group_leader)) {
> -                     /*
> -                      * If the event is a sw event, but the group_leader
> -                      * is on hw context.
> -                      *
> -                      * Allow the addition of software events to hw
> -                      * groups, this is safe because software events
> -                      * never fail to schedule.
> -                      */
> -                     pmu = group_leader->ctx->pmu;
> -             } else if (!is_software_event(event) &&
> -                        is_software_event(group_leader) &&
> -                        (group_leader->group_caps & PERF_EV_CAP_SOFTWARE)) {
> -                     /*
> -                      * In case the group is a pure software group, and we
> -                      * try to add a hardware event, move the whole group to
> -                      * the hardware context.
> -                      */
> -                     move_group = 1;
> -             }
> -     }
> -
>       /*
>        * Get the target context (task or percpu):
>        */
> -     ctx = find_get_context(pmu, task, event);
> +     ctx = find_get_context(task, event);
>       if (IS_ERR(ctx)) {
>               err = PTR_ERR(ctx);
>               goto err_alloc;
>       }
> 
> -     if ((pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE) && group_leader) {
> -             err = -EBUSY;
> -             goto err_context;
> +     mutex_lock(&ctx->mutex);
> +
> +     if (ctx->task == TASK_TOMBSTONE) {
> +             err = -ESRCH;
> +             goto err_locked;
> +     }
> +
> +     if (!task) {
> +             /*
> +              * Check if the @cpu we're creating an event for is online.
> +              *
> +              * We use the perf_cpu_context::ctx::mutex to serialize against
> +              * the hotplug notifiers. See perf_event_{init,exit}_cpu().
> +              */
> +             struct perf_cpu_context *cpuctx = per_cpu_ptr(&cpu_context, 
> event->cpu);
> +
> +             if (!cpuctx->online) {
> +                     err = -ENODEV;
> +                     goto err_locked;
> +             }
>       }
> 
> -     /*
> -      * Look up the group leader (we will attach this event to it):
> -      */
>       if (group_leader) {
>               err = -EINVAL;
> 
> @@ -10577,11 +10657,11 @@ SYSCALL_DEFINE5(perf_event_open,
>                * becoming part of another group-sibling):
>                */
>               if (group_leader->group_leader != group_leader)
> -                     goto err_context;
> +                     goto err_locked;
> 
>               /* All events in a group should have the same clock */
>               if (group_leader->clock != event->clock)
> -                     goto err_context;
> +                     goto err_locked;
> 
>               /*
>                * Make sure we're both events for the same CPU;
> @@ -10589,28 +10669,57 @@ SYSCALL_DEFINE5(perf_event_open,
>                * you can never concurrently schedule them anyhow.
>                */
>               if (group_leader->cpu != event->cpu)
> -                     goto err_context;
> -
> -             /*
> -              * Make sure we're both on the same task, or both
> -              * per-CPU events.
> -              */
> -             if (group_leader->ctx->task != ctx->task)
> -                     goto err_context;
> +                     goto err_locked;
> 
>               /*
> -              * Do not allow to attach to a group in a different task
> -              * or CPU context. If we're moving SW events, we'll fix
> -              * this up later, so allow that.
> +              * Make sure we're both on the same context; either task or cpu.
>                */
> -             if (!move_group && group_leader->ctx != ctx)
> -                     goto err_context;
> +             if (group_leader->ctx != ctx)
> +                     goto err_locked;
> 
>               /*
>                * Only a group leader can be exclusive or pinned
>                */
>               if (attr.exclusive || attr.pinned)
> -                     goto err_context;
> +                     goto err_locked;
> +
> +             if (is_software_event(event) &&
> +                 !in_software_context(group_leader)) {
> +                     /*
> +                      * If the event is a sw event, but the group_leader
> +                      * is on hw context.
> +                      *
> +                      * Allow the addition of software events to hw
> +                      * groups, this is safe because software events
> +                      * never fail to schedule.
> +                      */
> +                     pmu = group_leader->pmu_ctx->pmu;
> +             } else if (!is_software_event(event) &&
> +                        is_software_event(group_leader) &&
> +                        (group_leader->group_caps & PERF_EV_CAP_SOFTWARE)) {
> +                     /*
> +                      * In case the group is a pure software group, and we
> +                      * try to add a hardware event, move the whole group to
> +                      * the hardware context.
> +                      */
> +                     move_group = 1;
> +             }
> +     }
> +
> +     /*
> +      * Now that we're certain of the pmu; find the pmu_ctx.
> +      */
> +     pmu_ctx = find_get_pmu_context(pmu, ctx, event);
> +     if (IS_ERR(pmu_ctx)) {
> +             err = PTR_ERR(pmu_ctx);
> +             goto err_locked;
> +     }
> +     event->pmu_ctx = pmu_ctx;
> +
> +     // XXX think about exclusive
> +     if ((pmu->capabilities & PERF_PMU_CAP_EXCLUSIVE) && group_leader) {
> +             err = -EBUSY;
> +             goto err_context;
>       }
> 
>       if (output_event) {
> @@ -10619,71 +10728,18 @@ SYSCALL_DEFINE5(perf_event_open,
>                       goto err_context;
>       }
> 
> -     event_file = anon_inode_getfile("[perf_event]", &perf_fops, event,
> -                                     f_flags);
> +     event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, 
> f_flags);
>       if (IS_ERR(event_file)) {
>               err = PTR_ERR(event_file);
>               event_file = NULL;
>               goto err_context;
>       }
> 
> -     if (move_group) {
> -             gctx = __perf_event_ctx_lock_double(group_leader, ctx);
> -
> -             if (gctx->task == TASK_TOMBSTONE) {
> -                     err = -ESRCH;
> -                     goto err_locked;
> -             }
> -
> -             /*
> -              * Check if we raced against another sys_perf_event_open() call
> -              * moving the software group underneath us.
> -              */
> -             if (!(group_leader->group_caps & PERF_EV_CAP_SOFTWARE)) {
> -                     /*
> -                      * If someone moved the group out from under us, check
> -                      * if this new event wound up on the same ctx, if so
> -                      * its the regular !move_group case, otherwise fail.
> -                      */
> -                     if (gctx != ctx) {
> -                             err = -EINVAL;
> -                             goto err_locked;
> -                     } else {
> -                             perf_event_ctx_unlock(group_leader, gctx);
> -                             move_group = 0;
> -                     }
> -             }
> -     } else {
> -             mutex_lock(&ctx->mutex);
> -     }
> -
> -     if (ctx->task == TASK_TOMBSTONE) {
> -             err = -ESRCH;
> -             goto err_locked;
> -     }
> -
>       if (!perf_event_validate_size(event)) {
>               err = -E2BIG;
> -             goto err_locked;
> +             goto err_file;
>       }
> 
> -     if (!task) {
> -             /*
> -              * Check if the @cpu we're creating an event for is online.
> -              *
> -              * We use the perf_cpu_context::ctx::mutex to serialize against
> -              * the hotplug notifiers. See perf_event_{init,exit}_cpu().
> -              */
> -             struct perf_cpu_context *cpuctx =
> -                     container_of(ctx, struct perf_cpu_context, ctx);
> -
> -             if (!cpuctx->online) {
> -                     err = -ENODEV;
> -                     goto err_locked;
> -             }
> -     }
> -
> -
>       /*
>        * Must be under the same ctx::mutex as perf_install_in_context(),
>        * because we need to serialize with concurrent event creation.
> @@ -10693,7 +10749,7 @@ SYSCALL_DEFINE5(perf_event_open,
>               WARN_ON_ONCE(move_group);
> 
>               err = -EBUSY;
> -             goto err_locked;
> +             goto err_file;
>       }
> 
>       WARN_ON_ONCE(ctx->parent_ctx);
> @@ -10704,25 +10760,15 @@ SYSCALL_DEFINE5(perf_event_open,
>        */
> 
>       if (move_group) {
> -             /*
> -              * See perf_event_ctx_lock() for comments on the details
> -              * of swizzling perf_event::ctx.
> -              */
>               perf_remove_from_context(group_leader, 0);
> -             put_ctx(gctx);
> +             put_pmu_ctx(group_leader->pmu_ctx);
> 
>               for_each_sibling_event(sibling, group_leader) {
>                       perf_remove_from_context(sibling, 0);
> -                     put_ctx(gctx);
> +                     put_pmu_ctx(sibling->pmu_ctx);
>               }
> 
>               /*
> -              * Wait for everybody to stop referencing the events through
> -              * the old lists, before installing it on new lists.
> -              */
> -             synchronize_rcu();
> -
> -             /*
>                * Install the group siblings before the group leader.
>                *
>                * Because a group leader will try and install the entire group
> @@ -10733,9 +10779,10 @@ SYSCALL_DEFINE5(perf_event_open,
>                * reachable through the group lists.
>                */
>               for_each_sibling_event(sibling, group_leader) {
> +                     sibling->pmu_ctx = pmu_ctx;
> +                     get_pmu_ctx(pmu_ctx);
>                       perf_event__state_init(sibling);
>                       perf_install_in_context(ctx, sibling, sibling->cpu);
> -                     get_ctx(ctx);
>               }
> 
>               /*
> @@ -10743,9 +10790,10 @@ SYSCALL_DEFINE5(perf_event_open,
>                * event. What we want here is event in the initial
>                * startup state, ready to be add into new context.
>                */
> +             group_leader->pmu_ctx = pmu_ctx;
> +             get_pmu_ctx(pmu_ctx);
>               perf_event__state_init(group_leader);
>               perf_install_in_context(ctx, group_leader, group_leader->cpu);
> -             get_ctx(ctx);
>       }
> 
>       /*
> @@ -10762,8 +10810,6 @@ SYSCALL_DEFINE5(perf_event_open,
>       perf_install_in_context(ctx, event, event->cpu);
>       perf_unpin_context(ctx);
> 
> -     if (move_group)
> -             perf_event_ctx_unlock(group_leader, gctx);
>       mutex_unlock(&ctx->mutex);
> 
>       if (task) {
> @@ -10785,13 +10831,12 @@ SYSCALL_DEFINE5(perf_event_open,
>       fd_install(event_fd, event_file);
>       return event_fd;
> 
> -err_locked:
> -     if (move_group)
> -             perf_event_ctx_unlock(group_leader, gctx);
> -     mutex_unlock(&ctx->mutex);
> -/* err_file: */
> +err_file:
>       fput(event_file);
> err_context:
> +     /* event->pmu_ctx freed by free_event() */
> +err_locked:
> +     mutex_unlock(&ctx->mutex);
>       perf_unpin_context(ctx);
>       put_ctx(ctx);
> err_alloc:
> @@ -10827,8 +10872,10 @@ perf_event_create_kernel_counter(struct
>                                perf_overflow_handler_t overflow_handler,
>                                void *context)
> {
> +     struct perf_event_pmu_context *pmu_ctx;
>       struct perf_event_context *ctx;
>       struct perf_event *event;
> +     struct pmu *pmu;
>       int err;
> 
>       /*
> @@ -10844,12 +10891,28 @@ perf_event_create_kernel_counter(struct
> 
>       /* Mark owner so we could distinguish it from user events. */
>       event->owner = TASK_TOMBSTONE;
> +     pmu = event->pmu;
> +
> +     if (pmu->task_ctx_nr < 0 && task) {
> +             err = -EINVAL;
> +             goto err_alloc;
> +     }
> +
> +     if (pmu->task_ctx_nr == perf_sw_context)
> +             event->event_caps |= PERF_EV_CAP_SOFTWARE;
> 
> -     ctx = find_get_context(event->pmu, task, event);
> +     ctx = find_get_context(task, event);
>       if (IS_ERR(ctx)) {
>               err = PTR_ERR(ctx);
> -             goto err_free;
> +             goto err_alloc;
> +     }
> +
> +     pmu_ctx = find_get_pmu_context(pmu, ctx, event);
> +     if (IS_ERR(pmu_ctx)) {
> +             err = PTR_ERR(pmu_ctx);
> +             goto err_ctx;
>       }
> +     event->pmu_ctx = pmu_ctx;
> 
>       WARN_ON_ONCE(ctx->parent_ctx);
>       mutex_lock(&ctx->mutex);
> @@ -10886,9 +10949,10 @@ perf_event_create_kernel_counter(struct
> 
> err_unlock:
>       mutex_unlock(&ctx->mutex);
> +err_ctx:
>       perf_unpin_context(ctx);
>       put_ctx(ctx);
> -err_free:
> +err_alloc:
>       free_event(event);
> err:
>       return ERR_PTR(err);
> @@ -10897,6 +10961,7 @@ EXPORT_SYMBOL_GPL(perf_event_create_kern
> 
> void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
> {
> +#if 0 // XXX buggered - cpu hotplug, who cares
>       struct perf_event_context *src_ctx;
>       struct perf_event_context *dst_ctx;
>       struct perf_event *event, *tmp;
> @@ -10957,6 +11022,7 @@ void perf_pmu_migrate_context(struct pmu
>       }
>       mutex_unlock(&dst_ctx->mutex);
>       mutex_unlock(&src_ctx->mutex);
> +#endif
> }
> EXPORT_SYMBOL_GPL(perf_pmu_migrate_context);
> 
> @@ -11038,14 +11104,14 @@ perf_event_exit_event(struct perf_event
>       put_event(parent_event);
> }
> 
> -static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
> +static void perf_event_exit_task_context(struct task_struct *child)
> {
>       struct perf_event_context *child_ctx, *clone_ctx = NULL;
>       struct perf_event *child_event, *next;
> 
>       WARN_ON_ONCE(child != current);
> 
> -     child_ctx = perf_pin_task_context(child, ctxn);
> +     child_ctx = perf_pin_task_context(child);
>       if (!child_ctx)
>               return;
> 
> @@ -11067,13 +11133,13 @@ static void perf_event_exit_task_context
>        * in.
>        */
>       raw_spin_lock_irq(&child_ctx->lock);
> -     task_ctx_sched_out(__get_cpu_context(child_ctx), child_ctx, EVENT_ALL);
> +     task_ctx_sched_out(child_ctx, EVENT_ALL);
> 
>       /*
>        * Now that the context is inactive, destroy the task <-> ctx relation
>        * and mark the context dead.
>        */
> -     RCU_INIT_POINTER(child->perf_event_ctxp[ctxn], NULL);
> +     RCU_INIT_POINTER(child->perf_event_ctxp, NULL);
>       put_ctx(child_ctx); /* cannot be last */
>       WRITE_ONCE(child_ctx->task, TASK_TOMBSTONE);
>       put_task_struct(current); /* cannot be last */
> @@ -11108,7 +11174,6 @@ static void perf_event_exit_task_context
> void perf_event_exit_task(struct task_struct *child)
> {
>       struct perf_event *event, *tmp;
> -     int ctxn;
> 
>       mutex_lock(&child->perf_event_mutex);
>       list_for_each_entry_safe(event, tmp, &child->perf_event_list,
> @@ -11124,8 +11189,7 @@ void perf_event_exit_task(struct task_st
>       }
>       mutex_unlock(&child->perf_event_mutex);
> 
> -     for_each_task_context_nr(ctxn)
> -             perf_event_exit_task_context(child, ctxn);
> +     perf_event_exit_task_context(child);
> 
>       /*
>        * The perf_event_exit_task_context calls perf_event_task
> @@ -11168,40 +11232,34 @@ void perf_event_free_task(struct task_st
> {
>       struct perf_event_context *ctx;
>       struct perf_event *event, *tmp;
> -     int ctxn;
> 
> -     for_each_task_context_nr(ctxn) {
> -             ctx = task->perf_event_ctxp[ctxn];
> -             if (!ctx)
> -                     continue;
> +     ctx = rcu_dereference(task->perf_event_ctxp);
> +     if (!ctx)
> +             return;
> 
> -             mutex_lock(&ctx->mutex);
> -             raw_spin_lock_irq(&ctx->lock);
> -             /*
> -              * Destroy the task <-> ctx relation and mark the context dead.
> -              *
> -              * This is important because even though the task hasn't been
> -              * exposed yet the context has been (through child_list).
> -              */
> -             RCU_INIT_POINTER(task->perf_event_ctxp[ctxn], NULL);
> -             WRITE_ONCE(ctx->task, TASK_TOMBSTONE);
> -             put_task_struct(task); /* cannot be last */
> -             raw_spin_unlock_irq(&ctx->lock);
> +     mutex_lock(&ctx->mutex);
> +     raw_spin_lock_irq(&ctx->lock);
> +     /*
> +      * Destroy the task <-> ctx relation and mark the context dead.
> +      *
> +      * This is important because even though the task hasn't been
> +      * exposed yet the context has been (through child_list).
> +      */
> +     RCU_INIT_POINTER(task->perf_event_ctxp, NULL);
> +     WRITE_ONCE(ctx->task, TASK_TOMBSTONE);
> +     put_task_struct(task); /* cannot be last */
> +     raw_spin_unlock_irq(&ctx->lock);
> 
> -             list_for_each_entry_safe(event, tmp, &ctx->event_list, 
> event_entry)
> -                     perf_free_event(event, ctx);
> +     list_for_each_entry_safe(event, tmp, &ctx->event_list, event_entry)
> +             perf_free_event(event, ctx);
> 
> -             mutex_unlock(&ctx->mutex);
> -             put_ctx(ctx);
> -     }
> +     mutex_unlock(&ctx->mutex);
> +     put_ctx(ctx);
> }
> 
> void perf_event_delayed_put(struct task_struct *task)
> {
> -     int ctxn;
> -
> -     for_each_task_context_nr(ctxn)
> -             WARN_ON_ONCE(task->perf_event_ctxp[ctxn]);
> +     WARN_ON_ONCE(task->perf_event_ctxp);
> }
> 
> struct file *perf_event_get(unsigned int fd)
> @@ -11253,6 +11311,7 @@ inherit_event(struct perf_event *parent_
>             struct perf_event_context *child_ctx)
> {
>       enum perf_event_state parent_state = parent_event->state;
> +     struct perf_event_pmu_context *pmu_ctx;
>       struct perf_event *child_event;
>       unsigned long flags;
> 
> @@ -11273,18 +11332,12 @@ inherit_event(struct perf_event *parent_
>       if (IS_ERR(child_event))
>               return child_event;
> 
> -
> -     if ((child_event->attach_state & PERF_ATTACH_TASK_DATA) &&
> -         !child_ctx->task_ctx_data) {
> -             struct pmu *pmu = child_event->pmu;
> -
> -             child_ctx->task_ctx_data = kzalloc(pmu->task_ctx_size,
> -                                                GFP_KERNEL);
> -             if (!child_ctx->task_ctx_data) {
> -                     free_event(child_event);
> -                     return NULL;
> -             }
> +     pmu_ctx = find_get_pmu_context(child_event->pmu, child_ctx, 
> child_event);
> +     if (!pmu_ctx) {
> +             free_event(child_event);
> +             return NULL;
>       }
> +     child_event->pmu_ctx = pmu_ctx;
> 
>       /*
>        * is_orphaned_event() and list_add_tail(&parent_event->child_list)
> @@ -11402,18 +11455,18 @@ static int inherit_group(struct perf_eve
> static int
> inherit_task_group(struct perf_event *event, struct task_struct *parent,
>                  struct perf_event_context *parent_ctx,
> -                struct task_struct *child, int ctxn,
> +                struct task_struct *child,
>                  int *inherited_all)
> {
> -     int ret;
>       struct perf_event_context *child_ctx;
> +     int ret;
> 
>       if (!event->attr.inherit) {
>               *inherited_all = 0;
>               return 0;
>       }
> 
> -     child_ctx = child->perf_event_ctxp[ctxn];
> +     child_ctx = child->perf_event_ctxp;
>       if (!child_ctx) {
>               /*
>                * This is executed from the parent task context, so
> @@ -11421,16 +11474,14 @@ inherit_task_group(struct perf_event *ev
>                * First allocate and initialize a context for the
>                * child.
>                */
> -             child_ctx = alloc_perf_context(parent_ctx->pmu, child);
> +             child_ctx = alloc_perf_context(child);
>               if (!child_ctx)
>                       return -ENOMEM;
> 
> -             child->perf_event_ctxp[ctxn] = child_ctx;
> +             child->perf_event_ctxp = child_ctx;
>       }
> 
> -     ret = inherit_group(event, parent, parent_ctx,
> -                         child, child_ctx);
> -
> +     ret = inherit_group(event, parent, parent_ctx, child, child_ctx);
>       if (ret)
>               *inherited_all = 0;
> 
> @@ -11440,7 +11491,7 @@ inherit_task_group(struct perf_event *ev
> /*
>  * Initialize the perf_event context in task_struct
>  */
> -static int perf_event_init_context(struct task_struct *child, int ctxn)
> +static int perf_event_init_context(struct task_struct *child)
> {
>       struct perf_event_context *child_ctx, *parent_ctx;
>       struct perf_event_context *cloned_ctx;
> @@ -11450,14 +11501,14 @@ static int perf_event_init_context(struc
>       unsigned long flags;
>       int ret = 0;
> 
> -     if (likely(!parent->perf_event_ctxp[ctxn]))
> +     if (likely(!parent->perf_event_ctxp))
>               return 0;
> 
>       /*
>        * If the parent's context is a clone, pin it so it won't get
>        * swapped under us.
>        */
> -     parent_ctx = perf_pin_task_context(parent, ctxn);
> +     parent_ctx = perf_pin_task_context(parent);
>       if (!parent_ctx)
>               return 0;
> 
> @@ -11480,7 +11531,7 @@ static int perf_event_init_context(struc
>        */
>       perf_event_groups_for_each(event, &parent_ctx->pinned_groups) {
>               ret = inherit_task_group(event, parent, parent_ctx,
> -                                      child, ctxn, &inherited_all);
> +                                      child, &inherited_all);
>               if (ret)
>                       goto out_unlock;
>       }
> @@ -11496,7 +11547,7 @@ static int perf_event_init_context(struc
> 
>       perf_event_groups_for_each(event, &parent_ctx->flexible_groups) {
>               ret = inherit_task_group(event, parent, parent_ctx,
> -                                      child, ctxn, &inherited_all);
> +                                      child, &inherited_all);
>               if (ret)
>                       goto out_unlock;
>       }
> @@ -11504,7 +11555,7 @@ static int perf_event_init_context(struc
>       raw_spin_lock_irqsave(&parent_ctx->lock, flags);
>       parent_ctx->rotate_disable = 0;
> 
> -     child_ctx = child->perf_event_ctxp[ctxn];
> +     child_ctx = child->perf_event_ctxp;
> 
>       if (child_ctx && inherited_all) {
>               /*
> @@ -11540,18 +11591,16 @@ static int perf_event_init_context(struc
>  */
> int perf_event_init_task(struct task_struct *child)
> {
> -     int ctxn, ret;
> +     int ret;
> 
> -     memset(child->perf_event_ctxp, 0, sizeof(child->perf_event_ctxp));
> +     child->perf_event_ctxp = NULL;
>       mutex_init(&child->perf_event_mutex);
>       INIT_LIST_HEAD(&child->perf_event_list);
> 
> -     for_each_task_context_nr(ctxn) {
> -             ret = perf_event_init_context(child, ctxn);
> -             if (ret) {
> -                     perf_event_free_task(child);
> -                     return ret;
> -             }
> +     ret = perf_event_init_context(child);
> +     if (ret) {
> +             perf_event_free_task(child);
> +             return ret;
>       }
> 
>       return 0;
> @@ -11560,6 +11609,7 @@ int perf_event_init_task(struct task_str
> static void __init perf_event_init_all_cpus(void)
> {
>       struct swevent_htable *swhash;
> +     struct perf_cpu_context *cpuctx;
>       int cpu;
> 
>       zalloc_cpumask_var(&perf_online_mask, GFP_KERNEL);
> @@ -11567,7 +11617,6 @@ static void __init perf_event_init_all_c
>       for_each_possible_cpu(cpu) {
>               swhash = &per_cpu(swevent_htable, cpu);
>               mutex_init(&swhash->hlist_mutex);
> -             INIT_LIST_HEAD(&per_cpu(active_ctx_list, cpu));
> 
>               INIT_LIST_HEAD(&per_cpu(pmu_sb_events.list, cpu));
>               raw_spin_lock_init(&per_cpu(pmu_sb_events.lock, cpu));
> @@ -11576,6 +11625,12 @@ static void __init perf_event_init_all_c
>               INIT_LIST_HEAD(&per_cpu(cgrp_cpuctx_list, cpu));
> #endif
>               INIT_LIST_HEAD(&per_cpu(sched_cb_list, cpu));
> +
> +             cpuctx = per_cpu_ptr(&cpu_context, cpu);
> +             __perf_event_init_context(&cpuctx->ctx);
> +             lockdep_set_class(&cpuctx->ctx.mutex, &cpuctx_mutex);
> +             lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock);
> +             cpuctx->online = cpumask_test_cpu(cpu, perf_online_mask);
>       }
> }
> 
> @@ -11597,12 +11652,12 @@ void perf_swevent_init_cpu(unsigned int
> #if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC_CORE
> static void __perf_event_exit_context(void *__info)
> {
> +     struct perf_cpu_context *cpuctx = this_cpu_ptr(&cpu_context);
>       struct perf_event_context *ctx = __info;
> -     struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
>       struct perf_event *event;
> 
>       raw_spin_lock(&ctx->lock);
> -     ctx_sched_out(ctx, cpuctx, EVENT_TIME);
> +     ctx_sched_out(ctx, EVENT_TIME);
>       list_for_each_entry(event, &ctx->event_list, event_entry)
>               __perf_remove_from_context(event, cpuctx, ctx, (void 
> *)DETACH_GROUP);
>       raw_spin_unlock(&ctx->lock);
> @@ -11612,18 +11667,16 @@ static void perf_event_exit_cpu_context(
> {
>       struct perf_cpu_context *cpuctx;
>       struct perf_event_context *ctx;
> -     struct pmu *pmu;
> 
> +     // XXX simplify cpuctx->online
>       mutex_lock(&pmus_lock);
> -     list_for_each_entry(pmu, &pmus, entry) {
> -             cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
> -             ctx = &cpuctx->ctx;
> +     cpuctx = per_cpu_ptr(&cpu_context, cpu);
> +     ctx = &cpuctx->ctx;
> 
> -             mutex_lock(&ctx->mutex);
> -             smp_call_function_single(cpu, __perf_event_exit_context, ctx, 
> 1);
> -             cpuctx->online = 0;
> -             mutex_unlock(&ctx->mutex);
> -     }
> +     mutex_lock(&ctx->mutex);
> +     smp_call_function_single(cpu, __perf_event_exit_context, ctx, 1);
> +     cpuctx->online = 0;
> +     mutex_unlock(&ctx->mutex);
>       cpumask_clear_cpu(cpu, perf_online_mask);
>       mutex_unlock(&pmus_lock);
> }
> @@ -11637,20 +11690,17 @@ int perf_event_init_cpu(unsigned int cpu
> {
>       struct perf_cpu_context *cpuctx;
>       struct perf_event_context *ctx;
> -     struct pmu *pmu;
> 
>       perf_swevent_init_cpu(cpu);
> 
>       mutex_lock(&pmus_lock);
>       cpumask_set_cpu(cpu, perf_online_mask);
> -     list_for_each_entry(pmu, &pmus, entry) {
> -             cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
> -             ctx = &cpuctx->ctx;
> +     cpuctx = per_cpu_ptr(&cpu_context, cpu);
> +     ctx = &cpuctx->ctx;
> 
> -             mutex_lock(&ctx->mutex);
> -             cpuctx->online = 1;
> -             mutex_unlock(&ctx->mutex);
> -     }
> +     mutex_lock(&ctx->mutex);
> +     cpuctx->online = 1;
> +     mutex_unlock(&ctx->mutex);
>       mutex_unlock(&pmus_lock);
> 
>       return 0;

Reply via email to