1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252 |
- /* Copyright (c) 2010, The Linux Foundation. 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 and
- * only 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.
- */
- /*
- per-process_perf
- DESCRIPTION
- Capture the processor performances registers when the process context
- switches. The /proc file system is used to control and access the results
- of the performance counters.
- Each time a process is context switched, the performance counters for
- the Snoop Control Unit and the standard ARM counters are set according
- to the values stored for that process.
- The events to capture per process are set in the /proc/ppPerf/settings
- directory.
- EXTERNALIZED FUNCTIONS
- INITIALIZATION AND SEQUENCING REQUIREMENTS
- Detail how to initialize and use this service. The sequencing aspect
- is only needed if the order of operations is important.
- */
- /*
- INCLUDE FILES FOR MODULE
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/sysrq.h>
- #include <linux/time.h>
- #include "linux/proc_fs.h"
- #include "linux/kernel_stat.h"
- #include <asm/thread_notify.h>
- #include "asm/uaccess.h"
- #include "cp15_registers.h"
- #include "l2_cp15_registers.h"
- #include <asm/perftypes.h>
- #include "per-axi.h"
- #include "perf.h"
- #define DEBUG_SWAPIO
- #ifdef DEBUG_SWAPIO
- #define MR_SIZE 1024
- #define PM_PP_ERR -1
- struct mark_data_s {
- long c;
- long cpu;
- unsigned long pid_old;
- unsigned long pid_new;
- };
- struct mark_data_s markRay[MR_SIZE] __attribute__((aligned(16)));
- int mrcnt;
- DEFINE_SPINLOCK(_mark_lock);
- static inline void MARKPIDS(char a, int opid, int npid)
- {
- int cpu = smp_processor_id();
- if (opid == 0)
- return;
- spin_lock(&_mark_lock);
- if (++mrcnt >= MR_SIZE)
- mrcnt = 0;
- spin_unlock(&_mark_lock);
- markRay[mrcnt].pid_old = opid;
- markRay[mrcnt].pid_new = npid;
- markRay[mrcnt].cpu = cpu;
- markRay[mrcnt].c = a;
- }
- static inline void MARK(char a) { MARKPIDS(a, 0xFFFF, 0xFFFF); }
- static inline void MARKPID(char a, int pid) { MARKPIDS(a, pid, 0xFFFF); }
- #else
- #define MARK(a)
- #define MARKPID(a, b)
- #define MARKPIDS(a, b, c)
- #endif /* DEBUG_SWAPIO */
- /*
- DEFINITIONS AND DECLARATIONS FOR MODULE
- This section contains definitions for constants, macros, types, variables
- and other items needed by this module.
- */
- /*
- Constant / Define Declarations
- */
- #define PERF_MON_PROCESS_NUM 0x400
- #define PERF_MON_PROCESS_MASK (PERF_MON_PROCESS_NUM-1)
- #define PP_MAX_PROC_ENTRIES 32
- /*
- * The entry is locked and is not to be replaced.
- */
- #define PERF_ENTRY_LOCKED (1<<0)
- #define PERF_NOT_FIRST_TIME (1<<1)
- #define PERF_EXITED (1<<2)
- #define PERF_AUTOLOCK (1<<3)
- #define IS_LOCKED(p) (p->flags & PERF_ENTRY_LOCKED)
- #define PERF_NUM_MONITORS 4
- #define L1_EVENTS_0 0
- #define L1_EVENTS_1 1
- #define L2_EVENTS_0 2
- #define L2_EVENTS_1 3
- #define PM_CYCLE_OVERFLOW_MASK 0x80000000
- #define L2_PM_CYCLE_OVERFLOW_MASK 0x80000000
- #define PM_START_ALL() do {\
- if (pm_global) \
- pmStartAll();\
- } while (0);
- #define PM_STOP_ALL() do {\
- if (pm_global)\
- pmStopAll();\
- } while (0);
- #define PM_RESET_ALL() do {\
- if (pm_global)\
- pmResetAll();\
- } while (0);
- /*
- * Accessors for SMP based variables.
- */
- #define _SWAPS(p) ((p)->cnts[smp_processor_id()].swaps)
- #define _CYCLES(p) ((p)->cnts[smp_processor_id()].cycles)
- #define _COUNTS(p, i) ((p)->cnts[smp_processor_id()].counts[i])
- #define _L2COUNTS(p, i) ((p)->cnts[smp_processor_id()].l2_counts[i])
- #define _L2CYCLES(p) ((p)->cnts[smp_processor_id()].l2_cycles)
- /*
- Type Declarations
- */
- /*
- * Counts are on a per core basis.
- */
- struct pm_counters_s {
- unsigned long long cycles;
- unsigned long long l2_cycles;
- unsigned long long counts[PERF_NUM_MONITORS];
- unsigned long long l2_counts[PERF_NUM_MONITORS];
- unsigned long swaps;
- };
- struct per_process_perf_mon_type{
- struct pm_counters_s cnts[NR_CPUS];
- unsigned long control;
- unsigned long index[PERF_NUM_MONITORS];
- unsigned long l2_index[PERF_NUM_MONITORS];
- unsigned long pid;
- struct proc_dir_entry *proc;
- struct proc_dir_entry *l2_proc;
- unsigned short flags;
- unsigned short running_cpu;
- char *pidName;
- unsigned long lpm0evtyper;
- unsigned long lpm1evtyper;
- unsigned long lpm2evtyper;
- unsigned long l2lpmevtyper;
- unsigned long vlpmevtyper;
- unsigned long l2pmevtyper0;
- unsigned long l2pmevtyper1;
- unsigned long l2pmevtyper2;
- unsigned long l2pmevtyper3;
- unsigned long l2pmevtyper4;
- };
- unsigned long last_in_pid[NR_CPUS];
- unsigned long fake_swap_out[NR_CPUS] = {0};
- /*
- Local Object Definitions
- */
- struct per_process_perf_mon_type perf_mons[PERF_MON_PROCESS_NUM];
- struct proc_dir_entry *proc_dir;
- struct proc_dir_entry *settings_dir;
- struct proc_dir_entry *values_dir;
- struct proc_dir_entry *axi_dir;
- struct proc_dir_entry *l2_dir;
- struct proc_dir_entry *axi_settings_dir;
- struct proc_dir_entry *axi_results_dir;
- struct proc_dir_entry *l2_results_dir;
- unsigned long pp_enabled;
- unsigned long pp_settings_valid = -1;
- unsigned long pp_auto_lock;
- unsigned long pp_set_pid;
- signed long pp_clear_pid = -1;
- unsigned long per_proc_event[PERF_NUM_MONITORS];
- unsigned long l2_per_proc_event[PERF_NUM_MONITORS];
- unsigned long dbg_flags;
- unsigned long pp_lpm0evtyper;
- unsigned long pp_lpm1evtyper;
- unsigned long pp_lpm2evtyper;
- unsigned long pp_l2lpmevtyper;
- unsigned long pp_vlpmevtyper;
- unsigned long pm_stop_for_interrupts;
- unsigned long pm_global; /* track all, not process based */
- unsigned long pm_global_enable;
- unsigned long pm_remove_pid;
- unsigned long pp_l2pmevtyper0;
- unsigned long pp_l2pmevtyper1;
- unsigned long pp_l2pmevtyper2;
- unsigned long pp_l2pmevtyper3;
- unsigned long pp_l2pmevtyper4;
- unsigned long pp_proc_entry_index;
- char *per_process_proc_names[PP_MAX_PROC_ENTRIES];
- unsigned int axi_swaps;
- #define MAX_AXI_SWAPS 10
- int first_switch = 1;
- /*
- Forward Declarations
- */
- /*
- Function Definitions
- */
- /*
- FUNCTION per_process_find
- DESCRIPTION
- Find the per process information based on the process id (pid) passed.
- This is a simple mask based on the number of entries stored in the
- static array
- DEPENDENCIES
- RETURN VALUE
- Pointer to the per process data
- SIDE EFFECTS
- */
- struct per_process_perf_mon_type *per_process_find(unsigned long pid)
- {
- return &perf_mons[pid & PERF_MON_PROCESS_MASK];
- }
- /*
- FUNCTION per_process_get_name
- DESCRIPTION
- Retreive the name of the performance counter based on the table and
- index passed. We have two different sets of performance counters so
- different table need to be used.
- DEPENDENCIES
- RETURN VALUE
- Pointer to char string with the name of the event or "BAD"
- Never returns NULL or a bad pointer.
- SIDE EFFECTS
- */
- char *per_process_get_name(unsigned long index)
- {
- return pm_find_event_name(index);
- }
- /*
- FUNCTION per_process_results_read
- DESCRIPTION
- Print out the formatted results from the process id read. Event names
- and counts are printed.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- int per_process_results_read(char *page, char **start, off_t off, int count,
- int *eof, void *data)
- {
- struct per_process_perf_mon_type *p =
- (struct per_process_perf_mon_type *)data;
- struct pm_counters_s cnts;
- int i, j;
- /*
- * Total across all CPUS
- */
- memset(&cnts, 0, sizeof(cnts));
- for (i = 0; i < num_possible_cpus(); i++) {
- cnts.swaps += p->cnts[i].swaps;
- cnts.cycles += p->cnts[i].cycles;
- for (j = 0; j < PERF_NUM_MONITORS; j++)
- cnts.counts[j] += p->cnts[i].counts[j];
- }
- /*
- * Display as single results of the totals calculated above.
- * Do we want to display or have option to display individula cores?
- */
- return sprintf(page, "pid:%lu one:%s:%llu two:%s:%llu three:%s:%llu \
- four:%s:%llu cycles:%llu swaps:%lu\n",
- p->pid,
- per_process_get_name(p->index[0]), cnts.counts[0],
- per_process_get_name(p->index[1]), cnts.counts[1],
- per_process_get_name(p->index[2]), cnts.counts[2],
- per_process_get_name(p->index[3]), cnts.counts[3],
- cnts.cycles, cnts.swaps);
- }
- int per_process_l2_results_read(char *page, char **start, off_t off, int count,
- int *eof, void *data)
- {
- struct per_process_perf_mon_type *p =
- (struct per_process_perf_mon_type *)data;
- struct pm_counters_s cnts;
- int i, j;
- /*
- * Total across all CPUS
- */
- memset(&cnts, 0, sizeof(cnts));
- for (i = 0; i < num_possible_cpus(); i++) {
- cnts.l2_cycles += p->cnts[i].l2_cycles;
- for (j = 0; j < PERF_NUM_MONITORS; j++)
- cnts.l2_counts[j] += p->cnts[i].l2_counts[j];
- }
- /*
- * Display as single results of the totals calculated above.
- * Do we want to display or have option to display individula cores?
- */
- return sprintf(page, "pid:%lu l2_one:%s:%llu l2_two:%s:%llu \
- l2_three:%s:%llu \
- l2_four:%s:%llu l2_cycles:%llu\n",
- p->pid,
- per_process_get_name(p->l2_index[0]), cnts.l2_counts[0],
- per_process_get_name(p->l2_index[1]), cnts.l2_counts[1],
- per_process_get_name(p->l2_index[2]), cnts.l2_counts[2],
- per_process_get_name(p->l2_index[3]), cnts.l2_counts[3],
- cnts.l2_cycles);
- }
- /*
- FUNCTION per_process_results_write
- DESCRIPTION
- Allow some control over the results. If the user forgets to autolock or
- wants to unlock the results so they will be deleted, then this is
- where it is processed.
- For example, to unlock process 23
- echo "unlock" > 23
- DEPENDENCIES
- RETURN VALUE
- Number of characters used (all of them!)
- SIDE EFFECTS
- */
- int per_process_results_write(struct file *file, const char *buff,
- unsigned long cnt, void *data)
- {
- char *newbuf;
- struct per_process_perf_mon_type *p =
- (struct per_process_perf_mon_type *)data;
- if (p == 0)
- return cnt;
- /*
- * Alloc the user data in kernel space. and then copy user to kernel
- */
- newbuf = kmalloc(cnt + 1, GFP_KERNEL);
- if (0 == newbuf)
- return cnt;
- if (copy_from_user(newbuf, buff, cnt) != 0) {
- printk(KERN_INFO "%s copy_from_user failed\n", __func__);
- return cnt;
- }
- if (0 == strcmp("lock", newbuf))
- p->flags |= PERF_ENTRY_LOCKED;
- else if (0 == strcmp("unlock", newbuf))
- p->flags &= ~PERF_ENTRY_LOCKED;
- else if (0 == strcmp("auto", newbuf))
- p->flags |= PERF_AUTOLOCK;
- else if (0 == strcmp("autoun", newbuf))
- p->flags &= ~PERF_AUTOLOCK;
- return cnt;
- }
- /*
- FUNCTION perProcessCreateResults
- DESCRIPTION
- Create the results /proc file if the system parameters allow it...
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- void per_process_create_results_proc(struct per_process_perf_mon_type *p)
- {
- if (0 == p->pidName)
- p->pidName = kmalloc(12, GFP_KERNEL);
- if (0 == p->pidName)
- return;
- sprintf(p->pidName, "%ld", p->pid);
- if (0 == p->proc) {
- p->proc = create_proc_entry(p->pidName, 0777, values_dir);
- if (0 == p->proc)
- return;
- } else {
- p->proc->name = p->pidName;
- }
- p->proc->read_proc = per_process_results_read;
- p->proc->write_proc = per_process_results_write;
- p->proc->data = (void *)p;
- }
- void per_process_create_l2_results_proc(struct per_process_perf_mon_type *p)
- {
- if (0 == p->pidName)
- p->pidName = kmalloc(12, GFP_KERNEL);
- if (0 == p->pidName)
- return;
- sprintf(p->pidName, "%ld", p->pid);
- if (0 == p->l2_proc) {
- p->l2_proc = create_proc_entry(p->pidName, 0777,
- l2_results_dir);
- if (0 == p->l2_proc)
- return;
- } else {
- p->l2_proc->name = p->pidName;
- }
- p->l2_proc->read_proc = per_process_l2_results_read;
- p->l2_proc->write_proc = per_process_results_write;
- p->l2_proc->data = (void *)p;
- }
- /*
- FUNCTION per_process_swap_out
- DESCRIPTION
- Store the counters from the process that is about to swap out. We take
- the old counts and add them to the current counts in the perf registers.
- Before the new process is swapped in, the counters are reset.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- typedef void (*vfun)(void *);
- void per_process_swap_out(struct per_process_perf_mon_type *data)
- {
- int i;
- unsigned long overflow;
- #ifdef CONFIG_ARCH_MSM8X60
- unsigned long l2_overflow;
- #endif
- struct per_process_perf_mon_type *p = data;
- MARKPIDS('O', p->pid, 0);
- RCP15_PMOVSR(overflow);
- #ifdef CONFIG_ARCH_MSM8X60
- RCP15_L2PMOVSR(l2_overflow);
- #endif
- if (!pp_enabled)
- return;
- /*
- * The kernel for some reason (2.6.32.9) starts a process context on
- * one core and ends on another. So the swap in and swap out can be
- * on different cores. If this happens, we need to stop the
- * counters and collect the data on the core that started the counters
- * ....otherwise we receive invalid data. So we mark the the core with
- * the process as deferred. The next time a process is swapped on
- * the core that the process was running on, the counters will be
- * updated.
- */
- if ((smp_processor_id() != p->running_cpu) && (p->pid != 0)) {
- fake_swap_out[p->running_cpu] = 1;
- return;
- }
- _SWAPS(p)++;
- _CYCLES(p) += pm_get_cycle_count();
- if (overflow & PM_CYCLE_OVERFLOW_MASK)
- _CYCLES(p) += 0xFFFFFFFF;
- for (i = 0; i < PERF_NUM_MONITORS; i++) {
- _COUNTS(p, i) += pm_get_count(i);
- if (overflow & (1 << i))
- _COUNTS(p, i) += 0xFFFFFFFF;
- }
- #ifdef CONFIG_ARCH_MSM8X60
- _L2CYCLES(p) += l2_pm_get_cycle_count();
- if (l2_overflow & L2_PM_CYCLE_OVERFLOW_MASK)
- _L2CYCLES(p) += 0xFFFFFFFF;
- for (i = 0; i < PERF_NUM_MONITORS; i++) {
- _L2COUNTS(p, i) += l2_pm_get_count(i);
- if (l2_overflow & (1 << i))
- _L2COUNTS(p, i) += 0xFFFFFFFF;
- }
- #endif
- }
- /*
- FUNCTION per_process_remove_manual
- DESCRIPTION
- Remove an entry from the results directory if the flags allow this.
- When not enbled or the entry is locked, the values/results will
- not be removed.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- void per_process_remove_manual(unsigned long pid)
- {
- struct per_process_perf_mon_type *p = per_process_find(pid);
- /*
- * Check all of the flags to see if we can remove this one
- * Then mark as not used
- */
- if (0 == p)
- return;
- p->pid = (0xFFFFFFFF);
- /*
- * Remove the proc entry.
- */
- if (p->proc)
- remove_proc_entry(p->pidName, values_dir);
- if (p->l2_proc)
- remove_proc_entry(p->pidName, l2_results_dir);
- kfree(p->pidName);
- /*
- * Clear them out...and ensure the pid is invalid
- */
- memset(p, 0, sizeof *p);
- p->pid = 0xFFFFFFFF;
- pm_remove_pid = -1;
- }
- /*
- * Remove called when a process exits...
- */
- void _per_process_remove(unsigned long pid) {}
- /*
- FUNCTION per_process_initialize
- DESCRIPTION
- Initialize performance collection information for a new process.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- May create a new proc entry
- */
- void per_process_initialize(struct per_process_perf_mon_type *p,
- unsigned long pid)
- {
- int i;
- /*
- * See if this is the pid we are interested in...
- */
- if (pp_settings_valid == -1)
- return;
- if ((pp_set_pid != pid) && (pp_set_pid != 0))
- return;
- /*
- * Clear out the statistics table then insert this pid
- * We want to keep the proc entry and the name
- */
- p->pid = pid;
- /*
- * Create a proc entry for this pid, then get the current event types and
- * store in data struct so when the process is switched in we can track
- * it.
- */
- if (p->proc == 0) {
- per_process_create_results_proc(p);
- #ifdef CONFIG_ARCH_MSM8X60
- per_process_create_l2_results_proc(p);
- #endif
- }
- _CYCLES(p) = 0;
- _L2CYCLES(p) = 0;
- _SWAPS(p) = 0;
- /*
- * Set the per process data struct, but not the monitors until later...
- * Init only happens with the user sets the SetPID variable to this pid
- * so we can load new values.
- */
- for (i = 0; i < PERF_NUM_MONITORS; i++) {
- p->index[i] = per_proc_event[i];
- #ifdef CONFIG_ARCH_MSM8X60
- p->l2_index[i] = l2_per_proc_event[i];
- #endif
- _COUNTS(p, i) = 0;
- _L2COUNTS(p, i) = 0;
- }
- p->lpm0evtyper = pp_lpm0evtyper;
- p->lpm1evtyper = pp_lpm1evtyper;
- p->lpm2evtyper = pp_lpm2evtyper;
- p->l2lpmevtyper = pp_l2lpmevtyper;
- p->vlpmevtyper = pp_vlpmevtyper;
- #ifdef CONFIG_ARCH_MSM8X60
- p->l2pmevtyper0 = pp_l2pmevtyper0;
- p->l2pmevtyper1 = pp_l2pmevtyper1;
- p->l2pmevtyper2 = pp_l2pmevtyper2;
- p->l2pmevtyper3 = pp_l2pmevtyper3;
- p->l2pmevtyper4 = pp_l2pmevtyper4;
- #endif
- /*
- * Reset pid and settings value
- */
- pp_set_pid = -1;
- pp_settings_valid = -1;
- }
- /*
- FUNCTION per_process_swap_in
- DESCRIPTION
- Called when a context switch is about to start this PID.
- We check to see if this process has an entry or not and create one
- if not locked...
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- void per_process_swap_in(struct per_process_perf_mon_type *p_new,
- unsigned long pid)
- {
- int i;
- MARKPIDS('I', p_new->pid, 0);
- /*
- * If the set proc variable == the current pid then init a new
- * entry...
- */
- if (pp_set_pid == pid)
- per_process_initialize(p_new, pid);
- p_new->running_cpu = smp_processor_id();
- last_in_pid[smp_processor_id()] = pid;
- /*
- * setup the monitors for this process.
- */
- for (i = 0; i < PERF_NUM_MONITORS; i++) {
- pm_set_event(i, p_new->index[i]);
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_set_event(i, p_new->l2_index[i]);
- #endif
- }
- pm_set_local_iu(p_new->lpm0evtyper);
- pm_set_local_xu(p_new->lpm1evtyper);
- pm_set_local_su(p_new->lpm2evtyper);
- pm_set_local_l2(p_new->l2lpmevtyper);
- #ifdef CONFIG_ARCH_MSM8X60
- pm_set_local_bu(p_new->l2pmevtyper0);
- pm_set_local_cb(p_new->l2pmevtyper1);
- pm_set_local_mp(p_new->l2pmevtyper2);
- pm_set_local_sp(p_new->l2pmevtyper3);
- pm_set_local_scu(p_new->l2pmevtyper4);
- #endif
- }
- /*
- FUNCTION perProcessSwitch
- DESCRIPTION
- Called during context switch. Updates the counts on the process about to
- be swapped out and brings in the counters for the process about to be
- swapped in.
- All is dependant on the enabled and lock flags.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- DEFINE_SPINLOCK(pm_lock);
- void _per_process_switch(unsigned long old_pid, unsigned long new_pid)
- {
- struct per_process_perf_mon_type *p_old, *p_new;
- if (pm_global_enable == 0)
- return;
- spin_lock(&pm_lock);
- pm_stop_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_stop_all();
- #endif
- /*
- * We detected that the process was swapped in on one core and out on
- * a different core. This does not allow us to stop and stop counters
- * properly so we need to defer processing. This checks to see if there
- * is any defered processing necessary. And does it... */
- if (fake_swap_out[smp_processor_id()] != 0) {
- fake_swap_out[smp_processor_id()] = 0;
- p_old = per_process_find(last_in_pid[smp_processor_id()]);
- last_in_pid[smp_processor_id()] = 0;
- if (p_old != 0)
- per_process_swap_out(p_old);
- }
- /*
- * Clear the data collected so far for this process?
- */
- if (pp_clear_pid != -1) {
- struct per_process_perf_mon_type *p_clear =
- per_process_find(pp_clear_pid);
- if (p_clear) {
- memset(p_clear->cnts, 0,
- sizeof(struct pm_counters_s)*num_possible_cpus());
- printk(KERN_INFO "Clear Per Processor Stats for \
- PID:%ld\n", pp_clear_pid);
- pp_clear_pid = -1;
- }
- }
- /*
- * Always collect for 0, it collects for all.
- */
- if (pp_enabled) {
- if (first_switch == 1) {
- per_process_initialize(&perf_mons[0], 0);
- first_switch = 0;
- }
- if (pm_global) {
- per_process_swap_out(&perf_mons[0]);
- per_process_swap_in(&perf_mons[0], 0);
- } else {
- p_old = per_process_find(old_pid);
- p_new = per_process_find(new_pid);
- /*
- * save the old counts to the old data struct, if the
- * returned ptr is NULL or the process id passed is not
- * the same as the process id in the data struct then
- * don't update the data.
- */
- if ((p_old) && (p_old->pid == old_pid) &&
- (p_old->pid != 0)) {
- per_process_swap_out(p_old);
- }
- /*
- * Setup the counters for the new process
- */
- if (pp_set_pid == new_pid)
- per_process_initialize(p_new, new_pid);
- if ((p_new->pid == new_pid) && (new_pid != 0))
- per_process_swap_in(p_new, new_pid);
- }
- pm_reset_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_reset_all();
- #endif
- #ifdef CONFIG_ARCH_QSD8X50
- axi_swaps++;
- if (axi_swaps%pm_axi_info.refresh == 0) {
- if (pm_axi_info.clear == 1) {
- pm_axi_clear_cnts();
- pm_axi_info.clear = 0;
- }
- if (pm_axi_info.enable == 0)
- pm_axi_disable();
- else
- pm_axi_update_cnts();
- axi_swaps = 0;
- }
- #endif
- }
- pm_start_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_start_all();
- #endif
- spin_unlock(&pm_lock);
- }
- /*
- FUNCTION pmInterruptIn
- DESCRIPTION
- Called when an interrupt is being processed. If the pmStopForInterrutps
- flag is non zero then we disable the counting of performance monitors.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- static int pm_interrupt_nesting_count;
- static unsigned long pm_cycle_in, pm_cycle_out;
- void _perf_mon_interrupt_in(void)
- {
- if (pm_global_enable == 0)
- return;
- if (pm_stop_for_interrupts == 0)
- return;
- pm_interrupt_nesting_count++; /* Atomic */
- pm_stop_all();
- pm_cycle_in = pm_get_cycle_count();
- }
- /*
- FUNCTION perfMonInterruptOut
- DESCRIPTION
- Reenable performance monitor counting whn the nest count goes to zero
- provided the counting has been stoped
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- void _perf_mon_interrupt_out(void)
- {
- if (pm_global_enable == 0)
- return;
- if (pm_stop_for_interrupts == 0)
- return;
- --pm_interrupt_nesting_count; /* Atomic?? */
- if (pm_interrupt_nesting_count <= 0) {
- pm_cycle_out = pm_get_cycle_count();
- if (pm_cycle_in != pm_cycle_out)
- printk(KERN_INFO "pmIn!=pmOut in:%lx out:%lx\n",
- pm_cycle_in, pm_cycle_out);
- if (pp_enabled) {
- pm_start_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_start_all();
- #endif
- }
- pm_interrupt_nesting_count = 0;
- }
- }
- void per_process_do_global(unsigned long g)
- {
- pm_global = g;
- if (pm_global == 1) {
- pm_stop_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_stop_all();
- #endif
- pm_reset_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_reset_all();
- #endif
- pp_set_pid = 0;
- per_process_swap_in(&perf_mons[0], 0);
- pm_start_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_start_all();
- #endif
- } else {
- pm_stop_all();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_stop_all();
- #endif
- }
- }
- /*
- FUNCTION per_process_write
- DESCRIPTION
- Generic routine to handle any of the settings /proc directory writes.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- int per_process_write(struct file *file, const char *buff,
- unsigned long cnt, void *data, const char *fmt)
- {
- char *newbuf;
- unsigned long *d = (unsigned long *)data;
- /*
- * Alloc the user data in kernel space. and then copy user to kernel
- */
- newbuf = kmalloc(cnt + 1, GFP_KERNEL);
- if (0 == newbuf)
- return PM_PP_ERR;
- if (copy_from_user(newbuf, buff, cnt) != 0) {
- printk(KERN_INFO "%s copy_from_user failed\n", __func__);
- return cnt;
- }
- sscanf(newbuf, fmt, d);
- kfree(newbuf);
- /*
- * If this is a remove command then do it now...
- */
- if (d == &pm_remove_pid)
- per_process_remove_manual(*d);
- if (d == &pm_global)
- per_process_do_global(*d);
- return cnt;
- }
- int per_process_write_dec(struct file *file, const char *buff,
- unsigned long cnt, void *data)
- {
- return per_process_write(file, buff, cnt, data, "%ld");
- }
- int per_process_write_hex(struct file *file, const char *buff,
- unsigned long cnt, void *data)
- {
- return per_process_write(file, buff, cnt, data, "%lx");
- }
- /*
- FUNCTION per_process_read
- DESCRIPTION
- Generic read handler for the /proc settings directory.
- DEPENDENCIES
- RETURN VALUE
- Number of characters to output.
- SIDE EFFECTS
- */
- int per_process_read(char *page, char **start, off_t off, int count,
- int *eof, void *data)
- {
- unsigned long *d = (unsigned long *)data;
- return sprintf(page, "%lx", *d);
- }
- int per_process_read_decimal(char *page, char **start, off_t off, int count,
- int *eof, void *data)
- {
- unsigned long *d = (unsigned long *)data;
- return sprintf(page, "%ld", *d);
- }
- /*
- FUNCTION per_process_proc_entry
- DESCRIPTION
- Create a generic entry for the /proc settings directory.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- void per_process_proc_entry(char *name, unsigned long *var,
- struct proc_dir_entry *d, int hex)
- {
- struct proc_dir_entry *pe;
- pe = create_proc_entry(name, 0777, d);
- if (0 == pe)
- return;
- if (hex) {
- pe->read_proc = per_process_read;
- pe->write_proc = per_process_write_hex;
- } else {
- pe->read_proc = per_process_read_decimal;
- pe->write_proc = per_process_write_dec;
- }
- pe->data = (void *)var;
- if (pp_proc_entry_index >= PP_MAX_PROC_ENTRIES) {
- printk(KERN_INFO "PERF: proc entry overflow,\
- memleak on module unload occured");
- return;
- }
- per_process_proc_names[pp_proc_entry_index++] = name;
- }
- static int perfmon_notifier(struct notifier_block *self, unsigned long cmd,
- void *v)
- {
- static int old_pid = -1;
- struct thread_info *thread = v;
- int current_pid;
- if (cmd != THREAD_NOTIFY_SWITCH)
- return old_pid;
- current_pid = thread->task->pid;
- if (old_pid != -1)
- _per_process_switch(old_pid, current_pid);
- old_pid = current_pid;
- return old_pid;
- }
- static struct notifier_block perfmon_notifier_block = {
- .notifier_call = perfmon_notifier,
- };
- /*
- FUNCTION per_process_perf_init
- DESCRIPTION
- Initialze the per process performance monitor variables and /proc space.
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- */
- int per_process_perf_init(void)
- {
- #ifdef CONFIG_ARCH_MSM8X60
- smp_call_function_single(0, (void *)pm_initialize, (void *)NULL, 1);
- smp_call_function_single(1, (void *)pm_initialize, (void *)NULL, 1);
- l2_pm_initialize();
- #else
- pm_initialize();
- #endif
- pm_axi_init();
- pm_axi_clear_cnts();
- proc_dir = proc_mkdir("ppPerf", NULL);
- values_dir = proc_mkdir("results", proc_dir);
- settings_dir = proc_mkdir("settings", proc_dir);
- per_process_proc_entry("enable", &pp_enabled, settings_dir, 1);
- per_process_proc_entry("valid", &pp_settings_valid, settings_dir, 1);
- per_process_proc_entry("setPID", &pp_set_pid, settings_dir, 0);
- per_process_proc_entry("clearPID", &pp_clear_pid, settings_dir, 0);
- per_process_proc_entry("event0", &per_proc_event[0], settings_dir, 1);
- per_process_proc_entry("event1", &per_proc_event[1], settings_dir, 1);
- per_process_proc_entry("event2", &per_proc_event[2], settings_dir, 1);
- per_process_proc_entry("event3", &per_proc_event[3], settings_dir, 1);
- per_process_proc_entry("l2_event0", &l2_per_proc_event[0], settings_dir,
- 1);
- per_process_proc_entry("l2_event1", &l2_per_proc_event[1], settings_dir,
- 1);
- per_process_proc_entry("l2_event2", &l2_per_proc_event[2], settings_dir,
- 1);
- per_process_proc_entry("l2_event3", &l2_per_proc_event[3], settings_dir,
- 1);
- per_process_proc_entry("debug", &dbg_flags, settings_dir, 1);
- per_process_proc_entry("autolock", &pp_auto_lock, settings_dir, 1);
- per_process_proc_entry("lpm0evtyper", &pp_lpm0evtyper, settings_dir, 1);
- per_process_proc_entry("lpm1evtyper", &pp_lpm1evtyper, settings_dir, 1);
- per_process_proc_entry("lpm2evtyper", &pp_lpm2evtyper, settings_dir, 1);
- per_process_proc_entry("l2lpmevtyper", &pp_l2lpmevtyper, settings_dir,
- 1);
- per_process_proc_entry("vlpmevtyper", &pp_vlpmevtyper, settings_dir, 1);
- per_process_proc_entry("l2pmevtyper0", &pp_l2pmevtyper0, settings_dir,
- 1);
- per_process_proc_entry("l2pmevtyper1", &pp_l2pmevtyper1, settings_dir,
- 1);
- per_process_proc_entry("l2pmevtyper2", &pp_l2pmevtyper2, settings_dir,
- 1);
- per_process_proc_entry("l2pmevtyper3", &pp_l2pmevtyper3, settings_dir,
- 1);
- per_process_proc_entry("l2pmevtyper4", &pp_l2pmevtyper4, settings_dir,
- 1);
- per_process_proc_entry("stopForInterrupts", &pm_stop_for_interrupts,
- settings_dir, 1);
- per_process_proc_entry("global", &pm_global, settings_dir, 1);
- per_process_proc_entry("globalEnable", &pm_global_enable, settings_dir,
- 1);
- per_process_proc_entry("removePID", &pm_remove_pid, settings_dir, 0);
- axi_dir = proc_mkdir("axi", proc_dir);
- axi_settings_dir = proc_mkdir("settings", axi_dir);
- axi_results_dir = proc_mkdir("results", axi_dir);
- pm_axi_set_proc_entry("axi_enable", &pm_axi_info.enable,
- axi_settings_dir, 1);
- pm_axi_set_proc_entry("axi_clear", &pm_axi_info.clear, axi_settings_dir,
- 0);
- pm_axi_set_proc_entry("axi_valid", &pm_axi_info.valid, axi_settings_dir,
- 1);
- pm_axi_set_proc_entry("axi_sel_reg0", &pm_axi_info.sel_reg0,
- axi_settings_dir, 1);
- pm_axi_set_proc_entry("axi_sel_reg1", &pm_axi_info.sel_reg1,
- axi_settings_dir, 1);
- pm_axi_set_proc_entry("axi_ten_sel", &pm_axi_info.ten_sel_reg,
- axi_settings_dir, 1);
- pm_axi_set_proc_entry("axi_refresh", &pm_axi_info.refresh,
- axi_settings_dir, 1);
- pm_axi_get_cnt_proc_entry("axi_cnts", &axi_cnts, axi_results_dir, 0);
- l2_dir = proc_mkdir("l2", proc_dir);
- l2_results_dir = proc_mkdir("results", l2_dir);
- memset(perf_mons, 0, sizeof(perf_mons));
- per_process_create_results_proc(&perf_mons[0]);
- per_process_create_l2_results_proc(&perf_mons[0]);
- thread_register_notifier(&perfmon_notifier_block);
- /*
- * Set the function pointers so the module can be activated.
- */
- pp_interrupt_out_ptr = _perf_mon_interrupt_out;
- pp_interrupt_in_ptr = _perf_mon_interrupt_in;
- pp_process_remove_ptr = _per_process_remove;
- pp_loaded = 1;
- pm_axi_info.refresh = 1;
- #ifdef CONFIG_ARCH_MSM8X60
- smp_call_function_single(0, (void *)pm_reset_all, (void *)NULL, 1);
- smp_call_function_single(1, (void *)pm_reset_all, (void *)NULL, 1);
- smp_call_function_single(0, (void *)l2_pm_reset_all, (void *)NULL, 1);
- smp_call_function_single(1, (void *)l2_pm_reset_all, (void *)NULL, 1);
- #else
- pm_reset_all();
- #endif
- return 0;
- }
- /*
- FUNCTION per_process_perf_exit
- DESCRIPTION
- Module exit functionm, clean up, renmove proc entries
- DEPENDENCIES
- RETURN VALUE
- SIDE EFFECTS
- No more per process
- */
- void per_process_perf_exit(void)
- {
- unsigned long i;
- /*
- * Sert the function pointers to 0 so the functions will no longer
- * be invoked
- */
- pp_loaded = 0;
- pp_interrupt_out_ptr = 0;
- pp_interrupt_in_ptr = 0;
- pp_process_remove_ptr = 0;
- /*
- * Remove the results
- */
- for (i = 0; i < PERF_MON_PROCESS_NUM; i++)
- per_process_remove_manual(perf_mons[i].pid);
- /*
- * Remove the proc entries in the settings dir
- */
- i = 0;
- for (i = 0; i < pp_proc_entry_index; i++)
- remove_proc_entry(per_process_proc_names[i], settings_dir);
- /*remove proc axi files*/
- remove_proc_entry("axi_enable", axi_settings_dir);
- remove_proc_entry("axi_valid", axi_settings_dir);
- remove_proc_entry("axi_refresh", axi_settings_dir);
- remove_proc_entry("axi_clear", axi_settings_dir);
- remove_proc_entry("axi_sel_reg0", axi_settings_dir);
- remove_proc_entry("axi_sel_reg1", axi_settings_dir);
- remove_proc_entry("axi_ten_sel", axi_settings_dir);
- remove_proc_entry("axi_cnts", axi_results_dir);
- /*
- * Remove the directories
- */
- remove_proc_entry("results", l2_dir);
- remove_proc_entry("l2", proc_dir);
- remove_proc_entry("results", proc_dir);
- remove_proc_entry("settings", proc_dir);
- remove_proc_entry("results", axi_dir);
- remove_proc_entry("settings", axi_dir);
- remove_proc_entry("axi", proc_dir);
- remove_proc_entry("ppPerf", NULL);
- pm_free_irq();
- #ifdef CONFIG_ARCH_MSM8X60
- l2_pm_free_irq();
- #endif
- thread_unregister_notifier(&perfmon_notifier_block);
- #ifdef CONFIG_ARCH_MSM8X60
- smp_call_function_single(0, (void *)pm_deinitialize, (void *)NULL, 1);
- smp_call_function_single(1, (void *)pm_deinitialize, (void *)NULL, 1);
- l2_pm_deinitialize();
- #else
- pm_deinitialize();
- #endif
- }
|