Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/perfmon/per-process-perf.c b/arch/arm/perfmon/per-process-perf.c
new file mode 100644
index 0000000..c8bebd8
--- /dev/null
+++ b/arch/arm/perfmon/per-process-perf.c
@@ -0,0 +1,1251 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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
+}