patch-2.3.51 linux/arch/ia64/kernel/perfmon.c

Next file: linux/arch/ia64/kernel/process.c
Previous file: linux/arch/ia64/kernel/pci.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.50/linux/arch/ia64/kernel/perfmon.c linux/arch/ia64/kernel/perfmon.c
@@ -1,15 +1,53 @@
-#include <linux/config.h>
+/*
+ * This file contains the code to configure and read/write the ia64 performance
+ * monitoring stuff.
+ *
+ * Originaly Written by Ganesh Venkitachalam, IBM Corp.
+ * Modifications by David Mosberger-Tang, Hewlett-Packard Co.
+ * Copyright (C) 1999 Ganesh Venkitachalam <venkitac@us.ibm.com>
+ * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
 
 #include <asm/errno.h>
-#include <asm/irq.h>
+#include <asm/hw_irq.h>
 #include <asm/processor.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
+/* Long blurb on how this works: 
+ * We set dcr.pp, psr.pp, and the appropriate pmc control values with
+ * this.  Notice that we go about modifying _each_ task's pt_regs to
+ * set cr_ipsr.pp.  This will start counting when "current" does an
+ * _rfi_. Also, since each task's cr_ipsr.pp, and cr_ipsr is inherited
+ * across forks, we do _not_ need additional code on context
+ * switches. On stopping of the counters we dont need to go about
+ * changing every task's cr_ipsr back to where it wuz, because we can
+ * just set pmc[0]=1. But we do it anyways becuase we will probably
+ * add thread specific accounting later.
+ *
+ * The obvious problem with this is that on SMP systems, it is a bit
+ * of work (when someone wants to do it:-)) - it would be easier if we
+ * just added code to the context-switch path, but if we wanted to support
+ * per-thread accounting, the context-switch path might be long unless 
+ * we introduce a flag in the task_struct. Right now, the following code 
+ * will NOT work correctly on MP (for more than one reason:-)).
+ *
+ * The short answer is that to make this work on SMP,  we would need 
+ * to lock the run queue to ensure no context switches, send 
+ * an IPI to each processor, and in that IPI handler, set processor regs,
+ * and just modify the psr bit of only the _current_ thread, since we have 
+ * modified the psr bit correctly in the kernel stack for every process 
+ * which is not running. Also, we need pmd arrays per-processor, and 
+ * the READ_PMD command will need to get values off of other processors. 
+ * IPIs are the answer, irrespective of what the question is. Might 
+ * crash on SMP systems without the lock_kernel().
+ */
+
 #ifdef CONFIG_PERFMON
 
 #define MAX_PERF_COUNTER	4	/* true for Itanium, at least */
@@ -22,33 +60,12 @@
 
 struct perfmon_counter {
         unsigned long data;
-        int counter_num;
+        unsigned long counter_num;
 };
 
 unsigned long pmds[MAX_PERF_COUNTER];
-struct task_struct *perf_owner;
+struct task_struct *perf_owner=NULL;
 
-/*
- * We set dcr.pp, psr.pp, and the appropriate pmc control values with
- * this.  Notice that we go about modifying _each_ task's pt_regs to
- * set cr_ipsr.pp.  This will start counting when "current" does an
- * _rfi_. Also, since each task's cr_ipsr.pp, and cr_ipsr is inherited
- * across forks, we do _not_ need additional code on context
- * switches. On stopping of the counters we dont _need_ to go about
- * changing every task's cr_ipsr back to where it wuz, because we can
- * just set pmc[0]=1. But we do it anyways becuase we will probably
- * add thread specific accounting later.
- *
- * The obvious problem with this is that on SMP systems, it is a bit
- * of work (when someone wants to do it) - it would be easier if we
- * just added code to the context-switch path.  I think we would need
- * to lock the run queue to ensure no context switches, send an IPI to
- * each processor, and in that IPI handler, just modify the psr bit of
- * only the _current_ thread, since we have modified the psr bit
- * correctly in the kernel stack for every process which is not
- * running.  Might crash on SMP systems without the
- * lock_kernel(). Hence the lock..
- */
 asmlinkage unsigned long
 sys_perfmonctl (int cmd1, int cmd2, void *ptr)
 {
@@ -66,7 +83,7 @@
 		if (!access_ok(VERIFY_READ, cptr, sizeof(struct perf_counter)*cmd2))
 			return -EFAULT;
 
-		if (cmd2 >= MAX_PERF_COUNTER)
+		if (cmd2 > MAX_PERF_COUNTER)
 			return -EFAULT;
 
 		if (perf_owner && perf_owner != current)
@@ -91,15 +108,12 @@
 			/*
 			 * This is a no can do.  It obviously wouldn't
 			 * work on SMP where another process may not
-			 * be blocked at all.
-			 *
-			 * Perhaps we need a global predicate in the
-			 * leave_kernel path to control if pp should
-			 * be on or off?
+			 * be blocked at all. We need to put in a  perfmon 
+			 * IPI to take care of MP systems. See blurb above.
 			 */
 			lock_kernel();
 			for_each_task(p) {
-				regs = (struct pt_regs *) (((char *)p) + IA64_STK_OFFSET) - 1;
+				regs = (struct pt_regs *) (((char *)p) + IA64_STK_OFFSET) -1 ;	
 				ia64_psr(regs)->pp = 1;
 			}
 			unlock_kernel();
@@ -108,12 +122,18 @@
                 break;
 
 	      case READ_PMDS:
-		if (cmd2 >= MAX_PERF_COUNTER)
+		if (cmd2 > MAX_PERF_COUNTER)
 			return -EFAULT;
 		if (!access_ok(VERIFY_WRITE, cptr, sizeof(struct perf_counter)*cmd2))
 			return -EFAULT;
+
+		/* This looks shady, but IMHO this will work fine. This is  
+		 * the sequence that I could come up with to avoid races
+		 * with the interrupt handler. See explanation in the 
+		 * following comment.
+		 */
+
 		local_irq_save(flags);
-		/* XXX this looks wrong */
 		__asm__ __volatile__("rsm psr.pp\n");
 		dcr = ia64_get_dcr();
 		dcr &= ~IA64_DCR_PP;
@@ -121,23 +141,23 @@
 		local_irq_restore(flags);
 
 		/*
-		 * We cannot touch pmc[0] to stop counting here, as
+		 * We cannot write to pmc[0] to stop counting here, as
 		 * that particular instruction might cause an overflow
-		 * and the mask in pmc[0] might get lost. I'm not very
+		 * and the mask in pmc[0] might get lost. I'm _not_ 
 		 * sure of the hardware behavior here. So we stop
 		 * counting by psr.pp = 0. And we reset dcr.pp to
 		 * prevent an interrupt from mucking up psr.pp in the
 		 * meanwhile. Perfmon interrupts are pended, hence the
-		 * above code should be ok if one of the above
-		 * instructions cause overflows. Is this ok?  When I
-		 * muck with dcr, is the cli/sti needed??
+		 * above code should be ok if one of the above instructions 
+		 * caused overflows, i.e the interrupt should get serviced
+		 * when we re-enabled interrupts. When I muck with dcr, 
+		 * is the irq_save/restore needed?
 		 */
-		for (i = 0, cnum = 4; i < MAX_PERF_COUNTER; i++, cnum++, cptr++) {
+		for (i = 0, cnum = 4;i < MAX_PERF_COUNTER; i++, cnum++, cptr++){
 			pmd = pmds[i] + (ia64_get_pmd(cnum) & PERF_OVFL_VAL);
 			put_user(pmd, &cptr->data);
 		}
 		local_irq_save(flags);
-		/* XXX this looks wrong */
 		__asm__ __volatile__("ssm psr.pp");
 		dcr = ia64_get_dcr();
 		dcr |= IA64_DCR_PP;
@@ -158,11 +178,8 @@
 		/*
 		 * This is a no can do.  It obviously wouldn't
 		 * work on SMP where another process may not
-		 * be blocked at all.
-		 *
-		 * Perhaps we need a global predicate in the
-		 * leave_kernel path to control if pp should
-		 * be on or off?
+		 * be blocked at all. We need to put in a  perfmon 
+		 * IPI to take care of MP systems. See blurb above.
 		 */
 		lock_kernel();
 		for_each_task(p) {
@@ -170,7 +187,7 @@
 			ia64_psr(regs)->pp = 0;
 		}
 		unlock_kernel();
-		perf_owner = 0;
+		perf_owner = NULL;
 		break;
 
 	      default:
@@ -184,12 +201,12 @@
 {
 	unsigned long mask, i, cnum, val;
 
-	mask = ia64_get_pmd(0) >> 4;
+	mask = ia64_get_pmc(0) >> 4;
 	for (i = 0, cnum = 4; i < MAX_PERF_COUNTER; cnum++, i++, mask >>= 1) {
 		if (mask & 0x1) 
 			val = PERF_OVFL_VAL;
 		else
-			/* since we got an interrupt, might as well clear every pmd. */
+		/* since we got an interrupt, might as well clear every pmd. */
 			val = ia64_get_pmd(cnum) & PERF_OVFL_VAL;
 		pmds[i] += val;
 		ia64_set_pmd(cnum, 0);
@@ -214,10 +231,10 @@
 	}
 	ia64_set_pmv(PERFMON_IRQ);
 	ia64_srlz_d();
+	printk("Initialized perfmon vector to %u\n",PERFMON_IRQ);
 }
 
 #else /* !CONFIG_PERFMON */
-
 asmlinkage unsigned long
 sys_perfmonctl (int cmd1, int cmd2, void *ptr)
 {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)