/** * @file linux-powercap.c * @author Philip Vaccaro * @ingroup papi_components * @brief powercap component * * To work, the powercap kernel module must be loaded. */ #include #include #include #include #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" typedef struct _powercap_register { unsigned int selector; } _powercap_register_t; typedef struct _powercap_native_event_entry { char name[PAPI_MAX_STR_LEN]; char units[PAPI_MIN_STR_LEN]; char description[PAPI_MAX_STR_LEN]; int socket_id; int component_id; int event_id; int type; int return_type; _powercap_register_t resources; } _powercap_native_event_entry_t; typedef struct _powercap_reg_alloc { _powercap_register_t ra_bits; } _powercap_reg_alloc_t; static char read_buff[PAPI_MAX_STR_LEN]; static char write_buff[PAPI_MAX_STR_LEN]; static int num_events=0; // package events #define PKG_ENERGY 0 #define PKG_MAX_ENERGY_RANGE 1 #define PKG_MAX_POWER_A 2 #define PKG_POWER_LIMIT_A 3 #define PKG_TIME_WINDOW_A 4 #define PKG_MAX_POWER_B 5 #define PKG_POWER_LIMIT_B 6 #define PKG_TIME_WINDOW_B 7 #define PKG_ENABLED 8 #define PKG_NAME 9 #define PKG_NUM_EVENTS 10 static int pkg_events[PKG_NUM_EVENTS] = {PKG_ENERGY, PKG_MAX_ENERGY_RANGE, PKG_MAX_POWER_A, PKG_POWER_LIMIT_A, PKG_TIME_WINDOW_A, PKG_MAX_POWER_B, PKG_POWER_LIMIT_B, PKG_TIME_WINDOW_B, PKG_ENABLED, PKG_NAME}; static char *pkg_event_names[PKG_NUM_EVENTS] = {"ENERGY_UJ", "MAX_ENERGY_RANGE_UJ", "MAX_POWER_A_UW", "POWER_LIMIT_A_UW", "TIME_WINDOW_A_US", "MAX_POWER_B_UW", "POWER_LIMIT_B_UW", "TIME_WINDOW_B", "ENABLED", "NAME"}; static char *pkg_sys_names[PKG_NUM_EVENTS] = {"energy_uj", "max_energy_range_uj", "constraint_0_max_power_uw", "constraint_0_power_limit_uw", "constraint_0_time_window_us", "constraint_1_max_power_uw", "constraint_1_power_limit_uw", "constraint_1_time_window_us", "enabled", "name"}; static mode_t pkg_sys_flags[PKG_NUM_EVENTS] = {O_RDONLY, O_RDONLY, O_RDONLY, O_RDWR, O_RDONLY, O_RDONLY, O_RDWR, O_RDONLY, O_RDONLY, O_RDONLY}; // non-package events #define COMPONENT_ENERGY 10 #define COMPONENT_MAX_ENERGY_RANGE 11 #define COMPONENT_MAX_POWER_A 12 #define COMPONENT_POWER_LIMIT_A 13 #define COMPONENT_TIME_WINDOW_A 14 #define COMPONENT_ENABLED 15 #define COMPONENT_NAME 16 #define COMPONENT_NUM_EVENTS 7 static int component_events[COMPONENT_NUM_EVENTS] = {COMPONENT_ENERGY, COMPONENT_MAX_ENERGY_RANGE, COMPONENT_MAX_POWER_A, COMPONENT_POWER_LIMIT_A, COMPONENT_TIME_WINDOW_A, COMPONENT_ENABLED, COMPONENT_NAME}; static char *component_event_names[COMPONENT_NUM_EVENTS] = {"ENERGY_UJ", "MAX_ENERGY_RANGE_UJ", "MAX_POWER_A_UW", "POWER_LIMIT_A_UW", "TIME_WINDOW_A_US", "ENABLED", "NAME"}; static char *component_sys_names[COMPONENT_NUM_EVENTS] = {"energy_uj", "max_energy_range_uj", "constraint_0_max_power_uw", "constraint_0_power_limit_uw", "constraint_0_time_window_us", "enabled", "name"}; static mode_t component_sys_flags[COMPONENT_NUM_EVENTS] = {O_RDONLY, O_RDONLY, O_RDONLY, O_RDWR, O_RDONLY, O_RDONLY, O_RDONLY}; #define POWERCAP_MAX_COUNTERS (2 * (PKG_NUM_EVENTS + (3 * COMPONENT_NUM_EVENTS))) static _powercap_native_event_entry_t powercap_ntv_events[(2 * (PKG_NUM_EVENTS + (3 * COMPONENT_NUM_EVENTS)))]; static int event_fds[POWERCAP_MAX_COUNTERS]; typedef struct _powercap_control_state { long long count[POWERCAP_MAX_COUNTERS]; long long which_counter[POWERCAP_MAX_COUNTERS]; long long lastupdate; } _powercap_control_state_t; typedef struct _powercap_context { long long start_value[POWERCAP_MAX_COUNTERS]; _powercap_control_state_t state; } _powercap_context_t; papi_vector_t _powercap_vector; /***************************************************************************/ /****** BEGIN FUNCTIONS USED INTERNALLY SPECIFIC TO THIS COMPONENT *******/ /***************************************************************************/ /* Null terminated version of strncpy */ static char * _local_strlcpy( char *dst, const char *src, size_t size ) { char *retval = strncpy( dst, src, size ); if ( size>0 ) dst[size-1] = '\0'; return( retval ); } static long long read_powercap_value( int index ) { int sz = pread(event_fds[index], read_buff, PAPI_MAX_STR_LEN, 0); read_buff[sz] = '\0'; return atoll(read_buff); } static int write_powercap_value( int index, long long value ) { snprintf(write_buff, sizeof(write_buff), "%lld", value); int sz = pwrite(event_fds[index], write_buff, PAPI_MAX_STR_LEN, 0); if(sz == -1) { perror("Error in pwrite(): "); } return 1; } /************************* PAPI Functions **********************************/ /* * This is called whenever a thread is initialized */ static int _powercap_init_thread( hwd_context_t *ctx ) { ( void ) ctx; return PAPI_OK; } /* * Called when PAPI process is initialized (i.e. PAPI_library_init) */ static int _powercap_init_component( int cidx ) { int num_sockets = -1; int s = -1, e = -1, c = -1; char events_dir[128]; char event_path[128]; DIR *events; // get hw info const PAPI_hw_info_t *hw_info; hw_info=&( _papi_hwi_system_info.hw_info ); // check if intel processor if ( hw_info->vendor!=PAPI_VENDOR_INTEL ) { strncpy(_powercap_vector.cmp_info.disabled_reason, "Not an Intel processor", PAPI_MAX_STR_LEN); return PAPI_ENOSUPP; } // store number of sockets for adding events num_sockets = hw_info->sockets; num_events = 0; for(s = 0; s < num_sockets; s++) { // compose string of a pkg directory path snprintf(events_dir, sizeof(events_dir), "/sys/class/powercap/intel-rapl:%d/", s); // open directory to make sure it exists events = opendir(events_dir); // not a valid pkg/component directory so continue if (events == NULL) { continue; } // loop through pkg events and create powercap event entries for (e = 0; e < PKG_NUM_EVENTS; e++) { // compose string to individual event snprintf(event_path, sizeof(event_path), "%s%s", events_dir, pkg_sys_names[e]); // not a valid pkg event path so continue if (access(event_path, F_OK) == -1) { continue; } snprintf(powercap_ntv_events[num_events].name, sizeof(powercap_ntv_events[num_events].name), "%s:ZONE%d", pkg_event_names[e], s); //snprintf(powercap_ntv_events[num_events].description, sizeof(powercap_ntv_events[num_events].name), "%s:ZONE%d", pkg_event_names[e], s); //snprintf(powercap_ntv_events[num_events].units, sizeof(powercap_ntv_events[num_events].name), "%s:ZONE%d", pkg_event_names[e], s); powercap_ntv_events[num_events].return_type = PAPI_DATATYPE_UINT64; powercap_ntv_events[num_events].type = pkg_events[e]; powercap_ntv_events[num_events].resources.selector = num_events + 1; event_fds[num_events] = open(event_path, O_SYNC|pkg_sys_flags[e]); if(powercap_ntv_events[num_events].type == PKG_NAME) { int sz = pread(event_fds[num_events], read_buff, PAPI_MAX_STR_LEN, 0); read_buff[sz] = '\0'; snprintf(powercap_ntv_events[num_events].description, sizeof(powercap_ntv_events[num_events].description), "%s", read_buff); } num_events++; } // reset component count for each socket c = 0; snprintf(events_dir, sizeof(events_dir), "/sys/class/powercap/intel-rapl:%d:%d/", s, c); while((events = opendir(events_dir)) != NULL) { // loop through pkg events and create powercap event entries for (e = 0; e < COMPONENT_NUM_EVENTS; e++) { // compose string to individual event snprintf(event_path, sizeof(event_path), "%s%s", events_dir, component_sys_names[e]); // not a valid pkg event path so continue if (access(event_path, F_OK) == -1) { continue; } snprintf(powercap_ntv_events[num_events].name, sizeof(powercap_ntv_events[num_events].name), "%s:ZONE%d_SUBZONE%d", component_event_names[e], s, c); //snprintf(powercap_ntv_events[num_events].description, sizeof(powercap_ntv_events[num_events].name), "%s:ZONE%d_SUBZONE%d", component_event_names[e], s, c); //snprintf(powercap_ntv_events[num_events].units, sizeof(powercap_ntv_events[num_events].name), "%s:ZONE%d_SUBZONE%d", component_event_names[e], s, c); powercap_ntv_events[num_events].return_type = PAPI_DATATYPE_UINT64; powercap_ntv_events[num_events].type = component_events[e]; powercap_ntv_events[num_events].resources.selector = num_events + 1; event_fds[num_events] = open(event_path, O_SYNC|component_sys_flags[e]); if(powercap_ntv_events[num_events].type == COMPONENT_NAME) { int sz = pread(event_fds[num_events], read_buff, PAPI_MAX_STR_LEN, 0); read_buff[sz] = '\0'; snprintf(powercap_ntv_events[num_events].description, sizeof(powercap_ntv_events[num_events].description), "%s", read_buff); } num_events++; } // test for next component c++; // compose string of an pkg directory path snprintf(events_dir, sizeof(events_dir), "/sys/class/powercap/intel-rapl:%d:%d/", s, c); } } /* Export the total number of events available */ _powercap_vector.cmp_info.num_native_events = num_events; _powercap_vector.cmp_info.num_cntrs = num_events; _powercap_vector.cmp_info.num_mpx_cntrs = num_events; /* Export the component id */ _powercap_vector.cmp_info.CmpIdx = cidx; return PAPI_OK; } /* * Control of counters (Reading/Writing/Starting/Stopping/Setup) * functions */ static int _powercap_init_control_state( hwd_control_state_t *ctl ) { _powercap_control_state_t* control = ( _powercap_control_state_t* ) ctl; memset( control, 0, sizeof ( _powercap_control_state_t ) ); return PAPI_OK; } static int _powercap_start( hwd_context_t *ctx, hwd_control_state_t *ctl ) { _powercap_context_t* context = ( _powercap_context_t* ) ctx; (void) ctl; int b; for( b = 0; b < num_events; b++ ) { context->start_value[b]=read_powercap_value(b); } return PAPI_OK; } static int _powercap_stop( hwd_context_t *ctx, hwd_control_state_t *ctl ) { (void) ctx; _powercap_control_state_t* control = ( _powercap_control_state_t* ) ctl; int c; for( c = 0; c < num_events; c++ ) { control->count[c]=read_powercap_value(c); } return PAPI_OK; } /* Shutdown a thread */ static int _powercap_shutdown_thread( hwd_context_t *ctx ) { ( void ) ctx; SUBDBG( "Enter\n" ); return PAPI_OK; } static int _powercap_read( hwd_context_t *ctx, hwd_control_state_t *ctl, long long **events, int flags ) { (void) ctx; (void) flags; _powercap_control_state_t* control = ( _powercap_control_state_t* ) ctl; int i; for(i=0;icount[i]=read_powercap_value(control->which_counter[i]); } *events = ( ( _powercap_control_state_t* ) ctl )->count; return PAPI_OK; } static int _powercap_write( hwd_context_t * ctx, hwd_control_state_t * ctl, long long *values ) { /* write values */ ( void ) ctx; _powercap_control_state_t *control = ( _powercap_control_state_t * ) ctl; int i; for(i=0;iwhich_counter[i]].type == PKG_POWER_LIMIT_A) || (powercap_ntv_events[control->which_counter[i]].type == PKG_POWER_LIMIT_B) ) { write_powercap_value(control->which_counter[i], values[i]); } } return PAPI_OK; } /* * Clean up what was setup in powercap_init_component(). */ static int _powercap_shutdown_component( void ) { int i; /* Read counters into expected slot */ for(i=0;iwhich_counter[i]=index; native[i].ni_position = i; } return PAPI_OK; } static int _powercap_set_domain( hwd_control_state_t *ctl, int domain ) { ( void ) ctl; if ( PAPI_DOM_ALL != domain ) return PAPI_EINVAL; return PAPI_OK; } static int _powercap_reset( hwd_context_t *ctx, hwd_control_state_t *ctl ) { ( void ) ctx; ( void ) ctl; return PAPI_OK; } /* * Native Event functions */ static int _powercap_ntv_enum_events( unsigned int *EventCode, int modifier ) { int index; switch ( modifier ) { case PAPI_ENUM_FIRST: *EventCode = 0; return PAPI_OK; case PAPI_ENUM_EVENTS:index = *EventCode; if (index < num_events - 1) { *EventCode = *EventCode + 1; return PAPI_OK; } else { return PAPI_ENOEVNT; } default:return PAPI_EINVAL; } } /* * */ static int _powercap_ntv_code_to_name( unsigned int EventCode, char *name, int len ) { int index = EventCode & PAPI_NATIVE_AND_MASK; if ( index >= 0 && index < num_events ) { _local_strlcpy( name, powercap_ntv_events[index].name, len ); return PAPI_OK; } return PAPI_ENOEVNT; } /* * */ static int _powercap_ntv_code_to_descr( unsigned int EventCode, char *name, int len ) { int index = EventCode; if ( index < 0 && index >= num_events ) return PAPI_ENOEVNT; _local_strlcpy( name, powercap_ntv_events[index].description, len ); return PAPI_OK; } static int _powercap_ntv_code_to_info( unsigned int EventCode, PAPI_event_info_t *info ) { int index = EventCode; if ( index < 0 || index >= num_events ) return PAPI_ENOEVNT; _local_strlcpy( info->symbol, powercap_ntv_events[index].name, sizeof( info->symbol )); _local_strlcpy( info->long_descr, powercap_ntv_events[index].description, sizeof( info->long_descr ) ); _local_strlcpy( info->units, powercap_ntv_events[index].units, sizeof( info->units ) ); info->data_type = powercap_ntv_events[index].return_type; return PAPI_OK; } papi_vector_t _powercap_vector = { .cmp_info = { /* (unspecified values are initialized to 0) */ .name = "powercap", .short_name = "powercap", .description = "Linux powercap energy measurements", .version = "5.3.0", .default_domain = PAPI_DOM_ALL, .default_granularity = PAPI_GRN_SYS, .available_granularities = PAPI_GRN_SYS, .hardware_intr_sig = PAPI_INT_SIGNAL, .available_domains = PAPI_DOM_ALL, }, /* sizes of framework-opaque component-private structures */ .size = { .context = sizeof ( _powercap_context_t ), .control_state = sizeof ( _powercap_control_state_t ), .reg_value = sizeof ( _powercap_register_t ), .reg_alloc = sizeof ( _powercap_reg_alloc_t ), }, /* function pointers in this component */ .init_thread = _powercap_init_thread, .init_component = _powercap_init_component, .init_control_state = _powercap_init_control_state, .update_control_state = _powercap_update_control_state, .start = _powercap_start, .stop = _powercap_stop, .read = _powercap_read, .write = _powercap_write, .shutdown_thread = _powercap_shutdown_thread, .shutdown_component = _powercap_shutdown_component, .ctl = _powercap_ctl, .set_domain = _powercap_set_domain, .reset = _powercap_reset, .ntv_enum_events = _powercap_ntv_enum_events, .ntv_code_to_name = _powercap_ntv_code_to_name, .ntv_code_to_descr = _powercap_ntv_code_to_descr, .ntv_code_to_info = _powercap_ntv_code_to_info, };