19251f904SBorislav Petkov /* 29251f904SBorislav Petkov * Performance events callchain code, extracted from core.c: 39251f904SBorislav Petkov * 49251f904SBorislav Petkov * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de> 59251f904SBorislav Petkov * Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar 69251f904SBorislav Petkov * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> 79251f904SBorislav Petkov * Copyright � 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com> 89251f904SBorislav Petkov * 99251f904SBorislav Petkov * For licensing details see kernel-base/COPYING 109251f904SBorislav Petkov */ 119251f904SBorislav Petkov 129251f904SBorislav Petkov #include <linux/perf_event.h> 139251f904SBorislav Petkov #include <linux/slab.h> 149251f904SBorislav Petkov #include "internal.h" 159251f904SBorislav Petkov 169251f904SBorislav Petkov struct callchain_cpus_entries { 179251f904SBorislav Petkov struct rcu_head rcu_head; 189251f904SBorislav Petkov struct perf_callchain_entry *cpu_entries[0]; 199251f904SBorislav Petkov }; 209251f904SBorislav Petkov 219251f904SBorislav Petkov static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]); 229251f904SBorislav Petkov static atomic_t nr_callchain_events; 239251f904SBorislav Petkov static DEFINE_MUTEX(callchain_mutex); 249251f904SBorislav Petkov static struct callchain_cpus_entries *callchain_cpus_entries; 259251f904SBorislav Petkov 269251f904SBorislav Petkov 279251f904SBorislav Petkov __weak void perf_callchain_kernel(struct perf_callchain_entry *entry, 289251f904SBorislav Petkov struct pt_regs *regs) 299251f904SBorislav Petkov { 309251f904SBorislav Petkov } 319251f904SBorislav Petkov 329251f904SBorislav Petkov __weak void perf_callchain_user(struct perf_callchain_entry *entry, 339251f904SBorislav Petkov struct pt_regs *regs) 349251f904SBorislav Petkov { 359251f904SBorislav Petkov } 369251f904SBorislav Petkov 379251f904SBorislav Petkov static void release_callchain_buffers_rcu(struct rcu_head *head) 389251f904SBorislav Petkov { 399251f904SBorislav Petkov struct callchain_cpus_entries *entries; 409251f904SBorislav Petkov int cpu; 419251f904SBorislav Petkov 429251f904SBorislav Petkov entries = container_of(head, struct callchain_cpus_entries, rcu_head); 439251f904SBorislav Petkov 449251f904SBorislav Petkov for_each_possible_cpu(cpu) 459251f904SBorislav Petkov kfree(entries->cpu_entries[cpu]); 469251f904SBorislav Petkov 479251f904SBorislav Petkov kfree(entries); 489251f904SBorislav Petkov } 499251f904SBorislav Petkov 509251f904SBorislav Petkov static void release_callchain_buffers(void) 519251f904SBorislav Petkov { 529251f904SBorislav Petkov struct callchain_cpus_entries *entries; 539251f904SBorislav Petkov 549251f904SBorislav Petkov entries = callchain_cpus_entries; 559251f904SBorislav Petkov rcu_assign_pointer(callchain_cpus_entries, NULL); 569251f904SBorislav Petkov call_rcu(&entries->rcu_head, release_callchain_buffers_rcu); 579251f904SBorislav Petkov } 589251f904SBorislav Petkov 599251f904SBorislav Petkov static int alloc_callchain_buffers(void) 609251f904SBorislav Petkov { 619251f904SBorislav Petkov int cpu; 629251f904SBorislav Petkov int size; 639251f904SBorislav Petkov struct callchain_cpus_entries *entries; 649251f904SBorislav Petkov 659251f904SBorislav Petkov /* 669251f904SBorislav Petkov * We can't use the percpu allocation API for data that can be 679251f904SBorislav Petkov * accessed from NMI. Use a temporary manual per cpu allocation 689251f904SBorislav Petkov * until that gets sorted out. 699251f904SBorislav Petkov */ 709251f904SBorislav Petkov size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]); 719251f904SBorislav Petkov 729251f904SBorislav Petkov entries = kzalloc(size, GFP_KERNEL); 739251f904SBorislav Petkov if (!entries) 749251f904SBorislav Petkov return -ENOMEM; 759251f904SBorislav Petkov 769251f904SBorislav Petkov size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS; 779251f904SBorislav Petkov 789251f904SBorislav Petkov for_each_possible_cpu(cpu) { 799251f904SBorislav Petkov entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL, 809251f904SBorislav Petkov cpu_to_node(cpu)); 819251f904SBorislav Petkov if (!entries->cpu_entries[cpu]) 829251f904SBorislav Petkov goto fail; 839251f904SBorislav Petkov } 849251f904SBorislav Petkov 859251f904SBorislav Petkov rcu_assign_pointer(callchain_cpus_entries, entries); 869251f904SBorislav Petkov 879251f904SBorislav Petkov return 0; 889251f904SBorislav Petkov 899251f904SBorislav Petkov fail: 909251f904SBorislav Petkov for_each_possible_cpu(cpu) 919251f904SBorislav Petkov kfree(entries->cpu_entries[cpu]); 929251f904SBorislav Petkov kfree(entries); 939251f904SBorislav Petkov 949251f904SBorislav Petkov return -ENOMEM; 959251f904SBorislav Petkov } 969251f904SBorislav Petkov 979251f904SBorislav Petkov int get_callchain_buffers(void) 989251f904SBorislav Petkov { 999251f904SBorislav Petkov int err = 0; 1009251f904SBorislav Petkov int count; 1019251f904SBorislav Petkov 1029251f904SBorislav Petkov mutex_lock(&callchain_mutex); 1039251f904SBorislav Petkov 1049251f904SBorislav Petkov count = atomic_inc_return(&nr_callchain_events); 1059251f904SBorislav Petkov if (WARN_ON_ONCE(count < 1)) { 1069251f904SBorislav Petkov err = -EINVAL; 1079251f904SBorislav Petkov goto exit; 1089251f904SBorislav Petkov } 1099251f904SBorislav Petkov 1109251f904SBorislav Petkov if (count > 1) { 1119251f904SBorislav Petkov /* If the allocation failed, give up */ 1129251f904SBorislav Petkov if (!callchain_cpus_entries) 1139251f904SBorislav Petkov err = -ENOMEM; 1149251f904SBorislav Petkov goto exit; 1159251f904SBorislav Petkov } 1169251f904SBorislav Petkov 1179251f904SBorislav Petkov err = alloc_callchain_buffers(); 1189251f904SBorislav Petkov exit: 1199251f904SBorislav Petkov mutex_unlock(&callchain_mutex); 1209251f904SBorislav Petkov 1219251f904SBorislav Petkov return err; 1229251f904SBorislav Petkov } 1239251f904SBorislav Petkov 1249251f904SBorislav Petkov void put_callchain_buffers(void) 1259251f904SBorislav Petkov { 1269251f904SBorislav Petkov if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) { 1279251f904SBorislav Petkov release_callchain_buffers(); 1289251f904SBorislav Petkov mutex_unlock(&callchain_mutex); 1299251f904SBorislav Petkov } 1309251f904SBorislav Petkov } 1319251f904SBorislav Petkov 1329251f904SBorislav Petkov static struct perf_callchain_entry *get_callchain_entry(int *rctx) 1339251f904SBorislav Petkov { 1349251f904SBorislav Petkov int cpu; 1359251f904SBorislav Petkov struct callchain_cpus_entries *entries; 1369251f904SBorislav Petkov 1379251f904SBorislav Petkov *rctx = get_recursion_context(__get_cpu_var(callchain_recursion)); 1389251f904SBorislav Petkov if (*rctx == -1) 1399251f904SBorislav Petkov return NULL; 1409251f904SBorislav Petkov 1419251f904SBorislav Petkov entries = rcu_dereference(callchain_cpus_entries); 1429251f904SBorislav Petkov if (!entries) 1439251f904SBorislav Petkov return NULL; 1449251f904SBorislav Petkov 1459251f904SBorislav Petkov cpu = smp_processor_id(); 1469251f904SBorislav Petkov 1479251f904SBorislav Petkov return &entries->cpu_entries[cpu][*rctx]; 1489251f904SBorislav Petkov } 1499251f904SBorislav Petkov 1509251f904SBorislav Petkov static void 1519251f904SBorislav Petkov put_callchain_entry(int rctx) 1529251f904SBorislav Petkov { 1539251f904SBorislav Petkov put_recursion_context(__get_cpu_var(callchain_recursion), rctx); 1549251f904SBorislav Petkov } 1559251f904SBorislav Petkov 156e6dab5ffSAndrew Vagin struct perf_callchain_entry * 157e6dab5ffSAndrew Vagin perf_callchain(struct perf_event *event, struct pt_regs *regs) 1589251f904SBorislav Petkov { 1599251f904SBorislav Petkov int rctx; 1609251f904SBorislav Petkov struct perf_callchain_entry *entry; 1619251f904SBorislav Petkov 162*d0775264SFrederic Weisbecker int kernel = !event->attr.exclude_callchain_kernel; 163*d0775264SFrederic Weisbecker int user = !event->attr.exclude_callchain_user; 164*d0775264SFrederic Weisbecker 165*d0775264SFrederic Weisbecker if (!kernel && !user) 166*d0775264SFrederic Weisbecker return NULL; 1679251f904SBorislav Petkov 1689251f904SBorislav Petkov entry = get_callchain_entry(&rctx); 1699251f904SBorislav Petkov if (rctx == -1) 1709251f904SBorislav Petkov return NULL; 1719251f904SBorislav Petkov 1729251f904SBorislav Petkov if (!entry) 1739251f904SBorislav Petkov goto exit_put; 1749251f904SBorislav Petkov 1759251f904SBorislav Petkov entry->nr = 0; 1769251f904SBorislav Petkov 177*d0775264SFrederic Weisbecker if (kernel && !user_mode(regs)) { 1789251f904SBorislav Petkov perf_callchain_store(entry, PERF_CONTEXT_KERNEL); 1799251f904SBorislav Petkov perf_callchain_kernel(entry, regs); 180*d0775264SFrederic Weisbecker } 181*d0775264SFrederic Weisbecker 182*d0775264SFrederic Weisbecker if (user) { 183*d0775264SFrederic Weisbecker if (!user_mode(regs)) { 1849251f904SBorislav Petkov if (current->mm) 1859251f904SBorislav Petkov regs = task_pt_regs(current); 1869251f904SBorislav Petkov else 1879251f904SBorislav Petkov regs = NULL; 1889251f904SBorislav Petkov } 1899251f904SBorislav Petkov 1909251f904SBorislav Petkov if (regs) { 191e6dab5ffSAndrew Vagin /* 192e6dab5ffSAndrew Vagin * Disallow cross-task user callchains. 193e6dab5ffSAndrew Vagin */ 194e6dab5ffSAndrew Vagin if (event->ctx->task && event->ctx->task != current) 195e6dab5ffSAndrew Vagin goto exit_put; 196e6dab5ffSAndrew Vagin 1979251f904SBorislav Petkov perf_callchain_store(entry, PERF_CONTEXT_USER); 1989251f904SBorislav Petkov perf_callchain_user(entry, regs); 1999251f904SBorislav Petkov } 200*d0775264SFrederic Weisbecker } 2019251f904SBorislav Petkov 2029251f904SBorislav Petkov exit_put: 2039251f904SBorislav Petkov put_callchain_entry(rctx); 2049251f904SBorislav Petkov 2059251f904SBorislav Petkov return entry; 2069251f904SBorislav Petkov } 207