📄 perfmon.c
字号:
notify_task = find_task_by_pid(notify_pid); if (notify_task) { ret = -EPERM; /* * check if we can send this task a signal */ if (pfm_bad_permissions(notify_task)) goto buffer_error; /* * make visible * must be done inside critical section * * if the initialization does not go through it is still * okay because child will do the scan for nothing which * won't hurt. */ current->thread.pfm_context = ctx; /* * will cause task to check on exit for monitored * processes that would notify it. see release_thread() * Note: the scan MUST be done in release thread, once the * task has been detached from the tasklist otherwise you are * exposed to race conditions. */ atomic_add(1, &ctx->ctx_notify_task->thread.pfm_notifiers_check); ctx->ctx_notify_task = notify_task; } read_unlock(&tasklist_lock); } /* * notification process does not exist */ if (notify_pid != 0 && ctx->ctx_notify_task == NULL) { ret = -EINVAL; goto buffer_error; } if (tmp.ctx_smpl_entries) { DBprintk(("sampling entries=%ld\n",tmp.ctx_smpl_entries)); ret = pfm_smpl_buffer_alloc(ctx, tmp.ctx_smpl_regs, tmp.ctx_smpl_entries, &uaddr); if (ret<0) goto buffer_error; tmp.ctx_smpl_vaddr = uaddr; } /* initialization of context's flags */ ctx->ctx_fl_inherit = ctx_flags & PFM_FL_INHERIT_MASK; ctx->ctx_fl_block = (ctx_flags & PFM_FL_NOTIFY_BLOCK) ? 1 : 0; ctx->ctx_fl_system = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0; ctx->ctx_fl_frozen = 0; /* * setting this flag to 0 here means, that the creator or the task that the * context is being attached are granted access. Given that a context can only * be created for the calling process this, in effect only allows the creator * to access the context. See pfm_protect() for more. */ ctx->ctx_fl_protected = 0; /* for system wide mode only (only 1 bit set) */ ctx->ctx_cpu = cpu; atomic_set(&ctx->ctx_last_cpu,-1); /* SMP only, means no CPU */ /* * Keep track of the pmds we want to sample * XXX: may be we don't need to save/restore the DEAR/IEAR pmds * but we do need the BTB for sure. This is because of a hardware * buffer of 1 only for non-BTB pmds. * * We ignore the unimplemented pmds specified by the user */ ctx->ctx_used_pmds[0] = tmp.ctx_smpl_regs[0] & pmu_conf.impl_regs[4]; ctx->ctx_saved_pmcs[0] = 1; /* always save/restore PMC[0] */ sema_init(&ctx->ctx_restart_sem, 0); /* init this semaphore to locked */ if (copy_to_user(req, &tmp, sizeof(tmp))) { ret = -EFAULT; goto buffer_error; } DBprintk(("context=%p, pid=%d notify_task=%p\n", (void *)ctx, task->pid, ctx->ctx_notify_task)); DBprintk(("context=%p, pid=%d flags=0x%x inherit=%d block=%d system=%d\n", (void *)ctx, task->pid, ctx_flags, ctx->ctx_fl_inherit, ctx->ctx_fl_block, ctx->ctx_fl_system)); /* * when no notification is required, we can make this visible at the last moment */ if (notify_pid == 0) task->thread.pfm_context = ctx; /* * pin task to CPU and force reschedule on exit to ensure * that when back to user level the task runs on the designated * CPU. */ if (ctx->ctx_fl_system) { ctx->ctx_saved_cpus_allowed = task->cpus_allowed; task->cpus_allowed = 1UL << cpu; 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: /* * undo session reservation */ LOCK_PFS(); if (ctx_flags & PFM_FL_SYSTEM_WIDE) { pfm_sessions.pfs_sys_session[cpu] = NULL; pfm_sessions.pfs_sys_sessions--; } else { pfm_sessions.pfs_task_sessions--; }abort: UNLOCK_PFS(); return ret;}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; DBprintk(("masks=0x%lx\n", mask)); /* * 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 = flag == PFM_RELOAD_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]; DBprintk(("[%d] %s reset soft_pmd[%d]=%lx\n", current->pid, flag == PFM_RELOAD_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 = flag == PFM_RELOAD_LONG_RESET ? ctx->ctx_soft_pmds[i].long_reset: ctx->ctx_soft_pmds[i].short_reset; if (PMD_IS_COUNTING(i)) { pfm_write_soft_counter(ctx, i, val); } else { ia64_set_pmd(i, val); } DBprintk(("[%d] %s reset_others pmd[%d]=%lx\n", current->pid, flag == PFM_RELOAD_LONG_RESET ? "long" : "short", i, val)); } /* just in case ! */ ctx->ctx_ovfl_regs[0] = 0UL;}static intpfm_write_pmcs(struct task_struct *ta, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ struct thread_struct *th = &ta->thread; pfarg_reg_t tmp, *req = (pfarg_reg_t *)arg; unsigned int cnum; int i; int ret = 0, reg_retval = 0; /* we don't quite support this right now */ if (ta != 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; /* * 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)); ret = -EINVAL; goto abort_mission; } /* * A PMC used to configure monitors must be: * - system-wide session: privileged monitor * - per-task : user monitor * any other configuration is rejected. */ if (PMC_IS_MONITOR(cnum)) { pfm_monitor_t *p = (pfm_monitor_t *)&tmp.reg_value; DBprintk(("pmc[%u].pm = %d\n", cnum, p->pmc_pm)); if (ctx->ctx_fl_system ^ p->pmc_pm) { //if ((ctx->ctx_fl_system == 1 && p->pmc_pm == 0) // ||(ctx->ctx_fl_system == 0 && p->pmc_pm == 1)) { ret = -EINVAL; goto abort_mission; } /* * enforce generation of overflow interrupt. Necessary on all * CPUs which do not implement 64-bit hardware counters. */ p->pmc_oi = 1; } if (PMC_IS_COUNTING(cnum)) { if (tmp.reg_flags & PFM_REGFL_OVFL_NOTIFY) { /* * must have a target for the signal */ if (ctx->ctx_notify_task == NULL) { ret = -EINVAL; goto abort_mission; } ctx->ctx_soft_pmds[cnum].flags |= PFM_REGFL_OVFL_NOTIFY; } /* * copy reset vector */ ctx->ctx_soft_pmds[cnum].reset_pmds[0] = tmp.reg_reset_pmds[0]; 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[3] = tmp.reg_reset_pmds[3]; /* * 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, cnum); }abort_mission: if (ret == -EINVAL) reg_retval = PFM_REG_RETFL_EINVAL; PFM_REG_RETFLAG_SET(tmp.reg_flags, reg_retval); /* * update register return value, abort all if problem during copy. */ if (copy_to_user(req, &tmp, sizeof(tmp))) return -EFAULT; /* * if there was something wrong on this register, don't touch * the hardware at all and abort write request for others. * * On error, the user mut sequentially scan the table and the first * entry which has a return flag set is the one that caused the error. */ if (ret != 0) { DBprintk(("[%d] pmc[%u]=0x%lx error %d\n", ta->pid, cnum, tmp.reg_value, reg_retval)); break; } /* * We can proceed with this register! */ /* * keep copy the pmc, used for register reload */ th->pmc[cnum] = tmp.reg_value; ia64_set_pmc(cnum, tmp.reg_value); DBprintk(("[%d] pmc[%u]=0x%lx flags=0x%x save_pmcs=0%lx reload_pmcs=0x%lx\n", ta->pid, cnum, tmp.reg_value, ctx->ctx_soft_pmds[cnum].flags, ctx->ctx_saved_pmcs[0], ctx->ctx_reload_pmcs[0])); } return ret;}static intpfm_write_pmds(struct task_struct *ta, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ pfarg_reg_t tmp, *req = (pfarg_reg_t *)arg; unsigned int cnum; int i; int ret = 0, reg_retval = 0; /* we don't quite support this right now */ if (ta != 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; if (!PMD_IS_IMPL(cnum)) { ret = -EINVAL; goto abort_mission; } /* update virtualized (64bits) counter */ if (PMD_IS_COUNTING(cnum)) { ctx->ctx_soft_pmds[cnum].ival = tmp.reg_value; 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].short_reset = tmp.reg_short_reset; }abort_mission: if (ret == -EINVAL) reg_retval = PFM_REG_RETFL_EINVAL; PFM_REG_RETFLAG_SET(tmp.reg_flags, reg_retval); if (copy_to_user(req, &tmp, sizeof(tmp))) return -EFAULT; /* * if there was something wrong on this register, don't touch * the hardware at all and abort write request for others. * * On error, the user mut sequentially scan the table and the first * entry which has a return flag set is the one that caused the error. */ if (ret != 0) { DBprintk(("[%d] pmc[%u]=0x%lx error %d\n", ta->pid, cnum, tmp.reg_value, reg_retval)); break; } /* keep track of what we use */ CTX_USED_PMD(ctx, cnum); /* writes to unimplemented part is ignored, so this is safe */ ia64_set_pmd(cnum, tmp.reg_value); /* to go away */ ia64_srlz_d(); DBprintk(("[%d] pmd[%u]: 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\n", ta->pid, cnum, 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.perf_ovfl_val, PMC_OVFL_NOTIFY(ctx, cnum) ? 'Y':'N', ctx->ctx_used_pmds[0], ctx->ctx_soft_pmds[cnum].reset_pmds[0])); } return ret;}static intpfm_read_pmds(struct task_struct *ta, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ struct thread_struct *th = &ta->thread; unsigned long val=0; pfarg_reg_t tmp, *req = (pfarg_reg_t *)arg; int i; if (!CTX_IS_ENABLED(ctx)) 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 */ DBprintk(("ctx_last_cpu=%d for [%d]\n", atomic_read(&ctx->ctx_last_cpu), ta->pid)); for (i = 0; i < count; i++, req++) { unsigned long reg_val = ~0UL, ctx_val = ~0UL; if (copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT; if (!PMD_IS_IMPL(tmp.reg_num)) goto abort_mission; /* * If the task is not the current one, then we check if the * PMU state is still in the local live register due to lazy ctxsw. * If true, then we read directly from the registers. */ if (atomic_read(&ctx->ctx_last_cpu) == smp_processor_id()){ ia64_srlz_d(); val = reg_val = ia64_get_pmd(tmp.reg_num); DBprintk(("reading pmd[%u]=0x%lx from hw\n", tmp.reg_num, val)); } else {#ifdef CONFIG_SMP int cpu; /* * for SMP system, the context may still be live on another * CPU so we need to fetch it before proceeding with the read * This call we only be made once for the whole loop because * of ctx_last_cpu becoming == -1. * * We cannot reuse ctx_last_cpu as it may change before we get to the * actual IPI call. In this case, we will do the call for nothing but * there is no way around it. The receiving side will simply do nothing. */ cpu = atomic_read(&ctx->ctx_last_cpu); if (cpu != -1) { DBprintk(("must fetch on CPU%d for [%d]\n", cpu, ta->pid)); pfm_fetch_regs(cpu, ta, ctx); }#endif /* context has been saved */ val = reg_val = th->pmd[tmp.reg_num]; } if (PMD_IS_COUNTING(tmp.reg_num)) { /* * XXX: need to check for overflow */ val &= pmu_conf.perf_ovfl_val; val += ctx_val = ctx->ctx_soft_pmds[tmp.reg_num].val; } else { val = reg_val = ia64_get_pmd(tmp.reg_num); } PFM_REG_RETFLAG_SET(tmp.reg_flags, 0); tmp.reg_value = val; DBprintk(("read pmd[%u] soft_pmd=0x%lx reg=0x%lx pmc=0x%lx\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -