📄 perfmon.c
字号:
task->cpus_allowed = tmp.ctx_cpu_mask; task->need_resched = 1; DBprintk(("[%d] rescheduled allowed=0x%lx\n", task->pid, task->cpus_allowed)); } return 0;buffer_error: pfm_context_free(ctx);error: pfm_unreserve_session(task, ctx_flags & PFM_FL_SYSTEM_WIDE , tmp.ctx_cpu_mask);abort: /* make sure we don't leave anything behind */ task->thread.pfm_context = NULL; return ret;}static inline unsigned longpfm_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 voidpfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag){ unsigned long mask = ovfl_regs[0]; unsigned long reset_others = 0UL; unsigned long val; int i, is_long_reset = (flag == PFM_PMD_LONG_RESET); /* * now restore reset value on sampling overflowed counters */ mask >>= PMU_FIRST_COUNTER; for(i = PMU_FIRST_COUNTER; mask; i++, mask >>= 1) { if (mask & 0x1) { val = pfm_new_counter_value(ctx->ctx_soft_pmds + i, is_long_reset); reset_others |= ctx->ctx_soft_pmds[i].reset_pmds[0]; DBprintk_ovfl(("[%d] %s reset soft_pmd[%d]=%lx\n", current->pid, is_long_reset ? "long" : "short", i, val)); /* upper part is ignored on rval */ pfm_write_soft_counter(ctx, i, val); } } /* * Now take care of resetting the other registers */ for(i = 0; reset_others; i++, reset_others >>= 1) { if ((reset_others & 0x1) == 0) continue; val = pfm_new_counter_value(ctx->ctx_soft_pmds + i, is_long_reset); if (PMD_IS_COUNTING(i)) { pfm_write_soft_counter(ctx, i, val); } else { ia64_set_pmd(i, val); } DBprintk_ovfl(("[%d] %s reset_others pmd[%d]=%lx\n", current->pid, is_long_reset ? "long" : "short", i, val)); } ia64_srlz_d();}static intpfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ struct thread_struct *th = &task->thread; pfarg_reg_t tmp, *req = (pfarg_reg_t *)arg; unsigned long value, reset_pmds; unsigned int cnum, reg_flags, flags; int is_monitor, is_counting; int i, ret = -EINVAL;#define PFM_CHECK_PMC_PM(x, y, z) ((x)->ctx_fl_system ^ PMC_PM(y, z)) /* we don't quite support this right now */ if (task != current) return -EINVAL; if (!CTX_IS_ENABLED(ctx)) return -EINVAL; /* XXX: ctx locking may be required here */ for (i = 0; i < count; i++, req++) { if (__copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT; cnum = tmp.reg_num; reg_flags = tmp.reg_flags; value = tmp.reg_value; reset_pmds = tmp.reg_reset_pmds[0]; flags = 0; is_counting = PMC_IS_COUNTING(cnum); is_monitor = PMC_IS_MONITOR(cnum); /* * we reject all non implemented PMC as well * as attempts to modify PMC[0-3] which are used * as status registers by the PMU */ if (!PMC_IS_IMPL(cnum) || cnum < 4) { DBprintk(("pmc[%u] is unimplemented or invalid\n", cnum)); goto error; } /* * If the PMC is a monitor, then if the value is not the default: * - system-wide session: PMCx.pm=1 (privileged monitor) * - per-task : PMCx.pm=0 (user monitor) */ if ((is_monitor || is_counting) && value != PMC_DFL_VAL(cnum) && PFM_CHECK_PMC_PM(ctx, cnum, value)) { DBprintk(("pmc%u pmc_pm=%ld fl_system=%d\n", cnum, PMC_PM(cnum, value), ctx->ctx_fl_system)); goto error; } if (is_counting) { pfm_monitor_t *p = (pfm_monitor_t *)&value; /* * enforce generation of overflow interrupt. Necessary on all * CPUs. */ p->pmc_oi = 1; if (reg_flags & PFM_REGFL_OVFL_NOTIFY) { /* * must have a target for the signal */ if (ctx->ctx_notify_task == NULL) { DBprintk(("cannot set ovfl_notify: no notify_task\n")); goto error; } flags |= PFM_REGFL_OVFL_NOTIFY; } if (reg_flags & PFM_REGFL_RANDOM) flags |= PFM_REGFL_RANDOM; /* verify validity of reset_pmds */ if ((reset_pmds & pmu_conf.impl_pmds[0]) != reset_pmds) { DBprintk(("invalid reset_pmds 0x%lx for pmc%u\n", reset_pmds, cnum)); goto error; } } else if (reg_flags & (PFM_REGFL_OVFL_NOTIFY|PFM_REGFL_RANDOM)) { DBprintk(("cannot set ovfl_notify or random on pmc%u\n", cnum)); goto error; } /* * execute write checker, if any */ if (PMC_WR_FUNC(cnum)) { ret = PMC_WR_FUNC(cnum)(task, cnum, &value, regs); if (ret) goto error; ret = -EINVAL; } /* * no error on this register */ PFM_REG_RETFLAG_SET(tmp.reg_flags, 0); /* * update register return value, abort all if problem during copy. * we only modify the reg_flags field. no check mode is fine because * access has been verified upfront in sys_perfmonctl(). * * If this fails, then the software state is not modified */ if (__put_user(tmp.reg_flags, &req->reg_flags)) return -EFAULT; /* * Now we commit the changes to the software state */ /* * full flag update each time a register is programmed */ ctx->ctx_soft_pmds[cnum].flags = flags; if (is_counting) { ctx->ctx_soft_pmds[cnum].reset_pmds[0] = reset_pmds; /* mark all PMDS to be accessed as used */ CTX_USED_PMD(ctx, reset_pmds); } /* * Needed in case the user does not initialize the equivalent * PMD. Clearing is done in reset_pmu() so there is no possible * leak here. */ CTX_USED_PMD(ctx, pmu_conf.pmc_desc[cnum].dep_pmd[0]); /* * keep copy the pmc, used for register reload */ th->pmc[cnum] = value; ia64_set_pmc(cnum, value); DBprintk(("[%d] pmc[%u]=0x%lx flags=0x%x used_pmds=0x%lx\n", task->pid, cnum, value, ctx->ctx_soft_pmds[cnum].flags, ctx->ctx_used_pmds[0])); } return 0;error: PFM_REG_RETFLAG_SET(tmp.reg_flags, PFM_REG_RETFL_EINVAL); if (__put_user(tmp.reg_flags, &req->reg_flags)) ret = -EFAULT; DBprintk(("[%d] pmc[%u]=0x%lx error %d\n", task->pid, cnum, value, ret)); return ret;}static intpfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ pfarg_reg_t tmp, *req = (pfarg_reg_t *)arg; unsigned long value, hw_value; unsigned int cnum; int i; int ret = -EINVAL; /* we don't quite support this right now */ if (task != current) return -EINVAL; /* * Cannot do anything before PMU is enabled */ if (!CTX_IS_ENABLED(ctx)) return -EINVAL; /* XXX: ctx locking may be required here */ for (i = 0; i < count; i++, req++) { if (__copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT; cnum = tmp.reg_num; value = tmp.reg_value; if (!PMD_IS_IMPL(cnum)) { DBprintk(("pmd[%u] is unimplemented or invalid\n", cnum)); goto abort_mission; } /* * execute write checker, if any */ if (PMD_WR_FUNC(cnum)) { unsigned long v = value; ret = PMD_WR_FUNC(cnum)(task, cnum, &v, regs); if (ret) goto abort_mission; value = v; ret = -EINVAL; } hw_value = value; /* * no error on this register */ PFM_REG_RETFLAG_SET(tmp.reg_flags, 0); if (__put_user(tmp.reg_flags, &req->reg_flags)) return -EFAULT; /* * now commit changes to software state */ /* update virtualized (64bits) counter */ if (PMD_IS_COUNTING(cnum)) { ctx->ctx_soft_pmds[cnum].lval = value; ctx->ctx_soft_pmds[cnum].val = value & ~pmu_conf.ovfl_val; hw_value = value & pmu_conf.ovfl_val; 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].seed = tmp.reg_random_seed; ctx->ctx_soft_pmds[cnum].mask = tmp.reg_random_mask; } /* keep track of what we use */ CTX_USED_PMD(ctx, pmu_conf.pmd_desc[(cnum)].dep_pmd[0]); /* mark this register as used as well */ CTX_USED_PMD(ctx, RDEP(cnum)); /* writes to unimplemented part is ignored, so this is safe */ ia64_set_pmd(cnum, hw_value); /* to go away */ ia64_srlz_d(); DBprintk(("[%d] pmd[%u]: value=0x%lx hw_value=0x%lx soft_pmd=0x%lx short_reset=0x%lx " "long_reset=0x%lx hw_pmd=%lx notify=%c used_pmds=0x%lx reset_pmds=0x%lx psr=%d\n", task->pid, cnum, value, hw_value, ctx->ctx_soft_pmds[cnum].val, ctx->ctx_soft_pmds[cnum].short_reset, ctx->ctx_soft_pmds[cnum].long_reset, ia64_get_pmd(cnum) & pmu_conf.ovfl_val, PMC_OVFL_NOTIFY(ctx, cnum) ? 'Y':'N', ctx->ctx_used_pmds[0], ctx->ctx_soft_pmds[cnum].reset_pmds[0], ia64_psr(regs)->sp)); } return 0;abort_mission: /* * for now, we have only one possibility for error */ PFM_REG_RETFLAG_SET(tmp.reg_flags, PFM_REG_RETFL_EINVAL); /* * we change the return value to EFAULT in case we cannot write register return code. * The caller first must correct this error, then a resubmission of the request will * eventually yield the EINVAL. */ if (__put_user(tmp.reg_flags, &req->reg_flags)) ret = -EFAULT; DBprintk(("[%d] pmc[%u]=0x%lx ret %d\n", task->pid, cnum, value, ret)); return ret;}static intpfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ struct thread_struct *th = &task->thread; unsigned long val, lval; pfarg_reg_t *req = (pfarg_reg_t *)arg; unsigned int cnum, reg_flags = 0; int i, ret = 0;#if __GNUC__ < 3 int foo;#endif if (!CTX_IS_ENABLED(ctx)) { DBprintk(("context for [%d] is disabled\n", task->pid)); return -EINVAL; } /* * XXX: MUST MAKE SURE WE DON"T HAVE ANY PENDING OVERFLOW BEFORE READING * This is required when the monitoring has been stoppped by user or kernel. * If it is still going on, then that's fine because we a re not guaranteed * to return an accurate value in this case. */ /* XXX: ctx locking may be required here */ /* * should we need to access the PMU, serialization is needed */ ia64_srlz_d(); for (i = 0; i < count; i++, req++) {#if __GNUC__ < 3 foo = __get_user(cnum, &req->reg_num); if (foo) return -EFAULT; foo = __get_user(reg_flags, &req->reg_flags); if (foo) return -EFAULT;#else if (__get_user(cnum, &req->reg_num)) return -EFAULT; if (__get_user(reg_flags, &req->reg_flags)) return -EFAULT;#endif lval = 0UL; if (!PMD_IS_IMPL(cnum)) goto abort_mission; /* * we can only read the register that we use. That includes * the one we explicitely initialize AND the one we want included * in the sampling buffer (smpl_regs). * * Having this restriction allows optimization in the ctxsw routine * without compromising security (leaks) */ if (!CTX_IS_USED_PMD(ctx, cnum)) goto abort_mission; /* * we can access the registers directly only when task * is the OWNER of the local PMU. In SMP, this can * happen only when task == current. In addition * this can happen when task != currrent but * only in UP mode. */ if (task == PMU_OWNER()) { val = ia64_get_pmd(cnum); DBprintk(("reading pmd[%u]=0x%lx from hw\n", cnum, val)); } else { /* context has been saved */ val = th->pmd[cnum]; } if (PMD_IS_COUNTING(cnum)) { /* * XXX: need to check for overflow */ val &= pmu_conf.ovfl_val; val += ctx->ctx_soft_pmds[cnum].val; lval = ctx->ctx_soft_pmds[cnum].lval; } /* * execute read checker, if any */ if (PMD_RD_FUNC(cnum)) { unsigned long v = val; ret = PMD_RD_FUNC(cnum)(task, cnum, &v, regs); val = v; } PFM_REG_RETFLAG_SET(reg_flags, ret); DBprintk(("read pmd[%u] ret=%d value=0x%lx pmc=0x%lx\n", cnum, ret, val, ia64_get_pmc(cnum))); /* * update register return value, abort all if problem during copy. * we only modify the reg_flags field. no check mode is fine because * access has been verified upfront in sys_perfmonctl(). */ if (__put_user(cnum, &req->reg_num)) return -EFAULT; if (__put_user(val, &req->reg_value)) return -EFAULT; if (__put_user(reg_flags, &req->reg_flags)) return -EFAULT; if (__put_user(lval, &req->reg_last_reset_value)) return -EFAULT; } return 0;abort_mission: PFM_REG_RETFLAG_SET(reg_flags, PFM_REG_RETFL_EINVAL); /* * XXX: if this fails, we stick with the original failure, flag not updated! */ __put_user(reg_flags, &req->reg_flags); return -EINVAL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -