Blame view
kernel/events/callchain.c
4.33 KB
9251f904f perf: Carve out c... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
/* * Performance events callchain code, extracted from core.c: * * 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 <pzijlstr@redhat.com> * Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com> * * For licensing details see kernel-base/COPYING */ #include <linux/perf_event.h> #include <linux/slab.h> #include "internal.h" struct callchain_cpus_entries { struct rcu_head rcu_head; struct perf_callchain_entry *cpu_entries[0]; }; static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]); static atomic_t nr_callchain_events; static DEFINE_MUTEX(callchain_mutex); static struct callchain_cpus_entries *callchain_cpus_entries; __weak void perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) { } __weak void perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) { } static void release_callchain_buffers_rcu(struct rcu_head *head) { struct callchain_cpus_entries *entries; int cpu; entries = container_of(head, struct callchain_cpus_entries, rcu_head); for_each_possible_cpu(cpu) kfree(entries->cpu_entries[cpu]); kfree(entries); } static void release_callchain_buffers(void) { struct callchain_cpus_entries *entries; entries = callchain_cpus_entries; |
e0455e194 perf/callchain: R... |
55 |
RCU_INIT_POINTER(callchain_cpus_entries, NULL); |
9251f904f perf: Carve out c... |
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
call_rcu(&entries->rcu_head, release_callchain_buffers_rcu); } static int alloc_callchain_buffers(void) { int cpu; int size; struct callchain_cpus_entries *entries; /* * We can't use the percpu allocation API for data that can be * accessed from NMI. Use a temporary manual per cpu allocation * until that gets sorted out. */ size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]); entries = kzalloc(size, GFP_KERNEL); if (!entries) return -ENOMEM; size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS; for_each_possible_cpu(cpu) { entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu)); if (!entries->cpu_entries[cpu]) goto fail; } rcu_assign_pointer(callchain_cpus_entries, entries); return 0; fail: for_each_possible_cpu(cpu) kfree(entries->cpu_entries[cpu]); kfree(entries); return -ENOMEM; } int get_callchain_buffers(void) { int err = 0; int count; mutex_lock(&callchain_mutex); count = atomic_inc_return(&nr_callchain_events); if (WARN_ON_ONCE(count < 1)) { err = -EINVAL; goto exit; } if (count > 1) { /* If the allocation failed, give up */ if (!callchain_cpus_entries) err = -ENOMEM; goto exit; } err = alloc_callchain_buffers(); |
9251f904f perf: Carve out c... |
118 |
exit: |
90983b160 perf: Sanitize ge... |
119 120 |
if (err) atomic_dec(&nr_callchain_events); |
9251f904f perf: Carve out c... |
121 |
|
fc3b86d67 perf: Roll back c... |
122 |
mutex_unlock(&callchain_mutex); |
9251f904f perf: Carve out c... |
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
return err; } void put_callchain_buffers(void) { if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) { release_callchain_buffers(); mutex_unlock(&callchain_mutex); } } static struct perf_callchain_entry *get_callchain_entry(int *rctx) { int cpu; struct callchain_cpus_entries *entries; |
4a32fea9d scheduler: Replac... |
138 |
*rctx = get_recursion_context(this_cpu_ptr(callchain_recursion)); |
9251f904f perf: Carve out c... |
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
if (*rctx == -1) return NULL; entries = rcu_dereference(callchain_cpus_entries); if (!entries) return NULL; cpu = smp_processor_id(); return &entries->cpu_entries[cpu][*rctx]; } static void put_callchain_entry(int rctx) { |
4a32fea9d scheduler: Replac... |
154 |
put_recursion_context(this_cpu_ptr(callchain_recursion), rctx); |
9251f904f perf: Carve out c... |
155 |
} |
e6dab5ffa perf/trace: Add a... |
156 157 |
struct perf_callchain_entry * perf_callchain(struct perf_event *event, struct pt_regs *regs) |
9251f904f perf: Carve out c... |
158 159 160 |
{ int rctx; struct perf_callchain_entry *entry; |
d07752648 perf: Add attribu... |
161 162 163 164 165 |
int kernel = !event->attr.exclude_callchain_kernel; int user = !event->attr.exclude_callchain_user; if (!kernel && !user) return NULL; |
9251f904f perf: Carve out c... |
166 167 168 169 170 171 172 173 174 |
entry = get_callchain_entry(&rctx); if (rctx == -1) return NULL; if (!entry) goto exit_put; entry->nr = 0; |
d07752648 perf: Add attribu... |
175 |
if (kernel && !user_mode(regs)) { |
9251f904f perf: Carve out c... |
176 177 |
perf_callchain_store(entry, PERF_CONTEXT_KERNEL); perf_callchain_kernel(entry, regs); |
9251f904f perf: Carve out c... |
178 |
} |
d07752648 perf: Add attribu... |
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
if (user) { if (!user_mode(regs)) { if (current->mm) regs = task_pt_regs(current); else regs = NULL; } if (regs) { /* * Disallow cross-task user callchains. */ if (event->ctx->task && event->ctx->task != current) goto exit_put; perf_callchain_store(entry, PERF_CONTEXT_USER); perf_callchain_user(entry, regs); } |
9251f904f perf: Carve out c... |
197 198 199 200 201 202 203 |
} exit_put: put_callchain_entry(rctx); return entry; } |