Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/kernel/events/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 360 kB image not shown  

Quellcode-Bibliothek core.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Performance events core code:
 *
 *  Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
 *  Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar
 *  Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra
 *  Copyright  ©  2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
 */


#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/cpu.h>
#include <linux/smp.h>
#include <linux/idr.h>
#include <linux/file.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/hash.h>
#include <linux/tick.h>
#include <linux/sysfs.h>
#include <linux/dcache.h>
#include <linux/percpu.h>
#include <linux/ptrace.h>
#include <linux/reboot.h>
#include <linux/vmstat.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/vmalloc.h>
#include <linux/hardirq.h>
#include <linux/hugetlb.h>
#include <linux/rculist.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
#include <linux/anon_inodes.h>
#include <linux/kernel_stat.h>
#include <linux/cgroup.h>
#include <linux/perf_event.h>
#include <linux/trace_events.h>
#include <linux/hw_breakpoint.h>
#include <linux/mm_types.h>
#include <linux/module.h>
#include <linux/mman.h>
#include <linux/compat.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/namei.h>
#include <linux/parser.h>
#include <linux/sched/clock.h>
#include <linux/sched/mm.h>
#include <linux/proc_ns.h>
#include <linux/mount.h>
#include <linux/min_heap.h>
#include <linux/highmem.h>
#include <linux/pgtable.h>
#include <linux/buildid.h>
#include <linux/task_work.h>
#include <linux/percpu-rwsem.h>

#include "internal.h"

#include <asm/irq_regs.h>

typedef int (*remote_function_f)(void *);

struct remote_function_call {
 struct task_struct *p;
 remote_function_f func;
 void   *info;
 int   ret;
};

static void remote_function(void *data)
{
 struct remote_function_call *tfc = data;
 struct task_struct *p = tfc->p;

 if (p) {
  /* -EAGAIN */
  if (task_cpu(p) != smp_processor_id())
   return;

  /*
 * Now that we're on right CPU with IRQs disabled, we can test
 * if we hit the right task without races.
 */


  tfc->ret = -ESRCH; /* No such (running) process */
  if (p != current)
   return;
 }

 tfc->ret = tfc->func(tfc->info);
}

/**
 * task_function_call - call a function on the cpu on which a task runs
 * @p: the task to evaluate
 * @func: the function to be called
 * @info: the function call argument
 *
 * Calls the function @func when the task is currently running. This might
 * be on the current CPU, which just calls the function directly.  This will
 * retry due to any failures in smp_call_function_single(), such as if the
 * task_cpu() goes offline concurrently.
 *
 * returns @func return value or -ESRCH or -ENXIO when the process isn't running
 */

static int
task_function_call(struct task_struct *p, remote_function_f func, void *info)
{
 struct remote_function_call data = {
  .p = p,
  .func = func,
  .info = info,
  .ret = -EAGAIN,
 };
 int ret;

 for (;;) {
  ret = smp_call_function_single(task_cpu(p), remote_function,
            &data, 1);
  if (!ret)
   ret = data.ret;

  if (ret != -EAGAIN)
   break;

  cond_resched();
 }

 return ret;
}

/**
 * cpu_function_call - call a function on the cpu
 * @cpu: target cpu to queue this function
 * @func: the function to be called
 * @info: the function call argument
 *
 * Calls the function @func on the remote cpu.
 *
 * returns: @func return value or -ENXIO when the cpu is offline
 */

static int cpu_function_call(int cpu, remote_function_f func, void *info)
{
 struct remote_function_call data = {
  .p = NULL,
  .func = func,
  .info = info,
  .ret = -ENXIO, /* No such CPU */
 };

 smp_call_function_single(cpu, remote_function, &data, 1);

 return data.ret;
}

enum event_type_t {
 EVENT_FLEXIBLE = 0x01,
 EVENT_PINNED = 0x02,
 EVENT_TIME = 0x04,
 EVENT_FROZEN = 0x08,
 /* see ctx_resched() for details */
 EVENT_CPU = 0x10,
 EVENT_CGROUP = 0x20,

 /* compound helpers */
 EVENT_ALL         = EVENT_FLEXIBLE | EVENT_PINNED,
 EVENT_TIME_FROZEN = EVENT_TIME | EVENT_FROZEN,
};

static inline void __perf_ctx_lock(struct perf_event_context *ctx)
{
 raw_spin_lock(&ctx->lock);
 WARN_ON_ONCE(ctx->is_active & EVENT_FROZEN);
}

static void perf_ctx_lock(struct perf_cpu_context *cpuctx,
     struct perf_event_context *ctx)
{
 __perf_ctx_lock(&cpuctx->ctx);
 if (ctx)
  __perf_ctx_lock(ctx);
}

static inline void __perf_ctx_unlock(struct perf_event_context *ctx)
{
 /*
 * If ctx_sched_in() didn't again set any ALL flags, clean up
 * after ctx_sched_out() by clearing is_active.
 */

 if (ctx->is_active & EVENT_FROZEN) {
  if (!(ctx->is_active & EVENT_ALL))
   ctx->is_active = 0;
  else
   ctx->is_active &= ~EVENT_FROZEN;
 }
 raw_spin_unlock(&ctx->lock);
}

static void perf_ctx_unlock(struct perf_cpu_context *cpuctx,
       struct perf_event_context *ctx)
{
 if (ctx)
  __perf_ctx_unlock(ctx);
 __perf_ctx_unlock(&cpuctx->ctx);
}

typedef struct {
 struct perf_cpu_context *cpuctx;
 struct perf_event_context *ctx;
} class_perf_ctx_lock_t;

static inline void class_perf_ctx_lock_destructor(class_perf_ctx_lock_t *_T)
{ perf_ctx_unlock(_T->cpuctx, _T->ctx); }

static inline class_perf_ctx_lock_t
class_perf_ctx_lock_constructor(struct perf_cpu_context *cpuctx,
    struct perf_event_context *ctx)
{ perf_ctx_lock(cpuctx, ctx); return (class_perf_ctx_lock_t){ cpuctx, ctx }; }

#define TASK_TOMBSTONE ((void *)-1L)

static bool is_kernel_event(struct perf_event *event)
{
 return READ_ONCE(event->owner) == TASK_TOMBSTONE;
}

static DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context);

struct perf_event_context *perf_cpu_task_ctx(void)
{
 lockdep_assert_irqs_disabled();
 return this_cpu_ptr(&perf_cpu_context)->task_ctx;
}

/*
 * On task ctx scheduling...
 *
 * When !ctx->nr_events a task context will not be scheduled. This means
 * we can disable the scheduler hooks (for performance) without leaving
 * pending task ctx state.
 *
 * This however results in two special cases:
 *
 *  - removing the last event from a task ctx; this is relatively straight
 *    forward and is done in __perf_remove_from_context.
 *
 *  - adding the first event to a task ctx; this is tricky because we cannot
 *    rely on ctx->is_active and therefore cannot use event_function_call().
 *    See perf_install_in_context().
 *
 * If ctx->nr_events, then ctx->is_active and cpuctx->task_ctx are set.
 */


typedef void (*event_f)(struct perf_event *, struct perf_cpu_context *,
   struct perf_event_context *, void *);

struct event_function_struct {
 struct perf_event *event;
 event_f func;
 void *data;
};

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 = this_cpu_ptr(&perf_cpu_context);
 struct perf_event_context *task_ctx = cpuctx->task_ctx;
 int ret = 0;

 lockdep_assert_irqs_disabled();

 perf_ctx_lock(cpuctx, task_ctx);
 /*
 * Since we do the IPI call without holding ctx->lock things can have
 * changed, double check we hit the task we set out to hit.
 */

 if (ctx->task) {
  if (ctx->task != current) {
   ret = -ESRCH;
   goto unlock;
  }

  /*
 * We only use event_function_call() on established contexts,
 * and event_function() is only ever called when active (or
 * rather, we'll have bailed in task_function_call() or the
 * above ctx->task != current test), therefore we must have
 * ctx->is_active here.
 */

  WARN_ON_ONCE(!ctx->is_active);
  /*
 * And since we have ctx->is_active, cpuctx->task_ctx must
 * match.
 */

  WARN_ON_ONCE(task_ctx != ctx);
 } else {
  WARN_ON_ONCE(&cpuctx->ctx != ctx);
 }

 efs->func(event, cpuctx, ctx, efs->data);
unlock:
 perf_ctx_unlock(cpuctx, task_ctx);

 return ret;
}

static void event_function_call(struct perf_event *event, event_f func, void *data)
{
 struct perf_event_context *ctx = event->ctx;
 struct task_struct *task = READ_ONCE(ctx->task); /* verified in event_function */
 struct perf_cpu_context *cpuctx;
 struct event_function_struct efs = {
  .event = event,
  .func = func,
  .data = data,
 };

 if (!event->parent) {
  /*
 * If this is a !child event, we must hold ctx::mutex to
 * stabilize the event->ctx relation. See
 * perf_event_ctx_lock().
 */

  lockdep_assert_held(&ctx->mutex);
 }

 if (!task) {
  cpu_function_call(event->cpu, event_function, &efs);
  return;
 }

 if (task == TASK_TOMBSTONE)
  return;

again:
 if (!task_function_call(task, event_function, &efs))
  return;

 local_irq_disable();
 cpuctx = this_cpu_ptr(&perf_cpu_context);
 perf_ctx_lock(cpuctx, ctx);
 /*
 * Reload the task pointer, it might have been changed by
 * a concurrent perf_event_context_sched_out().
 */

 task = ctx->task;
 if (task == TASK_TOMBSTONE)
  goto unlock;
 if (ctx->is_active) {
  perf_ctx_unlock(cpuctx, ctx);
  local_irq_enable();
  goto again;
 }
 func(event, NULL, ctx, data);
unlock:
 perf_ctx_unlock(cpuctx, ctx);
 local_irq_enable();
}

/*
 * Similar to event_function_call() + event_function(), but hard assumes IRQs
 * are already disabled and we're on the right CPU.
 */

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 = this_cpu_ptr(&perf_cpu_context);
 struct task_struct *task = READ_ONCE(ctx->task);
 struct perf_event_context *task_ctx = NULL;

 lockdep_assert_irqs_disabled();

 if (task) {
  if (task == TASK_TOMBSTONE)
   return;

  task_ctx = ctx;
 }

 perf_ctx_lock(cpuctx, task_ctx);

 task = ctx->task;
 if (task == TASK_TOMBSTONE)
  goto unlock;

 if (task) {
  /*
 * We must be either inactive or active and the right task,
 * otherwise we're screwed, since we cannot IPI to somewhere
 * else.
 */

  if (ctx->is_active) {
   if (WARN_ON_ONCE(task != current))
    goto unlock;

   if (WARN_ON_ONCE(cpuctx->task_ctx != ctx))
    goto unlock;
  }
 } else {
  WARN_ON_ONCE(&cpuctx->ctx != ctx);
 }

 func(event, cpuctx, ctx, data);
unlock:
 perf_ctx_unlock(cpuctx, task_ctx);
}

#define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\
         PERF_FLAG_FD_OUTPUT  |\
         PERF_FLAG_PID_CGROUP |\
         PERF_FLAG_FD_CLOEXEC)

/*
 * branch priv levels that need permission checks
 */

#define PERF_SAMPLE_BRANCH_PERM_PLM \
 (PERF_SAMPLE_BRANCH_KERNEL |\
  PERF_SAMPLE_BRANCH_HV)

/*
 * perf_sched_events : >0 events exist
 */


static void perf_sched_delayed(struct work_struct *work);
DEFINE_STATIC_KEY_FALSE(perf_sched_events);
static DECLARE_DELAYED_WORK(perf_sched_work, perf_sched_delayed);
static DEFINE_MUTEX(perf_sched_mutex);
static atomic_t perf_sched_count;

static DEFINE_PER_CPU(struct pmu_event_list, pmu_sb_events);

static atomic_t nr_mmap_events __read_mostly;
static atomic_t nr_comm_events __read_mostly;
static atomic_t nr_namespaces_events __read_mostly;
static atomic_t nr_task_events __read_mostly;
static atomic_t nr_freq_events __read_mostly;
static atomic_t nr_switch_events __read_mostly;
static atomic_t nr_ksymbol_events __read_mostly;
static atomic_t nr_bpf_events __read_mostly;
static atomic_t nr_cgroup_events __read_mostly;
static atomic_t nr_text_poke_events __read_mostly;
static atomic_t nr_build_id_events __read_mostly;

static LIST_HEAD(pmus);
static DEFINE_MUTEX(pmus_lock);
static struct srcu_struct pmus_srcu;
static cpumask_var_t perf_online_mask;
static cpumask_var_t perf_online_core_mask;
static cpumask_var_t perf_online_die_mask;
static cpumask_var_t perf_online_cluster_mask;
static cpumask_var_t perf_online_pkg_mask;
static cpumask_var_t perf_online_sys_mask;
static struct kmem_cache *perf_event_cache;

/*
 * perf event paranoia level:
 *  -1 - not paranoid at all
 *   0 - disallow raw tracepoint access for unpriv
 *   1 - disallow cpu events for unpriv
 *   2 - disallow kernel profiling for unpriv
 */

int sysctl_perf_event_paranoid __read_mostly = 2;

/* Minimum for 512 kiB + 1 user control page. 'free' kiB per user. */
static int sysctl_perf_event_mlock __read_mostly = 512 + (PAGE_SIZE / 1024);

/*
 * max perf event sample rate
 */

#define DEFAULT_MAX_SAMPLE_RATE  100000
#define DEFAULT_SAMPLE_PERIOD_NS (NSEC_PER_SEC / DEFAULT_MAX_SAMPLE_RATE)
#define DEFAULT_CPU_TIME_MAX_PERCENT 25

int sysctl_perf_event_sample_rate __read_mostly = DEFAULT_MAX_SAMPLE_RATE;
static int sysctl_perf_cpu_time_max_percent __read_mostly = DEFAULT_CPU_TIME_MAX_PERCENT;

static int max_samples_per_tick __read_mostly = DIV_ROUND_UP(DEFAULT_MAX_SAMPLE_RATE, HZ);
static int perf_sample_period_ns __read_mostly = DEFAULT_SAMPLE_PERIOD_NS;

static int perf_sample_allowed_ns __read_mostly =
 DEFAULT_SAMPLE_PERIOD_NS * DEFAULT_CPU_TIME_MAX_PERCENT / 100;

static void update_perf_cpu_limits(void)
{
 u64 tmp = perf_sample_period_ns;

 tmp *= sysctl_perf_cpu_time_max_percent;
 tmp = div_u64(tmp, 100);
 if (!tmp)
  tmp = 1;

 WRITE_ONCE(perf_sample_allowed_ns, tmp);
}

static bool perf_rotate_context(struct perf_cpu_pmu_context *cpc);

static int perf_event_max_sample_rate_handler(const struct ctl_table *table, int write,
           void *buffer, size_t *lenp, loff_t *ppos)
{
 int ret;
 int perf_cpu = sysctl_perf_cpu_time_max_percent;
 /*
 * If throttling is disabled don't allow the write:
 */

 if (write && (perf_cpu == 100 || perf_cpu == 0))
  return -EINVAL;

 ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 if (ret || !write)
  return ret;

 max_samples_per_tick = DIV_ROUND_UP(sysctl_perf_event_sample_rate, HZ);
 perf_sample_period_ns = NSEC_PER_SEC / sysctl_perf_event_sample_rate;
 update_perf_cpu_limits();

 return 0;
}

static int perf_cpu_time_max_percent_handler(const struct ctl_table *table, int write,
  void *buffer, size_t *lenp, loff_t *ppos)
{
 int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);

 if (ret || !write)
  return ret;

 if (sysctl_perf_cpu_time_max_percent == 100 ||
     sysctl_perf_cpu_time_max_percent == 0) {
  printk(KERN_WARNING
         "perf: Dynamic interrupt throttling disabled, can hang your system!\n");
  WRITE_ONCE(perf_sample_allowed_ns, 0);
 } else {
  update_perf_cpu_limits();
 }

 return 0;
}

static const struct ctl_table events_core_sysctl_table[] = {
 /*
 * User-space relies on this file as a feature check for
 * perf_events being enabled. It's an ABI, do not remove!
 */

 {
  .procname = "perf_event_paranoid",
  .data  = &sysctl_perf_event_paranoid,
  .maxlen  = sizeof(sysctl_perf_event_paranoid),
  .mode  = 0644,
  .proc_handler = proc_dointvec,
 },
 {
  .procname = "perf_event_mlock_kb",
  .data  = &sysctl_perf_event_mlock,
  .maxlen  = sizeof(sysctl_perf_event_mlock),
  .mode  = 0644,
  .proc_handler = proc_dointvec,
 },
 {
  .procname = "perf_event_max_sample_rate",
  .data  = &sysctl_perf_event_sample_rate,
  .maxlen  = sizeof(sysctl_perf_event_sample_rate),
  .mode  = 0644,
  .proc_handler = perf_event_max_sample_rate_handler,
  .extra1  = SYSCTL_ONE,
 },
 {
  .procname = "perf_cpu_time_max_percent",
  .data  = &sysctl_perf_cpu_time_max_percent,
  .maxlen  = sizeof(sysctl_perf_cpu_time_max_percent),
  .mode  = 0644,
  .proc_handler = perf_cpu_time_max_percent_handler,
  .extra1  = SYSCTL_ZERO,
  .extra2  = SYSCTL_ONE_HUNDRED,
 },
};

static int __init init_events_core_sysctls(void)
{
 register_sysctl_init("kernel", events_core_sysctl_table);
 return 0;
}
core_initcall(init_events_core_sysctls);


/*
 * perf samples are done in some very critical code paths (NMIs).
 * If they take too much CPU time, the system can lock up and not
 * get any real work done.  This will drop the sample rate when
 * we detect that events are taking too long.
 */

#define NR_ACCUMULATED_SAMPLES 128
static DEFINE_PER_CPU(u64, running_sample_length);

static u64 __report_avg;
static u64 __report_allowed;

static void perf_duration_warn(struct irq_work *w)
{
 printk_ratelimited(KERN_INFO
  "perf: interrupt took too long (%lld > %lld), lowering "
  "kernel.perf_event_max_sample_rate to %d\n",
  __report_avg, __report_allowed,
  sysctl_perf_event_sample_rate);
}

