cvmx-profiler.c revision 259065
1/***********************license start***************
2 * Copyright (c) 2011  Cavium Inc. (support@cavium.com). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17 *
18 *   * Neither the name of Cavium Inc. nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export  control
24 * laws, including the U.S. Export Administration Act and its  associated
25 * regulations, and may be subject to export or import  regulations in other
26 * countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ************************license end**************************************/
39
40/**
41 * @file
42 *
43 * Interface to event profiler.
44 *
45 */
46
47#include "cvmx-config.h"
48#include "cvmx.h"
49#include "cvmx-interrupt.h"
50#include "cvmx-sysinfo.h"
51#include "cvmx-coremask.h"
52#include "cvmx-spinlock.h"
53#include "cvmx-atomic.h"
54#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
55#include "cvmx-error.h"
56#endif
57#include "cvmx-asm.h"
58#include "cvmx-bootmem.h"
59#include "cvmx-profiler.h"
60
61#ifdef PROFILER_DEBUG
62#define PRINTF(fmt, args...)    cvmx_safe_printf(fmt, ##args)
63#else
64#define PRINTF(fmt, args...)
65#endif
66
67CVMX_SHARED static event_counter_control_block_t eccb;
68cvmx_config_block_t *pcpu_cfg_blk;
69
70int read_percpu_block = 1;
71
72/**
73 * Set Interrupt IRQ line for Performance Counter
74 *
75 */
76void cvmx_update_perfcnt_irq(void)
77{
78    uint64_t cvmctl;
79
80    /* Clear CvmCtl[IPPCI] bit and move the Performance Counter
81     * interrupt to IRQ 6
82     */
83    CVMX_MF_COP0(cvmctl, COP0_CVMCTL);
84    cvmctl &= ~(7 << 7);
85    cvmctl |= 6 << 7;
86    CVMX_MT_COP0(cvmctl, COP0_CVMCTL);
87}
88
89/**
90 * @INTERNAL
91 * Return the baseaddress of the namedblock
92 * @param buf_name  Name of Namedblock
93 *
94 * @return baseaddress of block on Success, NULL on failure.
95 */
96static
97void *cvmx_get_memory_addr(const char* buf_name)
98{
99    void *buffer_ptr = NULL;
100    const struct cvmx_bootmem_named_block_desc *block_desc = cvmx_bootmem_find_named_block(buf_name);
101    if (block_desc)
102        buffer_ptr = cvmx_phys_to_ptr(block_desc->base_addr);
103    assert (buffer_ptr != NULL);
104
105    return buffer_ptr;
106}
107
108/**
109 * @INTERNAL
110 * Initialize the cpu block metadata.
111 *
112 * @param cpu	core no
113 * @param size	size of per cpu memory in named block
114 *
115 */
116static
117void cvmx_init_pcpu_block(int cpu, int size)
118{
119    eccb.cfg_blk.pcpu_base_addr[cpu] = (char *)cvmx_get_memory_addr(EVENT_BUFFER_BLOCK) + (size * cpu);
120    assert (eccb.cfg_blk.pcpu_base_addr[cpu] != NULL);
121
122    cvmx_ringbuf_t  *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
123
124    cpu_buf->pcpu_blk_info.size = size;
125    cpu_buf->pcpu_blk_info.max_samples = ((size - sizeof(cvmx_cpu_event_block_t)) / sizeof(cvmx_sample_entry_t));
126    cpu_buf->pcpu_blk_info.sample_count = 0;
127    cpu_buf->pcpu_blk_info.sample_read = 0;
128    cpu_buf->pcpu_blk_info.data = eccb.cfg_blk.pcpu_base_addr[cpu] + sizeof(cvmx_cpu_event_block_t) + PADBYTES;
129    cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.tail = \
130       cpu_buf->pcpu_data = cpu_buf->pcpu_blk_info.data;
131    cpu_buf->pcpu_blk_info.end = eccb.cfg_blk.pcpu_base_addr[cpu] + size;
132
133    cvmx_atomic_set32(&read_percpu_block, 0);
134
135    /*
136     * Write per cpu mem base address info in to 'event config' named block,
137     * This info is needed by oct-remote-profile to get Per cpu memory
138     * base address of each core of the named block.
139     */
140    pcpu_cfg_blk = (cvmx_config_block_t *) eccb.config_blk_base_addr;
141    pcpu_cfg_blk->pcpu_base_addr[cpu] = eccb.cfg_blk.pcpu_base_addr[cpu];
142}
143
144/**
145 * @INTERNAL
146 * Retrieve the info from the 'event_config' named block.
147 *
148 * Here events value is read(as passed to oct-remote-profile) to reset perf
149 * counters on every Perf counter overflow.
150 *
151 */
152static
153void cvmx_read_config_blk(void)
154{
155    eccb.config_blk_base_addr = (char *)cvmx_get_memory_addr(EVENT_BUFFER_CONFIG_BLOCK);
156    memcpy(&(eccb.cfg_blk.events), eccb.config_blk_base_addr + \
157       offsetof(cvmx_config_block_t, events), sizeof(int64_t));
158
159    cvmx_atomic_set32(&eccb.read_cfg_blk,1);
160    PRINTF("cfg_blk.events=%lu, sample_count=%ld\n", eccb.cfg_blk.events, eccb.cfg_blk.sample_count);
161}
162
163/**
164 * @INTERNAL
165 * Add new sample to the buffer and increment the head pointer and
166 * global sample count(i.e sum total of samples collected on all cores)
167 *
168 */
169static
170void cvmx_add_sample_to_buffer(void)
171{
172    uint32_t epc;
173    int cpu = cvmx_get_core_num();
174    CVMX_MF_COP0(epc, COP0_EPC);
175
176    cvmx_ringbuf_t  *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
177
178    /*
179     * head/tail pointer can be NULL, and this case arises when oct-remote-profile is
180     * invoked afresh. To keep memory sane for current instance, we clear namedblock off
181     * previous data and this is accomplished by octeon_remote_write_mem from host.
182     */
183    if (cvmx_unlikely(!cpu_buf->pcpu_blk_info.head && !cpu_buf->pcpu_blk_info.end)) {
184       /* Reread the event count as a different threshold val could be
185        * passed with profiler alongside --events flag */
186        cvmx_read_config_blk();
187        cvmx_init_pcpu_block(cpu, EVENT_PERCPU_BUFFER_SIZE);
188    }
189
190    /* In case of hitting end of buffer, reset head,data ptr to start */
191    if (cpu_buf->pcpu_blk_info.head == cpu_buf->pcpu_blk_info.end)
192        cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data = cpu_buf->pcpu_data;
193
194    /* Store the pc, respective core no.*/
195    cvmx_sample_entry_t *sample = (cvmx_sample_entry_t *) cpu_buf->pcpu_blk_info.data;
196    sample->pc = epc;
197    sample->core = cpu;
198
199    /* Update Per CPU stats */
200    cpu_buf->pcpu_blk_info.sample_count++;
201    cpu_buf->pcpu_blk_info.data += sizeof(cvmx_sample_entry_t);
202    cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data;
203
204    /* Increment the global sample count i.e sum total of samples on all cores*/
205    cvmx_atomic_add64(&(pcpu_cfg_blk->sample_count), 1);
206
207    PRINTF("the core%d:pc 0x%016lx, sample_count=%ld\n", cpu, sample->pc, cpu_buf->pcpu_blk_info.sample_count);
208}
209
210/**
211 * @INTERNAL
212 * Reset performance counters
213 *
214 * @param pf     The performance counter Number (0, 1)
215 * @param events The threshold value for which interrupt has to be asserted
216 */
217static
218void cvmx_reset_perf_counter(int pf, uint64_t events)
219{
220    uint64_t pfc;
221    pfc = (1ull << 63) - events;
222
223    if (!pf) {
224        CVMX_MT_COP0(pfc, COP0_PERFVALUE0);
225    } else
226        CVMX_MT_COP0(pfc, COP0_PERFVALUE1);
227}
228
229void cvmx_collect_sample(void)
230{
231    if (!eccb.read_cfg_blk)
232        cvmx_read_config_blk();
233
234    if (read_percpu_block)
235        cvmx_init_pcpu_block(cvmx_get_core_num(), EVENT_PERCPU_BUFFER_SIZE);
236
237    cvmx_add_sample_to_buffer();
238    cvmx_reset_perf_counter(0, eccb.cfg_blk.events);
239}
240