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 + -
显示快捷键?