static DEFINE_IRQ_WORK(perf_duration_work, perf_duration_warn);

void perf_sample_event_took(u64 sample_len_ns)
{
 u64 max_len = READ_ONCE(perf_sample_allowed_ns);
 u64 running_len;
 u64 avg_len;
 u32 max;

 if (max_len == 0)
  return;

 /* Decay the counter by 1 average sample. */
 running_len = __this_cpu_read(running_sample_length);
 running_len -= running_len/NR_ACCUMULATED_SAMPLES;
 running_len += sample_len_ns;
 __this_cpu_write(running_sample_length, running_len);

 /*
 * Note: this will be biased artificially low until we have
 * seen NR_ACCUMULATED_SAMPLES. Doing it this way keeps us
 * from having to maintain a count.
 */

 avg_len = running_len/NR_ACCUMULATED_SAMPLES;
 if (avg_len <= max_len)
  return;

 __report_avg = avg_len;
 __report_allowed = max_len;

 /*
 * Compute a throttle threshold 25% below the current duration.
 */

 avg_len += avg_len / 4;
 max = (TICK_NSEC / 100) * sysctl_perf_cpu_time_max_percent;
 if (avg_len < max)
  max /= (u32)avg_len;
 else
  max = 1;

 WRITE_ONCE(perf_sample_allowed_ns, avg_len);
 WRITE_ONCE(max_samples_per_tick, max);

 sysctl_perf_event_sample_rate = max * HZ;
 perf_sample_period_ns = NSEC_PER_SEC / sysctl_perf_event_sample_rate;

 if (!irq_work_queue(&perf_duration_work)) {
  early_printk("perf: interrupt took too long (%lld > %lld), lowering "
        "kernel.perf_event_max_sample_rate to %d\n",
        __report_avg, __report_allowed,
        sysctl_perf_event_sample_rate);
 }
}

static atomic64_t perf_event_id;

static void update_context_time(struct perf_event_context *ctx);
static u64 perf_event_time(struct perf_event *event);

void __weak perf_event_print_debug(void) { }

static inline u64 perf_clock(void)
{
 return local_clock();
}

static inline u64 perf_event_clock(struct perf_event *event)
{
 return event->clock();
}

/*
 * State based event timekeeping...
 *
 * The basic idea is to use event->state to determine which (if any) time
 * fields to increment with the current delta. This means we only need to
 * update timestamps when we change state or when they are explicitly requested
 * (read).
 *
 * Event groups make things a little more complicated, but not terribly so. The
 * rules for a group are that if the group leader is OFF the entire group is
 * OFF, irrespective of what the group member states are. This results in
 * __perf_effective_state().
 *
 * A further ramification is that when a group leader flips between OFF and
 * !OFF, we need to update all group member times.
 *
 *
 * NOTE: perf_event_time() is based on the (cgroup) context time, and thus we
 * need to make sure the relevant context time is updated before we try and
 * update our timestamps.
 */


static __always_inline enum perf_event_state
__perf_effective_state(struct perf_event *event)
{
 struct perf_event *leader = event->group_leader;

 if (leader->state <= PERF_EVENT_STATE_OFF)
  return leader->state;

 return event->state;
}

static __always_inline void
__perf_update_times(struct perf_event *event, u64 now, u64 *enabled, u64 *running)
{
 enum perf_event_state state = __perf_effective_state(event);
 u64 delta = now - event->tstamp;

 *enabled = event->total_time_enabled;
 if (state >= PERF_EVENT_STATE_INACTIVE)
  *enabled += delta;

 *running = event->total_time_running;
 if (state >= PERF_EVENT_STATE_ACTIVE)
  *running += delta;
}

static void perf_event_update_time(struct perf_event *event)
{
 u64 now = perf_event_time(event);

 __perf_update_times(event, now, &event->total_time_enabled,
     &event->total_time_running);
 event->tstamp = now;
}

static void perf_event_update_sibling_time(struct perf_event *leader)
{
 struct perf_event *sibling;

 for_each_sibling_event(sibling, leader)
  perf_event_update_time(sibling);
}

static void
perf_event_set_state(struct perf_event *event, enum perf_event_state state)
{
 if (event->state == state)
  return;

 perf_event_update_time(event);
 /*
 * If a group leader gets enabled/disabled all its siblings
 * are affected too.
 */

 if ((event->state < 0) ^ (state < 0))
  perf_event_update_sibling_time(event);

 WRITE_ONCE(event->state, state);
}

/*
 * UP store-release, load-acquire
 */


#define __store_release(ptr, val)     \
do {         \
 barrier();       \
 WRITE_ONCE(*(ptr), (val));     \
while (0)

#define __load_acquire(ptr)      \
({         \
 __unqual_scalar_typeof(*(ptr)) ___p = READ_ONCE(*(ptr)); \
 barrier();       \
 ___p;        \
})

#define for_each_epc(_epc, _ctx, _pmu, _cgroup)    \
 list_for_each_entry(_epc, &((_ctx)->pmu_ctx_list), pmu_ctx_entry) \
  if (_cgroup && !_epc->nr_cgroups)   \
   continue;     \
  else if (_pmu && _epc->pmu != _pmu)   \
   continue;     \
  else

static void perf_ctx_disable(struct perf_event_context *ctx, bool cgroup)
{
 struct perf_event_pmu_context *pmu_ctx;

 for_each_epc(pmu_ctx, ctx, NULL, cgroup)
  perf_pmu_disable(pmu_ctx->pmu);
}

static void perf_ctx_enable(struct perf_event_context *ctx, bool cgroup)
{
 struct perf_event_pmu_context *pmu_ctx;

 for_each_epc(pmu_ctx, ctx, NULL, cgroup)
  perf_pmu_enable(pmu_ctx->pmu);
}

static void ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type);
static void ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type);

#ifdef CONFIG_CGROUP_PERF

static inline bool
perf_cgroup_match(struct perf_event *event)
{
 struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context);

 /* @event doesn't care about cgroup */
 if (!event->cgrp)
  return true;

 /* wants specific cgroup scope but @cpuctx isn't associated with any */
 if (!cpuctx->cgrp)
  return false;

 /*
 * Cgroup scoping is recursive.  An event enabled for a cgroup is
 * also enabled for all its descendant cgroups.  If @cpuctx's
 * cgroup is a descendant of @event's (the test covers identity
 * case), it's a match.
 */

 return cgroup_is_descendant(cpuctx->cgrp->css.cgroup,
        event->cgrp->css.cgroup);
}

static inline void perf_detach_cgroup(struct perf_event *event)
{
 css_put(&event->cgrp->css);
 event->cgrp = NULL;
}

static inline int is_cgroup_event(struct perf_event *event)
{
 return event->cgrp != NULL;
}

static inline u64 perf_cgroup_event_time(struct perf_event *event)
{
 struct perf_cgroup_info *t;

 t = per_cpu_ptr(event->cgrp->info, event->cpu);
 return t->time;
}

static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now)
{
 struct perf_cgroup_info *t;

 t = per_cpu_ptr(event->cgrp->info, event->cpu);
 if (!__load_acquire(&t->active))
  return t->time;
 now += READ_ONCE(t->timeoffset);
 return now;
}

static inline void __update_cgrp_time(struct perf_cgroup_info *info, u64 now, bool adv)
{
 if (adv)
  info->time += now - info->timestamp;
 info->timestamp = now;
 /*
 * see update_context_time()
 */

 WRITE_ONCE(info->timeoffset, info->time - info->timestamp);
}

static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx, bool final)
{
 struct perf_cgroup *cgrp = cpuctx->cgrp;
 struct cgroup_subsys_state *css;
 struct perf_cgroup_info *info;

 if (cgrp) {
  u64 now = perf_clock();

  for (css = &cgrp->css; css; css = css->parent) {
   cgrp = container_of(css, struct perf_cgroup, css);
   info = this_cpu_ptr(cgrp->info);

   __update_cgrp_time(info, now, true);
   if (final)
    __store_release(&info->active, 0);
  }
 }
}

static inline void update_cgrp_time_from_event(struct perf_event *event)
{
 struct perf_cgroup_info *info;

 /*
 * ensure we access cgroup data only when needed and
 * when we know the cgroup is pinned (css_get)
 */

 if (!is_cgroup_event(event))
  return;

 info = this_cpu_ptr(event->cgrp->info);
 /*
 * Do not update time when cgroup is not active
 */

 if (info->active)
  __update_cgrp_time(info, perf_clock(), true);
}

static inline void
perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx)
{
 struct perf_event_context *ctx = &cpuctx->ctx;
 struct perf_cgroup *cgrp = cpuctx->cgrp;
 struct perf_cgroup_info *info;
 struct cgroup_subsys_state *css;

 /*
 * ctx->lock held by caller
 * ensure we do not access cgroup data
 * unless we have the cgroup pinned (css_get)
 */

 if (!cgrp)
  return;

 WARN_ON_ONCE(!ctx->nr_cgroups);

 for (css = &cgrp->css; css; css = css->parent) {
  cgrp = container_of(css, struct perf_cgroup, css);
  info = this_cpu_ptr(cgrp->info);
  __update_cgrp_time(info, ctx->timestamp, false);
  __store_release(&info->active, 1);
 }
}

