op_model_cell.c
来自「linux 内核源代码」· C语言 代码 · 共 1,212 行 · 第 1/3 页
C
1,212 行
if ((n >> 16) == 0) index = 0; else if (((n - V2_16) >> 19) == 0) index = ((n - V2_16) >> 12) + 1; else if (((n - V2_16 - V2_19) >> 22) == 0) index = ((n - V2_16 - V2_19) >> 15 ) + 1 + 128; else if (((n - V2_16 - V2_19 - V2_22) >> 24) == 0) index = ((n - V2_16 - V2_19 - V2_22) >> 18 ) + 1 + 256; else index = ENTRIES-1; /* make sure index is valid */ if ((index > ENTRIES) || (index < 0)) index = ENTRIES-1; return initial_lfsr[index];}static int pm_rtas_activate_spu_profiling(u32 node){ int ret, i; struct pm_signal pm_signal_local[NR_PHYS_CTRS]; /* * Set up the rtas call to configure the debug bus to * route the SPU PCs. Setup the pm_signal for each SPU */ for (i = 0; i < NUM_SPUS_PER_NODE; i++) { pm_signal_local[i].cpu = node; pm_signal_local[i].signal_group = 41; /* spu i on word (i/2) */ pm_signal_local[i].bus_word = 1 << i / 2; /* spu i */ pm_signal_local[i].sub_unit = i; pm_signal_local[i].bit = 63; } ret = rtas_ibm_cbe_perftools(SUBFUNC_ACTIVATE, PASSTHRU_ENABLE, pm_signal_local, (NUM_SPUS_PER_NODE * sizeof(struct pm_signal))); if (unlikely(ret)) { printk(KERN_WARNING "%s: rtas returned: %d\n", __FUNCTION__, ret); return -EIO; } return 0;}#ifdef CONFIG_CPU_FREQstatic intoprof_cpufreq_notify(struct notifier_block *nb, unsigned long val, void *data){ int ret = 0; struct cpufreq_freqs *frq = data; if ((val == CPUFREQ_PRECHANGE && frq->old < frq->new) || (val == CPUFREQ_POSTCHANGE && frq->old > frq->new) || (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) set_spu_profiling_frequency(frq->new, spu_cycle_reset); return ret;}static struct notifier_block cpu_freq_notifier_block = { .notifier_call = oprof_cpufreq_notify};#endifstatic int cell_global_start_spu(struct op_counter_config *ctr){ int subfunc; unsigned int lfsr_value; int cpu; int ret; int rtas_error; unsigned int cpu_khzfreq = 0; /* The SPU profiling uses time-based profiling based on * cpu frequency, so if configured with the CPU_FREQ * option, we should detect frequency changes and react * accordingly. */#ifdef CONFIG_CPU_FREQ ret = cpufreq_register_notifier(&cpu_freq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); if (ret < 0) /* this is not a fatal error */ printk(KERN_ERR "CPU freq change registration failed: %d\n", ret); else cpu_khzfreq = cpufreq_quick_get(smp_processor_id());#endif set_spu_profiling_frequency(cpu_khzfreq, spu_cycle_reset); for_each_online_cpu(cpu) { if (cbe_get_hw_thread_id(cpu)) continue; /* * Setup SPU cycle-based profiling. * Set perf_mon_control bit 0 to a zero before * enabling spu collection hardware. */ cbe_write_pm(cpu, pm_control, 0); if (spu_cycle_reset > MAX_SPU_COUNT) /* use largest possible value */ lfsr_value = calculate_lfsr(MAX_SPU_COUNT-1); else lfsr_value = calculate_lfsr(spu_cycle_reset); /* must use a non zero value. Zero disables data collection. */ if (lfsr_value == 0) lfsr_value = calculate_lfsr(1); lfsr_value = lfsr_value << 8; /* shift lfsr to correct * register location */ /* debug bus setup */ ret = pm_rtas_activate_spu_profiling(cbe_cpu_to_node(cpu)); if (unlikely(ret)) { rtas_error = ret; goto out; } subfunc = 2; /* 2 - activate SPU tracing, 3 - deactivate */ /* start profiling */ ret = rtas_call(spu_rtas_token, 3, 1, NULL, subfunc, cbe_cpu_to_node(cpu), lfsr_value); if (unlikely(ret != 0)) { printk(KERN_ERR "%s: rtas call ibm,cbe-spu-perftools failed, return = %d\n", __FUNCTION__, ret); rtas_error = -EIO; goto out; } } rtas_error = start_spu_profiling(spu_cycle_reset); if (rtas_error) goto out_stop; oprofile_running = 1; return 0;out_stop: cell_global_stop_spu(); /* clean up the PMU/debug bus */out: return rtas_error;}static int cell_global_start_ppu(struct op_counter_config *ctr){ u32 cpu, i; u32 interrupt_mask = 0; /* This routine gets called once for the system. * There is one performance monitor per node, so we * only need to perform this function once per node. */ for_each_online_cpu(cpu) { if (cbe_get_hw_thread_id(cpu)) continue; interrupt_mask = 0; for (i = 0; i < num_counters; ++i) { if (ctr_enabled & (1 << i)) { cbe_write_ctr(cpu, i, reset_value[i]); enable_ctr(cpu, i, pm_regs.pm07_cntrl); interrupt_mask |= CBE_PM_CTR_OVERFLOW_INTR(i); } else { /* Disable counter */ cbe_write_pm07_control(cpu, i, 0); } } cbe_get_and_clear_pm_interrupts(cpu); cbe_enable_pm_interrupts(cpu, hdw_thread, interrupt_mask); cbe_enable_pm(cpu); } virt_cntr_inter_mask = interrupt_mask; oprofile_running = 1; smp_wmb(); /* * NOTE: start_virt_cntrs will result in cell_virtual_cntr() being * executed which manipulates the PMU. We start the "virtual counter" * here so that we do not need to synchronize access to the PMU in * the above for-loop. */ start_virt_cntrs(); return 0;}static int cell_global_start(struct op_counter_config *ctr){ if (spu_cycle_reset) return cell_global_start_spu(ctr); else return cell_global_start_ppu(ctr);}/* * Note the generic OProfile stop calls do not support returning * an error on stop. Hence, will not return an error if the FW * calls fail on stop. Failure to reset the debug bus is not an issue. * Failure to disable the SPU profiling is not an issue. The FW calls * to enable the performance counters and debug bus will work even if * the hardware was not cleanly reset. */static void cell_global_stop_spu(void){ int subfunc, rtn_value; unsigned int lfsr_value; int cpu; oprofile_running = 0;#ifdef CONFIG_CPU_FREQ cpufreq_unregister_notifier(&cpu_freq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);#endif for_each_online_cpu(cpu) { if (cbe_get_hw_thread_id(cpu)) continue; subfunc = 3; /* * 2 - activate SPU tracing, * 3 - deactivate */ lfsr_value = 0x8f100000; rtn_value = rtas_call(spu_rtas_token, 3, 1, NULL, subfunc, cbe_cpu_to_node(cpu), lfsr_value); if (unlikely(rtn_value != 0)) { printk(KERN_ERR "%s: rtas call ibm,cbe-spu-perftools failed, return = %d\n", __FUNCTION__, rtn_value); } /* Deactivate the signals */ pm_rtas_reset_signals(cbe_cpu_to_node(cpu)); } stop_spu_profiling();}static void cell_global_stop_ppu(void){ int cpu; /* * This routine will be called once for the system. * There is one performance monitor per node, so we * only need to perform this function once per node. */ del_timer_sync(&timer_virt_cntr); oprofile_running = 0; smp_wmb(); for_each_online_cpu(cpu) { if (cbe_get_hw_thread_id(cpu)) continue; cbe_sync_irq(cbe_cpu_to_node(cpu)); /* Stop the counters */ cbe_disable_pm(cpu); /* Deactivate the signals */ pm_rtas_reset_signals(cbe_cpu_to_node(cpu)); /* Deactivate interrupts */ cbe_disable_pm_interrupts(cpu); }}static void cell_global_stop(void){ if (spu_cycle_reset) cell_global_stop_spu(); else cell_global_stop_ppu();}static void cell_handle_interrupt(struct pt_regs *regs, struct op_counter_config *ctr){ u32 cpu; u64 pc; int is_kernel; unsigned long flags = 0; u32 interrupt_mask; int i; cpu = smp_processor_id(); /* * Need to make sure the interrupt handler and the virt counter * routine are not running at the same time. See the * cell_virtual_cntr() routine for additional comments. */ spin_lock_irqsave(&virt_cntr_lock, flags); /* * Need to disable and reenable the performance counters * to get the desired behavior from the hardware. This * is hardware specific. */ cbe_disable_pm(cpu); interrupt_mask = cbe_get_and_clear_pm_interrupts(cpu); /* * If the interrupt mask has been cleared, then the virt cntr * has cleared the interrupt. When the thread that generated * the interrupt is restored, the data count will be restored to * 0xffffff0 to cause the interrupt to be regenerated. */ if ((oprofile_running == 1) && (interrupt_mask != 0)) { pc = regs->nip; is_kernel = is_kernel_addr(pc); for (i = 0; i < num_counters; ++i) { if ((interrupt_mask & CBE_PM_CTR_OVERFLOW_INTR(i)) && ctr[i].enabled) { oprofile_add_pc(pc, is_kernel, i); cbe_write_ctr(cpu, i, reset_value[i]); } } /* * The counters were frozen by the interrupt. * Reenable the interrupt and restart the counters. * If there was a race between the interrupt handler and * the virtual counter routine. The virutal counter * routine may have cleared the interrupts. Hence must * use the virt_cntr_inter_mask to re-enable the interrupts. */ cbe_enable_pm_interrupts(cpu, hdw_thread, virt_cntr_inter_mask); /* * The writes to the various performance counters only writes * to a latch. The new values (interrupt setting bits, reset * counter value etc.) are not copied to the actual registers * until the performance monitor is enabled. In order to get * this to work as desired, the permormance monitor needs to * be disabled while writing to the latches. This is a * HW design issue. */ cbe_enable_pm(cpu); } spin_unlock_irqrestore(&virt_cntr_lock, flags);}/* * This function is called from the generic OProfile * driver. When profiling PPUs, we need to do the * generic sync start; otherwise, do spu_sync_start. */static int cell_sync_start(void){ if (spu_cycle_reset) return spu_sync_start(); else return DO_GENERIC_SYNC;}static int cell_sync_stop(void){ if (spu_cycle_reset) return spu_sync_stop(); else return 1;}struct op_powerpc_model op_model_cell = { .reg_setup = cell_reg_setup, .cpu_setup = cell_cpu_setup, .global_start = cell_global_start, .global_stop = cell_global_stop, .sync_start = cell_sync_start, .sync_stop = cell_sync_stop, .handle_interrupt = cell_handle_interrupt,};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?