2012-03-11 04:14:00 +00:00
|
|
|
/***********************license start***************
|
|
|
|
* Copyright (c) 2011 Cavium Inc. (support@cavium.com). All rights
|
|
|
|
* reserved.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are
|
|
|
|
* met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* * Redistributions in binary form must reproduce the above
|
|
|
|
* copyright notice, this list of conditions and the following
|
|
|
|
* disclaimer in the documentation and/or other materials provided
|
|
|
|
* with the distribution.
|
|
|
|
*
|
|
|
|
* * Neither the name of Cavium Inc. nor the names of
|
|
|
|
* its contributors may be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written
|
|
|
|
* permission.
|
|
|
|
*
|
|
|
|
* This Software, including technical data, may be subject to U.S. export control
|
|
|
|
* laws, including the U.S. Export Administration Act and its associated
|
|
|
|
* regulations, and may be subject to export or import regulations in other
|
|
|
|
* countries.
|
|
|
|
*
|
|
|
|
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
|
|
|
|
* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
|
|
|
|
* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
|
|
|
|
* THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
|
|
|
|
* DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
|
|
|
|
* SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
|
|
|
|
* MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
|
|
|
|
* VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
|
|
|
|
* CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
|
|
|
|
* PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
|
|
|
|
************************license end**************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
*
|
|
|
|
* Interface to event profiler.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cvmx-config.h"
|
|
|
|
#include "cvmx.h"
|
|
|
|
#include "cvmx-interrupt.h"
|
|
|
|
#include "cvmx-sysinfo.h"
|
|
|
|
#include "cvmx-coremask.h"
|
|
|
|
#include "cvmx-spinlock.h"
|
|
|
|
#include "cvmx-atomic.h"
|
2012-03-11 06:55:17 +00:00
|
|
|
#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL)
|
2012-03-11 04:14:00 +00:00
|
|
|
#include "cvmx-error.h"
|
2012-03-11 06:55:17 +00:00
|
|
|
#endif
|
2012-03-11 04:14:00 +00:00
|
|
|
#include "cvmx-asm.h"
|
|
|
|
#include "cvmx-bootmem.h"
|
|
|
|
#include "cvmx-profiler.h"
|
|
|
|
|
|
|
|
#ifdef PROFILER_DEBUG
|
|
|
|
#define PRINTF(fmt, args...) cvmx_safe_printf(fmt, ##args)
|
|
|
|
#else
|
|
|
|
#define PRINTF(fmt, args...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
CVMX_SHARED static event_counter_control_block_t eccb;
|
|
|
|
cvmx_config_block_t *pcpu_cfg_blk;
|
|
|
|
|
|
|
|
int read_percpu_block = 1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set Interrupt IRQ line for Performance Counter
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void cvmx_update_perfcnt_irq(void)
|
|
|
|
{
|
|
|
|
uint64_t cvmctl;
|
|
|
|
|
|
|
|
/* Clear CvmCtl[IPPCI] bit and move the Performance Counter
|
|
|
|
* interrupt to IRQ 6
|
|
|
|
*/
|
|
|
|
CVMX_MF_COP0(cvmctl, COP0_CVMCTL);
|
|
|
|
cvmctl &= ~(7 << 7);
|
|
|
|
cvmctl |= 6 << 7;
|
|
|
|
CVMX_MT_COP0(cvmctl, COP0_CVMCTL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @INTERNAL
|
|
|
|
* Return the baseaddress of the namedblock
|
|
|
|
* @param buf_name Name of Namedblock
|
|
|
|
*
|
|
|
|
* @return baseaddress of block on Success, NULL on failure.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void *cvmx_get_memory_addr(const char* buf_name)
|
|
|
|
{
|
|
|
|
void *buffer_ptr = NULL;
|
|
|
|
const struct cvmx_bootmem_named_block_desc *block_desc = cvmx_bootmem_find_named_block(buf_name);
|
|
|
|
if (block_desc)
|
|
|
|
buffer_ptr = cvmx_phys_to_ptr(block_desc->base_addr);
|
|
|
|
assert (buffer_ptr != NULL);
|
|
|
|
|
|
|
|
return buffer_ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @INTERNAL
|
|
|
|
* Initialize the cpu block metadata.
|
|
|
|
*
|
|
|
|
* @param cpu core no
|
|
|
|
* @param size size of per cpu memory in named block
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void cvmx_init_pcpu_block(int cpu, int size)
|
|
|
|
{
|
|
|
|
eccb.cfg_blk.pcpu_base_addr[cpu] = (char *)cvmx_get_memory_addr(EVENT_BUFFER_BLOCK) + (size * cpu);
|
|
|
|
assert (eccb.cfg_blk.pcpu_base_addr[cpu] != NULL);
|
|
|
|
|
|
|
|
cvmx_ringbuf_t *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
|
|
|
|
|
|
|
|
cpu_buf->pcpu_blk_info.size = size;
|
|
|
|
cpu_buf->pcpu_blk_info.max_samples = ((size - sizeof(cvmx_cpu_event_block_t)) / sizeof(cvmx_sample_entry_t));
|
|
|
|
cpu_buf->pcpu_blk_info.sample_count = 0;
|
|
|
|
cpu_buf->pcpu_blk_info.sample_read = 0;
|
|
|
|
cpu_buf->pcpu_blk_info.data = eccb.cfg_blk.pcpu_base_addr[cpu] + sizeof(cvmx_cpu_event_block_t) + PADBYTES;
|
|
|
|
cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.tail = \
|
|
|
|
cpu_buf->pcpu_data = cpu_buf->pcpu_blk_info.data;
|
|
|
|
cpu_buf->pcpu_blk_info.end = eccb.cfg_blk.pcpu_base_addr[cpu] + size;
|
|
|
|
|
|
|
|
cvmx_atomic_set32(&read_percpu_block, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write per cpu mem base address info in to 'event config' named block,
|
|
|
|
* This info is needed by oct-remote-profile to get Per cpu memory
|
|
|
|
* base address of each core of the named block.
|
|
|
|
*/
|
|
|
|
pcpu_cfg_blk = (cvmx_config_block_t *) eccb.config_blk_base_addr;
|
|
|
|
pcpu_cfg_blk->pcpu_base_addr[cpu] = eccb.cfg_blk.pcpu_base_addr[cpu];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @INTERNAL
|
|
|
|
* Retrieve the info from the 'event_config' named block.
|
|
|
|
*
|
|
|
|
* Here events value is read(as passed to oct-remote-profile) to reset perf
|
|
|
|
* counters on every Perf counter overflow.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void cvmx_read_config_blk(void)
|
|
|
|
{
|
|
|
|
eccb.config_blk_base_addr = (char *)cvmx_get_memory_addr(EVENT_BUFFER_CONFIG_BLOCK);
|
|
|
|
memcpy(&(eccb.cfg_blk.events), eccb.config_blk_base_addr + \
|
|
|
|
offsetof(cvmx_config_block_t, events), sizeof(int64_t));
|
|
|
|
|
|
|
|
cvmx_atomic_set32(&eccb.read_cfg_blk,1);
|
|
|
|
PRINTF("cfg_blk.events=%lu, sample_count=%ld\n", eccb.cfg_blk.events, eccb.cfg_blk.sample_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @INTERNAL
|
|
|
|
* Add new sample to the buffer and increment the head pointer and
|
|
|
|
* global sample count(i.e sum total of samples collected on all cores)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void cvmx_add_sample_to_buffer(void)
|
|
|
|
{
|
|
|
|
uint32_t epc;
|
|
|
|
int cpu = cvmx_get_core_num();
|
|
|
|
CVMX_MF_COP0(epc, COP0_EPC);
|
|
|
|
|
|
|
|
cvmx_ringbuf_t *cpu_buf = (cvmx_ringbuf_t *) eccb.cfg_blk.pcpu_base_addr[cpu];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* head/tail pointer can be NULL, and this case arises when oct-remote-profile is
|
|
|
|
* invoked afresh. To keep memory sane for current instance, we clear namedblock off
|
|
|
|
* previous data and this is accomplished by octeon_remote_write_mem from host.
|
|
|
|
*/
|
|
|
|
if (cvmx_unlikely(!cpu_buf->pcpu_blk_info.head && !cpu_buf->pcpu_blk_info.end)) {
|
|
|
|
/* Reread the event count as a different threshold val could be
|
|
|
|
* passed with profiler alongside --events flag */
|
|
|
|
cvmx_read_config_blk();
|
|
|
|
cvmx_init_pcpu_block(cpu, EVENT_PERCPU_BUFFER_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In case of hitting end of buffer, reset head,data ptr to start */
|
|
|
|
if (cpu_buf->pcpu_blk_info.head == cpu_buf->pcpu_blk_info.end)
|
|
|
|
cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data = cpu_buf->pcpu_data;
|
|
|
|
|
|
|
|
/* Store the pc, respective core no.*/
|
|
|
|
cvmx_sample_entry_t *sample = (cvmx_sample_entry_t *) cpu_buf->pcpu_blk_info.data;
|
|
|
|
sample->pc = epc;
|
|
|
|
sample->core = cpu;
|
|
|
|
|
|
|
|
/* Update Per CPU stats */
|
|
|
|
cpu_buf->pcpu_blk_info.sample_count++;
|
|
|
|
cpu_buf->pcpu_blk_info.data += sizeof(cvmx_sample_entry_t);
|
|
|
|
cpu_buf->pcpu_blk_info.head = cpu_buf->pcpu_blk_info.data;
|
|
|
|
|
|
|
|
/* Increment the global sample count i.e sum total of samples on all cores*/
|
|
|
|
cvmx_atomic_add64(&(pcpu_cfg_blk->sample_count), 1);
|
|
|
|
|
|
|
|
PRINTF("the core%d:pc 0x%016lx, sample_count=%ld\n", cpu, sample->pc, cpu_buf->pcpu_blk_info.sample_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @INTERNAL
|
|
|
|
* Reset performance counters
|
|
|
|
*
|
|
|
|
* @param pf The performance counter Number (0, 1)
|
|
|
|
* @param events The threshold value for which interrupt has to be asserted
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void cvmx_reset_perf_counter(int pf, uint64_t events)
|
|
|
|
{
|
|
|
|
uint64_t pfc;
|
|
|
|
pfc = (1ull << 63) - events;
|
|
|
|
|
|
|
|
if (!pf) {
|
|
|
|
CVMX_MT_COP0(pfc, COP0_PERFVALUE0);
|
|
|
|
} else
|
|
|
|
CVMX_MT_COP0(pfc, COP0_PERFVALUE1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cvmx_collect_sample(void)
|
|
|
|
{
|
|
|
|
if (!eccb.read_cfg_blk)
|
|
|
|
cvmx_read_config_blk();
|
|
|
|
|
|
|
|
if (read_percpu_block)
|
|
|
|
cvmx_init_pcpu_block(cvmx_get_core_num(), EVENT_PERCPU_BUFFER_SIZE);
|
|
|
|
|
|
|
|
cvmx_add_sample_to_buffer();
|
|
|
|
cvmx_reset_perf_counter(0, eccb.cfg_blk.events);
|
|
|
|
}
|