/*
 * reschedule events based on the cgroup constraint of task.
 */

static void perf_cgroup_switch(struct task_struct *task)
{
 struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context);
 struct perf_cgroup *cgrp;

 /*
 * cpuctx->cgrp is set when the first cgroup event enabled,
 * and is cleared when the last cgroup event disabled.
 */

 if (READ_ONCE(cpuctx->cgrp) == NULL)
  return;

 cgrp = perf_cgroup_from_task(task, NULL);
 if (READ_ONCE(cpuctx->cgrp) == cgrp)
  return;

 guard(perf_ctx_lock)(cpuctx, cpuctx->task_ctx);
 /*
 * Re-check, could've raced vs perf_remove_from_context().
 */

 if (READ_ONCE(cpuctx->cgrp) == NULL)
  return;

 WARN_ON_ONCE(cpuctx->ctx.nr_cgroups == 0);

 perf_ctx_disable(&cpuctx->ctx, true);

 ctx_sched_out(&cpuctx->ctx, NULL, EVENT_ALL|EVENT_CGROUP);
 /*
 * must not be done before ctxswout due
 * to update_cgrp_time_from_cpuctx() in
 * ctx_sched_out()
 */

 cpuctx->cgrp = cgrp;
 /*
 * set cgrp before ctxsw in to allow
 * perf_cgroup_set_timestamp() in ctx_sched_in()
 * to not have to pass task around
 */

 ctx_sched_in(&cpuctx->ctx, NULL, EVENT_ALL|EVENT_CGROUP);

 perf_ctx_enable(&cpuctx->ctx, true);
}

static int perf_cgroup_ensure_storage(struct perf_event *event,
    struct cgroup_subsys_state *css)
{
 struct perf_cpu_context *cpuctx;
 struct perf_event **storage;
 int cpu, heap_size, ret = 0;

 /*
 * Allow storage to have sufficient space for an iterator for each
 * possibly nested cgroup plus an iterator for events with no cgroup.
 */

 for (heap_size = 1; css; css = css->parent)
  heap_size++;

 for_each_possible_cpu(cpu) {
  cpuctx = per_cpu_ptr(&perf_cpu_context, cpu);
  if (heap_size <= cpuctx->heap_size)
   continue;

  storage = kmalloc_node(heap_size * sizeof(struct perf_event *),
           GFP_KERNEL, cpu_to_node(cpu));
  if (!storage) {
   ret = -ENOMEM;
   break;
  }

  raw_spin_lock_irq(&cpuctx->ctx.lock);
  if (cpuctx->heap_size < heap_size) {
   swap(cpuctx->heap, storage);
   if (storage == cpuctx->heap_default)
    storage = NULL;
   cpuctx->heap_size = heap_size;
  }
  raw_spin_unlock_irq(&cpuctx->ctx.lock);

  kfree(storage);
 }

 return ret;
}

static inline int perf_cgroup_connect(int fd, struct perf_event *event,
          struct perf_event_attr *attr,
          struct perf_event *group_leader)
{
 struct perf_cgroup *cgrp;
 struct cgroup_subsys_state *css;
 CLASS(fd, f)(fd);
 int ret = 0;

 if (fd_empty(f))
  return -EBADF;

 css = css_tryget_online_from_dir(fd_file(f)->f_path.dentry,
      &perf_event_cgrp_subsys);
 if (IS_ERR(css))
  return PTR_ERR(css);

 ret = perf_cgroup_ensure_storage(event, css);
 if (ret)
  return ret;

 cgrp = container_of(css, struct perf_cgroup, css);
 event->cgrp = cgrp;

 /*
 * all events in a group must monitor
 * the same cgroup because a task belongs
 * to only one perf cgroup at a time
 */

 if (group_leader && group_leader->cgrp != cgrp) {
  perf_detach_cgroup(event);
  ret = -EINVAL;
 }
 return ret;
}

static inline void
perf_cgroup_event_enable(struct perf_event *event, struct perf_event_context *ctx)
{
 struct perf_cpu_context *cpuctx;

 if (!is_cgroup_event(event))
  return;

 event->pmu_ctx->nr_cgroups++;

 /*
 * Because cgroup events are always per-cpu events,
 * @ctx == &cpuctx->ctx.
 */

 cpuctx = container_of(ctx, struct perf_cpu_context, ctx);

 if (ctx->nr_cgroups++)
  return;

 cpuctx->cgrp = perf_cgroup_from_task(current, ctx);
}

static inline void
perf_cgroup_event_disable(struct perf_event *event, struct perf_event_context *ctx)
{
 struct perf_cpu_context *cpuctx;

 if (!is_cgroup_event(event))
  return;

 event->pmu_ctx->nr_cgroups--;

 /*
 * Because cgroup events are always per-cpu events,
 * @ctx == &cpuctx->ctx.
 */

 cpuctx = container_of(ctx, struct perf_cpu_context, ctx);

 if (--ctx->nr_cgroups)
  return;

 cpuctx->cgrp = NULL;
}

#else /* !CONFIG_CGROUP_PERF */

static inline bool
perf_cgroup_match(struct perf_event *event)
{
 return true;
}

static inline void perf_detach_cgroup(struct perf_event *event)
{}

static inline int is_cgroup_event(struct perf_event *event)
{
 return 0;
}

static inline void update_cgrp_time_from_event(struct perf_event *event)
{
}

static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx,
      bool final)
{
}

static inline int perf_cgroup_connect(pid_t pid, struct perf_event *event,
          struct perf_event_attr *attr,
          struct perf_event *group_leader)
{
 return -EINVAL;
}

static inline void
perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx)
{
}

static inline u64 perf_cgroup_event_time(struct perf_event *event)
{
 return 0;
}

static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now)
{
 return 0;
}

static inline void
perf_cgroup_event_enable(struct perf_event *event, struct perf_event_context *ctx)
{
}

static inline void
perf_cgroup_event_disable(struct perf_event *event, struct perf_event_context *ctx)
{
}

static void perf_cgroup_switch(struct task_struct *task)
{
}
#endif

/*
 * set default to be dependent on timer tick just
 * like original code
 */

#define PERF_CPU_HRTIMER (1000 / HZ)
/*
 * function must be called with interrupts disabled
 */

static enum hrtimer_restart perf_mux_hrtimer_handler(struct hrtimer *hr)
{
 struct perf_cpu_pmu_context *cpc;
 bool rotations;

 lockdep_assert_irqs_disabled();

 cpc = container_of(hr, struct perf_cpu_pmu_context, hrtimer);
 rotations = perf_rotate_context(cpc);

 raw_spin_lock(&cpc->hrtimer_lock);
 if (rotations)
  hrtimer_forward_now(hr, cpc->hrtimer_interval);
 else
  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_pmu_context *cpc, int cpu)
{
 struct hrtimer *timer = &cpc->hrtimer;
 struct pmu *pmu = cpc->epc.pmu;
 u64 interval;

 /*
 * check default is sane, if not set then force to
 * default interval (1/tick)
 */

 interval = pmu->hrtimer_interval_ms;
 if (interval < 1)
  interval = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;

 cpc->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);

 raw_spin_lock_init(&cpc->hrtimer_lock);
 hrtimer_setup(timer, perf_mux_hrtimer_handler, CLOCK_MONOTONIC,
        HRTIMER_MODE_ABS_PINNED_HARD);
}

static int perf_mux_hrtimer_restart(struct perf_cpu_pmu_context *cpc)
{
 struct hrtimer *timer = &cpc->hrtimer;
 unsigned long flags;

 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_HARD);
 }
 raw_spin_unlock_irqrestore(&cpc->hrtimer_lock, flags);

 return 0;
}

static int perf_mux_hrtimer_restart_ipi(void *arg)
{
 return perf_mux_hrtimer_restart(arg);
}

static __always_inline struct perf_cpu_pmu_context *this_cpc(struct pmu *pmu)
{
 return *this_cpu_ptr(pmu->cpu_pmu_context);
}

void perf_pmu_disable(struct pmu *pmu)
{
 int *count = &this_cpc(pmu)->pmu_disable_count;
 if (!(*count)++)
  pmu->pmu_disable(pmu);
}

void perf_pmu_enable(struct pmu *pmu)
{
 int *count = &this_cpc(pmu)->pmu_disable_count;
 if (!--(*count))
  pmu->pmu_enable(pmu);
}

static void perf_assert_pmu_disabled(struct pmu *pmu)
{
 int *count = &this_cpc(pmu)->pmu_disable_count;
 WARN_ON_ONCE(*count == 0);
}

static inline void perf_pmu_read(struct perf_event *event)
{
 if (event->state == PERF_EVENT_STATE_ACTIVE)
  event->pmu->read(event);
}

static void get_ctx(struct perf_event_context *ctx)
{
 refcount_inc(&ctx->refcount);
}

static void free_ctx(struct rcu_head *head)
{
 struct perf_event_context *ctx;

 ctx = container_of(head, struct perf_event_context, rcu_head);
 kfree(ctx);
}

