inline void perfctr_cpu_suspend(struct perfctr_cpu_state *state) { unsigned int i, cstatus, nractrs; struct perfctr_low_ctrs now; int cpu_id; cpu_id = hard_smp_processor_id() / 4; spin_lock (&pmc_resource[cpu_id].lock); if ( pmc_resource[cpu_id].current_thread != netlogic_thr_id() ) { // printk (KERN_INFO "PMCounters do not belong to this process[%d]\n", current->pid); spin_unlock (&pmc_resource[cpu_id].lock); return; } pmc_resource[cpu_id].current_thread = -1; spin_unlock (&pmc_resource[cpu_id].lock); // To prevent polluting the numbers, can we freeze the counters // here, as early as possible ? if (perfctr_cstatus_has_ictrs(state->cstatus)) { perfctr_cpu_isuspend(state); } perfctr_cpu_read_counters(state, &now); cstatus = state->cstatus; if (perfctr_cstatus_has_tsc(cstatus)) { state->tsc_sum += now.tsc - state->tsc_start; } nractrs = perfctr_cstatus_nractrs(cstatus); for(i = 0; i < nractrs; ++i) { state->pmc[i].sum += now.pmc[i] - state->pmc[i].start; } }
static int sys_vperfctr_iresume(struct vperfctr *perfctr, const struct task_struct *tsk) { #ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT unsigned int iresume_cstatus; if (!tsk) return -ESRCH; /* attempt to update unlinked perfctr */ iresume_cstatus = perfctr->iresume_cstatus; if (!perfctr_cstatus_has_ictrs(iresume_cstatus)) return -EPERM; /* PREEMPT note: preemption is disabled over the entire region because we're updating an active perfctr. */ preempt_disable(); if (IS_RUNNING(perfctr) && tsk == current) vperfctr_suspend(perfctr); perfctr->cpu_state.cstatus = iresume_cstatus; perfctr->iresume_cstatus = 0; /* remote access note: perfctr_cpu_ireload() is ok */ perfctr_cpu_ireload(&perfctr->cpu_state); if (tsk == current) vperfctr_resume(perfctr); preempt_enable(); return 0; #else return -ENOSYS; #endif }
static int do_vperfctr_iresume(struct vperfctr *perfctr, const struct task_struct *tsk) { #ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT unsigned int iresume_cstatus; if (!tsk) return -ESRCH; /* attempt to update unlinked perfctr */ iresume_cstatus = perfctr->iresume_cstatus; if (!perfctr_cstatus_has_ictrs(iresume_cstatus)) { return -EPERM; } /* PREEMPT note: preemption is disabled over the entire region because we're updating an active perfctr. */ preempt_disable(); // this is for resuming a task whose signal was handled prior to this call // are the i-mode counters frozen before the overflow-signal is delivered // yes, they are. in the suspend call invoked in the handler // why exactly are we suspending the following? Makes sense ... if the // counters are already running, then one should not just resume the task // which will overwrite the PMC registers with old values. Nice. Under // what condition do counters continue to count after the signal is delivered // remember TSC was not suspend in the handler and continues to count if (IS_RUNNING(perfctr) && tsk == current) vperfctr_suspend(perfctr); // setting the cstatus of 'cpu_state' back to what it was prior to its // zeroing out in the interrupt handler perfctr->cpu_state.cstatus = iresume_cstatus; perfctr->iresume_cstatus = 0; /* remote access note: perfctr_cpu_ireload() is ok */ // the following forces the reload of control registers that // unfreezes the i-mode registers perfctr_cpu_ireload(&perfctr->cpu_state); if (tsk == current) vperfctr_resume(perfctr); preempt_enable(); return 0; #else return -ENOSYS; #endif }
inline void perfctr_cpu_resume(struct perfctr_cpu_state *state) { int cpu_id; cpu_id = hard_smp_processor_id() / 4; spin_lock (&pmc_resource[cpu_id].lock); if ( pmc_resource[cpu_id].current_thread != -1 ) { // printk (KERN_INFO "PMCounters unavailable for process %d\n", current->pid); spin_unlock (&pmc_resource[cpu_id].lock); return; } pmc_resource[cpu_id].current_thread = netlogic_thr_id(); spin_unlock (&pmc_resource[cpu_id].lock); if (perfctr_cstatus_has_ictrs(state->cstatus)) { perfctr_cpu_iresume(state); } // the counters are triggered, having been frozen in _iresume() // that preceded this point. So, the model is to trigger the // registere to collect the numbers and record the start state // that completes the 'resume' process. perfctr_cpu_write_control(state); { struct perfctr_low_ctrs now; unsigned int i, cstatus, nrctrs; perfctr_cpu_read_counters(state, &now); cstatus = state->cstatus; // the start state of the registers has to be recorded only // in resume() and that is what is being done. if (perfctr_cstatus_has_tsc(cstatus)) { state->tsc_start = now.tsc; } nrctrs = perfctr_cstatus_nractrs(cstatus); for (i = 0; i < nrctrs; ++i) { state->pmc[i].start = now.pmc[i]; } } /* XXX: if (SMP && start.tsc == now.tsc) ++now.tsc; */ }
/* PREEMPT note: called in IRQ context with preemption disabled. */ static void vperfctr_ihandler(unsigned long pc) { struct task_struct *tsk = current; struct vperfctr *perfctr; perfctr = tsk->arch.thread.perfctr; if (!perfctr) { printk(KERN_ERR "%s: BUG! pid %d has no vperfctr\n", __FUNCTION__, tsk->id); return; } if (!perfctr_cstatus_has_ictrs(perfctr->cpu_state.user.cstatus)) { printk(KERN_ERR "%s: BUG! vperfctr has cstatus %#x (pid %d, comm %s)\n", __FUNCTION__, perfctr->cpu_state.user.cstatus, tsk->id, tsk->comm); return; } vperfctr_suspend(perfctr); vperfctr_handle_overflow(tsk, perfctr); }
// PREEMPT note: called in IRQ context with preemption disabled. static void vperfctr_ihandler(unsigned long pc) { struct task_struct *tsk = current; struct vperfctr *perfctr; unsigned int pmc, cstatus, now = 0; int i; perfctr = tsk->thread.perfctr; if (!perfctr) { return; } if (!perfctr_cstatus_has_ictrs(perfctr->cpu_state.cstatus)) { return; } // if someone has really overflown then continue else return // just read, don't freeze them cstatus = perfctr->cpu_state.cstatus; for (i = perfctr_cstatus_nractrs(cstatus); (i < perfctr_cstatus_nrctrs(cstatus)) && ((int)now >= 0); ++i) { pmc = perfctr->cpu_state.pmc[i].map; now = read_pmc(pmc); } if ((int)now >= 0) { return; } // Fine, we are suspending the counters and reading them. vperfctr_suspend() // in turn invokes _suspend() on i-mode ctrs (where they are frozen and read) // and a-mode counters (where they are just read) vperfctr_suspend(perfctr); // Ok, Signal to the userland is sent in the following routine. But before that // the following routine calls vperfctr_resume() if the TSC counting is on. // what happens in that resume is just the TSC value is read and stored in the // 'start' state of the TSC vperfctr_handle_overflow(tsk, perfctr); }