op_model_cell.c
来自「linux 内核源代码」· C语言 代码 · 共 1,212 行 · 第 1/3 页
C
1,212 行
/* * Cell Broadband Engine OProfile Support * * (C) Copyright IBM Corporation 2006 * * Author: David Erb (djerb@us.ibm.com) * Modifications: * Carl Love <carll@us.ibm.com> * Maynard Johnson <maynardj@us.ibm.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/cpufreq.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/jiffies.h>#include <linux/kthread.h>#include <linux/oprofile.h>#include <linux/percpu.h>#include <linux/smp.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <asm/cell-pmu.h>#include <asm/cputable.h>#include <asm/firmware.h>#include <asm/io.h>#include <asm/oprofile_impl.h>#include <asm/processor.h>#include <asm/prom.h>#include <asm/ptrace.h>#include <asm/reg.h>#include <asm/rtas.h>#include <asm/system.h>#include <asm/cell-regs.h>#include "../platforms/cell/interrupt.h"#include "cell/pr_util.h"static void cell_global_stop_spu(void);/* * spu_cycle_reset is the number of cycles between samples. * This variable is used for SPU profiling and should ONLY be set * at the beginning of cell_reg_setup; otherwise, it's read-only. */static unsigned int spu_cycle_reset;#define NUM_SPUS_PER_NODE 8#define SPU_CYCLES_EVENT_NUM 2 /* event number for SPU_CYCLES */#define PPU_CYCLES_EVENT_NUM 1 /* event number for CYCLES */#define PPU_CYCLES_GRP_NUM 1 /* special group number for identifying * PPU_CYCLES event */#define CBE_COUNT_ALL_CYCLES 0x42800000 /* PPU cycle event specifier */#define NUM_THREADS 2 /* number of physical threads in * physical processor */#define NUM_TRACE_BUS_WORDS 4#define NUM_INPUT_BUS_WORDS 2#define MAX_SPU_COUNT 0xFFFFFF /* maximum 24 bit LFSR value */struct pmc_cntrl_data { unsigned long vcntr; unsigned long evnts; unsigned long masks; unsigned long enabled;};/* * ibm,cbe-perftools rtas parameters */struct pm_signal { u16 cpu; /* Processor to modify */ u16 sub_unit; /* hw subunit this applies to (if applicable)*/ short int signal_group; /* Signal Group to Enable/Disable */ u8 bus_word; /* Enable/Disable on this Trace/Trigger/Event * Bus Word(s) (bitmask) */ u8 bit; /* Trigger/Event bit (if applicable) */};/* * rtas call arguments */enum { SUBFUNC_RESET = 1, SUBFUNC_ACTIVATE = 2, SUBFUNC_DEACTIVATE = 3, PASSTHRU_IGNORE = 0, PASSTHRU_ENABLE = 1, PASSTHRU_DISABLE = 2,};struct pm_cntrl { u16 enable; u16 stop_at_max; u16 trace_mode; u16 freeze; u16 count_mode;};static struct { u32 group_control; u32 debug_bus_control; struct pm_cntrl pm_cntrl; u32 pm07_cntrl[NR_PHYS_CTRS];} pm_regs;#define GET_SUB_UNIT(x) ((x & 0x0000f000) >> 12)#define GET_BUS_WORD(x) ((x & 0x000000f0) >> 4)#define GET_BUS_TYPE(x) ((x & 0x00000300) >> 8)#define GET_POLARITY(x) ((x & 0x00000002) >> 1)#define GET_COUNT_CYCLES(x) (x & 0x00000001)#define GET_INPUT_CONTROL(x) ((x & 0x00000004) >> 2)static DEFINE_PER_CPU(unsigned long[NR_PHYS_CTRS], pmc_values);static struct pmc_cntrl_data pmc_cntrl[NUM_THREADS][NR_PHYS_CTRS];/* * The CELL profiling code makes rtas calls to setup the debug bus to * route the performance signals. Additionally, SPU profiling requires * a second rtas call to setup the hardware to capture the SPU PCs. * The EIO error value is returned if the token lookups or the rtas * call fail. The EIO error number is the best choice of the existing * error numbers. The probability of rtas related error is very low. But * by returning EIO and printing additional information to dmsg the user * will know that OProfile did not start and dmesg will tell them why. * OProfile does not support returning errors on Stop. Not a huge issue * since failure to reset the debug bus or stop the SPU PC collection is * not a fatel issue. Chances are if the Stop failed, Start doesn't work * either. *//* * Interpetation of hdw_thread: * 0 - even virtual cpus 0, 2, 4,... * 1 - odd virtual cpus 1, 3, 5, ... * * FIXME: this is strictly wrong, we need to clean this up in a number * of places. It works for now. -arnd */static u32 hdw_thread;static u32 virt_cntr_inter_mask;static struct timer_list timer_virt_cntr;/* * pm_signal needs to be global since it is initialized in * cell_reg_setup at the time when the necessary information * is available. */static struct pm_signal pm_signal[NR_PHYS_CTRS];static int pm_rtas_token; /* token for debug bus setup call */static int spu_rtas_token; /* token for SPU cycle profiling */static u32 reset_value[NR_PHYS_CTRS];static int num_counters;static int oprofile_running;static DEFINE_SPINLOCK(virt_cntr_lock);static u32 ctr_enabled;static unsigned char trace_bus[NUM_TRACE_BUS_WORDS];static unsigned char input_bus[NUM_INPUT_BUS_WORDS];/* * Firmware interface functions */static intrtas_ibm_cbe_perftools(int subfunc, int passthru, void *address, unsigned long length){ u64 paddr = __pa(address); return rtas_call(pm_rtas_token, 5, 1, NULL, subfunc, passthru, paddr >> 32, paddr & 0xffffffff, length);}static void pm_rtas_reset_signals(u32 node){ int ret; struct pm_signal pm_signal_local; /* * The debug bus is being set to the passthru disable state. * However, the FW still expects atleast one legal signal routing * entry or it will return an error on the arguments. If we don't * supply a valid entry, we must ignore all return values. Ignoring * all return values means we might miss an error we should be * concerned about. */ /* fw expects physical cpu #. */ pm_signal_local.cpu = node; pm_signal_local.signal_group = 21; pm_signal_local.bus_word = 1; pm_signal_local.sub_unit = 0; pm_signal_local.bit = 0; ret = rtas_ibm_cbe_perftools(SUBFUNC_RESET, PASSTHRU_DISABLE, &pm_signal_local, sizeof(struct pm_signal)); if (unlikely(ret)) /* * Not a fatal error. For Oprofile stop, the oprofile * functions do not support returning an error for * failure to stop OProfile. */ printk(KERN_WARNING "%s: rtas returned: %d\n", __FUNCTION__, ret);}static int pm_rtas_activate_signals(u32 node, u32 count){ int ret; int i, j; struct pm_signal pm_signal_local[NR_PHYS_CTRS]; /* * There is no debug setup required for the cycles event. * Note that only events in the same group can be used. * Otherwise, there will be conflicts in correctly routing * the signals on the debug bus. It is the responsiblity * of the OProfile user tool to check the events are in * the same group. */ i = 0; for (j = 0; j < count; j++) { if (pm_signal[j].signal_group != PPU_CYCLES_GRP_NUM) { /* fw expects physical cpu # */ pm_signal_local[i].cpu = node; pm_signal_local[i].signal_group = pm_signal[j].signal_group; pm_signal_local[i].bus_word = pm_signal[j].bus_word; pm_signal_local[i].sub_unit = pm_signal[j].sub_unit; pm_signal_local[i].bit = pm_signal[j].bit; i++; } } if (i != 0) { ret = rtas_ibm_cbe_perftools(SUBFUNC_ACTIVATE, PASSTHRU_ENABLE, pm_signal_local, i * sizeof(struct pm_signal)); if (unlikely(ret)) { printk(KERN_WARNING "%s: rtas returned: %d\n", __FUNCTION__, ret); return -EIO; } } return 0;}/* * PM Signal functions */static void set_pm_event(u32 ctr, int event, u32 unit_mask){ struct pm_signal *p; u32 signal_bit; u32 bus_word, bus_type, count_cycles, polarity, input_control; int j, i; if (event == PPU_CYCLES_EVENT_NUM) { /* Special Event: Count all cpu cycles */ pm_regs.pm07_cntrl[ctr] = CBE_COUNT_ALL_CYCLES; p = &(pm_signal[ctr]); p->signal_group = PPU_CYCLES_GRP_NUM; p->bus_word = 1; p->sub_unit = 0; p->bit = 0; goto out; } else { pm_regs.pm07_cntrl[ctr] = 0; } bus_word = GET_BUS_WORD(unit_mask); bus_type = GET_BUS_TYPE(unit_mask); count_cycles = GET_COUNT_CYCLES(unit_mask); polarity = GET_POLARITY(unit_mask); input_control = GET_INPUT_CONTROL(unit_mask); signal_bit = (event % 100); p = &(pm_signal[ctr]); p->signal_group = event / 100; p->bus_word = bus_word; p->sub_unit = (unit_mask & 0x0000f000) >> 12; pm_regs.pm07_cntrl[ctr] = 0; pm_regs.pm07_cntrl[ctr] |= PM07_CTR_COUNT_CYCLES(count_cycles); pm_regs.pm07_cntrl[ctr] |= PM07_CTR_POLARITY(polarity); pm_regs.pm07_cntrl[ctr] |= PM07_CTR_INPUT_CONTROL(input_control); /* * Some of the islands signal selection is based on 64 bit words. * The debug bus words are 32 bits, the input words to the performance * counters are defined as 32 bits. Need to convert the 64 bit island * specification to the appropriate 32 input bit and bus word for the * performance counter event selection. See the CELL Performance * monitoring signals manual and the Perf cntr hardware descriptions * for the details. */ if (input_control == 0) { if (signal_bit > 31) { signal_bit -= 32; if (bus_word == 0x3) bus_word = 0x2; else if (bus_word == 0xc) bus_word = 0x8; } if ((bus_type == 0) && p->signal_group >= 60) bus_type = 2; if ((bus_type == 1) && p->signal_group >= 50) bus_type = 0; pm_regs.pm07_cntrl[ctr] |= PM07_CTR_INPUT_MUX(signal_bit); } else { pm_regs.pm07_cntrl[ctr] = 0; p->bit = signal_bit; } for (i = 0; i < NUM_TRACE_BUS_WORDS; i++) { if (bus_word & (1 << i)) { pm_regs.debug_bus_control |= (bus_type << (31 - (2 * i) + 1)); for (j = 0; j < NUM_INPUT_BUS_WORDS; j++) { if (input_bus[j] == 0xff) { input_bus[j] = i; pm_regs.group_control |= (i << (31 - i)); break; } } } }out: ;}static void write_pm_cntrl(int cpu){ /* * Oprofile will use 32 bit counters, set bits 7:10 to 0 * pmregs.pm_cntrl is a global */ u32 val = 0; if (pm_regs.pm_cntrl.enable == 1) val |= CBE_PM_ENABLE_PERF_MON; if (pm_regs.pm_cntrl.stop_at_max == 1) val |= CBE_PM_STOP_AT_MAX; if (pm_regs.pm_cntrl.trace_mode == 1) val |= CBE_PM_TRACE_MODE_SET(pm_regs.pm_cntrl.trace_mode); if (pm_regs.pm_cntrl.freeze == 1) val |= CBE_PM_FREEZE_ALL_CTRS; /* * Routine set_count_mode must be called previously to set * the count mode based on the user selection of user and kernel. */ val |= CBE_PM_COUNT_MODE_SET(pm_regs.pm_cntrl.count_mode); cbe_write_pm(cpu, pm_control, val);}static inline voidset_count_mode(u32 kernel, u32 user){ /* * The user must specify user and kernel if they want them. If * neither is specified, OProfile will count in hypervisor mode. * pm_regs.pm_cntrl is a global */ if (kernel) { if (user) pm_regs.pm_cntrl.count_mode = CBE_COUNT_ALL_MODES; else pm_regs.pm_cntrl.count_mode = CBE_COUNT_SUPERVISOR_MODE; } else { if (user) pm_regs.pm_cntrl.count_mode = CBE_COUNT_PROBLEM_MODE; else pm_regs.pm_cntrl.count_mode = CBE_COUNT_HYPERVISOR_MODE;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?