static void put_ctx(struct perf_event_context *ctx)
{
 if (refcount_dec_and_test(&ctx->refcount)) {
  if (ctx->parent_ctx)
   put_ctx(ctx->parent_ctx);
  if (ctx->task && ctx->task != TASK_TOMBSTONE)
   put_task_struct(ctx->task);
  call_rcu(&ctx->rcu_head, free_ctx);
 } else {
  smp_mb__after_atomic(); /* pairs with wait_var_event() */
  if (ctx->task == TASK_TOMBSTONE)
   wake_up_var(&ctx->refcount);
 }
}

/*
 * Because of perf_event::ctx migration in sys_perf_event_open::move_group and
 * perf_pmu_migrate_context() we need some magic.
 *
 * Those places that change perf_event::ctx will hold both
 * perf_event_ctx::mutex of the 'old' and 'new' ctx value.
 *
 * Lock ordering is by mutex address. There are two other sites where
 * perf_event_context::mutex nests and those are:
 *
 *  - perf_event_exit_task_context() [ child , 0 ]
 *      perf_event_exit_event()
 *        put_event() [ parent, 1 ]
 *
 *  - perf_event_init_context() [ parent, 0 ]
 *      inherit_task_group()
 *        inherit_group()
 *          inherit_event()
 *            perf_event_alloc()
 *              perf_init_event()
 *                perf_try_init_event() [ child , 1 ]
 *
 * While it appears there is an obvious deadlock here -- the parent and child
 * nesting levels are inverted between the two. This is in fact safe because
 * life-time rules separate them. That is an exiting task cannot fork, and a
 * spawning task cannot (yet) exit.
 *
 * But remember that these are parent<->child context relations, and
 * migration does not affect children, therefore these two orderings should not
 * interact.
 *
 * The change in perf_event::ctx does not affect children (as claimed above)
 * because the sys_perf_event_open() case will install a new event and break
 * the ctx parent<->child relation, and perf_pmu_migrate_context() is only
 * concerned with cpuctx and that doesn't have children.
 *
 * The places that change perf_event::ctx will issue:
 *
 *   perf_remove_from_context();
 *   synchronize_rcu();
 *   perf_install_in_context();
 *
 * to affect the change. The remove_from_context() + synchronize_rcu() should
 * quiesce the event, after which we can install it in the new location. This
 * means that only external vectors (perf_fops, prctl) can perturb the event
 * while in transit. Therefore all such accessors should also acquire
 * perf_event_context::mutex to serialize against this.
 *
 * However; because event->ctx can change while we're waiting to acquire
 * ctx->mutex we must be careful and use the below perf_event_ctx_lock()
 * function.
 *
 * Lock order:
 *    exec_update_lock
 * task_struct::perf_event_mutex
 *   perf_event_context::mutex
 *     perf_event::child_mutex;
 *       perf_event_context::lock
 *     mmap_lock
 *       perf_event::mmap_mutex
 *         perf_buffer::aux_mutex
 *       perf_addr_filters_head::lock
 *
 *    cpu_hotplug_lock
 *      pmus_lock
 *   cpuctx->mutex / perf_event_context::mutex
 */

static struct perf_event_context *
perf_event_ctx_lock_nested(struct perf_event *event, int nesting)
{
 struct perf_event_context *ctx;

again:
 rcu_read_lock();
 ctx = READ_ONCE(event->ctx);
 if (!refcount_inc_not_zero(&ctx->refcount)) {
  rcu_read_unlock();
  goto again;
 }
 rcu_read_unlock();

 mutex_lock_nested(&ctx->mutex, nesting);
 if (event->ctx != ctx) {
  mutex_unlock(&ctx->mutex);
  put_ctx(ctx);
  goto again;
 }

 return ctx;
}

static inline struct perf_event_context *
perf_event_ctx_lock(struct perf_event *event)
{
 return perf_event_ctx_lock_nested(event, 0);
}

static void perf_event_ctx_unlock(struct perf_event *event,
      struct perf_event_context *ctx)
{
 mutex_unlock(&ctx->mutex);
 put_ctx(ctx);
}

/*
 * This must be done under the ctx->lock, such as to serialize against
 * context_equiv(), therefore we cannot call put_ctx() since that might end up
 * calling scheduler related locks and ctx->lock nests inside those.
 */

static __must_check struct perf_event_context *
unclone_ctx(struct perf_event_context *ctx)
{
 struct perf_event_context *parent_ctx = ctx->parent_ctx;

 lockdep_assert_held(&ctx->lock);

 if (parent_ctx)
  ctx->parent_ctx = NULL;
 ctx->generation++;

 return parent_ctx;
}

static u32 perf_event_pid_type(struct perf_event *event, struct task_struct *p,
    enum pid_type type)
{
 u32 nr;
 /*
 * only top level events have the pid namespace they were created in
 */

 if (event->parent)
  event = event->parent;

 nr = __task_pid_nr_ns(p, type, event->ns);
 /* avoid -1 if it is idle thread or runs in another ns */
 if (!nr && !pid_alive(p))
  nr = -1;
 return nr;
}

static u32 perf_event_pid(struct perf_event *event, struct task_struct *p)
{
 return perf_event_pid_type(event, p, PIDTYPE_TGID);
}

static u32 perf_event_tid(struct perf_event *event, struct task_struct *p)
{
 return perf_event_pid_type(event, p, PIDTYPE_PID);
}

/*
 * If we inherit events we want to return the parent event id
 * to userspace.
 */

static u64 primary_event_id(struct perf_event *event)
{
 u64 id = event->id;

 if (event->parent)
  id = event->parent->id;

 return id;
}

/*
 * Get the perf_event_context for a task and lock it.
 *
 * This has to cope with the fact that until it is locked,
 * the context could get moved to another task.
 */

static struct perf_event_context *
perf_lock_task_context(struct task_struct *task, unsigned long *flags)
{
 struct perf_event_context *ctx;

retry:
 /*
 * One of the few rules of preemptible RCU is that one cannot do
 * rcu_read_unlock() while holding a scheduler (or nested) lock when
 * part of the read side critical section was irqs-enabled -- see
 * rcu_read_unlock_special().
 *
 * Since ctx->lock nests under rq->lock we must ensure the entire read
 * side critical section has interrupts disabled.
 */

 local_irq_save(*flags);
 rcu_read_lock();
 ctx = rcu_dereference(task->perf_event_ctxp);
 if (ctx) {
  /*
 * If this context is a clone of another, it might
 * get swapped for another underneath us by
 * perf_event_task_sched_out, though the
 * rcu_read_lock() protects us from any context
 * getting freed.  Lock the context and check if it
 * got swapped before we could get the lock, and retry
 * if so.  If we locked the right context, then it
 * can't get swapped on us any more.
 */

  raw_spin_lock(&ctx->lock);
  if (ctx != rcu_dereference(task->perf_event_ctxp)) {
   raw_spin_unlock(&ctx->lock);
   rcu_read_unlock();
   local_irq_restore(*flags);
   goto retry;
  }

  if (ctx->task == TASK_TOMBSTONE ||
      !refcount_inc_not_zero(&ctx->refcount)) {
   raw_spin_unlock(&ctx->lock);
   ctx = NULL;
  } else {
   WARN_ON_ONCE(ctx->task != task);
  }
 }
 rcu_read_unlock();
 if (!ctx)
  local_irq_restore(*flags);
 return ctx;
}

/*
 * Get the context for a task and increment its pin_count so it
 * can't get swapped to another task.  This also increments its
 * reference count so that the context can't get freed.
 */

static struct perf_event_context *
perf_pin_task_context(struct task_struct *task)
{
 struct perf_event_context *ctx;
 unsigned long flags;

 ctx = perf_lock_task_context(task, &flags);
 if (ctx) {
  ++ctx->pin_count;
  raw_spin_unlock_irqrestore(&ctx->lock, flags);
 }
 return ctx;
}

static void perf_unpin_context(struct perf_event_context *ctx)
{
 unsigned long flags;

 raw_spin_lock_irqsave(&ctx->lock, flags);
 --ctx->pin_count;
 raw_spin_unlock_irqrestore(&ctx->lock, flags);
}

/*
 * Update the record of the current time in a context.
 */

static void __update_context_time(struct perf_event_context *ctx, bool adv)
{
 u64 now = perf_clock();

 lockdep_assert_held(&ctx->lock);

 if (adv)
  ctx->time += now - ctx->timestamp;
 ctx->timestamp = now;

 /*
 * The above: time' = time + (now - timestamp), can be re-arranged
 * into: time` = now + (time - timestamp), which gives a single value
 * offset to compute future time without locks on.
 *
 * See perf_event_time_now(), which can be used from NMI context where
 * it's (obviously) not possible to acquire ctx->lock in order to read
 * both the above values in a consistent manner.
 */

 WRITE_ONCE(ctx->timeoffset, ctx->time - ctx->timestamp);
}

static void update_context_time(struct perf_event_context *ctx)
{
 __update_context_time(ctx, true);
}

static u64 perf_event_time(struct perf_event *event)
{
 struct perf_event_context *ctx = event->ctx;

 if (unlikely(!ctx))
  return 0;

 if (is_cgroup_event(event))
  return perf_cgroup_event_time(event);

 return ctx->time;
}

