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
- Lines: 222
- Date:
Fri Mar 10 15:24:02 2000
- Orig file:
v2.3.50/linux/arch/ia64/kernel/perfmon.c
- Orig date:
Thu Feb 10 17:11:03 2000
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)