diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-11-23 02:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-11-23 02:00:47 +0000 |
commit | 06615f62b17d7de6e12d2f5ec6b88cf30af08413 (patch) | |
tree | 8766f208847d4876a6db619aebbf54d53b76eb44 /arch/ia64/kernel/perfmon.c | |
parent | fa9bdb574f4febb751848a685d9a9017e04e1d53 (diff) |
Merge with Linux 2.4.0-test10.
Diffstat (limited to 'arch/ia64/kernel/perfmon.c')
-rw-r--r-- | arch/ia64/kernel/perfmon.c | 236 |
1 files changed, 179 insertions, 57 deletions
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 752b2a9a1..e5efbc8b5 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -10,15 +10,19 @@ #include <linux/config.h> #include <linux/kernel.h> +#include <linux/init.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/smp_lock.h> +#include <linux/proc_fs.h> +#include <linux/ptrace.h> #include <asm/errno.h> #include <asm/hw_irq.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/uaccess.h> +#include <asm/pal.h> /* Long blurb on how this works: * We set dcr.pp, psr.pp, and the appropriate pmc control values with @@ -52,68 +56,107 @@ #ifdef CONFIG_PERFMON #define MAX_PERF_COUNTER 4 /* true for Itanium, at least */ +#define PMU_FIRST_COUNTER 4 /* first generic counter */ + #define WRITE_PMCS_AND_START 0xa0 #define WRITE_PMCS 0xa1 #define READ_PMDS 0xa2 #define STOP_PMCS 0xa3 -#define IA64_COUNTER_MASK 0xffffffffffffff6fL -#define PERF_OVFL_VAL 0xffffffffL -volatile int used_by_system; -struct perfmon_counter { - unsigned long data; - unsigned long counter_num; -}; +/* + * this structure needs to be enhanced + */ +typedef struct { + unsigned long pmu_reg_data; /* generic PMD register */ + unsigned long pmu_reg_num; /* which register number */ +} perfmon_reg_t; + +/* + * This structure is initialize at boot time and contains + * a description of the PMU main characteristic as indicated + * by PAL + */ +typedef struct { + unsigned long perf_ovfl_val; /* overflow value for generic counters */ + unsigned long max_pmc; /* highest PMC */ + unsigned long max_pmd; /* highest PMD */ + unsigned long max_counters; /* number of generic counter pairs (PMC/PMD) */ +} pmu_config_t; + +/* XXX will go static when ptrace() is cleaned */ +unsigned long perf_ovfl_val; /* overflow value for generic counters */ + +static pmu_config_t pmu_conf; +/* + * could optimize to avoid cache conflicts in SMP + */ unsigned long pmds[NR_CPUS][MAX_PERF_COUNTER]; asmlinkage unsigned long -sys_perfmonctl (int cmd1, int cmd2, void *ptr) +sys_perfmonctl (int cmd, int count, void *ptr, long arg4, long arg5, long arg6, long arg7, long arg8, long stack) { - struct perfmon_counter tmp, *cptr = ptr; - unsigned long cnum, dcr, flags; - struct perf_counter; + struct pt_regs *regs = (struct pt_regs *) &stack; + perfmon_reg_t tmp, *cptr = ptr; + unsigned long cnum; int i; - switch (cmd1) { + switch (cmd) { case WRITE_PMCS: /* Writes to PMC's and clears PMDs */ case WRITE_PMCS_AND_START: /* Also starts counting */ - if (cmd2 <= 0 || cmd2 > MAX_PERF_COUNTER - used_by_system) - return -EINVAL; - - if (!access_ok(VERIFY_READ, cptr, sizeof(struct perf_counter)*cmd2)) + if (!access_ok(VERIFY_READ, cptr, sizeof(struct perfmon_reg_t)*count)) return -EFAULT; - current->thread.flags |= IA64_THREAD_PM_VALID; + for (i = 0; i < count; i++, cptr++) { - for (i = 0; i < cmd2; i++, cptr++) { copy_from_user(&tmp, cptr, sizeof(tmp)); - /* XXX need to check validity of counter_num and perhaps data!! */ - if (tmp.counter_num < 4 - || tmp.counter_num >= 4 + MAX_PERF_COUNTER - used_by_system) - return -EFAULT; - - ia64_set_pmc(tmp.counter_num, tmp.data); - ia64_set_pmd(tmp.counter_num, 0); - pmds[smp_processor_id()][tmp.counter_num - 4] = 0; + + /* XXX need to check validity of pmu_reg_num and perhaps data!! */ + + if (tmp.pmu_reg_num > pmu_conf.max_pmc || tmp.pmu_reg_num == 0) return -EFAULT; + + ia64_set_pmc(tmp.pmu_reg_num, tmp.pmu_reg_data); + + /* to go away */ + if (tmp.pmu_reg_num >= PMU_FIRST_COUNTER && tmp.pmu_reg_num < PMU_FIRST_COUNTER+pmu_conf.max_counters) { + ia64_set_pmd(tmp.pmu_reg_num, 0); + pmds[smp_processor_id()][tmp.pmu_reg_num - PMU_FIRST_COUNTER] = 0; + + printk(__FUNCTION__" setting PMC/PMD[%ld] es=0x%lx pmd[%ld]=%lx\n", tmp.pmu_reg_num, (tmp.pmu_reg_data>>8) & 0x7f, tmp.pmu_reg_num, ia64_get_pmd(tmp.pmu_reg_num)); + } else + printk(__FUNCTION__" setting PMC[%ld]=0x%lx\n", tmp.pmu_reg_num, tmp.pmu_reg_data); } - if (cmd1 == WRITE_PMCS_AND_START) { + if (cmd == WRITE_PMCS_AND_START) { +#if 0 +/* irrelevant with user monitors */ local_irq_save(flags); + dcr = ia64_get_dcr(); dcr |= IA64_DCR_PP; ia64_set_dcr(dcr); + local_irq_restore(flags); +#endif + ia64_set_pmc(0, 0); + + /* will start monitoring right after rfi */ + ia64_psr(regs)->up = 1; } + /* + * mark the state as valid. + * this will trigger save/restore at context switch + */ + current->thread.flags |= IA64_THREAD_PM_VALID; break; case READ_PMDS: - if (cmd2 <= 0 || cmd2 > MAX_PERF_COUNTER - used_by_system) + if (count <= 0 || count > MAX_PERF_COUNTER) return -EINVAL; - if (!access_ok(VERIFY_WRITE, cptr, sizeof(struct perf_counter)*cmd2)) + if (!access_ok(VERIFY_WRITE, cptr, sizeof(struct perfmon_reg_t)*count)) return -EFAULT; /* This looks shady, but IMHO this will work fine. This is @@ -121,14 +164,15 @@ sys_perfmonctl (int cmd1, int cmd2, void *ptr) * with the interrupt handler. See explanation in the * following comment. */ - +#if 0 +/* irrelevant with user monitors */ local_irq_save(flags); __asm__ __volatile__("rsm psr.pp\n"); dcr = ia64_get_dcr(); dcr &= ~IA64_DCR_PP; ia64_set_dcr(dcr); local_irq_restore(flags); - +#endif /* * We cannot write to pmc[0] to stop counting here, as * that particular instruction might cause an overflow @@ -142,36 +186,47 @@ sys_perfmonctl (int cmd1, int cmd2, void *ptr) * when we re-enabled interrupts. When I muck with dcr, * is the irq_save/restore needed? */ - for (i = 0, cnum = 4;i < cmd2; i++, cnum++, cptr++) { - tmp.data = (pmds[smp_processor_id()][i] - + (ia64_get_pmd(cnum) & PERF_OVFL_VAL)); - tmp.counter_num = cnum; - if (copy_to_user(cptr, &tmp, sizeof(tmp))) - return -EFAULT; - //put_user(pmd, &cptr->data); + + + /* XXX: This needs to change to read more than just the counters */ + for (i = 0, cnum = PMU_FIRST_COUNTER;i < count; i++, cnum++, cptr++) { + + tmp.pmu_reg_data = (pmds[smp_processor_id()][i] + + (ia64_get_pmd(cnum) & pmu_conf.perf_ovfl_val)); + + tmp.pmu_reg_num = cnum; + + if (copy_to_user(cptr, &tmp, sizeof(tmp))) return -EFAULT; } +#if 0 +/* irrelevant with user monitors */ local_irq_save(flags); __asm__ __volatile__("ssm psr.pp"); dcr = ia64_get_dcr(); dcr |= IA64_DCR_PP; ia64_set_dcr(dcr); local_irq_restore(flags); +#endif break; case STOP_PMCS: ia64_set_pmc(0, 1); ia64_srlz_d(); - for (i = 0; i < MAX_PERF_COUNTER - used_by_system; ++i) + for (i = 0; i < MAX_PERF_COUNTER; ++i) ia64_set_pmc(4+i, 0); - if (!used_by_system) { - local_irq_save(flags); - dcr = ia64_get_dcr(); - dcr &= ~IA64_DCR_PP; - ia64_set_dcr(dcr); - local_irq_restore(flags); - } +#if 0 +/* irrelevant with user monitors */ + local_irq_save(flags); + dcr = ia64_get_dcr(); + dcr &= ~IA64_DCR_PP; + ia64_set_dcr(dcr); + local_irq_restore(flags); + ia64_psr(regs)->up = 0; +#endif + current->thread.flags &= ~(IA64_THREAD_PM_VALID); + break; default: @@ -187,13 +242,21 @@ update_counters (void) unsigned long mask, i, cnum, val; mask = ia64_get_pmc(0) >> 4; - for (i = 0, cnum = 4; i < MAX_PERF_COUNTER - used_by_system; cnum++, i++, mask >>= 1) { - val = 0; + for (i = 0, cnum = PMU_FIRST_COUNTER ; i < pmu_conf.max_counters; cnum++, i++, mask >>= 1) { + + + val = mask & 0x1 ? pmu_conf.perf_ovfl_val + 1 : 0; + if (mask & 0x1) - val += PERF_OVFL_VAL + 1; + printk(__FUNCTION__ " PMD%ld overflowed pmd=%lx pmod=%lx\n", cnum, ia64_get_pmd(cnum), pmds[smp_processor_id()][i]); + /* since we got an interrupt, might as well clear every pmd. */ - val += ia64_get_pmd(cnum) & PERF_OVFL_VAL; + val += ia64_get_pmd(cnum) & pmu_conf.perf_ovfl_val; + + printk(__FUNCTION__ " adding val=%lx to pmod[%ld]=%lx \n", val, i, pmds[smp_processor_id()][i]); + pmds[smp_processor_id()][i] += val; + ia64_set_pmd(cnum, 0); } } @@ -212,16 +275,69 @@ static struct irqaction perfmon_irqaction = { name: "perfmon" }; -void +static int +perfmon_proc_info(char *page) +{ + char *p = page; + u64 pmc0 = ia64_get_pmc(0); + + p += sprintf(p, "PMC[0]=%lx\n", pmc0); + + return p - page; +} + +static int +perfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = perfmon_proc_info(page); + + if (len <= off+count) *eof = 1; + + *start = page + off; + len -= off; + + if (len>count) len = count; + if (len<0) len = 0; + + return len; +} + +static struct proc_dir_entry *perfmon_dir; + +void __init perfmon_init (void) { + pal_perf_mon_info_u_t pm_info; + u64 pm_buffer[16]; + s64 status; + irq_desc[PERFMON_IRQ].status |= IRQ_PER_CPU; irq_desc[PERFMON_IRQ].handler = &irq_type_ia64_sapic; setup_irq(PERFMON_IRQ, &perfmon_irqaction); ia64_set_pmv(PERFMON_IRQ); ia64_srlz_d(); - printk("Initialized perfmon vector to %u\n",PERFMON_IRQ); + + printk("perfmon: Initialized vector to %u\n",PERFMON_IRQ); + + if ((status=ia64_pal_perf_mon_info(pm_buffer, &pm_info)) != 0) { + printk(__FUNCTION__ " pal call failed (%ld)\n", status); + return; + } + pmu_conf.perf_ovfl_val = perf_ovfl_val = (1L << pm_info.pal_perf_mon_info_s.width) - 1; + + /* XXX need to use PAL instead */ + pmu_conf.max_pmc = 13; + pmu_conf.max_pmd = 17; + pmu_conf.max_counters = pm_info.pal_perf_mon_info_s.generic; + + printk("perfmon: Counters are %d bits\n", pm_info.pal_perf_mon_info_s.width); + printk("perfmon: Maximum counter value 0x%lx\n", pmu_conf.perf_ovfl_val); + + /* + * for now here for debug purposes + */ + perfmon_dir = create_proc_read_entry ("perfmon", 0, 0, perfmon_read_entry, NULL); } void @@ -238,10 +354,13 @@ ia64_save_pm_regs (struct thread_struct *t) ia64_set_pmc(0, 1); ia64_srlz_d(); - for (i=0; i< IA64_NUM_PM_REGS - used_by_system ; i++) { - t->pmd[i] = ia64_get_pmd(4+i); + /* + * XXX: this will need to be extended beyong just counters + */ + for (i=0; i< IA64_NUM_PM_REGS; i++) { + t->pmd[i] = ia64_get_pmd(4+i); t->pmod[i] = pmds[smp_processor_id()][i]; - t->pmc[i] = ia64_get_pmc(4+i); + t->pmc[i] = ia64_get_pmc(4+i); } } @@ -250,7 +369,10 @@ ia64_load_pm_regs (struct thread_struct *t) { int i; - for (i=0; i< IA64_NUM_PM_REGS - used_by_system ; i++) { + /* + * XXX: this will need to be extended beyong just counters + */ + for (i=0; i< IA64_NUM_PM_REGS ; i++) { ia64_set_pmd(4+i, t->pmd[i]); pmds[smp_processor_id()][i] = t->pmod[i]; ia64_set_pmc(4+i, t->pmc[i]); @@ -262,7 +384,7 @@ ia64_load_pm_regs (struct thread_struct *t) #else /* !CONFIG_PERFMON */ asmlinkage unsigned long -sys_perfmonctl (int cmd1, int cmd2, void *ptr) +sys_perfmonctl (int cmd, int count, void *ptr) { return -ENOSYS; } |