static u64 perf_event_time_now(struct perf_event *event, u64 now)
{
 struct perf_event_context *ctx = event->ctx;

 if (unlikely(!ctx))
  return 0;

 if (is_cgroup_event(event))
  return perf_cgroup_event_time_now(event, now);

 if (!(__load_acquire(&ctx->is_active) & EVENT_TIME))
  return ctx->time;

 now += READ_ONCE(ctx->timeoffset);
 return now;
}

static enum event_type_t get_event_type(struct perf_event *event)
{
 struct perf_event_context *ctx = event->ctx;
 enum event_type_t event_type;

 lockdep_assert_held(&ctx->lock);

 /*
 * It's 'group type', really, because if our group leader is
 * pinned, so are we.
 */

 if (event->group_leader != event)
  event = event->group_leader;

 event_type = event->attr.pinned ? EVENT_PINNED : EVENT_FLEXIBLE;
 if (!ctx->task)
  event_type |= EVENT_CPU;

 return event_type;
}

/*
 * Helper function to initialize event group nodes.
 */

static void init_event_group(struct perf_event *event)
{
 RB_CLEAR_NODE(&event->group_node);
 event->group_index = 0;
}

/*
 * Extract pinned or flexible groups from the context
 * based on event attrs bits.
 */

static struct perf_event_groups *
get_event_groups(struct perf_event *event, struct perf_event_context *ctx)
{
 if (event->attr.pinned)
  return &ctx->pinned_groups;
 else
  return &ctx->flexible_groups;
}

/*
 * Helper function to initializes perf_event_group trees.
 */

static void perf_event_groups_init(struct perf_event_groups *groups)
{
 groups->tree = RB_ROOT;
 groups->index = 0;
}

static inline struct cgroup *event_cgroup(const struct perf_event *event)
{
 struct cgroup *cgroup = NULL;

#ifdef CONFIG_CGROUP_PERF
 if (event->cgrp)
  cgroup = event->cgrp->css.cgroup;
#endif

 return cgroup;
}

/*
 * Compare function for event groups;
 *
 * Implements complex key that first sorts by CPU and then by virtual index
 * which provides ordering when rotating groups for the same CPU.
 */

static __always_inline int
perf_event_groups_cmp(const int left_cpu, const struct pmu *left_pmu,
        const struct cgroup *left_cgroup, const u64 left_group_index,
        const struct perf_event *right)
{
 if (left_cpu < right->cpu)
  return -1;
 if (left_cpu > right->cpu)
  return 1;

 if (left_pmu) {
  if (left_pmu < right->pmu_ctx->pmu)
   return -1;
  if (left_pmu > right->pmu_ctx->pmu)
   return 1;
 }

#ifdef CONFIG_CGROUP_PERF
 {
  const struct cgroup *right_cgroup = event_cgroup(right);

  if (left_cgroup != right_cgroup) {
   if (!left_cgroup) {
    /*
 * Left has no cgroup but right does, no
 * cgroups come first.
 */

    return -1;
   }
   if (!right_cgroup) {
    /*
 * Right has no cgroup but left does, no
 * cgroups come first.
 */

    return 1;
   }
   /* Two dissimilar cgroups, order by id. */
   if (cgroup_id(left_cgroup) < cgroup_id(right_cgroup))
    return -1;

   return 1;
  }
 }
#endif

 if (left_group_index < right->group_index)
  return -1;
 if (left_group_index > right->group_index)
  return 1;

 return 0;
}

#define __node_2_pe(node) \
 rb_entry((node), struct perf_event, group_node)

static inline bool __group_less(struct rb_node *a, const struct rb_node *b)
{
 struct perf_event *e = __node_2_pe(a);
 return perf_event_groups_cmp(e->cpu, e->pmu_ctx->pmu, event_cgroup(e),
         e->group_index, __node_2_pe(b)) < 0;
}

struct __group_key {
 int cpu;
 struct pmu *pmu;
 struct cgroup *cgroup;
};

static inline int __group_cmp(const void *key, const struct rb_node *node)
{
 const struct __group_key *a = key;
 const struct perf_event *b = __node_2_pe(node);

 /* partial/subtree match: @cpu, @pmu, @cgroup; ignore: @group_index */
 return perf_event_groups_cmp(a->cpu, a->pmu, a->cgroup, b->group_index, b);
}

static inline int
__group_cmp_ignore_cgroup(const void *key, const struct rb_node *node)
{
 const struct __group_key *a = key;
 const struct perf_event *b = __node_2_pe(node);

 /* partial/subtree match: @cpu, @pmu, ignore: @cgroup, @group_index */
 return perf_event_groups_cmp(a->cpu, a->pmu, event_cgroup(b),
         b->group_index, b);
}

/*
 * Insert @event into @groups' tree; using
 *   {@event->cpu, @event->pmu_ctx->pmu, event_cgroup(@event), ++@groups->index}
 * as key. This places it last inside the {cpu,pmu,cgroup} subtree.
 */

static void
perf_event_groups_insert(struct perf_event_groups *groups,
    struct perf_event *event)
{
 event->group_index = ++groups->index;

 rb_add(&event->group_node, &groups->tree, __group_less);
}

/*
 * Helper function to insert event into the pinned or flexible groups.
 */

static void
add_event_to_groups(struct perf_event *event, struct perf_event_context *ctx)
{
 struct perf_event_groups *groups;

 groups = get_event_groups(event, ctx);
 perf_event_groups_insert(groups, event);
}

/*
 * Delete a group from a tree.
 */

static void
perf_event_groups_delete(struct perf_event_groups *groups,
    struct perf_event *event)
{
 WARN_ON_ONCE(RB_EMPTY_NODE(&event->group_node) ||
       RB_EMPTY_ROOT(&groups->tree));

 rb_erase(&event->group_node, &groups->tree);
 init_event_group(event);
}

/*
 * Helper function to delete event from its groups.
 */

static void
del_event_from_groups(struct perf_event *event, struct perf_event_context *ctx)
{
 struct perf_event_groups *groups;

 groups = get_event_groups(event, ctx);
 perf_event_groups_delete(groups, event);
}

/*
 * Get the leftmost event in the {cpu,pmu,cgroup} subtree.
 */

static struct perf_event *
perf_event_groups_first(struct perf_event_groups *groups, int cpu,
   struct pmu *pmu, struct cgroup *cgrp)
{
 struct __group_key key = {
  .cpu = cpu,
  .pmu = pmu,
  .cgroup = cgrp,
 };
 struct rb_node *node;

 node = rb_find_first(&key, &groups->tree, __group_cmp);
 if (node)
  return __node_2_pe(node);

 return NULL;
}

static struct perf_event *
perf_event_groups_next(struct perf_event *event, struct pmu *pmu)
{
 struct __group_key key = {
  .cpu = event->cpu,
  .pmu = pmu,
  .cgroup = event_cgroup(event),
 };
 struct rb_node *next;

 next = rb_next_match(&key, &event->group_node, __group_cmp);
 if (next)
  return __node_2_pe(next);

 return NULL;
}

#define perf_event_groups_for_cpu_pmu(event, groups, cpu, pmu)  \
 for (event = perf_event_groups_first(groups, cpu, pmu, NULL); \
      event; event = perf_event_groups_next(event, pmu))

/*
 * Iterate through the whole groups tree.
 */

#define perf_event_groups_for_each(event, groups)   \
 for (event = rb_entry_safe(rb_first(&((groups)->tree)),  \
    typeof(*event), group_node); event; \
  event = rb_entry_safe(rb_next(&event->group_node), \
    typeof(*event), group_node))

/*
 * Does the event attribute request inherit with PERF_SAMPLE_READ
 */

static inline bool has_inherit_and_sample_read(struct perf_event_attr *attr)
{
 return attr->inherit && (attr->sample_type & PERF_SAMPLE_READ);
}

/*
 * Add an event from the lists for its context.
 * Must be called with ctx->mutex and ctx->lock held.
 */

static void
list_add_event(struct perf_event *event, struct perf_event_context *ctx)
{
 lockdep_assert_held(&ctx->lock);

 WARN_ON_ONCE(event->attach_state & PERF_ATTACH_CONTEXT);
 event->attach_state |= PERF_ATTACH_CONTEXT;

 event->tstamp = perf_event_time(event);

 /*
 * If we're a stand alone event or group leader, we go to the context
 * list, group events are kept attached to the group so that
 * perf_group_detach can, at all times, locate all siblings.
 */

 if (event->group_leader == event) {
  event->group_caps = event->event_caps;
  add_event_to_groups(event, ctx);
 }

 list_add_rcu(&event->event_entry, &ctx->event_list);
 ctx->nr_events++;
 if (event->hw.flags & PERF_EVENT_FLAG_USER_READ_CNT)
  ctx->nr_user++;
 if (event->attr.inherit_stat)
  ctx->nr_stat++;
 if (has_inherit_and_sample_read(&event->attr))
  local_inc(&ctx->nr_no_switch_fast);

 if (event->state > PERF_EVENT_STATE_OFF)
  perf_cgroup_event_enable(event, ctx);

 ctx->generation++;
 event->pmu_ctx->nr_events++;
}

/*
 * Initialize event state based on the perf_event_attr::disabled.
 */

