From 979048e1f26190d16b5aea87166177f37e614439 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sun, 29 Aug 2010 14:51:59 -0400 Subject: oprofile: don't call arch exit code from init code on failure oprofile_init calls oprofile_arch_init to initialise the architecture-specific backend code. If this backend code returns failure, oprofile_arch_exit is called immediately, making it difficult to allocate and free resources correctly. This patch removes the oprofile_arch_exit call from oprofile_init, meaning that all architectures must ensure that oprofile_arch_init cleans up any mess it's made before returning an error. As far as I can tell, this only affects the code for ARM. Cc: Robert Richter Cc: Matt Fleming Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Will Deacon Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index b336cd9..b4a6857 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -257,16 +257,9 @@ static int __init oprofile_init(void) printk(KERN_INFO "oprofile: using timer interrupt.\n"); err = oprofile_timer_init(&oprofile_ops); if (err) - goto out_arch; + return err; } - err = oprofilefs_register(); - if (err) - goto out_arch; - return 0; - -out_arch: - oprofile_arch_exit(); - return err; + return oprofilefs_register(); } -- cgit v0.10.2 From c7fd239a647ead1c336a051012d6bb96465ea8c6 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sun, 29 Aug 2010 14:52:00 -0400 Subject: ARM: oprofile: fix and simplify init/exit functions Now that oprofile_arch_exit is only called when the OProfile module is unloaded, it can assume that init completed successfully and not have to worry about double frees or releasing NULL perf events. This patch ensures that oprofile_arch_init fails gracefully on ARM and simplifies the exit code based on the above. Cc: Robert Richter Cc: Matt Fleming Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Will Deacon Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 0691176..c3652f7 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -275,7 +275,7 @@ out: return ret; } -static void exit_driverfs(void) +static void __exit exit_driverfs(void) { platform_device_unregister(oprofile_pdev); platform_driver_unregister(&oprofile_driver); @@ -359,14 +359,13 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) if (!counter_config) { pr_info("oprofile: failed to allocate %d " "counters\n", perf_num_counters); - return -ENOMEM; + ret = -ENOMEM; + goto out; } ret = init_driverfs(); - if (ret) { - kfree(counter_config); - return ret; - } + if (ret) + goto out; for_each_possible_cpu(cpu) { perf_events[cpu] = kcalloc(perf_num_counters, @@ -374,9 +373,8 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) if (!perf_events[cpu]) { pr_info("oprofile: failed to allocate %d perf events " "for cpu %d\n", perf_num_counters, cpu); - while (--cpu >= 0) - kfree(perf_events[cpu]); - return -ENOMEM; + ret = -ENOMEM; + goto out; } } @@ -393,28 +391,33 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) else pr_info("oprofile: using %s\n", ops->cpu_type); +out: + if (ret) { + for_each_possible_cpu(cpu) + kfree(perf_events[cpu]); + kfree(counter_config); + } + return ret; } -void oprofile_arch_exit(void) +void __exit oprofile_arch_exit(void) { int cpu, id; struct perf_event *event; - if (*perf_events) { - exit_driverfs(); - for_each_possible_cpu(cpu) { - for (id = 0; id < perf_num_counters; ++id) { - event = perf_events[cpu][id]; - if (event != NULL) - perf_event_release_kernel(event); - } - kfree(perf_events[cpu]); + for_each_possible_cpu(cpu) { + for (id = 0; id < perf_num_counters; ++id) { + event = perf_events[cpu][id]; + if (event) + perf_event_release_kernel(event); } + + kfree(perf_events[cpu]); } - if (counter_config) - kfree(counter_config); + kfree(counter_config); + exit_driverfs(); } #else int __init oprofile_arch_init(struct oprofile_operations *ops) @@ -422,5 +425,5 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) pr_info("oprofile: hardware counters not available\n"); return -ENODEV; } -void oprofile_arch_exit(void) {} +void __exit oprofile_arch_exit(void) {} #endif /* CONFIG_HW_PERF_EVENTS */ -- cgit v0.10.2 From 4cbe75be5c6ae86bdc7daec864eeb2dfd66f48bb Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Mon, 30 Aug 2010 18:21:55 +0200 Subject: oprofile, arm: initialize perf_event pointers with NULL The pointers must be NULL'ed to avoid double-freeing the pointers in rare cases during reinitialization. Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index c3652f7..d660cb8 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -351,6 +351,8 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) { int cpu, ret = 0; + memset(&perf_events, 0, sizeof(perf_events)); + perf_num_counters = armpmu_get_max_events(); counter_config = kcalloc(perf_num_counters, -- cgit v0.10.2 From 40c6b3cb64cd1d02322df5f729cca25084580f40 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 29 Sep 2010 10:46:46 -0400 Subject: oprofile, x86: Using struct stack_frame for 64bit processes dump Removing unnecessary struct frame_head and replacing it with struct stack_frame. The struct stack_frame is already defined and used in other places in kernel, so there's no reason to define new structure. Signed-off-by: Jiri Olsa Signed-off-by: Robert Richter diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index 3855096..d640a86 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c @@ -48,35 +48,30 @@ static struct stacktrace_ops backtrace_ops = { .walk_stack = print_context_stack, }; -struct frame_head { - struct frame_head *bp; - unsigned long ret; -} __attribute__((packed)); - -static struct frame_head *dump_user_backtrace(struct frame_head *head) +static struct stack_frame *dump_user_backtrace(struct stack_frame *head) { - struct frame_head bufhead[2]; + struct stack_frame bufhead[2]; - /* Also check accessibility of one struct frame_head beyond */ + /* Also check accessibility of one struct stack_frame beyond */ if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) return NULL; if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) return NULL; - oprofile_add_trace(bufhead[0].ret); + oprofile_add_trace(bufhead[0].return_address); /* frame pointers should strictly progress back up the stack * (towards higher addresses) */ - if (head >= bufhead[0].bp) + if (head >= bufhead[0].next_frame) return NULL; - return bufhead[0].bp; + return bufhead[0].next_frame; } void x86_backtrace(struct pt_regs * const regs, unsigned int depth) { - struct frame_head *head = (struct frame_head *)frame_pointer(regs); + struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); if (!user_mode_vm(regs)) { unsigned long stack = kernel_stack_pointer(regs); -- cgit v0.10.2 From f6dedecc37164a58bb80ae2ed9d204669ffc4850 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 29 Sep 2010 10:46:47 -0400 Subject: oprofile, x86: Adding backtrace dump for 32bit process in compat mode This patch implements the oprofile backtrace generation for 32 bit applications running in the 64bit environment (compat mode). With this change it's possible to get backtrace for 32bits applications under the 64bits environment using oprofile's callgraph options. opcontrol --setup -c ... opreport -l -cg ... Signed-off-by: Jiri Olsa Signed-off-by: Robert Richter diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index d640a86..2d49d4e 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c @@ -14,6 +14,7 @@ #include #include #include +#include static void backtrace_warning_symbol(void *data, char *msg, unsigned long symbol) @@ -48,6 +49,55 @@ static struct stacktrace_ops backtrace_ops = { .walk_stack = print_context_stack, }; +#ifdef CONFIG_COMPAT +static struct stack_frame_ia32 * +dump_user_backtrace_32(struct stack_frame_ia32 *head) +{ + struct stack_frame_ia32 bufhead[2]; + struct stack_frame_ia32 *fp; + + /* Also check accessibility of one struct frame_head beyond */ + if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) + return NULL; + if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) + return NULL; + + fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); + + oprofile_add_trace(bufhead[0].return_address); + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (head >= fp) + return NULL; + + return fp; +} + +static inline int +x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) +{ + struct stack_frame_ia32 *head; + + /* User process is 32-bit */ + if (!current || !test_thread_flag(TIF_IA32)) + return 0; + + head = (struct stack_frame_ia32 *) regs->bp; + while (depth-- && head) + head = dump_user_backtrace_32(head); + + return 1; +} + +#else +static inline int +x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) +{ + return 0; +} +#endif /* CONFIG_COMPAT */ + static struct stack_frame *dump_user_backtrace(struct stack_frame *head) { struct stack_frame bufhead[2]; @@ -81,6 +131,9 @@ x86_backtrace(struct pt_regs * const regs, unsigned int depth) return; } + if (x86_backtrace_32(regs, depth)) + return; + while (depth-- && head) head = dump_user_backtrace(head); } -- cgit v0.10.2 From 5140434d5f82f2e2119926272ada2e9731ec04f1 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 30 Sep 2010 18:55:47 +0200 Subject: oprofile, x86: Simplify init/exit functions Now, that we only call the exit function if init succeeds with commit: 979048e oprofile: don't call arch exit code from init code on failure we can simplify the x86 init/exit functions too. Variable using_nmi becomes obsolete. Signed-off-by: Robert Richter diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index f1575c9..bd1489c 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -695,9 +695,6 @@ static int __init ppro_init(char **cpu_type) return 1; } -/* in order to get sysfs right */ -static int using_nmi; - int __init op_nmi_init(struct oprofile_operations *ops) { __u8 vendor = boot_cpu_data.x86_vendor; @@ -705,8 +702,6 @@ int __init op_nmi_init(struct oprofile_operations *ops) char *cpu_type = NULL; int ret = 0; - using_nmi = 0; - if (!cpu_has_apic) return -ENODEV; @@ -790,13 +785,11 @@ int __init op_nmi_init(struct oprofile_operations *ops) if (ret) return ret; - using_nmi = 1; printk(KERN_INFO "oprofile: using NMI interrupt.\n"); return 0; } void op_nmi_exit(void) { - if (using_nmi) - exit_sysfs(); + exit_sysfs(); } -- cgit v0.10.2 From 4d1814f48edec31f12737609cd1c871864c8298b Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 29 Sep 2010 15:42:30 +0200 Subject: oprofile, ARM: Use oprofile_arch_exit() to cleanup on failure There is duplicate cleanup code in the init and exit functions. Now, oprofile_arch_exit() is also used if oprofile_arch_init() fails. Acked-by: Will Deacon Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index bd7426f..7ae9eeb 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -348,10 +348,33 @@ static void arm_backtrace(struct pt_regs * const regs, unsigned int depth) tail = user_backtrace(tail); } +void oprofile_arch_exit(void) +{ + int cpu, id; + struct perf_event *event; + + for_each_possible_cpu(cpu) { + for (id = 0; id < perf_num_counters; ++id) { + event = perf_events[cpu][id]; + if (event) + perf_event_release_kernel(event); + } + + kfree(perf_events[cpu]); + } + + kfree(counter_config); + exit_driverfs(); +} + int __init oprofile_arch_init(struct oprofile_operations *ops) { int cpu, ret = 0; + ret = init_driverfs(); + if (ret) + return ret; + memset(&perf_events, 0, sizeof(perf_events)); perf_num_counters = armpmu_get_max_events(); @@ -363,13 +386,10 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) pr_info("oprofile: failed to allocate %d " "counters\n", perf_num_counters); ret = -ENOMEM; + perf_num_counters = 0; goto out; } - ret = init_driverfs(); - if (ret) - goto out; - for_each_possible_cpu(cpu) { perf_events[cpu] = kcalloc(perf_num_counters, sizeof(struct perf_event *), GFP_KERNEL); @@ -395,33 +415,12 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) pr_info("oprofile: using %s\n", ops->cpu_type); out: - if (ret) { - for_each_possible_cpu(cpu) - kfree(perf_events[cpu]); - kfree(counter_config); - } + if (ret) + oprofile_arch_exit(); return ret; } -void __exit oprofile_arch_exit(void) -{ - int cpu, id; - struct perf_event *event; - - for_each_possible_cpu(cpu) { - for (id = 0; id < perf_num_counters; ++id) { - event = perf_events[cpu][id]; - if (event) - perf_event_release_kernel(event); - } - - kfree(perf_events[cpu]); - } - - kfree(counter_config); - exit_driverfs(); -} #else int __init oprofile_arch_init(struct oprofile_operations *ops) { -- cgit v0.10.2 From f6be4b428197766521aca664a935c63888d096d9 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 29 Sep 2010 14:43:29 +0200 Subject: oprofile, ARM: Rework op_create_counter() This patch simplifies op_create_counter(). Removing if/else if paths and return code variable by direct returning from function. Acked-by: Will Deacon Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 7ae9eeb..cec9305 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -89,28 +89,28 @@ static void op_perf_setup(void) static int op_create_counter(int cpu, int event) { - int ret = 0; struct perf_event *pevent; - if (!counter_config[event].enabled || (perf_events[cpu][event] != NULL)) - return ret; + if (!counter_config[event].enabled || perf_events[cpu][event]) + return 0; pevent = perf_event_create_kernel_counter(&counter_config[event].attr, cpu, -1, op_overflow_handler); - if (IS_ERR(pevent)) { - ret = PTR_ERR(pevent); - } else if (pevent->state != PERF_EVENT_STATE_ACTIVE) { + if (IS_ERR(pevent)) + return PTR_ERR(pevent); + + if (pevent->state != PERF_EVENT_STATE_ACTIVE) { perf_event_release_kernel(pevent); pr_warning("oprofile: failed to enable event %d " "on CPU %d\n", event, cpu); - ret = -EBUSY; - } else { - perf_events[cpu][event] = pevent; + return -EBUSY; } - return ret; + perf_events[cpu][event] = pevent; + + return 0; } static void op_destroy_counter(int cpu, int event) -- cgit v0.10.2 From dc0c22b878a60373762495b8e5628a32c1b80ac4 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 27 Aug 2010 14:32:41 +0200 Subject: oprofile, ARM: Remove some goto statements This patch removes some unnecessary goto statements. Acked-by: Will Deacon Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index cec9305..ab875f3 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -135,11 +135,10 @@ static int op_perf_start(void) for (event = 0; event < perf_num_counters; ++event) { ret = op_create_counter(cpu, event); if (ret) - goto out; + return ret; } } -out: return ret; } @@ -263,7 +262,7 @@ static int __init init_driverfs(void) ret = platform_driver_register(&oprofile_driver); if (ret) - goto out; + return ret; oprofile_pdev = platform_device_register_simple( oprofile_driver.driver.name, 0, NULL, 0); @@ -272,7 +271,6 @@ static int __init init_driverfs(void) platform_driver_unregister(&oprofile_driver); } -out: return ret; } -- cgit v0.10.2 From 4fdaa7b682b413dfb7ca9fa74ff45b1e0cb3dade Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 19 Aug 2010 10:50:26 +0200 Subject: oprofile: Remove duplicate code around __oprofilefs_create_file() Removing duplicate code by assigning the inodes private data pointer in __oprofilefs_create_file(). Extending the function interface to pass the pointer. Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c index 2766a6d..789a1a8 100644 --- a/drivers/oprofile/oprofilefs.c +++ b/drivers/oprofile/oprofilefs.c @@ -126,50 +126,41 @@ static const struct file_operations ulong_ro_fops = { }; -static struct dentry *__oprofilefs_create_file(struct super_block *sb, +static int __oprofilefs_create_file(struct super_block *sb, struct dentry *root, char const *name, const struct file_operations *fops, - int perm) + int perm, void *priv) { struct dentry *dentry; struct inode *inode; dentry = d_alloc_name(root, name); if (!dentry) - return NULL; + return -ENOMEM; inode = oprofilefs_get_inode(sb, S_IFREG | perm); if (!inode) { dput(dentry); - return NULL; + return -ENOMEM; } inode->i_fop = fops; d_add(dentry, inode); - return dentry; + dentry->d_inode->i_private = priv; + return 0; } int oprofilefs_create_ulong(struct super_block *sb, struct dentry *root, char const *name, unsigned long *val) { - struct dentry *d = __oprofilefs_create_file(sb, root, name, - &ulong_fops, 0644); - if (!d) - return -EFAULT; - - d->d_inode->i_private = val; - return 0; + return __oprofilefs_create_file(sb, root, name, + &ulong_fops, 0644, val); } int oprofilefs_create_ro_ulong(struct super_block *sb, struct dentry *root, char const *name, unsigned long *val) { - struct dentry *d = __oprofilefs_create_file(sb, root, name, - &ulong_ro_fops, 0444); - if (!d) - return -EFAULT; - - d->d_inode->i_private = val; - return 0; + return __oprofilefs_create_file(sb, root, name, + &ulong_ro_fops, 0444, val); } @@ -189,31 +180,22 @@ static const struct file_operations atomic_ro_fops = { int oprofilefs_create_ro_atomic(struct super_block *sb, struct dentry *root, char const *name, atomic_t *val) { - struct dentry *d = __oprofilefs_create_file(sb, root, name, - &atomic_ro_fops, 0444); - if (!d) - return -EFAULT; - - d->d_inode->i_private = val; - return 0; + return __oprofilefs_create_file(sb, root, name, + &atomic_ro_fops, 0444, val); } int oprofilefs_create_file(struct super_block *sb, struct dentry *root, char const *name, const struct file_operations *fops) { - if (!__oprofilefs_create_file(sb, root, name, fops, 0644)) - return -EFAULT; - return 0; + return __oprofilefs_create_file(sb, root, name, fops, 0644, NULL); } int oprofilefs_create_file_perm(struct super_block *sb, struct dentry *root, char const *name, const struct file_operations *fops, int perm) { - if (!__oprofilefs_create_file(sb, root, name, fops, perm)) - return -EFAULT; - return 0; + return __oprofilefs_create_file(sb, root, name, fops, perm, NULL); } -- cgit v0.10.2 From 3bf101ba42a1c89b5afbc7492e7647dae5e18735 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 27 Sep 2010 20:22:24 +0100 Subject: perf: Add helper function to return number of counters The number of counters for the registered pmu is needed in a few places so provide a helper function that returns this number. Signed-off-by: Matt Fleming Tested-by: Will Deacon Acked-by: Paul Mundt Acked-by: Peter Zijlstra Signed-off-by: Robert Richter diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 417c392..3b0aedf 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -123,6 +123,12 @@ armpmu_get_max_events(void) } EXPORT_SYMBOL_GPL(armpmu_get_max_events); +int perf_num_counters(void) +{ + return armpmu_get_max_events(); +} +EXPORT_SYMBOL_GPL(perf_num_counters); + #define HW_OP_UNSUPPORTED 0xFFFF #define C(_x) \ diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index d660cb8..1e971a7 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -43,7 +43,7 @@ static DEFINE_MUTEX(op_arm_mutex); static struct op_counter_config *counter_config; static struct perf_event **perf_events[nr_cpumask_bits]; -static int perf_num_counters; +static int num_counters; /* * Overflow callback for oprofile. @@ -54,11 +54,11 @@ static void op_overflow_handler(struct perf_event *event, int unused, int id; u32 cpu = smp_processor_id(); - for (id = 0; id < perf_num_counters; ++id) + for (id = 0; id < num_counters; ++id) if (perf_events[cpu][id] == event) break; - if (id != perf_num_counters) + if (id != num_counters) oprofile_add_sample(regs, id); else pr_warning("oprofile: ignoring spurious overflow " @@ -76,7 +76,7 @@ static void op_perf_setup(void) u32 size = sizeof(struct perf_event_attr); struct perf_event_attr *attr; - for (i = 0; i < perf_num_counters; ++i) { + for (i = 0; i < num_counters; ++i) { attr = &counter_config[i].attr; memset(attr, 0, size); attr->type = PERF_TYPE_RAW; @@ -131,7 +131,7 @@ static int op_perf_start(void) int cpu, event, ret = 0; for_each_online_cpu(cpu) { - for (event = 0; event < perf_num_counters; ++event) { + for (event = 0; event < num_counters; ++event) { ret = op_create_counter(cpu, event); if (ret) goto out; @@ -150,7 +150,7 @@ static void op_perf_stop(void) int cpu, event; for_each_online_cpu(cpu) - for (event = 0; event < perf_num_counters; ++event) + for (event = 0; event < num_counters; ++event) op_destroy_counter(cpu, event); } @@ -179,7 +179,7 @@ static int op_arm_create_files(struct super_block *sb, struct dentry *root) { unsigned int i; - for (i = 0; i < perf_num_counters; i++) { + for (i = 0; i < num_counters; i++) { struct dentry *dir; char buf[4]; @@ -353,14 +353,19 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) memset(&perf_events, 0, sizeof(perf_events)); - perf_num_counters = armpmu_get_max_events(); + num_counters = perf_num_counters(); + if (num_counters <= 0) { + pr_info("oprofile: no performance counters\n"); + ret = -ENODEV; + goto out; + } - counter_config = kcalloc(perf_num_counters, + counter_config = kcalloc(num_counters, sizeof(struct op_counter_config), GFP_KERNEL); if (!counter_config) { pr_info("oprofile: failed to allocate %d " - "counters\n", perf_num_counters); + "counters\n", num_counters); ret = -ENOMEM; goto out; } @@ -370,11 +375,11 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) goto out; for_each_possible_cpu(cpu) { - perf_events[cpu] = kcalloc(perf_num_counters, + perf_events[cpu] = kcalloc(num_counters, sizeof(struct perf_event *), GFP_KERNEL); if (!perf_events[cpu]) { pr_info("oprofile: failed to allocate %d perf events " - "for cpu %d\n", perf_num_counters, cpu); + "for cpu %d\n", num_counters, cpu); ret = -ENOMEM; goto out; } @@ -409,7 +414,7 @@ void __exit oprofile_arch_exit(void) struct perf_event *event; for_each_possible_cpu(cpu) { - for (id = 0; id < perf_num_counters; ++id) { + for (id = 0; id < num_counters; ++id) { event = perf_events[cpu][id]; if (event) perf_event_release_kernel(event); diff --git a/arch/sh/kernel/perf_event.c b/arch/sh/kernel/perf_event.c index 7a3dc35..2cb9ad5 100644 --- a/arch/sh/kernel/perf_event.c +++ b/arch/sh/kernel/perf_event.c @@ -59,6 +59,15 @@ static inline int sh_pmu_initialized(void) return !!sh_pmu; } +int perf_num_counters(void) +{ + if (!sh_pmu) + return 0; + + return sh_pmu->num_events; +} +EXPORT_SYMBOL_GPL(perf_num_counters); + /* * Release the PMU if this is the last perf_event. */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 716f99b..1a02192 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -849,6 +849,7 @@ extern int perf_max_events; extern const struct pmu *hw_perf_event_init(struct perf_event *event); +extern int perf_num_counters(void); extern void perf_event_task_sched_in(struct task_struct *task); extern void perf_event_task_sched_out(struct task_struct *task, struct task_struct *next); extern void perf_event_task_tick(struct task_struct *task); -- cgit v0.10.2 From 84c7991059c9c4530cc911137c5bf508a41ed129 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 3 Oct 2010 21:41:13 +0100 Subject: perf: New helper function for pmu name Introduce perf_pmu_name() helper function that returns the name of the pmu. This gives us a generic way to get the name of a pmu regardless of how an architecture identifies it internally. Signed-off-by: Matt Fleming Acked-by: Peter Zijlstra Acked-by: Paul Mundt Signed-off-by: Robert Richter diff --git a/arch/sh/kernel/perf_event.c b/arch/sh/kernel/perf_event.c index 2cb9ad5..55fe89b 100644 --- a/arch/sh/kernel/perf_event.c +++ b/arch/sh/kernel/perf_event.c @@ -59,6 +59,15 @@ static inline int sh_pmu_initialized(void) return !!sh_pmu; } +const char *perf_pmu_name(void) +{ + if (!sh_pmu) + return NULL; + + return sh_pmu->name; +} +EXPORT_SYMBOL_GPL(perf_pmu_name); + int perf_num_counters(void) { if (!sh_pmu) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 1a02192..33f08da 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -850,6 +850,7 @@ extern int perf_max_events; extern const struct pmu *hw_perf_event_init(struct perf_event *event); extern int perf_num_counters(void); +extern const char *perf_pmu_name(void); extern void perf_event_task_sched_in(struct task_struct *task); extern void perf_event_task_sched_out(struct task_struct *task, struct task_struct *next); extern void perf_event_task_tick(struct task_struct *task); diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 403d180..e253469 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -85,6 +85,11 @@ void __weak hw_perf_enable(void) { barrier(); } void __weak perf_event_print_debug(void) { } +extern __weak const char *perf_pmu_name(void) +{ + return "pmu"; +} + static DEFINE_PER_CPU(int, perf_disable_count); void perf_disable(void) -- cgit v0.10.2 From 56946331b28d53232115a155ba662ab3dc598952 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 8 Oct 2010 21:42:17 +0100 Subject: oprofile: Make op_name_from_perf_id() global Make op_name_from_perf_id() global so that we have a way for each architecture to construct an oprofile name for op->cpu_type. We need to remove the argument from the function prototype so that we can hide all implementation details inside the function. Signed-off-by: Matt Fleming Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 1e971a7..4f67cfa 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -155,8 +155,10 @@ static void op_perf_stop(void) } -static char *op_name_from_perf_id(enum arm_perf_pmu_ids id) +char *op_name_from_perf_id(void) { + enum arm_perf_pmu_ids id = armpmu_get_pmu_id(); + switch (id) { case ARM_PERF_PMU_ID_XSCALE1: return "arm/xscale1"; @@ -391,7 +393,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) ops->start = op_arm_start; ops->stop = op_arm_stop; ops->shutdown = op_arm_stop; - ops->cpu_type = op_name_from_perf_id(armpmu_get_pmu_id()); + ops->cpu_type = op_name_from_perf_id(); if (!ops->cpu_type) ret = -ENODEV; diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 5171639..1574d4a 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -185,4 +185,8 @@ int oprofile_add_data(struct op_entry *entry, unsigned long val); int oprofile_add_data64(struct op_entry *entry, u64 val); int oprofile_write_commit(struct op_entry *entry); +#ifdef CONFIG_PERF_EVENTS +char *op_name_from_perf_id(void); +#endif /* CONFIG_PERF_EVENTS */ + #endif /* OPROFILE_H */ -- cgit v0.10.2 From 80e96b11f6cd261e1e569f3931604d656388af33 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 27 Sep 2010 20:29:58 +0100 Subject: ARM: oprofile: Rename op_arm to oprofile_perf In preparation for moving the generic functions out of this file, give the functions more general names (e.g. remove "arm" from the names). Signed-off-by: Matt Fleming Tested-by: Will Deacon Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 4f67cfa..d18c9f3 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -38,8 +38,8 @@ struct op_counter_config { struct perf_event_attr attr; }; -static int op_arm_enabled; -static DEFINE_MUTEX(op_arm_mutex); +static int oprofile_perf_enabled; +static DEFINE_MUTEX(oprofile_perf_mutex); static struct op_counter_config *counter_config; static struct perf_event **perf_events[nr_cpumask_bits]; @@ -66,7 +66,7 @@ static void op_overflow_handler(struct perf_event *event, int unused, } /* - * Called by op_arm_setup to create perf attributes to mirror the oprofile + * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile * settings in counter_config. Attributes are created as `pinned' events and * so are permanently scheduled on the PMU. */ @@ -123,7 +123,7 @@ static void op_destroy_counter(int cpu, int event) } /* - * Called by op_arm_start to create active perf events based on the + * Called by oprofile_perf_start to create active perf events based on the * perviously configured attributes. */ static int op_perf_start(void) @@ -143,7 +143,7 @@ out: } /* - * Called by op_arm_stop at the end of a profiling run. + * Called by oprofile_perf_stop at the end of a profiling run. */ static void op_perf_stop(void) { @@ -177,7 +177,7 @@ char *op_name_from_perf_id(void) } } -static int op_arm_create_files(struct super_block *sb, struct dentry *root) +static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root) { unsigned int i; @@ -198,7 +198,7 @@ static int op_arm_create_files(struct super_block *sb, struct dentry *root) return 0; } -static int op_arm_setup(void) +static int oprofile_perf_setup(void) { spin_lock(&oprofilefs_lock); op_perf_setup(); @@ -206,54 +206,54 @@ static int op_arm_setup(void) return 0; } -static int op_arm_start(void) +static int oprofile_perf_start(void) { int ret = -EBUSY; - mutex_lock(&op_arm_mutex); - if (!op_arm_enabled) { + mutex_lock(&oprofile_perf_mutex); + if (!oprofile_perf_enabled) { ret = 0; op_perf_start(); - op_arm_enabled = 1; + oprofile_perf_enabled = 1; } - mutex_unlock(&op_arm_mutex); + mutex_unlock(&oprofile_perf_mutex); return ret; } -static void op_arm_stop(void) +static void oprofile_perf_stop(void) { - mutex_lock(&op_arm_mutex); - if (op_arm_enabled) + mutex_lock(&oprofile_perf_mutex); + if (oprofile_perf_enabled) op_perf_stop(); - op_arm_enabled = 0; - mutex_unlock(&op_arm_mutex); + oprofile_perf_enabled = 0; + mutex_unlock(&oprofile_perf_mutex); } #ifdef CONFIG_PM -static int op_arm_suspend(struct platform_device *dev, pm_message_t state) +static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state) { - mutex_lock(&op_arm_mutex); - if (op_arm_enabled) + mutex_lock(&oprofile_perf_mutex); + if (oprofile_perf_enabled) op_perf_stop(); - mutex_unlock(&op_arm_mutex); + mutex_unlock(&oprofile_perf_mutex); return 0; } -static int op_arm_resume(struct platform_device *dev) +static int oprofile_perf_resume(struct platform_device *dev) { - mutex_lock(&op_arm_mutex); - if (op_arm_enabled && op_perf_start()) - op_arm_enabled = 0; - mutex_unlock(&op_arm_mutex); + mutex_lock(&oprofile_perf_mutex); + if (oprofile_perf_enabled && op_perf_start()) + oprofile_perf_enabled = 0; + mutex_unlock(&oprofile_perf_mutex); return 0; } static struct platform_driver oprofile_driver = { .driver = { - .name = "arm-oprofile", + .name = "oprofile-perf", }, - .resume = op_arm_resume, - .suspend = op_arm_suspend, + .resume = oprofile_perf_resume, + .suspend = oprofile_perf_suspend, }; static struct platform_device *oprofile_pdev; @@ -388,11 +388,11 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) } ops->backtrace = arm_backtrace; - ops->create_files = op_arm_create_files; - ops->setup = op_arm_setup; - ops->start = op_arm_start; - ops->stop = op_arm_stop; - ops->shutdown = op_arm_stop; + ops->create_files = oprofile_perf_create_files; + ops->setup = oprofile_perf_setup; + ops->start = oprofile_perf_start; + ops->stop = oprofile_perf_stop; + ops->shutdown = oprofile_perf_stop; ops->cpu_type = op_name_from_perf_id(); if (!ops->cpu_type) -- cgit v0.10.2 From 58850e210cd207399cf6461326e322541b2ec81c Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 27 Sep 2010 20:35:29 +0100 Subject: ARM: oprofile: Move non-ARM code into separate init/exit In preparation for moving the majority of this oprofile code into an architecture-neutral place separate the architecture-independent code into oprofile_perf_init() and oprofile_perf_exit(). Signed-off-by: Matt Fleming Tested-by: Will Deacon Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index d18c9f3..8718311c 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -349,7 +349,7 @@ static void arm_backtrace(struct pt_regs * const regs, unsigned int depth) tail = user_backtrace(tail); } -int __init oprofile_arch_init(struct oprofile_operations *ops) +int __init oprofile_perf_init(struct oprofile_operations *ops) { int cpu, ret = 0; @@ -387,7 +387,6 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) } } - ops->backtrace = arm_backtrace; ops->create_files = oprofile_perf_create_files; ops->setup = oprofile_perf_setup; ops->start = oprofile_perf_start; @@ -410,7 +409,14 @@ out: return ret; } -void __exit oprofile_arch_exit(void) +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + ops->backtrace = arm_backtrace; + + return oprofile_perf_init(ops); +} + +void __exit oprofile_perf_exit(void) { int cpu, id; struct perf_event *event; @@ -428,6 +434,11 @@ void __exit oprofile_arch_exit(void) kfree(counter_config); exit_driverfs(); } + +void __exit oprofile_arch_exit(void) +{ + oprofile_perf_exit(); +} #else int __init oprofile_arch_init(struct oprofile_operations *ops) { -- cgit v0.10.2 From 3d90a00763b51e1db344a7430c966be723b67a29 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 27 Sep 2010 20:45:08 +0100 Subject: oprofile: Abstract the perf-events backend Move the perf-events backend from arch/arm/oprofile into drivers/oprofile so that the code can be shared between architectures. This allows each architecture to maintain only a single copy of the PMU accessor functions instead of one for both perf and OProfile. It also becomes possible for other architectures to delete much of their OProfile code in favour of the common code now available in drivers/oprofile/oprofile_perf.c. Signed-off-by: Matt Fleming Tested-by: Will Deacon Signed-off-by: Robert Richter diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile index e666eaf..b2215c6 100644 --- a/arch/arm/oprofile/Makefile +++ b/arch/arm/oprofile/Makefile @@ -6,4 +6,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprofilefs.o oprofile_stats.o \ timer_int.o ) +ifeq ($(CONFIG_HW_PERF_EVENTS),y) +DRIVER_OBJS += $(addprefix ../../../drivers/oprofile/, oprofile_perf.o) +endif + oprofile-y := $(DRIVER_OBJS) common.o diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 8718311c..8aa9744 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -25,136 +25,6 @@ #include #ifdef CONFIG_HW_PERF_EVENTS -/* - * Per performance monitor configuration as set via oprofilefs. - */ -struct op_counter_config { - unsigned long count; - unsigned long enabled; - unsigned long event; - unsigned long unit_mask; - unsigned long kernel; - unsigned long user; - struct perf_event_attr attr; -}; - -static int oprofile_perf_enabled; -static DEFINE_MUTEX(oprofile_perf_mutex); - -static struct op_counter_config *counter_config; -static struct perf_event **perf_events[nr_cpumask_bits]; -static int num_counters; - -/* - * Overflow callback for oprofile. - */ -static void op_overflow_handler(struct perf_event *event, int unused, - struct perf_sample_data *data, struct pt_regs *regs) -{ - int id; - u32 cpu = smp_processor_id(); - - for (id = 0; id < num_counters; ++id) - if (perf_events[cpu][id] == event) - break; - - if (id != num_counters) - oprofile_add_sample(regs, id); - else - pr_warning("oprofile: ignoring spurious overflow " - "on cpu %u\n", cpu); -} - -/* - * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile - * settings in counter_config. Attributes are created as `pinned' events and - * so are permanently scheduled on the PMU. - */ -static void op_perf_setup(void) -{ - int i; - u32 size = sizeof(struct perf_event_attr); - struct perf_event_attr *attr; - - for (i = 0; i < num_counters; ++i) { - attr = &counter_config[i].attr; - memset(attr, 0, size); - attr->type = PERF_TYPE_RAW; - attr->size = size; - attr->config = counter_config[i].event; - attr->sample_period = counter_config[i].count; - attr->pinned = 1; - } -} - -static int op_create_counter(int cpu, int event) -{ - int ret = 0; - struct perf_event *pevent; - - if (!counter_config[event].enabled || (perf_events[cpu][event] != NULL)) - return ret; - - pevent = perf_event_create_kernel_counter(&counter_config[event].attr, - cpu, -1, - op_overflow_handler); - - if (IS_ERR(pevent)) { - ret = PTR_ERR(pevent); - } else if (pevent->state != PERF_EVENT_STATE_ACTIVE) { - pr_warning("oprofile: failed to enable event %d " - "on CPU %d\n", event, cpu); - ret = -EBUSY; - } else { - perf_events[cpu][event] = pevent; - } - - return ret; -} - -static void op_destroy_counter(int cpu, int event) -{ - struct perf_event *pevent = perf_events[cpu][event]; - - if (pevent) { - perf_event_release_kernel(pevent); - perf_events[cpu][event] = NULL; - } -} - -/* - * Called by oprofile_perf_start to create active perf events based on the - * perviously configured attributes. - */ -static int op_perf_start(void) -{ - int cpu, event, ret = 0; - - for_each_online_cpu(cpu) { - for (event = 0; event < num_counters; ++event) { - ret = op_create_counter(cpu, event); - if (ret) - goto out; - } - } - -out: - return ret; -} - -/* - * Called by oprofile_perf_stop at the end of a profiling run. - */ -static void op_perf_stop(void) -{ - int cpu, event; - - for_each_online_cpu(cpu) - for (event = 0; event < num_counters; ++event) - op_destroy_counter(cpu, event); -} - - char *op_name_from_perf_id(void) { enum arm_perf_pmu_ids id = armpmu_get_pmu_id(); @@ -177,116 +47,6 @@ char *op_name_from_perf_id(void) } } -static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root) -{ - unsigned int i; - - for (i = 0; i < num_counters; i++) { - struct dentry *dir; - char buf[4]; - - snprintf(buf, sizeof buf, "%d", i); - dir = oprofilefs_mkdir(sb, root, buf); - oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); - oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); - oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); - oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); - oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); - oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); - } - - return 0; -} - -static int oprofile_perf_setup(void) -{ - spin_lock(&oprofilefs_lock); - op_perf_setup(); - spin_unlock(&oprofilefs_lock); - return 0; -} - -static int oprofile_perf_start(void) -{ - int ret = -EBUSY; - - mutex_lock(&oprofile_perf_mutex); - if (!oprofile_perf_enabled) { - ret = 0; - op_perf_start(); - oprofile_perf_enabled = 1; - } - mutex_unlock(&oprofile_perf_mutex); - return ret; -} - -static void oprofile_perf_stop(void) -{ - mutex_lock(&oprofile_perf_mutex); - if (oprofile_perf_enabled) - op_perf_stop(); - oprofile_perf_enabled = 0; - mutex_unlock(&oprofile_perf_mutex); -} - -#ifdef CONFIG_PM -static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state) -{ - mutex_lock(&oprofile_perf_mutex); - if (oprofile_perf_enabled) - op_perf_stop(); - mutex_unlock(&oprofile_perf_mutex); - return 0; -} - -static int oprofile_perf_resume(struct platform_device *dev) -{ - mutex_lock(&oprofile_perf_mutex); - if (oprofile_perf_enabled && op_perf_start()) - oprofile_perf_enabled = 0; - mutex_unlock(&oprofile_perf_mutex); - return 0; -} - -static struct platform_driver oprofile_driver = { - .driver = { - .name = "oprofile-perf", - }, - .resume = oprofile_perf_resume, - .suspend = oprofile_perf_suspend, -}; - -static struct platform_device *oprofile_pdev; - -static int __init init_driverfs(void) -{ - int ret; - - ret = platform_driver_register(&oprofile_driver); - if (ret) - goto out; - - oprofile_pdev = platform_device_register_simple( - oprofile_driver.driver.name, 0, NULL, 0); - if (IS_ERR(oprofile_pdev)) { - ret = PTR_ERR(oprofile_pdev); - platform_driver_unregister(&oprofile_driver); - } - -out: - return ret; -} - -static void __exit exit_driverfs(void) -{ - platform_device_unregister(oprofile_pdev); - platform_driver_unregister(&oprofile_driver); -} -#else -static int __init init_driverfs(void) { return 0; } -#define exit_driverfs() do { } while (0) -#endif /* CONFIG_PM */ - static int report_trace(struct stackframe *frame, void *d) { unsigned int *depth = d; @@ -349,66 +109,6 @@ static void arm_backtrace(struct pt_regs * const regs, unsigned int depth) tail = user_backtrace(tail); } -int __init oprofile_perf_init(struct oprofile_operations *ops) -{ - int cpu, ret = 0; - - memset(&perf_events, 0, sizeof(perf_events)); - - num_counters = perf_num_counters(); - if (num_counters <= 0) { - pr_info("oprofile: no performance counters\n"); - ret = -ENODEV; - goto out; - } - - counter_config = kcalloc(num_counters, - sizeof(struct op_counter_config), GFP_KERNEL); - - if (!counter_config) { - pr_info("oprofile: failed to allocate %d " - "counters\n", num_counters); - ret = -ENOMEM; - goto out; - } - - ret = init_driverfs(); - if (ret) - goto out; - - for_each_possible_cpu(cpu) { - perf_events[cpu] = kcalloc(num_counters, - sizeof(struct perf_event *), GFP_KERNEL); - if (!perf_events[cpu]) { - pr_info("oprofile: failed to allocate %d perf events " - "for cpu %d\n", num_counters, cpu); - ret = -ENOMEM; - goto out; - } - } - - ops->create_files = oprofile_perf_create_files; - ops->setup = oprofile_perf_setup; - ops->start = oprofile_perf_start; - ops->stop = oprofile_perf_stop; - ops->shutdown = oprofile_perf_stop; - ops->cpu_type = op_name_from_perf_id(); - - if (!ops->cpu_type) - ret = -ENODEV; - else - pr_info("oprofile: using %s\n", ops->cpu_type); - -out: - if (ret) { - for_each_possible_cpu(cpu) - kfree(perf_events[cpu]); - kfree(counter_config); - } - - return ret; -} - int __init oprofile_arch_init(struct oprofile_operations *ops) { ops->backtrace = arm_backtrace; @@ -416,25 +116,6 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) return oprofile_perf_init(ops); } -void __exit oprofile_perf_exit(void) -{ - int cpu, id; - struct perf_event *event; - - for_each_possible_cpu(cpu) { - for (id = 0; id < num_counters; ++id) { - event = perf_events[cpu][id]; - if (event) - perf_event_release_kernel(event); - } - - kfree(perf_events[cpu]); - } - - kfree(counter_config); - exit_driverfs(); -} - void __exit oprofile_arch_exit(void) { oprofile_perf_exit(); diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c new file mode 100644 index 0000000..ebb40cb --- /dev/null +++ b/drivers/oprofile/oprofile_perf.c @@ -0,0 +1,326 @@ +/* + * Copyright 2010 ARM Ltd. + * + * Perf-events backend for OProfile. + */ +#include +#include +#include + +/* + * Per performance monitor configuration as set via oprofilefs. + */ +struct op_counter_config { + unsigned long count; + unsigned long enabled; + unsigned long event; + unsigned long unit_mask; + unsigned long kernel; + unsigned long user; + struct perf_event_attr attr; +}; + +static int oprofile_perf_enabled; +static DEFINE_MUTEX(oprofile_perf_mutex); + +static struct op_counter_config *counter_config; +static struct perf_event **perf_events[nr_cpumask_bits]; +static int num_counters; + +/* + * Overflow callback for oprofile. + */ +static void op_overflow_handler(struct perf_event *event, int unused, + struct perf_sample_data *data, struct pt_regs *regs) +{ + int id; + u32 cpu = smp_processor_id(); + + for (id = 0; id < num_counters; ++id) + if (perf_events[cpu][id] == event) + break; + + if (id != num_counters) + oprofile_add_sample(regs, id); + else + pr_warning("oprofile: ignoring spurious overflow " + "on cpu %u\n", cpu); +} + +/* + * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile + * settings in counter_config. Attributes are created as `pinned' events and + * so are permanently scheduled on the PMU. + */ +static void op_perf_setup(void) +{ + int i; + u32 size = sizeof(struct perf_event_attr); + struct perf_event_attr *attr; + + for (i = 0; i < num_counters; ++i) { + attr = &counter_config[i].attr; + memset(attr, 0, size); + attr->type = PERF_TYPE_RAW; + attr->size = size; + attr->config = counter_config[i].event; + attr->sample_period = counter_config[i].count; + attr->pinned = 1; + } +} + +static int op_create_counter(int cpu, int event) +{ + int ret = 0; + struct perf_event *pevent; + + if (!counter_config[event].enabled || (perf_events[cpu][event] != NULL)) + return ret; + + pevent = perf_event_create_kernel_counter(&counter_config[event].attr, + cpu, -1, + op_overflow_handler); + + if (IS_ERR(pevent)) { + ret = PTR_ERR(pevent); + } else if (pevent->state != PERF_EVENT_STATE_ACTIVE) { + pr_warning("oprofile: failed to enable event %d " + "on CPU %d\n", event, cpu); + ret = -EBUSY; + } else { + perf_events[cpu][event] = pevent; + } + + return ret; +} + +static void op_destroy_counter(int cpu, int event) +{ + struct perf_event *pevent = perf_events[cpu][event]; + + if (pevent) { + perf_event_release_kernel(pevent); + perf_events[cpu][event] = NULL; + } +} + +/* + * Called by oprofile_perf_start to create active perf events based on the + * perviously configured attributes. + */ +static int op_perf_start(void) +{ + int cpu, event, ret = 0; + + for_each_online_cpu(cpu) { + for (event = 0; event < num_counters; ++event) { + ret = op_create_counter(cpu, event); + if (ret) + goto out; + } + } + +out: + return ret; +} + +/* + * Called by oprofile_perf_stop at the end of a profiling run. + */ +static void op_perf_stop(void) +{ + int cpu, event; + + for_each_online_cpu(cpu) + for (event = 0; event < num_counters; ++event) + op_destroy_counter(cpu, event); +} + +static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root) +{ + unsigned int i; + + for (i = 0; i < num_counters; i++) { + struct dentry *dir; + char buf[4]; + + snprintf(buf, sizeof buf, "%d", i); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); + oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); + oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); + oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); + oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); + oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); + } + + return 0; +} + +static int oprofile_perf_setup(void) +{ + spin_lock(&oprofilefs_lock); + op_perf_setup(); + spin_unlock(&oprofilefs_lock); + return 0; +} + +static int oprofile_perf_start(void) +{ + int ret = -EBUSY; + + mutex_lock(&oprofile_perf_mutex); + if (!oprofile_perf_enabled) { + ret = 0; + op_perf_start(); + oprofile_perf_enabled = 1; + } + mutex_unlock(&oprofile_perf_mutex); + return ret; +} + +static void oprofile_perf_stop(void) +{ + mutex_lock(&oprofile_perf_mutex); + if (oprofile_perf_enabled) + op_perf_stop(); + oprofile_perf_enabled = 0; + mutex_unlock(&oprofile_perf_mutex); +} + +#ifdef CONFIG_PM +static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state) +{ + mutex_lock(&oprofile_perf_mutex); + if (oprofile_perf_enabled) + op_perf_stop(); + mutex_unlock(&oprofile_perf_mutex); + return 0; +} + +static int oprofile_perf_resume(struct platform_device *dev) +{ + mutex_lock(&oprofile_perf_mutex); + if (oprofile_perf_enabled && op_perf_start()) + oprofile_perf_enabled = 0; + mutex_unlock(&oprofile_perf_mutex); + return 0; +} + +static struct platform_driver oprofile_driver = { + .driver = { + .name = "oprofile-perf", + }, + .resume = oprofile_perf_resume, + .suspend = oprofile_perf_suspend, +}; + +static struct platform_device *oprofile_pdev; + +static int __init init_driverfs(void) +{ + int ret; + + ret = platform_driver_register(&oprofile_driver); + if (ret) + goto out; + + oprofile_pdev = platform_device_register_simple( + oprofile_driver.driver.name, 0, NULL, 0); + if (IS_ERR(oprofile_pdev)) { + ret = PTR_ERR(oprofile_pdev); + platform_driver_unregister(&oprofile_driver); + } + +out: + return ret; +} + +static void __exit exit_driverfs(void) +{ + platform_device_unregister(oprofile_pdev); + platform_driver_unregister(&oprofile_driver); +} +#else +static int __init init_driverfs(void) { return 0; } +#define exit_driverfs() do { } while (0) +#endif /* CONFIG_PM */ + +int __init oprofile_perf_init(struct oprofile_operations *ops) +{ + int cpu, ret = 0; + + memset(&perf_events, 0, sizeof(perf_events)); + + num_counters = perf_num_counters(); + if (num_counters <= 0) { + pr_info("oprofile: no performance counters\n"); + ret = -ENODEV; + goto out; + } + + counter_config = kcalloc(num_counters, + sizeof(struct op_counter_config), GFP_KERNEL); + + if (!counter_config) { + pr_info("oprofile: failed to allocate %d " + "counters\n", num_counters); + ret = -ENOMEM; + goto out; + } + + ret = init_driverfs(); + if (ret) + goto out; + + for_each_possible_cpu(cpu) { + perf_events[cpu] = kcalloc(num_counters, + sizeof(struct perf_event *), GFP_KERNEL); + if (!perf_events[cpu]) { + pr_info("oprofile: failed to allocate %d perf events " + "for cpu %d\n", num_counters, cpu); + ret = -ENOMEM; + goto out; + } + } + + ops->create_files = oprofile_perf_create_files; + ops->setup = oprofile_perf_setup; + ops->start = oprofile_perf_start; + ops->stop = oprofile_perf_stop; + ops->shutdown = oprofile_perf_stop; + ops->cpu_type = op_name_from_perf_id(); + + if (!ops->cpu_type) + ret = -ENODEV; + else + pr_info("oprofile: using %s\n", ops->cpu_type); + +out: + if (ret) { + for_each_possible_cpu(cpu) + kfree(perf_events[cpu]); + kfree(counter_config); + } + + return ret; +} + +void __exit oprofile_perf_exit(void) +{ + int cpu, id; + struct perf_event *event; + + for_each_possible_cpu(cpu) { + for (id = 0; id < num_counters; ++id) { + event = perf_events[cpu][id]; + if (event) + perf_event_release_kernel(event); + } + + kfree(perf_events[cpu]); + } + + kfree(counter_config); + exit_driverfs(); +} diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 1574d4a..d67a833 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -15,6 +15,7 @@ #include #include +#include #include /* Each escaped entry is prefixed by ESCAPE_CODE @@ -186,6 +187,8 @@ int oprofile_add_data64(struct op_entry *entry, u64 val); int oprofile_write_commit(struct op_entry *entry); #ifdef CONFIG_PERF_EVENTS +int __init oprofile_perf_init(struct oprofile_operations *ops); +void __exit oprofile_perf_exit(void); char *op_name_from_perf_id(void); #endif /* CONFIG_PERF_EVENTS */ -- cgit v0.10.2 From 86c8c04792f152c5469023885510140dd34817bc Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 10 Sep 2010 20:36:23 +0100 Subject: sh: oprofile: Use perf-events oprofile backend Now that we've got a generic perf-events based oprofile backend we might as well make use of it seeing as SH doesn't do anything special with its oprofile backend. Also introduce a new CONFIG_HW_PERF_EVENTS symbol so that we can fallback to using the timer interrupt for oprofile if the CPU doesn't support perf events. Also, to avoid a section mismatch warning we need to annotate oprofile_arch_exit() with an __exit marker. Signed-off-by: Matt Fleming Acked-by: Paul Mundt Signed-off-by: Robert Richter diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 33990fa..35b6c3f 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -249,6 +249,11 @@ config ARCH_SHMOBILE select PM select PM_RUNTIME +config CPU_HAS_PMU + depends on CPU_SH4 || CPU_SH4A + default y + bool + if SUPERH32 choice @@ -738,6 +743,14 @@ config GUSA_RB LLSC, this should be more efficient than the other alternative of disabling interrupts around the atomic sequence. +config HW_PERF_EVENTS + bool "Enable hardware performance counter support for perf events" + depends on PERF_EVENTS && CPU_HAS_PMU + default y + help + Enable hardware performance counter support for perf events. If + disabled, perf events will use software events only. + source "drivers/sh/Kconfig" endmenu diff --git a/arch/sh/oprofile/Makefile b/arch/sh/oprofile/Makefile index 4886c5c..e85aae7 100644 --- a/arch/sh/oprofile/Makefile +++ b/arch/sh/oprofile/Makefile @@ -6,4 +6,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprofilefs.o oprofile_stats.o \ timer_int.o ) +ifeq ($(CONFIG_HW_PERF_EVENTS),y) +DRIVER_OBJS += $(addprefix ../../../drivers/oprofile/, oprofile_perf.o) +endif + oprofile-y := $(DRIVER_OBJS) common.o backtrace.o diff --git a/arch/sh/oprofile/common.c b/arch/sh/oprofile/common.c index ac60493..e10d893 100644 --- a/arch/sh/oprofile/common.c +++ b/arch/sh/oprofile/common.c @@ -17,114 +17,45 @@ #include #include #include +#include #include -#include "op_impl.h" - -static struct op_sh_model *model; - -static struct op_counter_config ctr[20]; +#ifdef CONFIG_HW_PERF_EVENTS extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth); -static int op_sh_setup(void) -{ - /* Pre-compute the values to stuff in the hardware registers. */ - model->reg_setup(ctr); - - /* Configure the registers on all cpus. */ - on_each_cpu(model->cpu_setup, NULL, 1); - - return 0; -} - -static int op_sh_create_files(struct super_block *sb, struct dentry *root) +char *op_name_from_perf_id(void) { - int i, ret = 0; + const char *pmu; + char buf[20]; + int size; - for (i = 0; i < model->num_counters; i++) { - struct dentry *dir; - char buf[4]; + pmu = perf_pmu_name(); + if (!pmu) + return NULL; - snprintf(buf, sizeof(buf), "%d", i); - dir = oprofilefs_mkdir(sb, root, buf); + size = snprintf(buf, sizeof(buf), "sh/%s", pmu); + if (size > -1 && size < sizeof(buf)) + return buf; - ret |= oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled); - ret |= oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event); - ret |= oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel); - ret |= oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user); - - if (model->create_files) - ret |= model->create_files(sb, dir); - else - ret |= oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count); - - /* Dummy entries */ - ret |= oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask); - } - - return ret; + return NULL; } -static int op_sh_start(void) +int __init oprofile_arch_init(struct oprofile_operations *ops) { - /* Enable performance monitoring for all counters. */ - on_each_cpu(model->cpu_start, NULL, 1); + ops->backtrace = sh_backtrace; - return 0; + return oprofile_perf_init(ops); } -static void op_sh_stop(void) +void __exit oprofile_arch_exit(void) { - /* Disable performance monitoring for all counters. */ - on_each_cpu(model->cpu_stop, NULL, 1); + oprofile_perf_exit(); } - +#else int __init oprofile_arch_init(struct oprofile_operations *ops) { - struct op_sh_model *lmodel = NULL; - int ret; - - /* - * Always assign the backtrace op. If the counter initialization - * fails, we fall back to the timer which will still make use of - * this. - */ - ops->backtrace = sh_backtrace; - - /* - * XXX - * - * All of the SH7750/SH-4A counters have been converted to perf, - * this infrastructure hook is left for other users until they've - * had a chance to convert over, at which point all of this - * will be deleted. - */ - - if (!lmodel) - return -ENODEV; - if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER)) - return -ENODEV; - - ret = lmodel->init(); - if (unlikely(ret != 0)) - return ret; - - model = lmodel; - - ops->setup = op_sh_setup; - ops->create_files = op_sh_create_files; - ops->start = op_sh_start; - ops->stop = op_sh_stop; - ops->cpu_type = lmodel->cpu_type; - - printk(KERN_INFO "oprofile: using %s performance monitoring.\n", - lmodel->cpu_type); - - return 0; -} - -void oprofile_arch_exit(void) -{ - if (model && model->exit) - model->exit(); + pr_info("oprofile: hardware counters not available\n"); + return -ENODEV; } +void __exit oprofile_arch_exit(void) {} +#endif /* CONFIG_HW_PERF_EVENTS */ diff --git a/arch/sh/oprofile/op_impl.h b/arch/sh/oprofile/op_impl.h deleted file mode 100644 index 1244479..0000000 --- a/arch/sh/oprofile/op_impl.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef __OP_IMPL_H -#define __OP_IMPL_H - -/* Per-counter configuration as set via oprofilefs. */ -struct op_counter_config { - unsigned long enabled; - unsigned long event; - - unsigned long count; - - /* Dummy values for userspace tool compliance */ - unsigned long kernel; - unsigned long user; - unsigned long unit_mask; -}; - -/* Per-architecture configury and hooks. */ -struct op_sh_model { - void (*reg_setup)(struct op_counter_config *); - int (*create_files)(struct super_block *sb, struct dentry *dir); - void (*cpu_setup)(void *dummy); - int (*init)(void); - void (*exit)(void); - void (*cpu_start)(void *args); - void (*cpu_stop)(void *args); - char *cpu_type; - unsigned char num_counters; -}; - -/* arch/sh/oprofile/common.c */ -extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth); - -#endif /* __OP_IMPL_H */ -- cgit v0.10.2 From 81771974ae49bf79aab60c42eac7a6d730a9ef2b Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 29 Sep 2010 16:52:25 +0200 Subject: oprofile, ARM: Release resources on failure This patch fixes a resource leak on failure, where the oprofilefs and some counters may not released properly. Signed-off-by: Robert Richter Acked-by: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Cc: # .35.x LKML-Reference: <20100929145225.GJ13563@erda.amd.com> Signed-off-by: Ingo Molnar diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index ebb40cb..f3d3df2 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -84,6 +84,7 @@ static int op_create_counter(int cpu, int event) if (IS_ERR(pevent)) { ret = PTR_ERR(pevent); } else if (pevent->state != PERF_EVENT_STATE_ACTIVE) { + perf_event_release_kernel(pevent); pr_warning("oprofile: failed to enable event %d " "on CPU %d\n", event, cpu); ret = -EBUSY; -- cgit v0.10.2 From 9c91283a19c2d998a83f50f113f8585709c15caf Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 27 Aug 2010 14:32:41 +0200 Subject: oprofile, ARM: Remove some goto statements This patch removes some unnecessary goto statements. Acked-by: Will Deacon Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index f3d3df2..6853634 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -117,11 +117,10 @@ static int op_perf_start(void) for (event = 0; event < num_counters; ++event) { ret = op_create_counter(cpu, event); if (ret) - goto out; + return ret; } } -out: return ret; } @@ -224,7 +223,7 @@ static int __init init_driverfs(void) ret = platform_driver_register(&oprofile_driver); if (ret) - goto out; + return ret; oprofile_pdev = platform_device_register_simple( oprofile_driver.driver.name, 0, NULL, 0); @@ -233,7 +232,6 @@ static int __init init_driverfs(void) platform_driver_unregister(&oprofile_driver); } -out: return ret; } -- cgit v0.10.2 From 2bcb2b641a8f1524f7a9801eb9e52a00b8f18c6e Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 29 Sep 2010 14:43:29 +0200 Subject: oprofile, ARM: Rework op_create_counter() This patch simplifies op_create_counter(). Removing if/else if paths and return code variable by direct returning from function. Acked-by: Will Deacon Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index 6853634..a34137f 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -71,28 +71,28 @@ static void op_perf_setup(void) static int op_create_counter(int cpu, int event) { - int ret = 0; struct perf_event *pevent; - if (!counter_config[event].enabled || (perf_events[cpu][event] != NULL)) - return ret; + if (!counter_config[event].enabled || perf_events[cpu][event]) + return 0; pevent = perf_event_create_kernel_counter(&counter_config[event].attr, cpu, -1, op_overflow_handler); - if (IS_ERR(pevent)) { - ret = PTR_ERR(pevent); - } else if (pevent->state != PERF_EVENT_STATE_ACTIVE) { + if (IS_ERR(pevent)) + return PTR_ERR(pevent); + + if (pevent->state != PERF_EVENT_STATE_ACTIVE) { perf_event_release_kernel(pevent); pr_warning("oprofile: failed to enable event %d " "on CPU %d\n", event, cpu); - ret = -EBUSY; - } else { - perf_events[cpu][event] = pevent; + return -EBUSY; } - return ret; + perf_events[cpu][event] = pevent; + + return 0; } static void op_destroy_counter(int cpu, int event) -- cgit v0.10.2 From e9677b3ce207a07fad5746b6f7ddc70cae79de0a Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 29 Sep 2010 15:42:30 +0200 Subject: oprofile, ARM: Use oprofile_arch_exit() to cleanup on failure There is duplicate cleanup code in the init and exit functions. Now, oprofile_arch_exit() is also used if oprofile_arch_init() fails. Acked-by: Will Deacon Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index a34137f..b17235a 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -245,10 +245,33 @@ static int __init init_driverfs(void) { return 0; } #define exit_driverfs() do { } while (0) #endif /* CONFIG_PM */ +void oprofile_perf_exit(void) +{ + int cpu, id; + struct perf_event *event; + + for_each_possible_cpu(cpu) { + for (id = 0; id < num_counters; ++id) { + event = perf_events[cpu][id]; + if (event) + perf_event_release_kernel(event); + } + + kfree(perf_events[cpu]); + } + + kfree(counter_config); + exit_driverfs(); +} + int __init oprofile_perf_init(struct oprofile_operations *ops) { int cpu, ret = 0; + ret = init_driverfs(); + if (ret) + return ret; + memset(&perf_events, 0, sizeof(perf_events)); num_counters = perf_num_counters(); @@ -265,13 +288,10 @@ int __init oprofile_perf_init(struct oprofile_operations *ops) pr_info("oprofile: failed to allocate %d " "counters\n", num_counters); ret = -ENOMEM; + num_counters = 0; goto out; } - ret = init_driverfs(); - if (ret) - goto out; - for_each_possible_cpu(cpu) { perf_events[cpu] = kcalloc(num_counters, sizeof(struct perf_event *), GFP_KERNEL); @@ -296,30 +316,8 @@ int __init oprofile_perf_init(struct oprofile_operations *ops) pr_info("oprofile: using %s\n", ops->cpu_type); out: - if (ret) { - for_each_possible_cpu(cpu) - kfree(perf_events[cpu]); - kfree(counter_config); - } + if (ret) + oprofile_perf_exit(); return ret; } - -void __exit oprofile_perf_exit(void) -{ - int cpu, id; - struct perf_event *event; - - for_each_possible_cpu(cpu) { - for (id = 0; id < num_counters; ++id) { - event = perf_events[cpu][id]; - if (event) - perf_event_release_kernel(event); - } - - kfree(perf_events[cpu]); - } - - kfree(counter_config); - exit_driverfs(); -} -- cgit v0.10.2 From 7df01d96b295e400167e78061b81d4c91630b12d Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Mon, 4 Oct 2010 21:09:36 +0200 Subject: oprofile: disable write access to oprofilefs while profiler is running Oprofile counters are setup when profiling is disabled. Thus, writing to oprofilefs has no immediate effect. Changes are updated only after oprofile is reenabled. To keep userland and kernel states synchronized, we now allow configuration of oprofile only if profiling is disabled. In this case it checks if the profiler is running and then disables write access to oprofilefs by returning -EBUSY. The change should be backward compatible with current oprofile userland daemon. Acked-by: Maynard Johnson Cc: William Cohen Cc: Suravee Suthikulpanit Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index b4a6857..f9bda64 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -225,26 +225,17 @@ post_sync: mutex_unlock(&start_mutex); } -int oprofile_set_backtrace(unsigned long val) +int oprofile_set_ulong(unsigned long *addr, unsigned long val) { - int err = 0; + int err = -EBUSY; mutex_lock(&start_mutex); - - if (oprofile_started) { - err = -EBUSY; - goto out; + if (!oprofile_started) { + *addr = val; + err = 0; } - - if (!oprofile_ops.backtrace) { - err = -EINVAL; - goto out; - } - - oprofile_backtrace_depth = val; - -out: mutex_unlock(&start_mutex); + return err; } diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index 47e12cb..177b73d 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -37,7 +37,7 @@ void oprofile_create_files(struct super_block *sb, struct dentry *root); int oprofile_timer_init(struct oprofile_operations *ops); void oprofile_timer_exit(void); -int oprofile_set_backtrace(unsigned long depth); +int oprofile_set_ulong(unsigned long *addr, unsigned long val); int oprofile_set_timeout(unsigned long time); #endif /* OPROF_H */ diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c index bbd7516..ccf099e 100644 --- a/drivers/oprofile/oprofile_files.c +++ b/drivers/oprofile/oprofile_files.c @@ -79,14 +79,17 @@ static ssize_t depth_write(struct file *file, char const __user *buf, size_t cou if (*offset) return -EINVAL; + if (!oprofile_ops.backtrace) + return -EINVAL; + retval = oprofilefs_ulong_from_user(&val, buf, count); if (retval) return retval; - retval = oprofile_set_backtrace(val); - + retval = oprofile_set_ulong(&oprofile_backtrace_depth, val); if (retval) return retval; + return count; } diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c index 789a1a8..1944621 100644 --- a/drivers/oprofile/oprofilefs.c +++ b/drivers/oprofile/oprofilefs.c @@ -91,16 +91,20 @@ static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) { - unsigned long *value = file->private_data; + unsigned long value; int retval; if (*offset) return -EINVAL; - retval = oprofilefs_ulong_from_user(value, buf, count); + retval = oprofilefs_ulong_from_user(&value, buf, count); + if (retval) + return retval; + retval = oprofile_set_ulong(file->private_data, value); if (retval) return retval; + return count; } -- cgit v0.10.2 From 277dd984172b063497d2ff6cfa7f2355f13a292d Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Thu, 14 Oct 2010 11:31:43 -0400 Subject: oprofile: include platform_device.h to fix build break oprofile_perf.c needs to include platform_device.h Otherwise we get the following build break. CC arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.o arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:192: warning: 'struct platform_device' declared inside parameter list arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:192: warning: its scope is only this definition or declaration, which is probably not what you want arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:201: warning: 'struct platform_device' declared inside parameter list arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:210: error: variable 'oprofile_driver' has initializer but incomplete type arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:211: error: unknown field 'driver' specified in initializer arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:211: error: extra brace group at end of initializer arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:211: error: (near initialization for 'oprofile_driver') arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:213: warning: excess elements in struct initializer arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:213: warning: (near initialization for 'oprofile_driver') arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:214: error: unknown field 'resume' specified in initializer arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:214: warning: excess elements in struct initializer arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:214: warning: (near initialization for 'oprofile_driver') arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:215: error: unknown field 'suspend' specified in initializer arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:215: warning: excess elements in struct initializer arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:215: warning: (near initialization for 'oprofile_driver') arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c: In function 'init_driverfs': Signed-off-by: Anand Gadiyar Cc: Matt Fleming Cc: Will Deacon Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index 79c0005..e707a72 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -4,6 +4,7 @@ * Perf-events backend for OProfile. */ #include +#include #include #include -- cgit v0.10.2 From b3b3a9b63f2deacfd59137e3781211d21a568ca9 Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Thu, 14 Oct 2010 11:31:42 -0400 Subject: oprofile: fix linker errors Commit e9677b3ce (oprofile, ARM: Use oprofile_arch_exit() to cleanup on failure) caused oprofile_perf_exit to be called in the cleanup path of oprofile_perf_init. The __exit tag for oprofile_perf_exit should therefore be dropped. The same has to be done for exit_driverfs as well, as this function is called from oprofile_perf_exit. Else, we get the following two linker errors. LD .tmp_vmlinux1 `oprofile_perf_exit' referenced in section `.init.text' of arch/arm/oprofile/built-in.o: defined in discarded section `.exit.text' of arch/arm/oprofile/built-in.o make: *** [.tmp_vmlinux1] Error 1 LD .tmp_vmlinux1 `exit_driverfs' referenced in section `.text' of arch/arm/oprofile/built-in.o: defined in discarded section `.exit.text' of arch/arm/oprofile/built-in.o make: *** [.tmp_vmlinux1] Error 1 Signed-off-by: Anand Gadiyar Cc: Will Deacon Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index e707a72..36ec67e 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -236,7 +236,7 @@ static int __init init_driverfs(void) return ret; } -static void __exit exit_driverfs(void) +static void exit_driverfs(void) { platform_device_unregister(oprofile_pdev); platform_driver_unregister(&oprofile_driver); diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index d67a833..32fb812 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -188,7 +188,7 @@ int oprofile_write_commit(struct op_entry *entry); #ifdef CONFIG_PERF_EVENTS int __init oprofile_perf_init(struct oprofile_operations *ops); -void __exit oprofile_perf_exit(void); +void oprofile_perf_exit(void); char *op_name_from_perf_id(void); #endif /* CONFIG_PERF_EVENTS */ -- cgit v0.10.2 From cd254f295248c98b62ea824f361e10d80a081fe7 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 15 Oct 2010 11:28:07 +0200 Subject: oprofile: make !CONFIG_PM function stubs static inline Make !CONFIG_PM function stubs static inline and remove section attribute. Signed-off-by: Robert Richter diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index 36ec67e..9046f7b 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -190,6 +190,7 @@ static void oprofile_perf_stop(void) } #ifdef CONFIG_PM + static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state) { mutex_lock(&oprofile_perf_mutex); @@ -241,9 +242,12 @@ static void exit_driverfs(void) platform_device_unregister(oprofile_pdev); platform_driver_unregister(&oprofile_driver); } + #else -static int __init init_driverfs(void) { return 0; } -#define exit_driverfs() do { } while (0) + +static inline int init_driverfs(void) { return 0; } +static inline void exit_driverfs(void) { } + #endif /* CONFIG_PM */ void oprofile_perf_exit(void) -- cgit v0.10.2