/****************************/ /* 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 #include #include #include #include #include #include /* 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, };