static inline void perf_event__state_init(struct perf_event *event)
{
 event->state = event->attr.disabled ? PERF_EVENT_STATE_OFF :
           PERF_EVENT_STATE_INACTIVE;
}

static int __perf_event_read_size(u64 read_format, int nr_siblings)
{
 int entry = sizeof(u64); /* value */
 int size = 0;
 int nr = 1;

 if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
  size += sizeof(u64);

 if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
  size += sizeof(u64);

 if (read_format & PERF_FORMAT_ID)
  entry += sizeof(u64);

 if (read_format & PERF_FORMAT_LOST)
  entry += sizeof(u64);

 if (read_format & PERF_FORMAT_GROUP) {
  nr += nr_siblings;
  size += sizeof(u64);
 }

 /*
 * Since perf_event_validate_size() limits this to 16k and inhibits
 * adding more siblings, this will never overflow.
 */

 return size + nr * entry;
}

static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
{
 struct perf_sample_data *data;
 u16 size = 0;

 if (sample_type & PERF_SAMPLE_IP)
  size += sizeof(data->ip);

 if (sample_type & PERF_SAMPLE_ADDR)
  size += sizeof(data->addr);

 if (sample_type & PERF_SAMPLE_PERIOD)
  size += sizeof(data->period);

 if (sample_type & PERF_SAMPLE_WEIGHT_TYPE)
  size += sizeof(data->weight.full);

 if (sample_type & PERF_SAMPLE_READ)
  size += event->read_size;

 if (sample_type & PERF_SAMPLE_DATA_SRC)
  size += sizeof(data->data_src.val);

 if (sample_type & PERF_SAMPLE_TRANSACTION)
  size += sizeof(data->txn);

 if (sample_type & PERF_SAMPLE_PHYS_ADDR)
  size += sizeof(data->phys_addr);

 if (sample_type & PERF_SAMPLE_CGROUP)
  size += sizeof(data->cgroup);

 if (sample_type & PERF_SAMPLE_DATA_PAGE_SIZE)
  size += sizeof(data->data_page_size);

 if (sample_type & PERF_SAMPLE_CODE_PAGE_SIZE)
  size += sizeof(data->code_page_size);

 event->header_size = size;
}

/*
 * Called at perf_event creation and when events are attached/detached from a
 * group.
 */

static void perf_event__header_size(struct perf_event *event)
{
 event->read_size =
  __perf_event_read_size(event->attr.read_format,
           event->group_leader->nr_siblings);
 __perf_event_header_size(event, event->attr.sample_type);
}

static void perf_event__id_header_size(struct perf_event *event)
{
 struct perf_sample_data *data;
 u64 sample_type = event->attr.sample_type;
 u16 size = 0;

 if (sample_type & PERF_SAMPLE_TID)
  size += sizeof(data->tid_entry);

 if (sample_type & PERF_SAMPLE_TIME)
  size += sizeof(data->time);

 if (sample_type & PERF_SAMPLE_IDENTIFIER)
  size += sizeof(data->id);

 if (sample_type & PERF_SAMPLE_ID)
  size += sizeof(data->id);

 if (sample_type & PERF_SAMPLE_STREAM_ID)
  size += sizeof(data->stream_id);

 if (sample_type & PERF_SAMPLE_CPU)
  size += sizeof(data->cpu_entry);

 event->id_header_size = size;
}

/*
 * Check that adding an event to the group does not result in anybody
 * overflowing the 64k event limit imposed by the output buffer.
 *
 * Specifically, check that the read_size for the event does not exceed 16k,
 * read_size being the one term that grows with groups size. Since read_size
 * depends on per-event read_format, also (re)check the existing events.
 *
 * This leaves 48k for the constant size fields and things like callchains,
 * branch stacks and register sets.
 */

static bool perf_event_validate_size(struct perf_event *event)
{
 struct perf_event *sibling, *group_leader = event->group_leader;

 if (__perf_event_read_size(event->attr.read_format,
       group_leader->nr_siblings + 1) > 16*1024)
  return false;

 if (__perf_event_read_size(group_leader->attr.read_format,
       group_leader->nr_siblings + 1) > 16*1024)
  return false;

 /*
 * When creating a new group leader, group_leader->ctx is initialized
 * after the size has been validated, but we cannot safely use
 * for_each_sibling_event() until group_leader->ctx is set. A new group
 * leader cannot have any siblings yet, so we can safely skip checking
 * the non-existent siblings.
 */

 if (event == group_leader)
  return true;

 for_each_sibling_event(sibling, group_leader) {
  if (__perf_event_read_size(sibling->attr.read_format,
        group_leader->nr_siblings + 1) > 16*1024)
   return false;
 }

 return true;
}

static void perf_group_attach(struct perf_event *event)
{
 struct perf_event *group_leader = event->group_leader, *pos;

 lockdep_assert_held(&event->ctx->lock);

 /*
 * We can have double attach due to group movement (move_group) in
 * perf_event_open().
 */

 if (event->attach_state & PERF_ATTACH_GROUP)
  return;

 event->attach_state |= PERF_ATTACH_GROUP;

 if (group_leader == event)
  return;

 WARN_ON_ONCE(group_leader->ctx != event->ctx);

 group_leader->group_caps &= event->event_caps;

 list_add_tail(&event->sibling_list, &group_leader->sibling_list);
 group_leader->nr_siblings++;
 group_leader->group_generation++;

 perf_event__header_size(group_leader);

 for_each_sibling_event(pos, group_leader)
  perf_event__header_size(pos);
}

/*
 * Remove an event from the lists for its context.
 * Must be called with ctx->mutex and ctx->lock held.
 */

static void
list_del_event(struct perf_event *event, struct perf_event_context *ctx)
{
 WARN_ON_ONCE(event->ctx != ctx);
 lockdep_assert_held(&ctx->lock);

 /*
 * We can have double detach due to exit/hot-unplug + close.
 */

 if (!(event->attach_state & PERF_ATTACH_CONTEXT))
  return;

 event->attach_state &= ~PERF_ATTACH_CONTEXT;

 ctx->nr_events--;
 if (event->hw.flags & PERF_EVENT_FLAG_USER_READ_CNT)
  ctx->nr_user--;
 if (event->attr.inherit_stat)
  ctx->nr_stat--;
 if (has_inherit_and_sample_read(&event->attr))
  local_dec(&ctx->nr_no_switch_fast);

 list_del_rcu(&event->event_entry);

 if (event->group_leader == event)
  del_event_from_groups(event, ctx);

 ctx->generation++;
 event->pmu_ctx->nr_events--;
}

static int
perf_aux_output_match(struct perf_event *event, struct perf_event *aux_event)
{
 if (!has_aux(aux_event))
  return 0;

 if (!event->pmu->aux_output_match)
  return 0;

 return event->pmu->aux_output_match(aux_event);
}

static void put_event(struct perf_event *event);
static void __event_disable(struct perf_event *event,
       struct perf_event_context *ctx,
       enum perf_event_state state);

static void perf_put_aux_event(struct perf_event *event)
{
 struct perf_event_context *ctx = event->ctx;
 struct perf_event *iter;

 /*
 * If event uses aux_event tear down the link
 */

 if (event->aux_event) {
  iter = event->aux_event;
  event->aux_event = NULL;
  put_event(iter);
  return;
 }

 /*
 * If the event is an aux_event, tear down all links to
 * it from other events.
 */

 for_each_sibling_event(iter, event) {
  if (iter->aux_event != event)
   continue;

  iter->aux_event = NULL;
  put_event(event);

  /*
 * If it's ACTIVE, schedule it out and put it into ERROR
 * state so that we don't try to schedule it again. Note
 * that perf_event_enable() will clear the ERROR status.
 */

  __event_disable(iter, ctx, PERF_EVENT_STATE_ERROR);
 }
}

static bool perf_need_aux_event(struct perf_event *event)
{
 return event->attr.aux_output || has_aux_action(event);
}

static int perf_get_aux_event(struct perf_event *event,
         struct perf_event *group_leader)
{
 /*
 * Our group leader must be an aux event if we want to be
 * an aux_output. This way, the aux event will precede its
 * aux_output events in the group, and therefore will always
 * schedule first.
 */

 if (!group_leader)
  return 0;

 /*
 * aux_output and aux_sample_size are mutually exclusive.
 */

 if (event->attr.aux_output && event->attr.aux_sample_size)
  return 0;

 if (event->attr.aux_output &&
     !perf_aux_output_match(event, group_leader))
  return 0;

 if ((event->attr.aux_pause || event->attr.aux_resume) &&
     !(group_leader->pmu->capabilities & PERF_PMU_CAP_AUX_PAUSE))
  return 0;

 if (event->attr.aux_sample_size && !group_leader->pmu->snapshot_aux)
  return 0;

 if (!atomic_long_inc_not_zero(&group_leader->refcount))
  return 0;

 /*
 * Link aux_outputs to their aux event; this is undone in
 * perf_group_detach() by perf_put_aux_event(). When the
 * group in torn down, the aux_output events loose their
 * link to the aux_event and can't schedule any more.
 */

 event->aux_event = group_leader;

 return 1;
}

static inline struct list_head *get_event_list(struct perf_event *event)
{
 return event->attr.pinned ? &event->pmu_ctx->pinned_active :
        &event->pmu_ctx->flexible_active;
}

