/****************************/
/* THIS IS OPEN SOURCE CODE */
/****************************/
/**
* @file coretemp_freebsd.c
* @author Joachim Protze
* joachim.protze@zih.tu-dresden.de
* @author Vince Weaver
* vweaver1@eecs.utk.edu
* @author Harald Servat
* harald.servat@gmail.com
*
* @ingroup papi_components
*
* @brief
* This component is intended to access CPU On-Die Thermal Sensors in
* the Intel Core architecture in a FreeBSD machine using the coretemp.ko
* kernel module.
*/
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
/* Headers required by PAPI */
#include "papi.h"
#include "papi_internal.h"
#include "papi_vector.h"
#include "papi_memory.h"
#define CORETEMP_MAX_COUNTERS 32 /* Can we tune this dynamically? */
#define TRUE (1==1)
#define FALSE (1!=1)
#define UNREFERENCED(x) (void)x
/* Structure that stores private information for each event */
typedef struct coretemp_register
{
int mib[4];
/* Access to registers through these MIBs + sysctl (3) call */
unsigned int selector;
/**< Signifies which counter slot is being used */
/**< Indexed from 1 as 0 has a special meaning */
} coretemp_register_t;
/** This structure is used to build the table of events */
typedef struct coretemp_native_event_entry
{
coretemp_register_t resources; /**< Per counter resources */
char name[PAPI_MAX_STR_LEN]; /**< Name of the counter */
char description[PAPI_MAX_STR_LEN]; /**< Description of the counter */
} coretemp_native_event_entry_t;
/* This structure is used when doing register allocation
it possibly is not necessary when there are no
register constraints */
typedef struct coretemp_reg_alloc
{
coretemp_register_t ra_bits;
} coretemp_reg_alloc_t;
/* Holds control flags, usually out-of band configuration of the hardware */
typedef struct coretemp_control_state
{
int added[CORETEMP_MAX_COUNTERS];
long_long counters[CORETEMP_MAX_COUNTERS]; /**< Copy of counts, used for caching */
} coretemp_control_state_t;
/* Holds per-thread information */
typedef struct coretemp_context
{
coretemp_control_state_t state;
} coretemp_context_t;
/** This table contains the native events */
static coretemp_native_event_entry_t *coretemp_native_table;
/** number of events in the table*/
static int CORETEMP_NUM_EVENTS = 0;
/********************************************************************/
/* Below are the functions required by the PAPI component interface */
/********************************************************************/
/** This is called whenever a thread is initialized */
int coretemp_init_thread (hwd_context_t * ctx)
{
int mib[4];
size_t len;
UNREFERENCED(ctx);
SUBDBG("coretemp_init_thread %p...\n", ctx);
#if 0
/* what does this do? VMW */
len = 4;
if (sysctlnametomib ("dev.coretemp.0.%driver", mib, &len) == -1)
return PAPI_ECMP;
#endif
return PAPI_OK;
}
/** Initialize hardware counters, setup the function vector table
* and get hardware information, this routine is called when the
* PAPI process is initialized (IE PAPI_library_init)
*/
int coretemp_init_component ()
{
int ret;
int i;
int mib[4];
size_t len;
char tmp[128];
SUBDBG("coretemp_init_component...\n");
/* Count the number of cores (counters) that have sensors allocated */
i = 0;
CORETEMP_NUM_EVENTS = 0;
sprintf (tmp, "dev.coretemp.%d.%%driver", i);
len = 4;
ret = sysctlnametomib (tmp, mib, &len);
while (ret != -1)
{
CORETEMP_NUM_EVENTS++;
i++;
sprintf (tmp, "dev.coretemp.%d.%%driver", i);
len = 4;
ret = sysctlnametomib (tmp, mib, &len);
}
if (CORETEMP_NUM_EVENTS == 0)
return PAPI_OK;
/* Allocate memory for the our event table */
coretemp_native_table = (coretemp_native_event_entry_t *)
papi_malloc (sizeof (coretemp_native_event_entry_t) * CORETEMP_NUM_EVENTS);
if (coretemp_native_table == NULL)
{
perror( "malloc():Could not get memory for coretemp events table" );
return PAPI_ENOMEM;
}
/* Allocate native events internal structures */
for (i = 0; i < CORETEMP_NUM_EVENTS; i++)
{
/* Event name */
sprintf (coretemp_native_table[i].name, "CORETEMP_CPU_%d", i);
/* Event description */
sprintf (coretemp_native_table[i].description, "CPU On-Die Thermal Sensor #%d", i);
/* Event extra bits -> save MIB to faster access later */
sprintf (tmp, "dev.cpu.%d.temperature", i);
len = 4;
if (sysctlnametomib (tmp, coretemp_native_table[i].resources.mib, &len) == -1)
return PAPI_ECMP;
coretemp_native_table[i].resources.selector = i+1;
}
return PAPI_OK;
}
/** Setup the counter control structure */
int coretemp_init_control_state (hwd_control_state_t * ctrl)
{
int i;
SUBDBG("coretemp_init_control_state... %p\n", ctrl);
coretemp_control_state_t *c = (coretemp_control_state_t *) ctrl;
for (i = 0; i < CORETEMP_MAX_COUNTERS; i++)
c->added[i] = FALSE;
return PAPI_OK;
}
/** Enumerate Native Events
@param EventCode is the event of interest
@param modifier is one of PAPI_ENUM_FIRST, PAPI_ENUM_EVENTS
*/
int coretemp_ntv_enum_events (unsigned int *EventCode, int modifier)
{
switch ( modifier )
{
/* return EventCode of first event */
case PAPI_ENUM_FIRST:
*EventCode = 0;
return PAPI_OK;
break;
/* return EventCode of passed-in Event */
case PAPI_ENUM_EVENTS:
{
int index = *EventCode;
if ( index < CORETEMP_NUM_EVENTS - 1 )
{
*EventCode = *EventCode + 1;
return PAPI_OK;
}
else
return PAPI_ENOEVNT;
break;
}
default:
return PAPI_EINVAL;
}
return PAPI_EINVAL;
}
/** Takes a native event code and passes back the name
@param EventCode is the native event code
@param name is a pointer for the name to be copied to
@param len is the size of the string
*/
int coretemp_ntv_code_to_name (unsigned int EventCode, char *name, int len)
{
int index = EventCode;
strncpy( name, coretemp_native_table[index].name, len );
return PAPI_OK;
}
/** Takes a native event code and passes back the event description
@param EventCode is the native event code
@param name is a pointer for the description to be copied to
@param len is the size of the string
*/
int coretemp_ntv_code_to_descr (unsigned int EventCode, char *name, int len)
{
int index = EventCode;
strncpy( name, coretemp_native_table[index].description, len );
return PAPI_OK;
}
/** This takes an event and returns the bits that would be written
out to the hardware device (this is very much tied to CPU-type support */
int coretemp_ntv_code_to_bits (unsigned int EventCode, hwd_register_t * bits)
{
UNREFERENCED(EventCode);
UNREFERENCED(bits);
return PAPI_OK;
}
/** Triggered by eventset operations like add or remove */
int coretemp_update_control_state( hwd_control_state_t * ptr,
NativeInfo_t * native, int count, hwd_context_t * ctx )
{
int i, index;
coretemp_control_state_t *c = (coretemp_control_state_t *) ptr;
UNREFERENCED(ctx);
SUBDBG("coretemp_update_control_state %p %p...\n", ptr, ctx);
for (i = 0; i < count; i++)
{
index = native[i].ni_event;
native[i].ni_position = coretemp_native_table[index].resources.selector - 1;
c->added[native[i].ni_position] = TRUE;
SUBDBG ("\nnative[%i].ni_position = coretemp_native_table[%i].resources.selector-1 = %i;\n",
i, index, native[i].ni_position );
}
return PAPI_OK;
}
/** Triggered by PAPI_start() */
int coretemp_start (hwd_context_t * ctx, hwd_control_state_t * ctrl)
{
UNREFERENCED(ctx);
UNREFERENCED(ctrl);
SUBDBG( "coretemp_start %p %p...\n", ctx, ctrl );
/* Nothing to be done */
return PAPI_OK;
}
/** Triggered by PAPI_stop() */
int coretemp_stop (hwd_context_t * ctx, hwd_control_state_t * ctrl)
{
UNREFERENCED(ctx);
UNREFERENCED(ctrl);
SUBDBG("coretemp_stop %p %p...\n", ctx, ctrl);
/* Nothing to be done */
return PAPI_OK;
}
/** Triggered by PAPI_read() */
int coretemp_read (hwd_context_t * ctx, hwd_control_state_t * ctrl,
long_long ** events, int flags)
{
int i;
coretemp_control_state_t *c = (coretemp_control_state_t *) ctrl;
UNREFERENCED(ctx);
UNREFERENCED(flags);
SUBDBG("coretemp_read... %p %d\n", ctx, flags);
for (i = 0; i < CORETEMP_MAX_COUNTERS; i++)
if (c->added[i])
{
int tmp;
size_t len = sizeof(tmp);
if (sysctl (coretemp_native_table[i].resources.mib, 4, &tmp, &len, NULL, 0) == -1)
c->counters[i] = 0;
else
c->counters[i] = tmp/10;
/* Coretemp module returns temperature in tenths of kelvin
Kelvin are useful to avoid negative values... but will have
negative temperatures ??? */
}
*events = c->counters;
return PAPI_OK;
}
/** Triggered by PAPI_write(), but only if the counters are running */
/* otherwise, the updated state is written to ESI->hw_start */
int coretemp_write (hwd_context_t * ctx, hwd_control_state_t * ctrl,
long_long events[] )
{
UNREFERENCED(ctx);
UNREFERENCED(events);
UNREFERENCED(ctrl);
SUBDBG("coretemp_write... %p %p\n", ctx, ctrl);
/* These sensor counters cannot be writtn */
return PAPI_OK;
}
/** Triggered by PAPI_reset */
int coretemp_reset(hwd_context_t * ctx, hwd_control_state_t * ctrl)
{
UNREFERENCED(ctx);
UNREFERENCED(ctrl);
SUBDBG("coretemp_reset ctx=%p ctrl=%p...\n", ctx, ctrl);
/* These sensors cannot be reseted */
return PAPI_OK;
}
/** Triggered by PAPI_shutdown() */
int coretemp_shutdown_component (void)
{
SUBDBG( "coretemp_shutdown_component... %p\n");
/* Last chance to clean up */
papi_free (coretemp_native_table);
return PAPI_OK;
}
/** This function sets various options in the component
@param ctx unused
@param code valid are PAPI_SET_DEFDOM, PAPI_SET_DOMAIN, PAPI_SETDEFGRN, PAPI_SET_GRANUL and PAPI_SET_INHERIT
@param option unused
*/
int coretemp_ctl (hwd_context_t * ctx, int code, _papi_int_option_t * option)
{
UNREFERENCED(ctx);
UNREFERENCED(code);
UNREFERENCED(option);
SUBDBG( "coretemp_ctl... %p %d %p\n", ctx, code, option );
/* FIXME. This should maybe set up more state, such as which counters are active and */
/* counter mappings. */
return PAPI_OK;
}
/** This function has to set the bits needed to count different domains
In particular: PAPI_DOM_USER, PAPI_DOM_KERNEL PAPI_DOM_OTHER
By default return PAPI_EINVAL if none of those are specified
and PAPI_OK with success
PAPI_DOM_USER is only user context is counted
PAPI_DOM_KERNEL is only the Kernel/OS context is counted
PAPI_DOM_OTHER is Exception/transient mode (like user TLB misses)
PAPI_DOM_ALL is all of the domains
*/
int coretemp_set_domain (hwd_control_state_t * cntrl, int domain)
{
UNREFERENCED(cntrl);
SUBDBG ("coretemp_set_domain... %p %d\n", cntrl, domain);
if (PAPI_DOM_ALL & domain)
{
SUBDBG( " PAPI_DOM_ALL \n" );
return PAPI_OK;
}
return PAPI_EINVAL ;
}
/** Vector that points to entry points for our component */
papi_vector_t _coretemp_freebsd_vector = {
.cmp_info = {
/* default component information (unspecified values are initialized to 0) */
.name = "coretemp_freebsd",
.short_name = "coretemp",
.version = "5.0",
.num_mpx_cntrs = CORETEMP_MAX_COUNTERS,
.num_cntrs = CORETEMP_MAX_COUNTERS,
.default_domain = PAPI_DOM_ALL,
.available_domains = PAPI_DOM_ALL,
.default_granularity = PAPI_GRN_SYS,
.available_granularities = PAPI_GRN_SYS,
.hardware_intr_sig = PAPI_INT_SIGNAL,
/* component specific cmp_info initializations */
.fast_real_timer = 0,
.fast_virtual_timer = 0,
.attach = 0,
.attach_must_ptrace = 0,
}
,
/* sizes of framework-opaque component-private structures */
.size = {
.context = sizeof ( coretemp_context_t ),
.control_state = sizeof ( coretemp_control_state_t ),
.reg_value = sizeof ( coretemp_register_t ),
.reg_alloc = sizeof ( coretemp_reg_alloc_t ),
}
,
/* function pointers in this component */
.init_thread = coretemp_init_thread,
.init_component = coretemp_init_component,
.init_control_state = coretemp_init_control_state,
.start = coretemp_start,
.stop = coretemp_stop,
.read = coretemp_read,
.write = coretemp_write,
.shutdown_component = coretemp_shutdown_component,
.ctl = coretemp_ctl,
.update_control_state = coretemp_update_control_state,
.set_domain = coretemp_set_domain,
.reset = coretemp_reset,
.ntv_enum_events = coretemp_ntv_enum_events,
.ntv_code_to_name = coretemp_ntv_code_to_name,
.ntv_code_to_descr = coretemp_ntv_code_to_descr,
.ntv_code_to_bits = coretemp_ntv_code_to_bits,
};