[Feature]add MT2731_MP2_MR2_SVN388 baseline version
Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/devtools/met-driver/4.19/mt2712/sampler.c b/src/devtools/met-driver/4.19/mt2712/sampler.c
new file mode 100644
index 0000000..dca6883
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sampler.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/kernel.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0 /* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h" /* for using kernel perf PMU driver */
+#include "cpu_pmu_v2.h" /* for using kernel perf PMU v2 driver */
+#include "met_kernel_symbol.h"
+
+#undef DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define dbg_met_tag_oneshot met_tag_oneshot_real
+#else
+#define dbg_met_tag_oneshot met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define dbg_met_tag_oneshot(class_id, name, value) ({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+
+static int pmu_profiling_version = 0;
+
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static int preferred_cpu_list[] = { 0, 4, 1, 2, 3, 5, 6, 7 };
+
+int get_pmu_profiling_version()
+{
+ return pmu_profiling_version;
+}
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+ if (cpu_map & (1 << preferred_cpu_list[i]))
+ return preferred_cpu_list[i];
+ }
+
+ return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+ int cpu;
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+ cpu = smp_processor_id();
+ if (met_cpu_ptr->cpu != cpu) {
+ /* panic("ERROR"); */
+ return;
+ }
+
+ /* sync_samples(cpu); */
+ /* don't re-add the work if we're shutting down */
+ if (met_cpu_ptr->work_enabled)
+ schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+ int cpu;
+ int *count;
+ unsigned long long stamp;
+ struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+ struct metdevice *c;
+#if defined(DEBUG_CPU_NOTIFY)
+ char msg[32];
+#endif
+
+ cpu = smp_processor_id();
+#if defined(DEBUG_CPU_NOTIFY)
+ {
+ char msg[32];
+
+ snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+ dbg_met_tag_oneshot(0, msg, 1);
+ }
+#endif
+
+ if (met_cpu_ptr->cpu != cpu) {
+ /* panic("ERROR2"); */
+ dbg_met_tag_oneshot(0, msg, -3);
+ return HRTIMER_NORESTART;
+ }
+
+ list_for_each_entry(c, &met_list, list) {
+ if (c->ondiemet_mode == 0) {
+ if ((c->mode == 0) || (c->timed_polling == NULL))
+ continue;
+ } else if (c->ondiemet_mode == 1) {
+ if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+ continue;
+ } else if (c->ondiemet_mode == 2) {
+ if ((c->mode == 0) || ((c->timed_polling == NULL)
+ && (c->ondiemet_timed_polling == NULL)))
+ continue;
+ }
+
+ count = per_cpu_ptr(c->polling_count, cpu);
+ if ((*count) > 0) {
+ (*count)--;
+ continue;
+ }
+
+ *(count) = c->polling_count_reload;
+
+ stamp = cpu_clock(cpu);
+
+ if (c->cpu_related == 0) {
+ if (cpu == curr_polling_cpu) {
+ if (c->ondiemet_mode == 0) {
+ c->timed_polling(stamp, 0);
+ } else if (c->ondiemet_mode == 1) {
+ c->ondiemet_timed_polling(stamp, 0);
+ } else if (c->ondiemet_mode == 2) {
+ if (c->timed_polling)
+ c->timed_polling(stamp, 0);
+ if (c->ondiemet_timed_polling)
+ c->ondiemet_timed_polling(stamp, 0);
+ }
+ }
+ } else {
+ if (c->ondiemet_mode == 0) {
+ c->timed_polling(stamp, cpu);
+ } else if (c->ondiemet_mode == 1) {
+ c->ondiemet_timed_polling(stamp, cpu);
+ } else if (c->ondiemet_mode == 2) {
+ if (c->timed_polling)
+ c->timed_polling(stamp, 0);
+ if (c->ondiemet_timed_polling)
+ c->ondiemet_timed_polling(stamp, 0);
+ }
+ }
+ }
+
+ if (met_cpu_ptr->hrtimer_online_check) {
+ online_cpu_map |= (1 << cpu);
+ met_cpu_ptr->hrtimer_online_check = 0;
+ dbg_met_tag_oneshot(0, "met_online check done", cpu);
+ if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+ curr_polling_cpu = cpu;
+ dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+ }
+ }
+
+ if (met_cpu_ptr->work_enabled) {
+ hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+ dbg_met_tag_oneshot(0, msg, 0);
+ return HRTIMER_RESTART;
+ }
+ dbg_met_tag_oneshot(0, msg, 0);
+ return HRTIMER_NORESTART;
+}
+
+static void __met_hrtimer_start(void *unused)
+{
+ struct met_cpu_struct *met_cpu_ptr = NULL;
+ struct hrtimer *hrtimer = NULL;
+ /* struct delayed_work *dw; */
+ struct metdevice *c;
+
+ met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if defined(DEBUG_CPU_NOTIFY)
+ {
+ char msg[32];
+
+ snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+ dbg_met_tag_oneshot(0, msg, 1);
+ }
+#endif
+ /*
+ * do not open HRtimer when EVENT timer enable
+ */
+// if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+ hrtimer = &met_cpu_ptr->hrtimer;
+ /* dw = &met_cpu_ptr->dwork; */
+
+ hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer->function = met_hrtimer_notify;
+// }
+
+ list_for_each_entry(c, &met_list, list) {
+ if (c->ondiemet_mode == 0) {
+ if ((c->cpu_related) && (c->mode) && (c->start))
+ c->start();
+ } else if (c->ondiemet_mode == 1) {
+ if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+ c->ondiemet_start();
+ } else if (c->ondiemet_mode == 2) {
+ if ((c->cpu_related) && (c->mode) && (c->start))
+ c->start();
+ if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+ c->ondiemet_start();
+ }
+ }
+ /*
+ * do not open HRtimer when EVENT timer enable
+ */
+// if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+ if (DEFAULT_HRTIMER_EXPIRE) {
+ met_cpu_ptr->work_enabled = 1;
+ /* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+ hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+ HRTIMER_MODE_REL_PINNED);
+ }
+// }
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+ struct met_cpu_struct *met_cpu_ptr;
+ struct hrtimer *hrtimer;
+ /* struct delayed_work *dw; */
+ struct metdevice *c;
+
+ met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if defined(DEBUG_CPU_NOTIFY)
+ {
+ char msg[32];
+
+ snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+ dbg_met_tag_oneshot(0, msg, 0);
+ }
+#endif
+ /*
+ * do not open HRtimer when EVENT timer enable
+ */
+// if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+ hrtimer = &met_cpu_ptr->hrtimer;
+ /* dw = &met_cpu_ptr->dwork; */
+
+ met_cpu_ptr->work_enabled = 0;
+ hrtimer_cancel(hrtimer);
+ /* cancel_delayed_work_sync(dw); */
+// }
+ list_for_each_entry(c, &met_list, list) {
+ if (c->ondiemet_mode == 0) {
+ if ((c->cpu_related) && (c->mode) && (c->stop))
+ c->stop();
+ } else if (c->ondiemet_mode == 1) {
+ if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+ c->ondiemet_stop();
+ } else if (c->ondiemet_mode == 2) {
+ if ((c->cpu_related) && (c->mode) && (c->stop))
+ c->stop();
+ if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+ c->ondiemet_stop();
+ }
+ }
+}
+
+static int met_pmu_cpu_notify(enum met_action action, unsigned int cpu)
+{
+ struct met_cpu_struct *met_cpu_ptr;
+ struct delayed_work *dw;
+ int preferred_polling_cpu;
+
+ if (start == 0)
+ return NOTIFY_OK;
+
+#if defined(DEBUG_CPU_NOTIFY)
+ {
+ char msg[32];
+
+ snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+ dbg_met_tag_oneshot(0, msg, action);
+ }
+#elif defined(PR_CPU_NOTIFY)
+ {
+ char msg[32];
+
+ if (met_cpu_notify) {
+ snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+ dbg_met_tag_oneshot(0, msg, action);
+ }
+ }
+#endif
+
+ if (cpu < 0 || cpu >= NR_CPUS)
+ return NOTIFY_OK;
+
+ switch (action) {
+ case MET_CPU_ONLINE:
+ met_cpu_ptr = &per_cpu(met_cpu, cpu);
+ met_cpu_ptr->hrtimer_online_check = 1;
+ dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+ if (cpu_related_cnt == 0) {
+ /*printk("%s, %d: curr_polling_cpu is alive = %d\n",
+ * __func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+ */
+
+ online_cpu_map |= (1 << cpu);
+
+ /* check curr_polling_cpu is alive, if it is down,
+ * start current cpu hrtimer, and change it to be currr_pollling_cpu
+ */
+ if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+ met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+ curr_polling_cpu = cpu;
+ }
+ } else
+ met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+
+ if (met_cpu_pmu_method != 0) {
+ if (pmu_profiling_version == 1)
+ met_perf_cpupmu_online(cpu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+ else if (pmu_profiling_version == 2)
+ met_perf_cpupmu_online_v2(cpu);
+#endif
+ }
+
+#ifdef CONFIG_CPU_FREQ
+ force_power_log(cpu);
+#endif
+ break;
+
+ case MET_CPU_OFFLINE:
+ online_cpu_map &= ~(1 << cpu);
+ dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+ if (cpu == curr_polling_cpu) {
+ /* printk("%s, %d: curr_polling_cpu %d is down\n",
+ * __func__, __LINE__, curr_polling_cpu);
+ */
+ preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+ /* printk("%s, %d: preferred_polling_cpu = %d\n",
+ * __func__, __LINE__, preferred_polling_cpu);
+ */
+ if (preferred_polling_cpu != -1) {
+ curr_polling_cpu = preferred_polling_cpu;
+ dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+ if (cpu_related_cnt == 0)
+ /* printk("%s, %d: start cpu %d hrtimer start\n",
+ * __func__, __LINE__, curr_polling_cpu);
+ */
+ met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+ }
+ }
+
+ met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+ if (met_cpu_pmu_method != 0) {
+ if (pmu_profiling_version == 1) {
+ per_cpu(perf_cpuid, cpu) = cpu;
+ met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down, (void *)&per_cpu(perf_cpuid, cpu), 1);
+ }
+#ifdef MET_SUPPORT_CPUPMU_V2
+ else if (pmu_profiling_version == 2) {
+ per_cpu(perf_cpuid, cpu) = cpu;
+ met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down_v2, (void *)&per_cpu(perf_cpuid, cpu), 1);
+ }
+#endif
+ }
+
+ met_cpu_ptr = &per_cpu(met_cpu, cpu);
+ dw = &met_cpu_ptr->dwork;
+ cancel_delayed_work_sync(dw);
+
+ /* sync_samples(cpu); */
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int _met_pmu_cpu_notify_online(unsigned int cpu)
+{
+ met_pmu_cpu_notify(MET_CPU_ONLINE, cpu);
+
+ return 0;
+}
+
+static int _met_pmu_cpu_notify_offline(unsigned int cpu)
+{
+ met_pmu_cpu_notify(MET_CPU_OFFLINE, cpu);
+
+ return 0;
+}
+
+int sampler_start(void)
+{
+ int ret, cpu;
+ struct met_cpu_struct *met_cpu_ptr;
+ struct metdevice *c;
+ int preferred_polling_cpu;
+
+ met_set_suspend_notify(0);
+
+#ifdef CONFIG_CPU_FREQ
+ force_power_log(POWER_LOG_ALL);
+#endif
+
+ for_each_possible_cpu(cpu) {
+ met_cpu_ptr = &per_cpu(met_cpu, cpu);
+ met_cpu_ptr->work_enabled = 0;
+ met_cpu_ptr->hrtimer_online_check = 0;
+ hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+ INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+ }
+
+ start = 0;
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "met:online",
+ _met_pmu_cpu_notify_online,
+ _met_pmu_cpu_notify_offline);
+
+ list_for_each_entry(c, &met_list, list) {
+
+ if (try_module_get(c->owner) == 0)
+ continue;
+#ifdef CONFIG_MET_ARM_32BIT
+ if (strcmp(c->name, "cpu") == 0) {
+ if ((c->mode) && (c->start)) {
+ pmu_profiling_version = 1;
+ cpu_related_cnt = 1;
+ if (met_cpu_pmu_method != 0)
+ met_perf_cpupmu_start();
+ else
+ c->start();
+ }
+ continue;
+ }
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+ if (strcmp(c->name, "cpu-pmu") == 0) {
+ if ((c->mode) && (c->start)) {
+ pmu_profiling_version = 2;
+ cpu_related_cnt = 1;
+ if (met_cpu_pmu_method != 0)
+ met_perf_cpupmu_start_v2();
+ else
+ c->start();
+ }
+ continue;
+ } else if (strcmp(c->name, "cpu") == 0) {
+ if ((c->mode) && (c->start)) {
+ pmu_profiling_version = 1;
+ cpu_related_cnt = 1;
+ if (met_cpu_pmu_method != 0)
+ met_perf_cpupmu_start();
+ else
+ c->start();
+ }
+ continue;
+ }
+#endif
+ if ((c->mode) && (c->cpu_related == 1))
+ cpu_related_cnt = 1;
+
+ if (c->ondiemet_mode == 0) {
+ if ((!(c->cpu_related)) && (c->mode) && (c->start))
+ c->start();
+ } else if (c->ondiemet_mode == 1) {
+ if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+ c->ondiemet_start();
+ } else if (c->ondiemet_mode == 2) {
+ if ((!(c->cpu_related)) && (c->mode) && (c->start))
+ c->start();
+ if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+ c->ondiemet_start();
+ }
+ }
+
+ get_online_cpus();
+ online_cpu_map = 0;
+ for_each_online_cpu(cpu) {
+ online_cpu_map |= (1 << cpu);
+ }
+ dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+ preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+ if (preferred_polling_cpu != -1)
+ curr_polling_cpu = preferred_polling_cpu;
+ dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+ start = 1;
+
+ if (cpu_related_cnt == 0)
+ met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+ else
+ on_each_cpu(__met_hrtimer_start, NULL, 1);
+ put_online_cpus();
+
+ return ret;
+}
+
+void sampler_stop(void)
+{
+ int cpu;
+ struct met_cpu_struct *met_cpu_ptr;
+ struct metdevice *c;
+ struct delayed_work *dw;
+
+ get_online_cpus();
+
+ on_each_cpu(__met_hrtimer_stop, NULL, 1);
+/* for_each_online_cpu(cpu) { */
+ for_each_possible_cpu(cpu) { /* Just for case */
+ met_cpu_ptr = &per_cpu(met_cpu, cpu);
+ dw = &met_cpu_ptr->dwork;
+ cancel_delayed_work_sync(dw);
+ /* sync_samples(cpu); */
+ }
+
+ start = 0;
+ put_online_cpus();
+
+ cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
+
+ list_for_each_entry(c, &met_list, list) {
+#ifdef CONFIG_MET_ARM_32BIT
+ if (strcmp(c->name, "cpu") == 0) {
+ pmu_profiling_version = 0;
+ if ((c->mode) && (c->stop)) {
+ if (met_cpu_pmu_method != 0)
+ met_perf_cpupmu_stop();
+ else
+ c->stop();
+ }
+ module_put(c->owner);
+ continue;
+ }
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+ if (strcmp(c->name, "cpu-pmu") == 0) {
+ pmu_profiling_version = 0;
+ if ((c->mode) && (c->stop)) {
+ if (met_cpu_pmu_method != 0)
+ met_perf_cpupmu_stop_v2();
+ else
+ c->stop();
+ }
+ module_put(c->owner);
+ continue;
+ }
+ else if (strcmp(c->name, "cpu") == 0) {
+ pmu_profiling_version = 0;
+ if ((c->mode) && (c->stop)) {
+ if (met_cpu_pmu_method != 0)
+ met_perf_cpupmu_stop();
+ else
+ c->stop();
+ }
+ module_put(c->owner);
+ continue;
+ }
+#endif
+ if (c->ondiemet_mode == 0) {
+ if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+ c->stop();
+ } else if (c->ondiemet_mode == 1) {
+ if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+ c->ondiemet_stop();
+ } else if (c->ondiemet_mode == 2) {
+ if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+ c->stop();
+ if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+ c->ondiemet_stop();
+ }
+ module_put(c->owner);
+ }
+
+ cpu_related_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+ MET_SUSPEND = 1,
+ MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+ switch (op) {
+ case MET_SUSPEND:
+ MET_TRACE("C|0|MET_SUSPEND|1");
+ break;
+ case MET_RESUME:
+ MET_TRACE("C|0|MET_SUSPEND|0");
+ break;
+ }
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+ struct metdevice *c;
+
+ met_set_suspend_notify(1);
+ /* tracing_mark_write(MET_SUSPEND); */
+// tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+ if (start == 0)
+ return 0;
+
+ list_for_each_entry(c, &met_list, list) {
+ if (c->suspend)
+ c->suspend();
+ }
+
+ /* get current COUNT */
+ MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+ return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+ struct metdevice *c;
+
+ /* get current COUNT */
+ MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+ /* tracing_mark_write(MET_RESUME); */
+// tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+ if (start == 0)
+ return;
+
+ list_for_each_entry(c, &met_list, list) {
+ if (c->resume)
+ c->resume();
+ }
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+ unsigned long long stamp;
+ struct metdevice *c;
+ int cpu = -1;
+
+ if (start == 0)
+ return;
+
+ cpu = smp_processor_id();
+ list_for_each_entry(c, &met_list, list) {
+ stamp = local_clock();
+
+ if (c->prev_stamp == 0)
+ c->prev_stamp = stamp;
+
+ /* Critical Section Start */
+ /* try spinlock to prevent a event print twice between config time interval */
+ if (!spin_trylock(&(c->my_lock)))
+ continue;
+
+ /*
+ * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+ * sample_rate == 0 --> always print
+ * sample_rate == 1000 --> print interval larger than 1 ms
+ */
+ if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+ spin_unlock(&(c->my_lock));
+ continue;
+ }
+
+ c->prev_stamp = stamp;
+ spin_unlock(&(c->my_lock));
+ /* Critical Section End */
+
+ if ((c->mode == 0) || (c->timed_polling == NULL))
+ continue;
+
+ stamp = local_clock();
+ c->timed_polling(stamp, cpu);
+ }
+}
+