Commit 34df7e4e authored by David Mosberger's avatar David Mosberger

ia64: First draft of perfmon sampling-interval randomization support.

parent 5321ba97
...@@ -193,10 +193,12 @@ typedef enum { ...@@ -193,10 +193,12 @@ typedef enum {
*/ */
typedef struct { typedef struct {
u64 val; /* virtual 64bit counter value */ u64 val; /* virtual 64bit counter value */
u64 ival; /* initial value from user */ u64 lval; /* last value */
u64 long_reset; /* reset value on sampling overflow */ u64 long_reset; /* reset value on sampling overflow */
u64 short_reset;/* reset value on overflow */ u64 short_reset;/* reset value on overflow */
u64 reset_pmds[4]; /* which other pmds to reset when this counter overflows */ u64 reset_pmds[4]; /* which other pmds to reset when this counter overflows */
u64 seed; /* seed for random-number generator */
u64 mask; /* mask for random-number generator */
int flags; /* notify/do not notify */ int flags; /* notify/do not notify */
} pfm_counter_t; } pfm_counter_t;
...@@ -1150,13 +1152,32 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -1150,13 +1152,32 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
return ret; return ret;
} }
static inline unsigned long
new_counter_value (pfm_counter_t *reg, int is_long_reset)
{
unsigned long val = is_long_reset ? reg->long_reset : reg->short_reset;
unsigned long new_seed, old_seed = reg->seed, mask = reg->mask;
extern unsigned long carta_random32 (unsigned long seed);
if (reg->flags & PFM_REGFL_RANDOM) {
new_seed = carta_random32(old_seed);
val -= (old_seed & mask); /* counter values are negative numbers! */
if ((mask >> 32) != 0)
/* construct a full 64-bit random value: */
new_seed |= carta_random32(old_seed >> 32) << 32;
reg->seed = new_seed;
}
reg->lval = val;
return val;
}
static void static void
pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag) pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
{ {
unsigned long mask = ovfl_regs[0]; unsigned long mask = ovfl_regs[0];
unsigned long reset_others = 0UL; unsigned long reset_others = 0UL;
unsigned long val; unsigned long val;
int i; int i, is_long_reset = (flag & PFM_RELOAD_LONG_RESET);
DBprintk(("masks=0x%lx\n", mask)); DBprintk(("masks=0x%lx\n", mask));
...@@ -1166,15 +1187,11 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag) ...@@ -1166,15 +1187,11 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
mask >>= PMU_FIRST_COUNTER; mask >>= PMU_FIRST_COUNTER;
for(i = PMU_FIRST_COUNTER; mask; i++, mask >>= 1) { for(i = PMU_FIRST_COUNTER; mask; i++, mask >>= 1) {
if (mask & 0x1) { if (mask & 0x1) {
val = flag == PFM_RELOAD_LONG_RESET ? val = new_counter_value(ctx->ctx_soft_pmds + i, is_long_reset);
ctx->ctx_soft_pmds[i].long_reset:
ctx->ctx_soft_pmds[i].short_reset;
reset_others |= ctx->ctx_soft_pmds[i].reset_pmds[0]; reset_others |= ctx->ctx_soft_pmds[i].reset_pmds[0];
DBprintk(("[%d] %s reset soft_pmd[%d]=%lx\n", DBprintk(("[%d] %s reset soft_pmd[%d]=%lx\n", current->pid,
current->pid, is_long_reset ? "long" : "short", i, val));
flag == PFM_RELOAD_LONG_RESET ? "long" : "short", i, val));
/* upper part is ignored on rval */ /* upper part is ignored on rval */
pfm_write_soft_counter(ctx, i, val); pfm_write_soft_counter(ctx, i, val);
...@@ -1188,19 +1205,15 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag) ...@@ -1188,19 +1205,15 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
if ((reset_others & 0x1) == 0) continue; if ((reset_others & 0x1) == 0) continue;
val = flag == PFM_RELOAD_LONG_RESET ? val = new_counter_value(ctx->ctx_soft_pmds + i, is_long_reset);
ctx->ctx_soft_pmds[i].long_reset:
ctx->ctx_soft_pmds[i].short_reset;
if (PMD_IS_COUNTING(i)) { if (PMD_IS_COUNTING(i)) {
pfm_write_soft_counter(ctx, i, val); pfm_write_soft_counter(ctx, i, val);
} else { } else {
ia64_set_pmd(i, val); ia64_set_pmd(i, val);
} }
DBprintk(("[%d] %s reset_others pmd[%d]=%lx\n", current->pid,
DBprintk(("[%d] %s reset_others pmd[%d]=%lx\n", is_long_reset ? "long" : "short", i, val));
current->pid,
flag == PFM_RELOAD_LONG_RESET ? "long" : "short", i, val));
} }
ia64_srlz_d(); ia64_srlz_d();
/* just in case ! */ /* just in case ! */
...@@ -1283,6 +1296,9 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1283,6 +1296,9 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
ctx->ctx_soft_pmds[cnum].reset_pmds[1] = tmp.reg_reset_pmds[1]; ctx->ctx_soft_pmds[cnum].reset_pmds[1] = tmp.reg_reset_pmds[1];
ctx->ctx_soft_pmds[cnum].reset_pmds[2] = tmp.reg_reset_pmds[2]; ctx->ctx_soft_pmds[cnum].reset_pmds[2] = tmp.reg_reset_pmds[2];
ctx->ctx_soft_pmds[cnum].reset_pmds[3] = tmp.reg_reset_pmds[3]; ctx->ctx_soft_pmds[cnum].reset_pmds[3] = tmp.reg_reset_pmds[3];
if (tmp.reg_flags & PFM_REGFL_RANDOM)
ctx->ctx_soft_pmds[cnum].flags |= PFM_REGFL_RANDOM;
} }
/* /*
* execute write checker, if any * execute write checker, if any
...@@ -1369,11 +1385,12 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1369,11 +1385,12 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
/* update virtualized (64bits) counter */ /* update virtualized (64bits) counter */
if (PMD_IS_COUNTING(cnum)) { if (PMD_IS_COUNTING(cnum)) {
ctx->ctx_soft_pmds[cnum].ival = tmp.reg_value; ctx->ctx_soft_pmds[cnum].lval = tmp.reg_value;
ctx->ctx_soft_pmds[cnum].val = tmp.reg_value & ~pmu_conf.perf_ovfl_val; ctx->ctx_soft_pmds[cnum].val = tmp.reg_value & ~pmu_conf.perf_ovfl_val;
ctx->ctx_soft_pmds[cnum].long_reset = tmp.reg_long_reset; ctx->ctx_soft_pmds[cnum].long_reset = tmp.reg_long_reset;
ctx->ctx_soft_pmds[cnum].short_reset = tmp.reg_short_reset; ctx->ctx_soft_pmds[cnum].short_reset = tmp.reg_short_reset;
ctx->ctx_soft_pmds[cnum].seed = tmp.reserved[0];
ctx->ctx_soft_pmds[cnum].mask = tmp.reserved[1];
} }
/* /*
* execute write checker, if any * execute write checker, if any
...@@ -2554,7 +2571,7 @@ pfm_record_sample(struct task_struct *task, pfm_context_t *ctx, unsigned long ov ...@@ -2554,7 +2571,7 @@ pfm_record_sample(struct task_struct *task, pfm_context_t *ctx, unsigned long ov
*/ */
h->pid = current->pid; h->pid = current->pid;
h->cpu = smp_processor_id(); h->cpu = smp_processor_id();
h->rate = 0; /* XXX: add the sampling rate used here */ h->last_reset_value = ovfl_mask ? ctx->ctx_soft_pmds[ffz(~ovfl_mask)].lval : 0;
h->ip = regs ? regs->cr_iip : 0x0; /* where did the fault happened */ h->ip = regs ? regs->cr_iip : 0x0; /* where did the fault happened */
h->regs = ovfl_mask; /* which registers overflowed */ h->regs = ovfl_mask; /* which registers overflowed */
...@@ -3769,8 +3786,8 @@ pfm_inherit(struct task_struct *task, struct pt_regs *regs) ...@@ -3769,8 +3786,8 @@ pfm_inherit(struct task_struct *task, struct pt_regs *regs)
m = nctx->ctx_used_pmds[0] >> PMU_FIRST_COUNTER; m = nctx->ctx_used_pmds[0] >> PMU_FIRST_COUNTER;
for(i = PMU_FIRST_COUNTER ; m ; m>>=1, i++) { for(i = PMU_FIRST_COUNTER ; m ; m>>=1, i++) {
if ((m & 0x1) && pmu_conf.pmd_desc[i].type == PFM_REG_COUNTING) { if ((m & 0x1) && pmu_conf.pmd_desc[i].type == PFM_REG_COUNTING) {
nctx->ctx_soft_pmds[i].val = nctx->ctx_soft_pmds[i].ival & ~pmu_conf.perf_ovfl_val; nctx->ctx_soft_pmds[i].val = nctx->ctx_soft_pmds[i].lval & ~pmu_conf.perf_ovfl_val;
thread->pmd[i] = nctx->ctx_soft_pmds[i].ival & pmu_conf.perf_ovfl_val; thread->pmd[i] = nctx->ctx_soft_pmds[i].lval & pmu_conf.perf_ovfl_val;
} }
/* what about the other pmds? zero or keep as is */ /* what about the other pmds? zero or keep as is */
......
...@@ -15,6 +15,7 @@ obj-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ ...@@ -15,6 +15,7 @@ obj-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \
obj-$(CONFIG_ITANIUM) += copy_page.o copy_user.o memcpy.o obj-$(CONFIG_ITANIUM) += copy_page.o copy_user.o memcpy.o
obj-$(CONFIG_MCKINLEY) += copy_page_mck.o memcpy_mck.o obj-$(CONFIG_MCKINLEY) += copy_page_mck.o memcpy_mck.o
obj-$(CONFIG_PERFMON) += carta_random.o
IGNORE_FLAGS_OBJS = __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ IGNORE_FLAGS_OBJS = __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \
__divdi3.o __udivdi3.o __moddi3.o __umoddi3.o __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o
......
/*
* Fast, simple, yet decent quality random number generator based on
* a paper by David G. Carta ("Two Fast Implementations of the
* `Minimal Standard' Random Number Generator," Communications of the
* ACM, January, 1990).
*
* Copyright (C) 2002 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com>
*/
#include <asm/asmmacro.h>
#define a r2
#define m r3
#define lo r8
#define hi r9
#define t0 r16
#define t1 r17
#define seed r32
GLOBAL_ENTRY(carta_random32)
movl a = (16807 << 16) | 16807
;;
pmpyshr2.u t0 = a, seed, 0
pmpyshr2.u t1 = a, seed, 16
;;
unpack2.l t0 = t1, t0
dep m = -1, r0, 0, 31
;;
zxt4 lo = t0
shr.u hi = t0, 32
;;
dep t0 = 0, hi, 15, 49 // t0 = (hi & 0x7fff)
;;
shl t0 = t0, 16 // t0 = (hi & 0x7fff) << 16
shr t1 = hi, 15 // t1 = (hi >> 15)
;;
add lo = lo, t0
;;
cmp.gtu p6, p0 = lo, m
;;
(p6) and lo = lo, m
;;
(p6) add lo = 1, lo
;;
add lo = lo, t1
;;
cmp.gtu p6, p0 = lo, m
;;
(p6) and lo = lo, m
;;
(p6) add lo = 1, lo
br.ret.sptk.many rp
END(carta_random32)
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
* PMC flags * PMC flags
*/ */
#define PFM_REGFL_OVFL_NOTIFY 0x1 /* send notification on overflow */ #define PFM_REGFL_OVFL_NOTIFY 0x1 /* send notification on overflow */
#define PFM_REGFL_RANDOM 0x2 /* randomize sampling interval */
/* /*
* PMD/PMC/IBR/DBR return flags (ignored on input) * PMD/PMC/IBR/DBR return flags (ignored on input)
...@@ -132,28 +133,28 @@ typedef struct { ...@@ -132,28 +133,28 @@ typedef struct {
#define PFM_VERSION_MINOR(x) ((x) & 0xffff) #define PFM_VERSION_MINOR(x) ((x) & 0xffff)
/* /*
* Entry header in the sampling buffer. * Entry header in the sampling buffer. The header is directly followed
* The header is directly followed with the PMDS saved in increasing index * with the PMDs saved in increasing index order: PMD4, PMD5, .... How
* order: PMD4, PMD5, .... How many PMDs are present is determined by the * many PMDs are present is determined by the user program during
* user program during context creation. * context creation.
* *
* XXX: in this version of the entry, only up to 64 registers can be recorded * XXX: in this version of the entry, only up to 64 registers can be
* This should be enough for quite some time. Always check sampling format * recorded. This should be enough for quite some time. Always check
* before parsing entries! * sampling format before parsing entries!
* *
* Inn the case where multiple counters have overflowed at the same time, the * In the case where multiple counters overflow at the same time, the
* rate field indicate the initial value of the first PMD, based on the index. * last_reset_value member indicates the initial value of the PMD with
* For instance, if PMD2 and PMD5 have ovewrflowed for this entry, the rate field * the smallest index. For instance, if PMD2 and PMD5 have overflowed,
* will show the initial value of PMD2. * the last_reset_value member contains the initial value of PMD2.
*/ */
typedef struct { typedef struct {
int pid; /* identification of process */ int pid; /* identification of process */
int cpu; /* which cpu was used */ int cpu; /* which cpu was used */
unsigned long rate; /* initial value of overflowed counter */ unsigned long last_reset_value; /* initial value of counter that overflowed */
unsigned long stamp; /* timestamp */ unsigned long stamp; /* timestamp */
unsigned long ip; /* where did the overflow interrupt happened */ unsigned long ip; /* where did the overflow interrupt happened */
unsigned long regs; /* bitmask of which registers overflowed */ unsigned long regs; /* bitmask of which registers overflowed */
unsigned long period; /* sampling period used by overflowed counter (smallest pmd index) */ unsigned long period; /* unused */
} perfmon_smpl_entry_t; } perfmon_smpl_entry_t;
extern int perfmonctl(pid_t pid, int cmd, void *arg, int narg); extern int perfmonctl(pid_t pid, int cmd, void *arg, int narg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment