| /** |
| * Copyright (C) ARM Limited 2011-2013. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| */ |
| |
| // gator_hrtimer_gator.c is used if perf is not supported |
| // update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers |
| #if 0 |
| |
| // Note: perf Cortex support added in 2.6.35 and PERF_COUNT_SW_CPU_CLOCK/hrtimer broken on 2.6.35 and 2.6.36 |
| // not relevant as this code is not active until 3.0.0, but wanted to document the issue |
| |
| void (*callback)(void); |
| static int profiling_interval; |
| static DEFINE_PER_CPU(struct perf_event *, perf_hrtimer); |
| static DEFINE_PER_CPU(struct perf_event_attr *, perf_hrtimer_attr); |
| |
| static void gator_hrtimer_shutdown(void); |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) |
| static void hrtimer_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) |
| #else |
| static void hrtimer_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) |
| #endif |
| { |
| (*callback)(); |
| } |
| |
| static int gator_online_single_hrtimer(int cpu) |
| { |
| if (per_cpu(perf_hrtimer, cpu) != 0 || per_cpu(perf_hrtimer_attr, cpu) == 0) |
| return 0; |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) |
| per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler); |
| #else |
| per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler, 0); |
| #endif |
| if (IS_ERR(per_cpu(perf_hrtimer, cpu))) { |
| per_cpu(perf_hrtimer, cpu) = NULL; |
| return -1; |
| } |
| |
| if (per_cpu(perf_hrtimer, cpu)->state != PERF_EVENT_STATE_ACTIVE) { |
| perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); |
| per_cpu(perf_hrtimer, cpu) = NULL; |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void gator_hrtimer_online(int cpu) |
| { |
| if (gator_online_single_hrtimer(cpu) < 0) { |
| pr_debug("gator: unable to online the hrtimer on cpu%d\n", cpu); |
| } |
| } |
| |
| static void gator_hrtimer_offline(int cpu) |
| { |
| if (per_cpu(perf_hrtimer, cpu)) { |
| perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); |
| per_cpu(perf_hrtimer, cpu) = NULL; |
| } |
| } |
| |
| static int gator_hrtimer_init(int interval, void (*func)(void)) |
| { |
| u32 size = sizeof(struct perf_event_attr); |
| int cpu; |
| |
| callback = func; |
| |
| // calculate profiling interval |
| profiling_interval = 1000000000 / interval; |
| |
| for_each_present_cpu(cpu) { |
| per_cpu(perf_hrtimer, cpu) = 0; |
| per_cpu(perf_hrtimer_attr, cpu) = kmalloc(size, GFP_KERNEL); |
| if (per_cpu(perf_hrtimer_attr, cpu) == 0) { |
| gator_hrtimer_shutdown(); |
| return -1; |
| } |
| |
| memset(per_cpu(perf_hrtimer_attr, cpu), 0, size); |
| per_cpu(perf_hrtimer_attr, cpu)->type = PERF_TYPE_SOFTWARE; |
| per_cpu(perf_hrtimer_attr, cpu)->size = size; |
| per_cpu(perf_hrtimer_attr, cpu)->config = PERF_COUNT_SW_CPU_CLOCK; |
| per_cpu(perf_hrtimer_attr, cpu)->sample_period = profiling_interval; |
| per_cpu(perf_hrtimer_attr, cpu)->pinned = 1; |
| } |
| |
| return 0; |
| } |
| |
| static void gator_hrtimer_shutdown(void) |
| { |
| int cpu; |
| |
| for_each_present_cpu(cpu) { |
| if (per_cpu(perf_hrtimer_attr, cpu)) { |
| kfree(per_cpu(perf_hrtimer_attr, cpu)); |
| per_cpu(perf_hrtimer_attr, cpu) = NULL; |
| } |
| } |
| } |
| |
| #endif |