static void perf_group_detach(struct perf_event *event)
{
 struct perf_event *leader = event->group_leader;
 struct perf_event *sibling, *tmp;
 struct perf_event_context *ctx = event->ctx;

 lockdep_assert_held(&ctx->lock);

 /*
 * We can have double detach due to exit/hot-unplug + close.
 */

 if (!(event->attach_state & PERF_ATTACH_GROUP))
  return;

 event->attach_state &= ~PERF_ATTACH_GROUP;

 perf_put_aux_event(event);

 /*
 * If this is a sibling, remove it from its group.
 */

 if (leader != event) {
  list_del_init(&event->sibling_list);
  event->group_leader->nr_siblings--;
  event->group_leader->group_generation++;
  goto out;
 }

 /*
 * If this was a group event with sibling events then
 * upgrade the siblings to singleton events by adding them
 * to whatever list we are on.
 */

 list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) {

  /*
 * Events that have PERF_EV_CAP_SIBLING require being part of
 * a group and cannot exist on their own, schedule them out
 * and move them into the ERROR state. Also see
 * _perf_event_enable(), it will not be able to recover this
 * ERROR state.
 */

  if (sibling->event_caps & PERF_EV_CAP_SIBLING)
   __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR);

  sibling->group_leader = sibling;
  list_del_init(&sibling->sibling_list);

  /* Inherit group flags from the previous leader */
  sibling->group_caps = event->group_caps;

  if (sibling->attach_state & PERF_ATTACH_CONTEXT) {
   add_event_to_groups(sibling, event->ctx);

   if (sibling->state == PERF_EVENT_STATE_ACTIVE)
    list_add_tail(&sibling->active_list, get_event_list(sibling));
  }

  WARN_ON_ONCE(sibling->ctx != event->ctx);
 }

out:
 for_each_sibling_event(tmp, leader)
  perf_event__header_size(tmp);

 perf_event__header_size(leader);
}

static void sync_child_event(struct perf_event *child_event);

static void perf_child_detach(struct perf_event *event)
{
 struct perf_event *parent_event = event->parent;

 if (!(event->attach_state & PERF_ATTACH_CHILD))
  return;

 event->attach_state &= ~PERF_ATTACH_CHILD;

 if (WARN_ON_ONCE(!parent_event))
  return;

 /*
 * Can't check this from an IPI, the holder is likey another CPU.
 *
lockdep_assert_held(&parent_event->child_mutex);
 */


 sync_child_event(event);
 list_del_init(&event->child_list);
}

static bool is_orphaned_event(struct perf_event *event)
{
 return event->state == PERF_EVENT_STATE_DEAD;
}

static inline int
event_filter_match(struct perf_event *event)
{
 return (event->cpu == -1 || event->cpu == smp_processor_id()) &&
        perf_cgroup_match(event);
}

static inline bool is_event_in_freq_mode(struct perf_event *event)
{
 return event->attr.freq && event->attr.sample_freq;
}

static void
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_cpc(epc->pmu);
 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);

 if (event->state != PERF_EVENT_STATE_ACTIVE)
  return;

 /*
 * Asymmetry; we only schedule events _IN_ through ctx_sched_in(), but
 * we can schedule events _OUT_ individually through things like
 * __perf_remove_from_context().
 */

 list_del_init(&event->active_list);

 perf_pmu_disable(event->pmu);

 event->pmu->del(event, 0);
 event->oncpu = -1;

 if (event->pending_disable) {
  event->pending_disable = 0;
  perf_cgroup_event_disable(event, ctx);
  state = PERF_EVENT_STATE_OFF;
 }

 perf_event_set_state(event, state);

 if (!is_software_event(event))
  cpc->active_oncpu--;
 if (is_event_in_freq_mode(event)) {
  ctx->nr_freq--;
  epc->nr_freq--;
 }
 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_event_context *ctx)
{
 struct perf_event *event;

 if (group_event->state != PERF_EVENT_STATE_ACTIVE)
  return;

 perf_assert_pmu_disabled(group_event->pmu_ctx->pmu);

 event_sched_out(group_event, ctx);

 /*
 * Schedule out siblings (if any):
 */

 for_each_sibling_event(event, group_event)
  event_sched_out(event, ctx);
}

static inline void
__ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx, bool final)
{
 if (ctx->is_active & EVENT_TIME) {
  if (ctx->is_active & EVENT_FROZEN)
   return;
  update_context_time(ctx);
  update_cgrp_time_from_cpuctx(cpuctx, final);
 }
}

static inline void
ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx)
{
 __ctx_time_update(cpuctx, ctx, false);
}

/*
 * To be used inside perf_ctx_lock() / perf_ctx_unlock(). Lasts until perf_ctx_unlock().
 */

static inline void
ctx_time_freeze(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx)
{
 ctx_time_update(cpuctx, ctx);
 if (ctx->is_active & EVENT_TIME)
  ctx->is_active |= EVENT_FROZEN;
}

static inline void
ctx_time_update_event(struct perf_event_context *ctx, struct perf_event *event)
{
 if (ctx->is_active & EVENT_TIME) {
  if (ctx->is_active & EVENT_FROZEN)
   return;
  update_context_time(ctx);
  update_cgrp_time_from_event(event);
 }
}

#define DETACH_GROUP 0x01UL
#define DETACH_CHILD 0x02UL
#define DETACH_EXIT 0x04UL
#define DETACH_REVOKE 0x08UL
#define DETACH_DEAD 0x10UL

/*
 * Cross CPU call to remove a performance event
 *
 * We disable the event on the hardware level first. After that we
 * remove it from the context list.
 */

static void
__perf_remove_from_context(struct perf_event *event,
      struct perf_cpu_context *cpuctx,
      struct perf_event_context *ctx,
      void *info)
{
 struct perf_event_pmu_context *pmu_ctx = event->pmu_ctx;
 enum perf_event_state state = PERF_EVENT_STATE_OFF;
 unsigned long flags = (unsigned long)info;

 ctx_time_update(cpuctx, ctx);

 /*
 * Ensure event_sched_out() switches to OFF, at the very least
 * this avoids raising perf_pending_task() at this time.
 */

 if (flags & DETACH_EXIT)
  state = PERF_EVENT_STATE_EXIT;
 if (flags & DETACH_REVOKE)
  state = PERF_EVENT_STATE_REVOKED;
 if (flags & DETACH_DEAD)
  state = PERF_EVENT_STATE_DEAD;

 event_sched_out(event, ctx);

 if (event->state > PERF_EVENT_STATE_OFF)
  perf_cgroup_event_disable(event, ctx);

 perf_event_set_state(event, min(event->state, state));

 if (flags & DETACH_GROUP)
  perf_group_detach(event);
 if (flags & DETACH_CHILD)
  perf_child_detach(event);
 list_del_event(event, ctx);

 if (!pmu_ctx->nr_events) {
  pmu_ctx->rotate_necessary = 0;

  if (ctx->task && ctx->is_active) {
   struct perf_cpu_pmu_context *cpc = this_cpc(pmu_ctx->pmu);

   WARN_ON_ONCE(cpc->task_epc && cpc->task_epc != pmu_ctx);
   cpc->task_epc = NULL;
  }
 }

 if (!ctx->nr_events && ctx->is_active) {
  if (ctx == &cpuctx->ctx)
   update_cgrp_time_from_cpuctx(cpuctx, true);

  ctx->is_active = 0;
  if (ctx->task) {
   WARN_ON_ONCE(cpuctx->task_ctx != ctx);
   cpuctx->task_ctx = NULL;
  }
 }
}

/*
 * Remove the event from a task's (or a CPU's) list of events.
 *
 * If event->ctx is a cloned context, callers must make sure that
 * every task struct that event->ctx->task could possibly point to
 * remains valid.  This is OK when called from perf_release since
 * that only calls us on the top-level context, which can't be a clone.
 * When called from perf_event_exit_task, it's OK because the
 * context has been detached from its task.
 */

static void perf_remove_from_context(struct perf_event *event, unsigned long flags)
{
 struct perf_event_context *ctx = event->ctx;

 lockdep_assert_held(&ctx->mutex);

 /*
 * Because of perf_event_exit_task(), perf_remove_from_context() ought
 * to work in the face of TASK_TOMBSTONE, unlike every other
 * event_function_call() user.
 */

 raw_spin_lock_irq(&ctx->lock);
 if (!ctx->is_active) {
  __perf_remove_from_context(event, this_cpu_ptr(&perf_cpu_context),
        ctx, (void *)flags);
  raw_spin_unlock_irq(&ctx->lock);
  return;
 }
 raw_spin_unlock_irq(&ctx->lock);

 event_function_call(event, __perf_remove_from_context, (void *)flags);
}

static void __event_disable(struct perf_event *event,
       struct perf_event_context *ctx,
       enum perf_event_state state)
{
 event_sched_out(event, ctx);
 perf_cgroup_event_disable(event, ctx);
 perf_event_set_state(event, state);
}

/*
 * Cross CPU call to disable a performance event
 */

static void __perf_event_disable(struct perf_event *event,
     struct perf_cpu_context *cpuctx,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=91 G=91

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.68Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.