1232809Sjmallett/***********************license start***************
2232809Sjmallett * Copyright (c) 2011  Cavium Inc. (support@cavium.com). All rights
3232809Sjmallett * reserved.
4232809Sjmallett *
5232809Sjmallett *
6232809Sjmallett * Redistribution and use in source and binary forms, with or without
7232809Sjmallett * modification, are permitted provided that the following conditions are
8232809Sjmallett * met:
9232809Sjmallett *
10232809Sjmallett *   * Redistributions of source code must retain the above copyright
11232809Sjmallett *     notice, this list of conditions and the following disclaimer.
12232809Sjmallett *
13232809Sjmallett *   * Redistributions in binary form must reproduce the above
14232809Sjmallett *     copyright notice, this list of conditions and the following
15232809Sjmallett *     disclaimer in the documentation and/or other materials provided
16232809Sjmallett *     with the distribution.
17232809Sjmallett *
18232809Sjmallett *   * Neither the name of Cavium Inc. nor the names of
19232809Sjmallett *     its contributors may be used to endorse or promote products
20232809Sjmallett *     derived from this software without specific prior written
21232809Sjmallett *     permission.
22232809Sjmallett *
23232809Sjmallett * This Software, including technical data, may be subject to U.S. export  control
24232809Sjmallett * laws, including the U.S. Export Administration Act and its  associated
25232809Sjmallett * regulations, and may be subject to export or import  regulations in other
26232809Sjmallett * countries.
27232809Sjmallett *
28232809Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29232809Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30232809Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31232809Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32232809Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33232809Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34232809Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35232809Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36232809Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37232809Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38232809Sjmallett ************************license end**************************************/
39232809Sjmallett
40232809Sjmallett/**
41232809Sjmallett * @file
42232809Sjmallett *
43232809Sjmallett * Interface to event profiler.
44232809Sjmallett *
45232809Sjmallett */
46232809Sjmallett
47232809Sjmallett#include "cvmx-config.h"
48232809Sjmallett#include "cvmx.h"
49232809Sjmallett#include "cvmx-interrupt.h"
50232809Sjmallett#include "cvmx-sysinfo.h"
51232809Sjmallett#include "cvmx-coremask.h"
52232809Sjmallett#include "cvmx-spinlock.h"
53232809Sjmallett#include "cvmx-atomic.h"
54232816Sjmallett#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
55232809Sjmallett#include "cvmx-error.h"
56232816Sjmallett#endif
57232809Sjmallett#include "cvmx-asm.h"
58232809Sjmallett#include "cvmx-bootmem.h"
59232809Sjmallett#include "cvmx-profiler.h"
60232809Sjmallett
61232809Sjmallett#ifdef PROFILER_DEBUG
62232809Sjmallett#define PRINTF(fmt, args...)    cvmx_safe_printf(fmt, ##args)
63232809Sjmallett#else
64232809Sjmallett#define PRINTF(fmt, args...)
65232809Sjmallett#endif
66232809Sjmallett
67232809SjmallettCVMX_SHARED static event_counter_control_block_t eccb;
68232809Sjmallettcvmx_config_block_t *pcpu_cfg_blk;
69232809Sjmallett
70232809Sjmallettint read_percpu_block = 1;
71232809Sjmallett
72232809Sjmallett/**
73232809Sjmallett * Set Interrupt IRQ line for Performance Counter
74232809Sjmallett *
75232809Sjmallett */
76232809Sjmallettvoid cvmx_update_perfcnt_irq(void)
77232809Sjmallett{
78232809Sjmallett    uint64_t cvmctl;
79232809Sjmallett
80232809Sjmallett    /* Clear CvmCtl[IPPCI] bit and move the Performance Counter
81232809Sjmallett     * interrupt to IRQ 6
82232809Sjmallett     */
83232809Sjmallett    CVMX_MF_COP0(cvmctl, COP0_CVMCTL);
84232809Sjmallett    cvmctl &= ~(7 << 7);
85232809Sjmallett    cvmctl |= 6 << 7;
86232809Sjmallett    CVMX_MT_COP0(cvmctl, COP0_CVMCTL);
87232809Sjmallett}
88232809Sjmallett
89232809Sjmallett/**
90232809Sjmallett * @INTERNAL
91232809Sjmallett * Return the baseaddress of the namedblock
92232809Sjmallett * @param buf_name  Name of Namedblock
93232809Sjmallett *
94232809Sjmallett * @return baseaddress of block on Success, NULL on failure.
95232809Sjmallett */
96232809Sjmallettstatic
97232809Sjmallettvoid *cvmx_get_memory_addr(const char* buf_name)
98232809Sjmallett{
99232809Sjmallett    void *buffer_ptr = NULL;
100232809Sjmallett    const struct cvmx_bootmem_named_block_desc *block_desc = cvmx_bootmem_find_named_block(buf_name);
101232809Sjmallett    if (block_desc)
102232809Sjmallett        buffer_ptr = cvmx_phys_to_ptr(block_desc->base_addr);
103232809Sjmallett    assert (buffer_ptr != NULL);
104232809Sjmallett
105232809Sjmallett    return buffer_ptr;
106232809Sjmallett}
107232809Sjmallett
108232809Sjmallett/**
109232809Sjmallett * @INTERNAL
110232809Sjmallett * Initialize the cpu block metadata.
111232809Sjmallett *
112232809Sjmallett * @param cpu	core no
113232809Sjmallett * @param size	size of per cpu memory in named block
114232809Sjmallett *
115232809Sjmallett */
116232809Sjmallettstatic
117232809Sjmallettvoid cvmx_init_pcpu_block(int cpu, int size)
118232809Sjmallett{
119232809Sjmallett    eccb.cfg_blk.pcpu_base_addr[cpu] = (char *)cvmx_get_memory_addr(EVENT_BUFFER_BLOCK) + (size * cpu);
120232809Sjmallett    assert (eccb.cfg_blk.pcpu_base_addr[cpu] != NULL);
121232809Sjmallett
122232809Sjmallett    cvmx_ringbuf_t  *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
123232809Sjmallett
124232809Sjmallett    cpu_buf->pcpu_blk_info.size = size;
125232809Sjmallett    cpu_buf->pcpu_blk_info.max_samples = ((size - sizeof(cvmx_cpu_event_block_t)) / sizeof(cvmx_sample_entry_t));
126232809Sjmallett    cpu_buf->pcpu_blk_info.sample_count = 0;
127232809Sjmallett    cpu_buf->pcpu_blk_info.sample_read = 0;
128232809Sjmallett    cpu_buf->pcpu_blk_info.data = eccb.cfg_blk.pcpu_base_addr[cpu] + sizeof(cvmx_cpu_event_block_t) + PADBYTES;
129232809Sjmallett    cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.tail = \
130232809Sjmallett       cpu_buf->pcpu_data = cpu_buf->pcpu_blk_info.data;
131232809Sjmallett    cpu_buf->pcpu_blk_info.end = eccb.cfg_blk.pcpu_base_addr[cpu] + size;
132232809Sjmallett
133232809Sjmallett    cvmx_atomic_set32(&read_percpu_block, 0);
134232809Sjmallett
135232809Sjmallett    /*
136232809Sjmallett     * Write per cpu mem base address info in to 'event config' named block,
137232809Sjmallett     * This info is needed by oct-remote-profile to get Per cpu memory
138232809Sjmallett     * base address of each core of the named block.
139232809Sjmallett     */
140232809Sjmallett    pcpu_cfg_blk = (cvmx_config_block_t *) eccb.config_blk_base_addr;
141232809Sjmallett    pcpu_cfg_blk->pcpu_base_addr[cpu] = eccb.cfg_blk.pcpu_base_addr[cpu];
142232809Sjmallett}
143232809Sjmallett
144232809Sjmallett/**
145232809Sjmallett * @INTERNAL
146232809Sjmallett * Retrieve the info from the 'event_config' named block.
147232809Sjmallett *
148232809Sjmallett * Here events value is read(as passed to oct-remote-profile) to reset perf
149232809Sjmallett * counters on every Perf counter overflow.
150232809Sjmallett *
151232809Sjmallett */
152232809Sjmallettstatic
153232809Sjmallettvoid cvmx_read_config_blk(void)
154232809Sjmallett{
155232809Sjmallett    eccb.config_blk_base_addr = (char *)cvmx_get_memory_addr(EVENT_BUFFER_CONFIG_BLOCK);
156232809Sjmallett    memcpy(&(eccb.cfg_blk.events), eccb.config_blk_base_addr + \
157232809Sjmallett       offsetof(cvmx_config_block_t, events), sizeof(int64_t));
158232809Sjmallett
159232809Sjmallett    cvmx_atomic_set32(&eccb.read_cfg_blk,1);
160232809Sjmallett    PRINTF("cfg_blk.events=%lu, sample_count=%ld\n", eccb.cfg_blk.events, eccb.cfg_blk.sample_count);
161232809Sjmallett}
162232809Sjmallett
163232809Sjmallett/**
164232809Sjmallett * @INTERNAL
165232809Sjmallett * Add new sample to the buffer and increment the head pointer and
166232809Sjmallett * global sample count(i.e sum total of samples collected on all cores)
167232809Sjmallett *
168232809Sjmallett */
169232809Sjmallettstatic
170232809Sjmallettvoid cvmx_add_sample_to_buffer(void)
171232809Sjmallett{
172232809Sjmallett    uint32_t epc;
173232809Sjmallett    int cpu = cvmx_get_core_num();
174232809Sjmallett    CVMX_MF_COP0(epc, COP0_EPC);
175232809Sjmallett
176232809Sjmallett    cvmx_ringbuf_t  *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
177232809Sjmallett
178232809Sjmallett    /*
179232809Sjmallett     * head/tail pointer can be NULL, and this case arises when oct-remote-profile is
180232809Sjmallett     * invoked afresh. To keep memory sane for current instance, we clear namedblock off
181232809Sjmallett     * previous data and this is accomplished by octeon_remote_write_mem from host.
182232809Sjmallett     */
183232809Sjmallett    if (cvmx_unlikely(!cpu_buf->pcpu_blk_info.head && !cpu_buf->pcpu_blk_info.end)) {
184232809Sjmallett       /* Reread the event count as a different threshold val could be
185232809Sjmallett        * passed with profiler alongside --events flag */
186232809Sjmallett        cvmx_read_config_blk();
187232809Sjmallett        cvmx_init_pcpu_block(cpu, EVENT_PERCPU_BUFFER_SIZE);
188232809Sjmallett    }
189232809Sjmallett
190232809Sjmallett    /* In case of hitting end of buffer, reset head,data ptr to start */
191232809Sjmallett    if (cpu_buf->pcpu_blk_info.head == cpu_buf->pcpu_blk_info.end)
192232809Sjmallett        cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data = cpu_buf->pcpu_data;
193232809Sjmallett
194232809Sjmallett    /* Store the pc, respective core no.*/
195232809Sjmallett    cvmx_sample_entry_t *sample = (cvmx_sample_entry_t *) cpu_buf->pcpu_blk_info.data;
196232809Sjmallett    sample->pc = epc;
197232809Sjmallett    sample->core = cpu;
198232809Sjmallett
199232809Sjmallett    /* Update Per CPU stats */
200232809Sjmallett    cpu_buf->pcpu_blk_info.sample_count++;
201232809Sjmallett    cpu_buf->pcpu_blk_info.data += sizeof(cvmx_sample_entry_t);
202232809Sjmallett    cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data;
203232809Sjmallett
204232809Sjmallett    /* Increment the global sample count i.e sum total of samples on all cores*/
205232809Sjmallett    cvmx_atomic_add64(&(pcpu_cfg_blk->sample_count), 1);
206232809Sjmallett
207232809Sjmallett    PRINTF("the core%d:pc 0x%016lx, sample_count=%ld\n", cpu, sample->pc, cpu_buf->pcpu_blk_info.sample_count);
208232809Sjmallett}
209232809Sjmallett
210232809Sjmallett/**
211232809Sjmallett * @INTERNAL
212232809Sjmallett * Reset performance counters
213232809Sjmallett *
214232809Sjmallett * @param pf     The performance counter Number (0, 1)
215232809Sjmallett * @param events The threshold value for which interrupt has to be asserted
216232809Sjmallett */
217232809Sjmallettstatic
218232809Sjmallettvoid cvmx_reset_perf_counter(int pf, uint64_t events)
219232809Sjmallett{
220232809Sjmallett    uint64_t pfc;
221232809Sjmallett    pfc = (1ull << 63) - events;
222232809Sjmallett
223232809Sjmallett    if (!pf) {
224232809Sjmallett        CVMX_MT_COP0(pfc, COP0_PERFVALUE0);
225232809Sjmallett    } else
226232809Sjmallett        CVMX_MT_COP0(pfc, COP0_PERFVALUE1);
227232809Sjmallett}
228232809Sjmallett
229232809Sjmallettvoid cvmx_collect_sample(void)
230232809Sjmallett{
231232809Sjmallett    if (!eccb.read_cfg_blk)
232232809Sjmallett        cvmx_read_config_blk();
233232809Sjmallett
234232809Sjmallett    if (read_percpu_block)
235232809Sjmallett        cvmx_init_pcpu_block(cvmx_get_core_num(), EVENT_PERCPU_BUFFER_SIZE);
236232809Sjmallett
237232809Sjmallett    cvmx_add_sample_to_buffer();
238232809Sjmallett    cvmx_reset_perf_counter(0, eccb.cfg_blk.events);
239232809Sjmallett}
240