Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci
Commits
a076aa4f
Commit
a076aa4f
authored
Jan 08, 2009
by
Robert Richter
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'oprofile/cell' into oprofile/oprofile-for-tip
parents
d2852b93
25006644
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
705 additions
and
140 deletions
+705
-140
arch/powerpc/include/asm/cell-pmu.h
arch/powerpc/include/asm/cell-pmu.h
+2
-0
arch/powerpc/include/asm/oprofile_impl.h
arch/powerpc/include/asm/oprofile_impl.h
+6
-0
arch/powerpc/oprofile/cell/pr_util.h
arch/powerpc/oprofile/cell/pr_util.h
+8
-3
arch/powerpc/oprofile/cell/spu_profiler.c
arch/powerpc/oprofile/cell/spu_profiler.c
+43
-13
arch/powerpc/oprofile/common.c
arch/powerpc/oprofile/common.c
+22
-0
arch/powerpc/oprofile/op_model_cell.c
arch/powerpc/oprofile/op_model_cell.c
+624
-124
No files found.
arch/powerpc/include/asm/cell-pmu.h
View file @
a076aa4f
...
...
@@ -37,9 +37,11 @@
#define CBE_PM_STOP_AT_MAX 0x40000000
#define CBE_PM_TRACE_MODE_GET(pm_control) (((pm_control) >> 28) & 0x3)
#define CBE_PM_TRACE_MODE_SET(mode) (((mode) & 0x3) << 28)
#define CBE_PM_TRACE_BUF_OVFLW(bit) (((bit) & 0x1) << 17)
#define CBE_PM_COUNT_MODE_SET(count) (((count) & 0x3) << 18)
#define CBE_PM_FREEZE_ALL_CTRS 0x00100000
#define CBE_PM_ENABLE_EXT_TRACE 0x00008000
#define CBE_PM_SPU_ADDR_TRACE_SET(msk) (((msk) & 0x3) << 9)
/* Macros for the trace_address register. */
#define CBE_PM_TRACE_BUF_FULL 0x00000800
...
...
arch/powerpc/include/asm/oprofile_impl.h
View file @
a076aa4f
...
...
@@ -32,6 +32,12 @@ struct op_system_config {
unsigned
long
mmcr0
;
unsigned
long
mmcr1
;
unsigned
long
mmcra
;
#ifdef CONFIG_OPROFILE_CELL
/* Register for oprofile user tool to check cell kernel profiling
* suport.
*/
unsigned
long
cell_support
;
#endif
#endif
unsigned
long
enable_kernel
;
unsigned
long
enable_user
;
...
...
arch/powerpc/oprofile/cell/pr_util.h
View file @
a076aa4f
...
...
@@ -30,6 +30,10 @@
extern
struct
delayed_work
spu_work
;
extern
int
spu_prof_running
;
#define TRACE_ARRAY_SIZE 1024
extern
spinlock_t
oprof_spu_smpl_arry_lck
;
struct
spu_overlay_info
{
/* map of sections within an SPU overlay */
unsigned
int
vma
;
/* SPU virtual memory address from elf */
unsigned
int
size
;
/* size of section from elf */
...
...
@@ -89,10 +93,11 @@ void vma_map_free(struct vma_to_fileoffset_map *map);
* Entry point for SPU profiling.
* cycles_reset is the SPU_CYCLES count value specified by the user.
*/
int
start_spu_profiling
(
unsigned
int
cycles_reset
);
void
stop_spu_profiling
(
void
);
int
start_spu_profiling_cycles
(
unsigned
int
cycles_reset
);
void
start_spu_profiling_events
(
void
);
void
stop_spu_profiling_cycles
(
void
);
void
stop_spu_profiling_events
(
void
);
/* add the necessary profiling hooks */
int
spu_sync_start
(
void
);
...
...
arch/powerpc/oprofile/cell/spu_profiler.c
View file @
a076aa4f
...
...
@@ -18,11 +18,21 @@
#include <asm/cell-pmu.h>
#include "pr_util.h"
#define TRACE_ARRAY_SIZE 1024
#define SCALE_SHIFT 14
static
u32
*
samples
;
/* spu_prof_running is a flag used to indicate if spu profiling is enabled
* or not. It is set by the routines start_spu_profiling_cycles() and
* start_spu_profiling_events(). The flag is cleared by the routines
* stop_spu_profiling_cycles() and stop_spu_profiling_events(). These
* routines are called via global_start() and global_stop() which are called in
* op_powerpc_start() and op_powerpc_stop(). These routines are called once
* per system as a result of the user starting/stopping oprofile. Hence, only
* one CPU per user at a time will be changing the value of spu_prof_running.
* In general, OProfile does not protect against multiple users trying to run
* OProfile at a time.
*/
int
spu_prof_running
;
static
unsigned
int
profiling_interval
;
...
...
@@ -31,8 +41,8 @@ static unsigned int profiling_interval;
#define SPU_PC_MASK 0xFFFF
static
DEFINE_SPINLOCK
(
sample_array_lo
ck
);
unsigned
long
sample_array_lo
ck_flags
;
DEFINE_SPINLOCK
(
oprof_spu_smpl_arry_l
ck
);
unsigned
long
oprof_spu_smpl_arry_l
ck_flags
;
void
set_spu_profiling_frequency
(
unsigned
int
freq_khz
,
unsigned
int
cycles_reset
)
{
...
...
@@ -145,13 +155,13 @@ static enum hrtimer_restart profile_spus(struct hrtimer *timer)
* sample array must be loaded and then processed for a given
* cpu. The sample array is not per cpu.
*/
spin_lock_irqsave
(
&
sample_array_lo
ck
,
sample_array_lo
ck_flags
);
spin_lock_irqsave
(
&
oprof_spu_smpl_arry_l
ck
,
oprof_spu_smpl_arry_l
ck_flags
);
num_samples
=
cell_spu_pc_collection
(
cpu
);
if
(
num_samples
==
0
)
{
spin_unlock_irqrestore
(
&
sample_array_lo
ck
,
sample_array_lo
ck_flags
);
spin_unlock_irqrestore
(
&
oprof_spu_smpl_arry_l
ck
,
oprof_spu_smpl_arry_l
ck_flags
);
continue
;
}
...
...
@@ -162,8 +172,8 @@ static enum hrtimer_restart profile_spus(struct hrtimer *timer)
num_samples
);
}
spin_unlock_irqrestore
(
&
sample_array_lo
ck
,
sample_array_lo
ck_flags
);
spin_unlock_irqrestore
(
&
oprof_spu_smpl_arry_l
ck
,
oprof_spu_smpl_arry_l
ck_flags
);
}
smp_wmb
();
/* insure spu event buffer updates are written */
...
...
@@ -182,13 +192,13 @@ static enum hrtimer_restart profile_spus(struct hrtimer *timer)
static
struct
hrtimer
timer
;
/*
* Entry point for SPU profiling.
* Entry point for SPU
cycle
profiling.
* NOTE: SPU profiling is done system-wide, not per-CPU.
*
* cycles_reset is the count value specified by the user when
* setting up OProfile to count SPU_CYCLES.
*/
int
start_spu_profiling
(
unsigned
int
cycles_reset
)
int
start_spu_profiling
_cycles
(
unsigned
int
cycles_reset
)
{
ktime_t
kt
;
...
...
@@ -212,10 +222,30 @@ int start_spu_profiling(unsigned int cycles_reset)
return
0
;
}
void
stop_spu_profiling
(
void
)
/*
* Entry point for SPU event profiling.
* NOTE: SPU profiling is done system-wide, not per-CPU.
*
* cycles_reset is the count value specified by the user when
* setting up OProfile to count SPU_CYCLES.
*/
void
start_spu_profiling_events
(
void
)
{
spu_prof_running
=
1
;
schedule_delayed_work
(
&
spu_work
,
DEFAULT_TIMER_EXPIRE
);
return
;
}
void
stop_spu_profiling_cycles
(
void
)
{
spu_prof_running
=
0
;
hrtimer_cancel
(
&
timer
);
kfree
(
samples
);
pr_debug
(
"SPU_PROF: stop_spu_profiling issued
\n
"
);
pr_debug
(
"SPU_PROF: stop_spu_profiling_cycles issued
\n
"
);
}
void
stop_spu_profiling_events
(
void
)
{
spu_prof_running
=
0
;
}
arch/powerpc/oprofile/common.c
View file @
a076aa4f
...
...
@@ -132,6 +132,28 @@ static int op_powerpc_create_files(struct super_block *sb, struct dentry *root)
oprofilefs_create_ulong
(
sb
,
root
,
"mmcr0"
,
&
sys
.
mmcr0
);
oprofilefs_create_ulong
(
sb
,
root
,
"mmcr1"
,
&
sys
.
mmcr1
);
oprofilefs_create_ulong
(
sb
,
root
,
"mmcra"
,
&
sys
.
mmcra
);
#ifdef CONFIG_OPROFILE_CELL
/* create a file the user tool can check to see what level of profiling
* support exits with this kernel. Initialize bit mask to indicate
* what support the kernel has:
* bit 0 - Supports SPU event profiling in addition to PPU
* event and cycles; and SPU cycle profiling
* bits 1-31 - Currently unused.
*
* If the file does not exist, then the kernel only supports SPU
* cycle profiling, PPU event and cycle profiling.
*/
oprofilefs_create_ulong
(
sb
,
root
,
"cell_support"
,
&
sys
.
cell_support
);
sys
.
cell_support
=
0x1
;
/* Note, the user OProfile tool must check
* that this bit is set before attempting to
* user SPU event profiling. Older kernels
* will not have this file, hence the user
* tool is not allowed to do SPU event
* profiling on older kernels. Older kernels
* will accept SPU events but collected data
* is garbage.
*/
#endif
#endif
for
(
i
=
0
;
i
<
model
->
num_counters
;
++
i
)
{
...
...
arch/powerpc/oprofile/op_model_cell.c
View file @
a076aa4f
...
...
@@ -40,14 +40,15 @@
#include "../platforms/cell/interrupt.h"
#include "cell/pr_util.h"
static
void
cell_global_stop_spu
(
void
);
#define PPU_PROFILING 0
#define SPU_PROFILING_CYCLES 1
#define SPU_PROFILING_EVENTS 2
/*
* 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 SPU_EVENT_NUM_START 4100
#define SPU_EVENT_NUM_STOP 4399
#define SPU_PROFILE_EVENT_ADDR 4363
/* spu, address trace, decimal */
#define SPU_PROFILE_EVENT_ADDR_MASK_A 0x146
/* sub unit set to zero */
#define SPU_PROFILE_EVENT_ADDR_MASK_B 0x186
/* sub unit set to zero */
#define NUM_SPUS_PER_NODE 8
#define SPU_CYCLES_EVENT_NUM 2
/* event number for SPU_CYCLES */
...
...
@@ -66,6 +67,21 @@ static unsigned int spu_cycle_reset;
#define MAX_SPU_COUNT 0xFFFFFF
/* maximum 24 bit LFSR value */
/* Minumum HW interval timer setting to send value to trace buffer is 10 cycle.
* To configure counter to send value every N cycles set counter to
* 2^32 - 1 - N.
*/
#define NUM_INTERVAL_CYC 0xFFFFFFFF - 10
/*
* 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
;
static
unsigned
int
profiling_mode
;
static
int
spu_evnt_phys_spu_indx
;
struct
pmc_cntrl_data
{
unsigned
long
vcntr
;
unsigned
long
evnts
;
...
...
@@ -105,6 +121,8 @@ struct pm_cntrl {
u16
trace_mode
;
u16
freeze
;
u16
count_mode
;
u16
spu_addr_trace
;
u8
trace_buf_ovflw
;
};
static
struct
{
...
...
@@ -122,7 +140,7 @@ static struct {
#define GET_INPUT_CONTROL(x) ((x & 0x00000004) >> 2)
static
DEFINE_PER_CPU
(
unsigned
long
[
NR_PHYS_CTRS
],
pmc_values
);
static
unsigned
long
spu_pm_cnt
[
MAX_NUMNODES
*
NUM_SPUS_PER_NODE
];
static
struct
pmc_cntrl_data
pmc_cntrl
[
NUM_THREADS
][
NR_PHYS_CTRS
];
/*
...
...
@@ -152,6 +170,7 @@ static u32 hdw_thread;
static
u32
virt_cntr_inter_mask
;
static
struct
timer_list
timer_virt_cntr
;
static
struct
timer_list
timer_spu_event_swap
;
/*
* pm_signal needs to be global since it is initialized in
...
...
@@ -165,7 +184,7 @@ 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
DEFINE_SPINLOCK
(
cntr_lock
);
static
u32
ctr_enabled
;
...
...
@@ -336,13 +355,13 @@ static void set_pm_event(u32 ctr, int event, u32 unit_mask)
for
(
i
=
0
;
i
<
NUM_DEBUG_BUS_WORDS
;
i
++
)
{
if
(
bus_word
&
(
1
<<
i
))
{
pm_regs
.
debug_bus_control
|=
(
bus_type
<<
(
30
-
(
2
*
i
)));
(
bus_type
<<
(
30
-
(
2
*
i
)));
for
(
j
=
0
;
j
<
NUM_INPUT_BUS_WORDS
;
j
++
)
{
if
(
input_bus
[
j
]
==
0xff
)
{
input_bus
[
j
]
=
i
;
pm_regs
.
group_control
|=
(
i
<<
(
30
-
(
2
*
j
)));
(
i
<<
(
30
-
(
2
*
j
)));
break
;
}
...
...
@@ -367,12 +386,16 @@ static void write_pm_cntrl(int cpu)
if
(
pm_regs
.
pm_cntrl
.
stop_at_max
==
1
)
val
|=
CBE_PM_STOP_AT_MAX
;
if
(
pm_regs
.
pm_cntrl
.
trace_mode
==
1
)
if
(
pm_regs
.
pm_cntrl
.
trace_mode
!=
0
)
val
|=
CBE_PM_TRACE_MODE_SET
(
pm_regs
.
pm_cntrl
.
trace_mode
);
if
(
pm_regs
.
pm_cntrl
.
trace_buf_ovflw
==
1
)
val
|=
CBE_PM_TRACE_BUF_OVFLW
(
pm_regs
.
pm_cntrl
.
trace_buf_ovflw
);
if
(
pm_regs
.
pm_cntrl
.
freeze
==
1
)
val
|=
CBE_PM_FREEZE_ALL_CTRS
;
val
|=
CBE_PM_SPU_ADDR_TRACE_SET
(
pm_regs
.
pm_cntrl
.
spu_addr_trace
);
/*
* Routine set_count_mode must be called previously to set
* the count mode based on the user selection of user and kernel.
...
...
@@ -441,7 +464,7 @@ static void cell_virtual_cntr(unsigned long data)
* not both playing with the counters on the same node.
*/
spin_lock_irqsave
(
&
virt_
cntr_lock
,
flags
);
spin_lock_irqsave
(
&
cntr_lock
,
flags
);
prev_hdw_thread
=
hdw_thread
;
...
...
@@ -480,7 +503,7 @@ static void cell_virtual_cntr(unsigned long data)
cbe_disable_pm_interrupts
(
cpu
);
for
(
i
=
0
;
i
<
num_counters
;
i
++
)
{
per_cpu
(
pmc_values
,
cpu
+
prev_hdw_thread
)[
i
]
=
cbe_read_ctr
(
cpu
,
i
);
=
cbe_read_ctr
(
cpu
,
i
);
if
(
per_cpu
(
pmc_values
,
cpu
+
next_hdw_thread
)[
i
]
==
0xFFFFFFFF
)
...
...
@@ -527,7 +550,7 @@ static void cell_virtual_cntr(unsigned long data)
cbe_enable_pm
(
cpu
);
}
spin_unlock_irqrestore
(
&
virt_
cntr_lock
,
flags
);
spin_unlock_irqrestore
(
&
cntr_lock
,
flags
);
mod_timer
(
&
timer_virt_cntr
,
jiffies
+
HZ
/
10
);
}
...
...
@@ -541,38 +564,146 @@ static void start_virt_cntrs(void)
add_timer
(
&
timer_virt_cntr
);
}
/* This function is called once for all cpus combined */
static
int
cell_reg_setup
(
struct
op_counter_config
*
ctr
,
static
int
cell_reg_setup_spu_cycles
(
struct
op_counter_config
*
ctr
,
struct
op_system_config
*
sys
,
int
num_ctrs
)
{
int
i
,
j
,
cpu
;
spu_cycle_reset
=
0
;
spu_cycle_reset
=
ctr
[
0
].
count
;
if
(
ctr
[
0
].
event
==
SPU_CYCLES_EVENT_NUM
)
{
spu_cycle_reset
=
ctr
[
0
].
count
;
/*
* Each node will need to make the rtas call to start
* and stop SPU profiling. Get the token once and store it.
*/
spu_rtas_token
=
rtas_token
(
"ibm,cbe-spu-perftools"
);
if
(
unlikely
(
spu_rtas_token
==
RTAS_UNKNOWN_SERVICE
))
{
printk
(
KERN_ERR
"%s: rtas token ibm,cbe-spu-perftools unknown
\n
"
,
__func__
);
return
-
EIO
;
}
return
0
;
}
/* Unfortunately, the hardware will only support event profiling
* on one SPU per node at a time. Therefore, we must time slice
* the profiling across all SPUs in the node. Note, we do this
* in parallel for each node. The following routine is called
* periodically based on kernel timer to switch which SPU is
* being monitored in a round robbin fashion.
*/
static
void
spu_evnt_swap
(
unsigned
long
data
)
{
int
node
;
int
cur_phys_spu
,
nxt_phys_spu
,
cur_spu_evnt_phys_spu_indx
;
unsigned
long
flags
;
int
cpu
;
int
ret
;
u32
interrupt_mask
;
/* enable interrupts on cntr 0 */
interrupt_mask
=
CBE_PM_CTR_OVERFLOW_INTR
(
0
);
hdw_thread
=
0
;
/* Make sure spu event interrupt handler and spu event swap
* don't access the counters simultaneously.
*/
spin_lock_irqsave
(
&
cntr_lock
,
flags
);
cur_spu_evnt_phys_spu_indx
=
spu_evnt_phys_spu_indx
;
if
(
++
(
spu_evnt_phys_spu_indx
)
==
NUM_SPUS_PER_NODE
)
spu_evnt_phys_spu_indx
=
0
;
pm_signal
[
0
].
sub_unit
=
spu_evnt_phys_spu_indx
;
pm_signal
[
1
].
sub_unit
=
spu_evnt_phys_spu_indx
;
pm_signal
[
2
].
sub_unit
=
spu_evnt_phys_spu_indx
;
/* switch the SPU being profiled on each node */
for_each_online_cpu
(
cpu
)
{
if
(
cbe_get_hw_thread_id
(
cpu
))
continue
;
node
=
cbe_cpu_to_node
(
cpu
);
cur_phys_spu
=
(
node
*
NUM_SPUS_PER_NODE
)
+
cur_spu_evnt_phys_spu_indx
;
nxt_phys_spu
=
(
node
*
NUM_SPUS_PER_NODE
)
+
spu_evnt_phys_spu_indx
;
/*
*
Each node will need to make the rtas call to start
*
and stop SPU profiling. Get the token once and store it.
*
stop counters, save counter values, restore counts
*
for previous physical SPU
*/
spu_rtas_token
=
rtas_token
(
"ibm,cbe-spu-perftools"
);
cbe_disable_pm
(
cpu
);
cbe_disable_pm_interrupts
(
cpu
);
if
(
unlikely
(
spu_rtas_token
==
RTAS_UNKNOWN_SERVICE
))
{
printk
(
KERN_ERR
"%s: rtas token ibm,cbe-spu-perftools unknown
\n
"
,
__func__
);
return
-
EIO
;
}
spu_pm_cnt
[
cur_phys_spu
]
=
cbe_read_ctr
(
cpu
,
0
);
/* restore previous count for the next spu to sample */
/* NOTE, hardware issue, counter will not start if the
* counter value is at max (0xFFFFFFFF).
*/
if
(
spu_pm_cnt
[
nxt_phys_spu
]
>=
0xFFFFFFFF
)
cbe_write_ctr
(
cpu
,
0
,
0xFFFFFFF0
);
else
cbe_write_ctr
(
cpu
,
0
,
spu_pm_cnt
[
nxt_phys_spu
]);
pm_rtas_reset_signals
(
cbe_cpu_to_node
(
cpu
));
/* setup the debug bus measure the one event and
* the two events to route the next SPU's PC on
* the debug bus
*/
ret
=
pm_rtas_activate_signals
(
cbe_cpu_to_node
(
cpu
),
3
);
if
(
ret
)
printk
(
KERN_ERR
"%s: pm_rtas_activate_signals failed, "
"SPU event swap
\n
"
,
__func__
);
/* clear the trace buffer, don't want to take PC for
* previous SPU*/
cbe_write_pm
(
cpu
,
trace_address
,
0
);
enable_ctr
(
cpu
,
0
,
pm_regs
.
pm07_cntrl
);
/* Enable interrupts on the CPU thread that is starting */
cbe_enable_pm_interrupts
(
cpu
,
hdw_thread
,
interrupt_mask
);
cbe_enable_pm
(
cpu
);
}
pm_rtas_token
=
rtas_token
(
"ibm,cbe-perftools"
);
spin_unlock_irqrestore
(
&
cntr_lock
,
flags
);
/* swap approximately every 0.1 seconds */
mod_timer
(
&
timer_spu_event_swap
,
jiffies
+
HZ
/
25
);
}
static
void
start_spu_event_swap
(
void
)
{
init_timer
(
&
timer_spu_event_swap
);
timer_spu_event_swap
.
function
=
spu_evnt_swap
;
timer_spu_event_swap
.
data
=
0UL
;
timer_spu_event_swap
.
expires
=
jiffies
+
HZ
/
25
;
add_timer
(
&
timer_spu_event_swap
);
}
static
int
cell_reg_setup_spu_events
(
struct
op_counter_config
*
ctr
,
struct
op_system_config
*
sys
,
int
num_ctrs
)
{
int
i
;
/* routine is called once for all nodes */
spu_evnt_phys_spu_indx
=
0
;
/*
* For all events exce
tp
PPU CYCLEs, each node will need to make
* For all events exce
pt
PPU CYCLEs, each node will need to make
* the rtas cbe-perftools call to setup and reset the debug bus.
* Make the token lookup call once and store it in the global
* variable pm_rtas_token.
*/
pm_rtas_token
=
rtas_token
(
"ibm,cbe-perftools"
);
if
(
unlikely
(
pm_rtas_token
==
RTAS_UNKNOWN_SERVICE
))
{
printk
(
KERN_ERR
"%s: rtas token ibm,cbe-perftools unknown
\n
"
,
...
...
@@ -580,6 +711,58 @@ static int cell_reg_setup(struct op_counter_config *ctr,
return
-
EIO
;
}
/* setup the pm_control register settings,
* settings will be written per node by the
* cell_cpu_setup() function.
*/
pm_regs
.
pm_cntrl
.
trace_buf_ovflw
=
1
;
/* Use the occurrence trace mode to have SPU PC saved
* to the trace buffer. Occurrence data in trace buffer
* is not used. Bit 2 must be set to store SPU addresses.
*/
pm_regs
.
pm_cntrl
.
trace_mode
=
2
;
pm_regs
.
pm_cntrl
.
spu_addr_trace
=
0x1
;
/* using debug bus
event 2 & 3 */
/* setup the debug bus event array with the SPU PC routing events.
* Note, pm_signal[0] will be filled in by set_pm_event() call below.
*/
pm_signal
[
1
].
signal_group
=
SPU_PROFILE_EVENT_ADDR
/
100
;
pm_signal
[
1
].
bus_word
=
GET_BUS_WORD
(
SPU_PROFILE_EVENT_ADDR_MASK_A
);
pm_signal
[
1
].
bit
=
SPU_PROFILE_EVENT_ADDR
%
100
;
pm_signal
[
1
].
sub_unit
=
spu_evnt_phys_spu_indx
;
pm_signal
[
2
].
signal_group
=
SPU_PROFILE_EVENT_ADDR
/
100
;
pm_signal
[
2
].
bus_word
=
GET_BUS_WORD
(
SPU_PROFILE_EVENT_ADDR_MASK_B
);
pm_signal
[
2
].
bit
=
SPU_PROFILE_EVENT_ADDR
%
100
;
pm_signal
[
2
].
sub_unit
=
spu_evnt_phys_spu_indx
;
/* Set the user selected spu event to profile on,
* note, only one SPU profiling event is supported
*/
num_counters
=
1
;
/* Only support one SPU event at a time */
set_pm_event
(
0
,
ctr
[
0
].
event
,
ctr
[
0
].
unit_mask
);
reset_value
[
0
]
=
0xFFFFFFFF
-
ctr
[
0
].
count
;
/* global, used by cell_cpu_setup */
ctr_enabled
|=
1
;
/* Initialize the count for each SPU to the reset value */
for
(
i
=
0
;
i
<
MAX_NUMNODES
*
NUM_SPUS_PER_NODE
;
i
++
)
spu_pm_cnt
[
i
]
=
reset_value
[
0
];
return
0
;
}
static
int
cell_reg_setup_ppu
(
struct
op_counter_config
*
ctr
,
struct
op_system_config
*
sys
,
int
num_ctrs
)
{
/* routine is called once for all nodes */
int
i
,
j
,
cpu
;
num_counters
=
num_ctrs
;
if
(
unlikely
(
num_ctrs
>
NR_PHYS_CTRS
))
{
...
...
@@ -589,14 +772,6 @@ static int cell_reg_setup(struct op_counter_config *ctr,
__func__
);
return
-
EIO
;
}
pm_regs
.
group_control
=
0
;
pm_regs
.
debug_bus_control
=
0
;
/* setup the pm_control register */
memset
(
&
pm_regs
.
pm_cntrl
,
0
,
sizeof
(
struct
pm_cntrl
));
pm_regs
.
pm_cntrl
.
stop_at_max
=
1
;
pm_regs
.
pm_cntrl
.
trace_mode
=
0
;
pm_regs
.
pm_cntrl
.
freeze
=
1
;
set_count_mode
(
sys
->
enable_kernel
,
sys
->
enable_user
);
...
...
@@ -665,6 +840,63 @@ static int cell_reg_setup(struct op_counter_config *ctr,
}
/* This function is called once for all cpus combined */
static
int
cell_reg_setup
(
struct
op_counter_config
*
ctr
,
struct
op_system_config
*
sys
,
int
num_ctrs
)
{
int
ret
=
0
;
spu_cycle_reset
=
0
;
/* initialize the spu_arr_trace value, will be reset if
* doing spu event profiling.
*/
pm_regs
.
group_control
=
0
;
pm_regs
.
debug_bus_control
=
0
;
pm_regs
.
pm_cntrl
.
stop_at_max
=
1
;
pm_regs
.
pm_cntrl
.
trace_mode
=
0
;
pm_regs
.
pm_cntrl
.
freeze
=
1
;
pm_regs
.
pm_cntrl
.
trace_buf_ovflw
=
0
;
pm_regs
.
pm_cntrl
.
spu_addr_trace
=
0
;
/*
* For all events except PPU CYCLEs, each node will need to make
* the rtas cbe-perftools call to setup and reset the debug bus.
* Make the token lookup call once and store it in the global
* variable pm_rtas_token.
*/
pm_rtas_token
=
rtas_token
(
"ibm,cbe-perftools"
);
if
(
unlikely
(
pm_rtas_token
==
RTAS_UNKNOWN_SERVICE
))
{
printk
(
KERN_ERR
"%s: rtas token ibm,cbe-perftools unknown
\n
"
,
__func__
);
return
-
EIO
;
}
if
(
ctr
[
0
].
event
==
SPU_CYCLES_EVENT_NUM
)
{
profiling_mode
=
SPU_PROFILING_CYCLES
;
ret
=
cell_reg_setup_spu_cycles
(
ctr
,
sys
,
num_ctrs
);
}
else
if
((
ctr
[
0
].
event
>=
SPU_EVENT_NUM_START
)
&&
(
ctr
[
0
].
event
<=
SPU_EVENT_NUM_STOP
))
{
profiling_mode
=
SPU_PROFILING_EVENTS
;
spu_cycle_reset
=
ctr
[
0
].
count
;
/* for SPU event profiling, need to setup the
* pm_signal array with the events to route the
* SPU PC before making the FW call. Note, only
* one SPU event for profiling can be specified
* at a time.
*/
cell_reg_setup_spu_events
(
ctr
,
sys
,
num_ctrs
);
}
else
{
profiling_mode
=
PPU_PROFILING
;
ret
=
cell_reg_setup_ppu
(
ctr
,
sys
,
num_ctrs
);
}
return
ret
;
}
/* This function is called once for each cpu */
static
int
cell_cpu_setup
(
struct
op_counter_config
*
cntr
)
...
...
@@ -672,8 +904,13 @@ static int cell_cpu_setup(struct op_counter_config *cntr)
u32
cpu
=
smp_processor_id
();
u32
num_enabled
=
0
;
int
i
;
int
ret
;
if
(
spu_cycle_reset
)
/* Cycle based SPU profiling does not use the performance
* counters. The trace array is configured to collect
* the data.
*/
if
(
profiling_mode
==
SPU_PROFILING_CYCLES
)
return
0
;
/* There is one performance monitor per processor chip (i.e. node),
...
...
@@ -686,7 +923,6 @@ static int cell_cpu_setup(struct op_counter_config *cntr)
cbe_disable_pm
(
cpu
);
cbe_disable_pm_interrupts
(
cpu
);
cbe_write_pm
(
cpu
,
pm_interval
,
0
);
cbe_write_pm
(
cpu
,
pm_start_stop
,
0
);
cbe_write_pm
(
cpu
,
group_control
,
pm_regs
.
group_control
);
cbe_write_pm
(
cpu
,
debug_bus_control
,
pm_regs
.
debug_bus_control
);
...
...
@@ -703,7 +939,20 @@ static int cell_cpu_setup(struct op_counter_config *cntr)
* The pm_rtas_activate_signals will return -EIO if the FW
* call failed.
*/
return
pm_rtas_activate_signals
(
cbe_cpu_to_node
(
cpu
),
num_enabled
);
if
(
profiling_mode
==
SPU_PROFILING_EVENTS
)
{
/* For SPU event profiling also need to setup the
* pm interval timer
*/
ret
=
pm_rtas_activate_signals
(
cbe_cpu_to_node
(
cpu
),
num_enabled
+
2
);
/* store PC from debug bus to Trace buffer as often
* as possible (every 10 cycles)
*/
cbe_write_pm
(
cpu
,
pm_interval
,
NUM_INTERVAL_CYC
);
return
ret
;
}
else
return
pm_rtas_activate_signals
(
cbe_cpu_to_node
(
cpu
),
num_enabled
);
}
#define ENTRIES 303
...
...
@@ -885,7 +1134,122 @@ static struct notifier_block cpu_freq_notifier_block = {
};
#endif
static
int
cell_global_start_spu
(
struct
op_counter_config
*
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_cycles
(
void
)
{
int
subfunc
,
rtn_value
;
unsigned
int
lfsr_value
;
int
cpu
;
oprofile_running
=
0
;
smp_wmb
();
#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
"
,
__func__
,
rtn_value
);
}
/* Deactivate the signals */
pm_rtas_reset_signals
(
cbe_cpu_to_node
(
cpu
));
}
stop_spu_profiling_cycles
();
}
static
void
cell_global_stop_spu_events
(
void
)
{
int
cpu
;
oprofile_running
=
0
;
stop_spu_profiling_events
();
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
);
cbe_write_pm07_control
(
cpu
,
0
,
0
);
/* Deactivate the signals */
pm_rtas_reset_signals
(
cbe_cpu_to_node
(
cpu
));
/* Deactivate interrupts */
cbe_disable_pm_interrupts
(
cpu
);
}
del_timer_sync
(
&
timer_spu_event_swap
);
}
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
(
profiling_mode
==
PPU_PROFILING
)
cell_global_stop_ppu
();
else
if
(
profiling_mode
==
SPU_PROFILING_EVENTS
)
cell_global_stop_spu_events
();
else
cell_global_stop_spu_cycles
();
}
static
int
cell_global_start_spu_cycles
(
struct
op_counter_config
*
ctr
)
{
int
subfunc
;
unsigned
int
lfsr_value
;
...
...
@@ -951,18 +1315,18 @@ static int cell_global_start_spu(struct op_counter_config *ctr)
/* start profiling */
ret
=
rtas_call
(
spu_rtas_token
,
3
,
1
,
NULL
,
subfunc
,
cbe_cpu_to_node
(
cpu
),
lfsr_value
);
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
"
,
__func__
,
ret
);
"%s: rtas call ibm,cbe-spu-perftools failed,
"
\
"return = %d
\n
"
,
__func__
,
ret
);
rtas_error
=
-
EIO
;
goto
out
;
}
}
rtas_error
=
start_spu_profiling
(
spu_cycle_reset
);
rtas_error
=
start_spu_profiling
_cycles
(
spu_cycle_reset
);
if
(
rtas_error
)
goto
out_stop
;
...
...
@@ -970,11 +1334,74 @@ static int cell_global_start_spu(struct op_counter_config *ctr)
return
0
;
out_stop:
cell_global_stop_spu
();
/* clean up the PMU/debug bus */
cell_global_stop_spu
_cycles
();
/* clean up the PMU/debug bus */
out:
return
rtas_error
;
}
static
int
cell_global_start_spu_events
(
struct
op_counter_config
*
ctr
)
{
int
cpu
;
u32
interrupt_mask
=
0
;
int
rtn
=
0
;
hdw_thread
=
0
;
/* spu event profiling, uses the performance counters to generate
* an interrupt. The hardware is setup to store the SPU program
* counter into the trace array. The occurrence mode is used to
* enable storing data to the trace buffer. The bits are set
* to send/store the SPU address in the trace buffer. The debug
* bus must be setup to route the SPU program counter onto the
* debug bus. The occurrence data in the trace buffer is not used.
*/
/* 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
;
/*
* Setup SPU event-based profiling.
* Set perf_mon_control bit 0 to a zero before
* enabling spu collection hardware.
*
* Only support one SPU event on one SPU per node.
*/
if
(
ctr_enabled
&
1
)
{
cbe_write_ctr
(
cpu
,
0
,
reset_value
[
0
]);
enable_ctr
(
cpu
,
0
,
pm_regs
.
pm07_cntrl
);
interrupt_mask
|=
CBE_PM_CTR_OVERFLOW_INTR
(
0
);
}
else
{
/* Disable counter */
cbe_write_pm07_control
(
cpu
,
0
,
0
);
}
cbe_get_and_clear_pm_interrupts
(
cpu
);
cbe_enable_pm_interrupts
(
cpu
,
hdw_thread
,
interrupt_mask
);
cbe_enable_pm
(
cpu
);
/* clear the trace buffer */
cbe_write_pm
(
cpu
,
trace_address
,
0
);
}
/* Start the timer to time slice collecting the event profile
* on each of the SPUs. Note, can collect profile on one SPU
* per node at a time.
*/
start_spu_event_swap
();
start_spu_profiling_events
();
oprofile_running
=
1
;
smp_wmb
();
return
rtn
;
}
static
int
cell_global_start_ppu
(
struct
op_counter_config
*
ctr
)
{
u32
cpu
,
i
;
...
...
@@ -994,8 +1421,7 @@ static int cell_global_start_ppu(struct op_counter_config *ctr)
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
);
interrupt_mask
|=
CBE_PM_CTR_OVERFLOW_INTR
(
i
);
}
else
{
/* Disable counter */
cbe_write_pm07_control
(
cpu
,
i
,
0
);
...
...
@@ -1024,99 +1450,162 @@ static int cell_global_start_ppu(struct op_counter_config *ctr)
static
int
cell_global_start
(
struct
op_counter_config
*
ctr
)
{
if
(
spu_cycle_reset
)
return
cell_global_start_spu
(
ctr
);
if
(
profiling_mode
==
SPU_PROFILING_CYCLES
)
return
cell_global_start_spu_cycles
(
ctr
);
else
if
(
profiling_mode
==
SPU_PROFILING_EVENTS
)
return
cell_global_start_spu_events
(
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.
/* The SPU interrupt handler
*
* SPU event profiling works as follows:
* The pm_signal[0] holds the one SPU event to be measured. It is routed on
* the debug bus using word 0 or 1. The value of pm_signal[1] and
* pm_signal[2] contain the necessary events to route the SPU program
* counter for the selected SPU onto the debug bus using words 2 and 3.
* The pm_interval register is setup to write the SPU PC value into the
* trace buffer at the maximum rate possible. The trace buffer is configured
* to store the PCs, wrapping when it is full. The performance counter is
* intialized to the max hardware count minus the number of events, N, between
* samples. Once the N events have occured, a HW counter overflow occurs
* causing the generation of a HW counter interrupt which also stops the
* writing of the SPU PC values to the trace buffer. Hence the last PC
* written to the trace buffer is the SPU PC that we want. Unfortunately,
* we have to read from the beginning of the trace buffer to get to the
* last value written. We just hope the PPU has nothing better to do then
* service this interrupt. The PC for the specific SPU being profiled is
* extracted from the trace buffer processed and stored. The trace buffer
* is cleared, interrupts are cleared, the counter is reset to max - N.
* A kernel timer is used to periodically call the routine spu_evnt_swap()
* to switch to the next physical SPU in the node to profile in round robbin
* order. This way data is collected for all SPUs on the node. It does mean
* that we need to use a relatively small value of N to ensure enough samples
* on each SPU are collected each SPU is being profiled 1/8 of the time.
* It may also be necessary to use a longer sample collection period.
*/
static
void
cell_global_stop_spu
(
void
)
static
void
cell_handle_interrupt_spu
(
struct
pt_regs
*
regs
,
struct
op_counter_config
*
ctr
)
{
int
subfunc
,
rtn_value
;
unsigned
int
lfsr_value
;
int
cpu
;
u32
cpu
,
cpu_tmp
;
u64
trace_entry
;
u32
interrupt_mask
;
u64
trace_buffer
[
2
];
u64
last_trace_buffer
;
u32
sample
;
u32
trace_addr
;
unsigned
long
sample_array_lock_flags
;
int
spu_num
;
unsigned
long
flags
;
oprofile_running
=
0
;
/* Make sure spu event interrupt handler and spu event swap
* don't access the counters simultaneously.
*/
cpu
=
smp_processor_id
();
spin_lock_irqsave
(
&
cntr_lock
,
flags
);
#ifdef CONFIG_CPU_FREQ
cpufreq_unregister_notifier
(
&
cpu_freq_notifier_block
,
CPUFREQ_TRANSITION_NOTIFIER
);
#endif
cpu_tmp
=
cpu
;
cbe_disable_pm
(
cpu
);
for_each_online_cpu
(
cpu
)
{
if
(
cbe_get_hw_thread_id
(
cpu
))
continue
;
interrupt_mask
=
cbe_get_and_clear_pm_interrupts
(
cpu
);
subfunc
=
3
;
/*
* 2 - activate SPU tracing,
* 3 - deactivate
*/
lfsr_value
=
0x8f100000
;
sample
=
0xABCDEF
;
trace_entry
=
0xfedcba
;
last_trace_buffer
=
0xdeadbeaf
;
rtn_value
=
rtas_call
(
spu_rtas_token
,
3
,
1
,
NULL
,
subfunc
,
cbe_cpu_to_node
(
cpu
),
lfsr_value
);
if
((
oprofile_running
==
1
)
&&
(
interrupt_mask
!=
0
))
{
/* disable writes to trace buff */
cbe_write_pm
(
cpu
,
pm_interval
,
0
);
if
(
unlikely
(
rtn_value
!=
0
))
{
printk
(
KERN_ERR
"%s: rtas call ibm,cbe-spu-perftools failed, return = %d
\n
"
,
__func__
,
rtn_value
);
/* only have one perf cntr being used, cntr 0 */
if
((
interrupt_mask
&
CBE_PM_CTR_OVERFLOW_INTR
(
0
))
&&
ctr
[
0
].
enabled
)
/* The SPU PC values will be read
* from the trace buffer, reset counter
*/
cbe_write_ctr
(
cpu
,
0
,
reset_value
[
0
]);
trace_addr
=
cbe_read_pm
(
cpu
,
trace_address
);
while
(
!
(
trace_addr
&
CBE_PM_TRACE_BUF_EMPTY
))
{
/* There is data in the trace buffer to process
* Read the buffer until you get to the last
* entry. This is the value we want.
*/
cbe_read_trace_buffer
(
cpu
,
trace_buffer
);
trace_addr
=
cbe_read_pm
(
cpu
,
trace_address
);
}
/* Deactivate the signals */
pm_rtas_reset_signals
(
cbe_cpu_to_node
(
cpu
));
}
/* SPU Address 16 bit count format for 128 bit
* HW trace buffer is used for the SPU PC storage
* HDR bits 0:15
* SPU Addr 0 bits 16:31
* SPU Addr 1 bits 32:47
* unused bits 48:127
*
* HDR: bit4 = 1 SPU Address 0 valid
* HDR: bit5 = 1 SPU Address 1 valid
* - unfortunately, the valid bits don't seem to work
*
* Note trace_buffer[0] holds bits 0:63 of the HW
* trace buffer, trace_buffer[1] holds bits 64:127
*/
stop_spu_profiling
();
}
trace_entry
=
trace_buffer
[
0
]
&
0x00000000FFFF0000
;
static
void
cell_global_stop_ppu
(
void
)
{
int
cpu
;
/* only top 16 of the 18 bit SPU PC address
* is stored in trace buffer, hence shift right
* by 16 -2 bits */
sample
=
trace_entry
>>
14
;
last_trace_buffer
=
trace_buffer
[
0
];
/*
* 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
();
spu_num
=
spu_evnt_phys_spu_indx
+
(
cbe_cpu_to_node
(
cpu
)
*
NUM_SPUS_PER_NODE
);
for_each_online_cpu
(
cpu
)
{
if
(
cbe_get_hw_thread_id
(
cpu
))
continue
;
/* make sure only one process at a time is calling
* spu_sync_buffer()
*/
spin_lock_irqsave
(
&
oprof_spu_smpl_arry_lck
,
sample_array_lock_flags
);
spu_sync_buffer
(
spu_num
,
&
sample
,
1
);
spin_unlock_irqrestore
(
&
oprof_spu_smpl_arry_lck
,
sample_array_lock_flags
);
cbe_sync_irq
(
cbe_cpu_to_node
(
cpu
));
/* Stop the counters */
cbe_disable_pm
(
cpu
);
smp_wmb
();
/* insure spu event buffer updates are written
* don't want events intermingled... */
/* Deactivate the signals */
pm_rtas_reset_signals
(
cbe_cpu_to_node
(
cpu
));
/* The counters were frozen by the interrupt.
* Reenable the interrupt and restart the counters.
*/
cbe_write_pm
(
cpu
,
pm_interval
,
NUM_INTERVAL_CYC
);
cbe_enable_pm_interrupts
(
cpu
,
hdw_thread
,
virt_cntr_inter_mask
);
/* Deactivate interrupts */
cbe_disable_pm_interrupts
(
cpu
);
}
}
/* clear the trace buffer, re-enable writes to trace buff */
cbe_write_pm
(
cpu
,
trace_address
,
0
);
cbe_write_pm
(
cpu
,
pm_interval
,
NUM_INTERVAL_CYC
);
static
void
cell_global_stop
(
void
)
{
if
(
spu_cycle_reset
)
cell_global_stop_spu
();
else
cell_global_stop_ppu
();
/* 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.
*/
write_pm_cntrl
(
cpu
);
cbe_enable_pm
(
cpu
);
}
spin_unlock_irqrestore
(
&
cntr_lock
,
flags
);
}
static
void
cell_handle_interrupt
(
struct
pt_regs
*
regs
,
struct
op_counter_config
*
ctr
)
static
void
cell_handle_interrupt
_ppu
(
struct
pt_regs
*
regs
,
struct
op_counter_config
*
ctr
)
{
u32
cpu
;
u64
pc
;
...
...
@@ -1132,7 +1621,7 @@ static void cell_handle_interrupt(struct pt_regs *regs,
* routine are not running at the same time. See the
* cell_virtual_cntr() routine for additional comments.
*/
spin_lock_irqsave
(
&
virt_
cntr_lock
,
flags
);
spin_lock_irqsave
(
&
cntr_lock
,
flags
);
/*
* Need to disable and reenable the performance counters
...
...
@@ -1185,7 +1674,16 @@ static void cell_handle_interrupt(struct pt_regs *regs,
*/
cbe_enable_pm
(
cpu
);
}
spin_unlock_irqrestore
(
&
virt_cntr_lock
,
flags
);
spin_unlock_irqrestore
(
&
cntr_lock
,
flags
);
}
static
void
cell_handle_interrupt
(
struct
pt_regs
*
regs
,
struct
op_counter_config
*
ctr
)
{
if
(
profiling_mode
==
PPU_PROFILING
)
cell_handle_interrupt_ppu
(
regs
,
ctr
);
else
cell_handle_interrupt_spu
(
regs
,
ctr
);
}
/*
...
...
@@ -1195,7 +1693,8 @@ static void cell_handle_interrupt(struct pt_regs *regs,
*/
static
int
cell_sync_start
(
void
)
{
if
(
spu_cycle_reset
)
if
((
profiling_mode
==
SPU_PROFILING_CYCLES
)
||
(
profiling_mode
==
SPU_PROFILING_EVENTS
))
return
spu_sync_start
();
else
return
DO_GENERIC_SYNC
;
...
...
@@ -1203,7 +1702,8 @@ static int cell_sync_start(void)
static
int
cell_sync_stop
(
void
)
{
if
(
spu_cycle_reset
)
if
((
profiling_mode
==
SPU_PROFILING_CYCLES
)
||
(
profiling_mode
==
SPU_PROFILING_EVENTS
))
return
spu_sync_stop
();
else
return
1
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment