📄 perfmon.c
字号:
} /* * we have to set this here event hough we haven't necessarily started monitoring * because we may be context switched out */ if (ctx->ctx_fl_system==0) th->flags |= IA64_THREAD_PM_VALID; return 0;}static intpfm_read_pmds(struct task_struct *ta, perfmon_req_t *req, int count){ struct thread_struct *th = &ta->thread; pfm_context_t *ctx = th->pfm_context; unsigned long val=0; perfmon_req_t tmp; int i; /* * XXX: MUST MAKE SURE WE DON"T HAVE ANY PENDING OVERFLOW BEFORE READING * This is required when the monitoring has been stoppped by user of kernel. * If ity is still going on, then that's fine because we a re not gauranteed * to return an accurate value in this case */ /* XXX: ctx locking may be required here */ for (i = 0; i < count; i++, req++) { unsigned long reg_val = ~0, ctx_val = ~0; if (copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT; if (!PMD_IS_IMPL(tmp.pfr_reg.reg_num)) return -EINVAL; if (PMD_IS_COUNTER(tmp.pfr_reg.reg_num)) { if (ta == current){ val = ia64_get_pmd(tmp.pfr_reg.reg_num); } else { val = reg_val = th->pmd[tmp.pfr_reg.reg_num]; } val &= pmu_conf.perf_ovfl_val; /* * lower part of .val may not be zero, so we must be an addition because of * residual count (see update_counters). */ val += ctx_val = ctx->ctx_pmds[tmp.pfr_reg.reg_num - PMU_FIRST_COUNTER].val; } else { /* for now */ if (ta != current) return -EINVAL; ia64_srlz_d(); val = ia64_get_pmd(tmp.pfr_reg.reg_num); } tmp.pfr_reg.reg_value = val; DBprintk((" reading PMD[%ld]=0x%lx reg=0x%lx ctx_val=0x%lx pmc=0x%lx\n", tmp.pfr_reg.reg_num, val, reg_val, ctx_val, ia64_get_pmc(tmp.pfr_reg.reg_num))); if (copy_to_user(req, &tmp, sizeof(tmp))) return -EFAULT; } return 0;}static intpfm_do_restart(struct task_struct *task){ struct thread_struct *th = &task->thread; pfm_context_t *ctx = th->pfm_context; void *sem = &ctx->ctx_restart_sem; if (task == current) { DBprintk((" restarting self %d frozen=%d \n", current->pid, ctx->ctx_fl_frozen)); pfm_reset_regs(ctx); /* * 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_smpl_buf->psb_hdr->hdr_count = 0; ctx->ctx_smpl_buf->psb_index = 0; } /* pfm_reset_smpl_buffers(ctx,th->pfm_ovfl_regs);*/ /* simply unfreeze */ ia64_set_pmc(0, 0); ia64_srlz_d(); return 0; } /* check if blocking */ if (CTX_OVFL_NOBLOCK(ctx) == 0) { DBprintk((" unblocking %d \n", task->pid)); up(sem); return 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_smpl_buf->psb_hdr->hdr_count = 0; ctx->ctx_smpl_buf->psb_index = 0; } return 0;}/* * system-wide mode: propagate activation/desactivation throughout the tasklist * * XXX: does not work for SMP, of course */static voidpfm_process_tasklist(int cmd){ struct task_struct *p; struct pt_regs *regs; for_each_task(p) { regs = (struct pt_regs *)((unsigned long)p + IA64_STK_OFFSET); regs--; ia64_psr(regs)->pp = cmd; }}static intdo_perfmonctl (struct task_struct *task, int cmd, int flags, perfmon_req_t *req, int count, struct pt_regs *regs){ perfmon_req_t tmp; struct thread_struct *th = &task->thread; pfm_context_t *ctx = th->pfm_context; memset(&tmp, 0, sizeof(tmp)); if (ctx == NULL && cmd != PFM_CREATE_CONTEXT && cmd < PFM_DEBUG_BASE) { DBprintk((" PFM_WRITE_PMCS: no context for task %d\n", task->pid)); return -EINVAL; } switch (cmd) { case PFM_CREATE_CONTEXT: /* a context has already been defined */ if (ctx) return -EBUSY; /* * cannot directly create a context in another process */ if (task != current) return -EINVAL; if (req == NULL || count != 1) return -EINVAL; if (!access_ok(VERIFY_READ, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT; return pfm_context_create(flags, req); case PFM_WRITE_PMCS: /* we don't quite support this right now */ if (task != current) return -EINVAL; if (!access_ok(VERIFY_READ, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT; return pfm_write_pmcs(task, req, count); case PFM_WRITE_PMDS: /* we don't quite support this right now */ if (task != current) return -EINVAL; if (!access_ok(VERIFY_READ, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT; return pfm_write_pmds(task, req, count); case PFM_START: /* we don't quite support this right now */ if (task != current) return -EINVAL; if (PMU_OWNER() && PMU_OWNER() != current && PFM_CAN_DO_LAZY()) pfm_lazy_save_regs(PMU_OWNER()); SET_PMU_OWNER(current); /* will start monitoring right after rfi */ ia64_psr(regs)->up = 1; ia64_psr(regs)->pp = 1; if (ctx->ctx_fl_system) { pfm_process_tasklist(1); pfs_info.pfs_pp = 1; } /* * mark the state as valid. * this will trigger save/restore at context switch */ if (ctx->ctx_fl_system==0) th->flags |= IA64_THREAD_PM_VALID; ia64_set_pmc(0, 0); ia64_srlz_d(); break; case PFM_ENABLE: /* we don't quite support this right now */ if (task != current) return -EINVAL; if (PMU_OWNER() && PMU_OWNER() != current && PFM_CAN_DO_LAZY()) pfm_lazy_save_regs(PMU_OWNER()); /* reset all registers to stable quiet state */ ia64_reset_pmu(); /* make sure nothing starts */ ia64_psr(regs)->up = 0; ia64_psr(regs)->pp = 0; /* do it on the live register as well */ __asm__ __volatile__ ("rsm psr.pp|psr.pp;;"::: "memory"); SET_PMU_OWNER(current); /* * mark the state as valid. * this will trigger save/restore at context switch */ if (ctx->ctx_fl_system==0) th->flags |= IA64_THREAD_PM_VALID; /* simply unfreeze */ ia64_set_pmc(0, 0); ia64_srlz_d(); break; case PFM_DISABLE: /* we don't quite support this right now */ if (task != current) return -EINVAL; /* simply freeze */ ia64_set_pmc(0, 1); ia64_srlz_d(); /* * XXX: cannot really toggle IA64_THREAD_PM_VALID * but context is still considered valid, so any * read request would return something valid. Same * thing when this task terminates (pfm_flush_regs()). */ break; case PFM_READ_PMDS: if (!access_ok(VERIFY_READ, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT; if (!access_ok(VERIFY_WRITE, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT; return pfm_read_pmds(task, req, count); case PFM_STOP: /* we don't quite support this right now */ if (task != current) return -EINVAL; /* simply stop monitors, not PMU */ ia64_psr(regs)->up = 0; ia64_psr(regs)->pp = 0; if (ctx->ctx_fl_system) { pfm_process_tasklist(0); pfs_info.pfs_pp = 0; } break; case PFM_RESTART: /* temporary, will most likely end up as a PFM_ENABLE */ if ((th->flags & IA64_THREAD_PM_VALID) == 0 && ctx->ctx_fl_system==0) { printk(" PFM_RESTART not monitoring\n"); return -EINVAL; } if (CTX_OVFL_NOBLOCK(ctx) == 0 && ctx->ctx_fl_frozen==0) { printk("task %d without pmu_frozen set\n", task->pid); return -EINVAL; } return pfm_do_restart(task); /* we only look at first entry */ case PFM_DESTROY_CONTEXT: /* we don't quite support this right now */ if (task != current) return -EINVAL; /* first stop monitors */ ia64_psr(regs)->up = 0; ia64_psr(regs)->pp = 0; /* then freeze PMU */ ia64_set_pmc(0, 1); ia64_srlz_d(); /* don't save/restore on context switch */ if (ctx->ctx_fl_system ==0) task->thread.flags &= ~IA64_THREAD_PM_VALID; SET_PMU_OWNER(NULL); /* now free context and related state */ pfm_context_exit(task); break; case PFM_DEBUG_ON: printk("perfmon debugging on\n"); pfm_debug = 1; break; case PFM_DEBUG_OFF: printk("perfmon debugging off\n"); pfm_debug = 0; break; default: DBprintk((" UNknown command 0x%x\n", cmd)); return -EINVAL; } return 0;}/* * XXX: do something better here */static intperfmon_bad_permissions(struct task_struct *task){ /* stolen from bad_signal() */ return (current->session != task->session) && (current->euid ^ task->suid) && (current->euid ^ task->uid) && (current->uid ^ task->suid) && (current->uid ^ task->uid);}asmlinkage intsys_perfmonctl (int pid, int cmd, int flags, perfmon_req_t *req, int count, long arg6, long arg7, long arg8, long stack){ struct pt_regs *regs = (struct pt_regs *) &stack; struct task_struct *child = current; int ret = -ESRCH; /* sanity check: * * ensures that we don't do bad things in case the OS * does not have enough storage to save/restore PMC/PMD */ if (PERFMON_IS_DISABLED()) return -ENOSYS; /* XXX: pid interface is going away in favor of pfm context */ if (pid != current->pid) { read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (!child) goto abort_call; ret = -EPERM; if (perfmon_bad_permissions(child)) goto abort_call; /* * XXX: need to do more checking here */ if (child->state != TASK_ZOMBIE && child->state != TASK_STOPPED) { DBprintk((" warning process %d not in stable state %ld\n", pid, child->state)); } } ret = do_perfmonctl(child, cmd, flags, req, count, regs);abort_call: if (child != current) read_unlock(&tasklist_lock); return ret;}#if __GNUC__ >= 3void asmlinkagepfm_block_on_overflow(void)#elsevoid asmlinkagepfm_block_on_overflow(u64 arg0, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7)#endif{ struct thread_struct *th = ¤t->thread; pfm_context_t *ctx = current->thread.pfm_context; int ret; /* * NO matter what notify_pid is, * we clear overflow, won't notify again */ th->pfm_must_block = 0; /* * do some sanity checks first */ if (!ctx) { printk("perfmon: process %d has no PFM context\n", current->pid); return; } if (ctx->ctx_notify_task == 0) { printk("perfmon: process %d has no task to notify\n", current->pid); return; } DBprintk((" current=%d task=%d\n", current->pid, ctx->ctx_notify_task->pid)); /* should not happen */ if (CTX_OVFL_NOBLOCK(ctx)) { printk("perfmon: process %d non-blocking ctx should not be here\n", current->pid); return; } DBprintk((" CPU%d %d before sleep\n", smp_processor_id(), current->pid)); /* * may go through without blocking on SMP systems * if restart has been received already by the time we call down() */ ret = down_interruptible(&ctx->ctx_restart_sem); DBprintk((" CPU%d %d after sleep ret=%d\n", smp_processor_id(), current->pid, ret)); /* * in case of interruption of down() we don't restart anything */ if (ret >= 0) { /* we reactivate on context switch */ ctx->ctx_fl_frozen = 0; /* * the ovfl_sem is cleared by the restart task and this is safe because we always * use the local reference */ pfm_reset_regs(ctx); /* * Unlock sampling buffer and reset index atomically * XXX: not really needed when blocking */ if (CTX_HAS_SMPL(ctx)) { ctx->ctx_smpl_buf->psb_hdr->hdr_count = 0; ctx->ctx_smpl_buf->psb_index = 0; } DBprintk((" CPU%d %d unfreeze PMU\n", smp_processor_id(), current->pid)); ia64_set_pmc(0, 0); ia64_srlz_d(); /* state restored, can go back to work (user mode) */ }}/* * main overflow processing routine. * it can be called from the interrupt path or explicitely during the context switch code * Return:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -