📄 perfmon.c
字号:
tmp.reg_num, ctx_val, reg_val, ia64_get_pmc(tmp.reg_num))); if (copy_to_user(req, &tmp, sizeof(tmp))) return -EFAULT; } return 0;abort_mission: PFM_REG_RETFLAG_SET(tmp.reg_flags, PFM_REG_RETFL_EINVAL); /* * XXX: if this fails, we stick we the original failure, flag not updated! */ copy_to_user(req, &tmp, sizeof(tmp)); return -EINVAL;}#ifdef PFM_PMU_USES_DBR/* * Only call this function when a process it trying to * write the debug registers (reading is always allowed) */intpfm_use_debug_registers(struct task_struct *task){ pfm_context_t *ctx = task->thread.pfm_context; int ret = 0; DBprintk(("called for [%d]\n", task->pid)); /* * do it only once */ if (task->thread.flags & IA64_THREAD_DBG_VALID) return 0; /* * Even on SMP, we do not need to use an atomic here because * the only way in is via ptrace() and this is possible only when the * process is stopped. Even in the case where the ctxsw out is not totally * completed by the time we come here, there is no way the 'stopped' process * could be in the middle of fiddling with the pfm_write_ibr_dbr() routine. * So this is always safe. */ if (ctx && ctx->ctx_fl_using_dbreg == 1) return -1; /* * XXX: not pretty */ LOCK_PFS(); /* * We only allow the use of debug registers when there is no system * wide monitoring * XXX: we could relax this by */ if (pfm_sessions.pfs_sys_use_dbregs> 0) ret = -1; else pfm_sessions.pfs_ptrace_use_dbregs++; DBprintk(("ptrace_use_dbregs=%lu sys_use_dbregs=%lu by [%d] ret = %d\n", pfm_sessions.pfs_ptrace_use_dbregs, pfm_sessions.pfs_sys_use_dbregs, task->pid, ret)); UNLOCK_PFS(); return ret;}/* * This function is called for every task that exits with the * IA64_THREAD_DBG_VALID set. This indicates a task which was * able to use the debug registers for debugging purposes via * ptrace(). Therefore we know it was not using them for * perfmormance monitoring, so we only decrement the number * of "ptraced" debug register users to keep the count up to date */intpfm_release_debug_registers(struct task_struct *task){ int ret; LOCK_PFS(); if (pfm_sessions.pfs_ptrace_use_dbregs == 0) { printk("perfmon: invalid release for [%d] ptrace_use_dbregs=0\n", task->pid); ret = -1; } else { pfm_sessions.pfs_ptrace_use_dbregs--; ret = 0; } UNLOCK_PFS(); return ret;}#else /* PFM_PMU_USES_DBR is true *//* * in case, the PMU does not use the debug registers, these two functions are nops. * The first function is called from arch/ia64/kernel/ptrace.c. * The second function is called from arch/ia64/kernel/process.c. */intpfm_use_debug_registers(struct task_struct *task){ return 0;}intpfm_release_debug_registers(struct task_struct *task){ return 0;}#endif /* PFM_PMU_USES_DBR */static intpfm_restart(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ void *sem = &ctx->ctx_restart_sem; /* * Cannot do anything before PMU is enabled */ if (!CTX_IS_ENABLED(ctx)) return -EINVAL; if (ctx->ctx_fl_frozen==0) { printk("task %d without pmu_frozen set\n", task->pid); return -EINVAL; } if (task == current) { DBprintk(("restarting self %d frozen=%d \n", current->pid, ctx->ctx_fl_frozen)); pfm_reset_regs(ctx, ctx->ctx_ovfl_regs, PFM_RELOAD_LONG_RESET); ctx->ctx_ovfl_regs[0] = 0UL; /* * We ignore block/don't block because we never block * for a self-monitoring process. */ ctx->ctx_fl_frozen = 0; if (CTX_HAS_SMPL(ctx)) { ctx->ctx_psb->psb_hdr->hdr_count = 0; ctx->ctx_psb->psb_index = 0; } /* simply unfreeze */ ia64_set_pmc(0, 0); ia64_srlz_d(); return 0; } /* restart on another task */ /* * if blocking, then post the semaphore. * if non-blocking, then we ensure that the task will go into * pfm_overflow_must_block() before returning to user mode. * We cannot explicitely reset another task, it MUST always * be done by the task itself. This works for system wide because * the tool that is controlling the session is doing "self-monitoring". * * XXX: what if the task never goes back to user? * */ if (CTX_OVFL_NOBLOCK(ctx) == 0) { DBprintk(("unblocking %d \n", task->pid)); up(sem); } else { task->thread.pfm_ovfl_block_reset = 1; }#if 0 /* * in case of non blocking mode, then it's just a matter of * of reseting the sampling buffer (if any) index. The PMU * is already active. */ /* * must reset the header count first */ if (CTX_HAS_SMPL(ctx)) { DBprintk(("resetting sampling indexes for %d \n", task->pid)); ctx->ctx_psb->psb_hdr->hdr_count = 0; ctx->ctx_psb->psb_index = 0; }#endif return 0;}#ifndef CONFIG_SMP/* * On UP kernels, we do not need to constantly set the psr.pp bit * when a task is scheduled. The psr.pp bit can only be changed in * the kernel because of a user request. Given we are on a UP non preeemptive * kernel we know that no other task is running, so we cna simply update their * psr.pp from their saved state. There is this no impact on the context switch * code compared to the SMP case. */static voidpfm_tasklist_toggle_pp(unsigned int val){ struct task_struct *p; struct pt_regs *regs; DBprintk(("invoked by [%d] pp=%u\n", current->pid, val)); read_lock(&tasklist_lock); for_each_task(p) { regs = (struct pt_regs *)((unsigned long) p + IA64_STK_OFFSET); /* * position on pt_regs saved on stack on 1st entry into the kernel */ regs--; /* * update psr.pp */ ia64_psr(regs)->pp = val; } read_unlock(&tasklist_lock);}#endifstatic intpfm_stop(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ /* 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; DBprintk(("[%d] fl_system=%d owner=%p current=%p\n", current->pid, ctx->ctx_fl_system, PMU_OWNER(), current)); /* simply stop monitoring but not the PMU */ if (ctx->ctx_fl_system) { __asm__ __volatile__ ("rsm psr.pp;;"::: "memory"); /* disable dcr pp */ ia64_set_dcr(ia64_get_dcr() & ~IA64_DCR_PP);#ifdef CONFIG_SMP local_cpu_data->pfm_dcr_pp = 0;#else pfm_tasklist_toggle_pp(0);#endif ia64_psr(regs)->pp = 0; } else { __asm__ __volatile__ ("rum psr.up;;"::: "memory"); ia64_psr(regs)->up = 0; } return 0;}static intpfm_disable(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ /* we don't quite support this right now */ if (task != current) return -EINVAL; if (!CTX_IS_ENABLED(ctx)) return -EINVAL; /* * stop monitoring, freeze PMU, and save state in context * this call will clear IA64_THREAD_PM_VALID for per-task sessions. */ pfm_flush_regs(task); if (ctx->ctx_fl_system) { ia64_psr(regs)->pp = 0; } else { ia64_psr(regs)->up = 0; } /* * goes back to default behavior * no need to change live psr.sp because useless at the kernel level */ ia64_psr(regs)->sp = 1; DBprintk(("enabling psr.sp for [%d]\n", current->pid)); ctx->ctx_flags.state = PFM_CTX_DISABLED; return 0;}static intpfm_destroy_context(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ /* we don't quite support this right now */ if (task != current) return -EINVAL; /* * if context was never enabled, then there is not much * to do */ if (!CTX_IS_ENABLED(ctx)) goto skipped_stop; /* * Disable context: stop monitoring, flush regs to software state (useless here), * and freeze PMU * * The IA64_THREAD_PM_VALID is cleared by pfm_flush_regs() called from pfm_disable() */ pfm_disable(task, ctx, arg, count, regs); if (ctx->ctx_fl_system) { ia64_psr(regs)->pp = 0; } else { ia64_psr(regs)->up = 0; } /* restore security level */ ia64_psr(regs)->sp = 1;skipped_stop: /* * remove sampling buffer mapping, if any */ if (ctx->ctx_smpl_vaddr) pfm_remove_smpl_mapping(task); /* now free context and related state */ pfm_context_exit(task); return 0;}/* * does nothing at the moment */static intpfm_unprotect_context(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ return 0;}static intpfm_protect_context(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ DBprintk(("context from [%d] is protected\n", task->pid)); /* * from now on, only the creator of the context has access to it */ ctx->ctx_fl_protected = 1; /* * reinforce secure monitoring: cannot toggle psr.up */ ia64_psr(regs)->sp = 1; return 0;}static intpfm_debug(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs){ unsigned int mode = *(unsigned int *)arg; pfm_debug_mode = mode == 0 ? 0 : 1; printk("perfmon debugging %s\n", pfm_debug_mode ? "on" : "off"); return 0;}#ifdef PFM_PMU_USES_DBRtypedef struct { unsigned long ibr_mask:56; unsigned long ibr_plm:4; unsigned long ibr_ig:3; unsigned long ibr_x:1;} ibr_mask_reg_t;typedef struct { unsigned long dbr_mask:56; unsigned long dbr_plm:4; unsigned long dbr_ig:2; unsigned long dbr_w:1; unsigned long dbr_r:1;} dbr_mask_reg_t;typedef union { unsigned long val; ibr_mask_reg_t ibr; dbr_mask_reg_t dbr;} dbreg_t;static intpfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs){ struct thread_struct *thread = &task->thread; pfm_context_t *ctx = task->thread.pfm_context; pfarg_dbreg_t tmp, *req = (pfarg_dbreg_t *)arg; dbreg_t dbreg; unsigned int rnum; int first_time; int i, ret = 0; /* * for range restriction: psr.db must be cleared or the * the PMU will ignore the debug registers. * * XXX: may need more in system wide mode, * no task can have this bit set? */ if (ia64_psr(regs)->db == 1) return -EINVAL; first_time = ctx->ctx_fl_using_dbreg == 0; /* * check for debug registers in system wide mode * */ LOCK_PFS(); if (ctx->ctx_fl_system && first_time) { if (pfm_sessions.pfs_ptrace_use_dbregs) ret = -EBUSY; else pfm_sessions.pfs_sys_use_dbregs++; } UNLOCK_PFS(); if (ret != 0) return ret; if (ctx->ctx_fl_system) { /* we mark ourselves as owner of the debug registers */ ctx->ctx_fl_using_dbreg = 1; } else { if (ctx->ctx_fl_using_dbreg == 0) { ret= -EBUSY; if ((thread->flags & IA64_THREAD_DBG_VALID) != 0) { DBprintk(("debug registers already in use for [%d]\n", task->pid)); goto abort_mission; } /* we mark ourselves as owner of the debug registers */ ctx->ctx_fl_using_dbreg = 1; /* * Given debug registers cannot be used for both debugging * and performance monitoring at the same time, we reuse * the storage area to save and restore the registers on ctxsw. */ memset(task->thread.dbr, 0, sizeof(task->thread.dbr)); memset(task->thread.ibr, 0, sizeof(task->thread.ibr)); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -