2025-02-16 12:18:36 +01:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( c ) 2022 Solidigm .
*
* Author : leonardo . da . cunha @ solidigm . com
*/
# include <fcntl.h>
# include <errno.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include "common.h"
# include "nvme.h"
# include "libnvme.h"
# include "plugin.h"
# include "nvme-print.h"
# include "solidigm-telemetry.h"
# include "solidigm-telemetry/telemetry-log.h"
# include "solidigm-telemetry/cod.h"
# include "solidigm-telemetry/header.h"
# include "solidigm-telemetry/config.h"
# include "solidigm-telemetry/data-area.h"
2025-02-16 12:25:41 +01:00
# include "solidigm-util.h"
2025-02-16 12:18:36 +01:00
static int read_file2buffer ( char * file_name , char * * buffer , size_t * length )
{
FILE * fd = fopen ( file_name , " rb " ) ;
if ( ! fd )
return errno ;
fseek ( fd , 0 , SEEK_END ) ;
size_t length_bytes = ftell ( fd ) ;
fseek ( fd , 0 , SEEK_SET ) ;
* buffer = malloc ( length_bytes ) ;
if ( ! * buffer ) {
fclose ( fd ) ;
return errno ;
}
* length = fread ( * buffer , 1 , length_bytes , fd ) ;
fclose ( fd ) ;
return 0 ;
}
struct config {
__u32 host_gen ;
bool ctrl_init ;
int data_area ;
char * cfg_file ;
bool is_input_file ;
} ;
int solidigm_get_telemetry_log ( int argc , char * * argv , struct command * cmd , struct plugin * plugin )
{
const char * desc = " Parse Solidigm Telemetry log " ;
const char * hgen = " Controls when to generate new host initiated report. Default value '1' generates new host initiated report, value '0' causes retrieval of existing log. " ;
const char * cgen = " Gather report generated by the controller. " ;
const char * dgen = " Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4. " ;
const char * cfile = " JSON configuration file " ;
const char * sfile = " data source <device> is binary file containing log dump instead of block or character device " ;
struct nvme_dev * dev ;
struct telemetry_log tl = {
. root = json_create_object ( ) ,
. log = NULL ,
} ;
struct config cfg = {
. host_gen = 1 ,
. ctrl_init = false ,
2025-02-16 12:25:41 +01:00
. data_area = - 1 ,
2025-02-16 12:18:36 +01:00
. cfg_file = NULL ,
. is_input_file = false ,
} ;
OPT_ARGS ( opts ) = {
OPT_UINT ( " host-generate " , ' g ' , & cfg . host_gen , hgen ) ,
OPT_FLAG ( " controller-init " , ' c ' , & cfg . ctrl_init , cgen ) ,
OPT_UINT ( " data-area " , ' d ' , & cfg . data_area , dgen ) ,
OPT_FILE ( " config-file " , ' j ' , & cfg . cfg_file , cfile ) ,
OPT_FLAG ( " source-file " , ' s ' , & cfg . is_input_file , sfile ) ,
OPT_END ( )
} ;
int err = argconfig_parse ( argc , argv , desc , opts ) ;
if ( err )
goto ret ;
2025-02-16 12:25:41 +01:00
/* When not selected on the command line, get minimum data area required */
if ( cfg . data_area = = - 1 )
cfg . data_area = cfg . cfg_file ? 3 : 1 ;
2025-02-16 12:18:36 +01:00
if ( cfg . is_input_file ) {
if ( optind > = argc ) {
err = errno = EINVAL ;
perror ( argv [ 0 ] ) ;
goto ret ;
}
char * binary_file_name = argv [ optind ] ;
err = read_file2buffer ( binary_file_name , ( char * * ) & tl . log , & tl . log_size ) ;
} else {
err = parse_and_open ( & dev , argc , argv , desc , opts ) ;
}
if ( err )
goto ret ;
if ( cfg . host_gen > 1 ) {
SOLIDIGM_LOG_WARNING ( " Invalid host-generate value '%d' " , cfg . host_gen ) ;
err = EINVAL ;
goto close_fd ;
}
if ( cfg . cfg_file ) {
2025-02-16 12:24:13 +01:00
char * conf_str = NULL ;
2025-02-16 12:18:36 +01:00
size_t length = 0 ;
err = read_file2buffer ( cfg . cfg_file , & conf_str , & length ) ;
if ( err ) {
SOLIDIGM_LOG_WARNING ( " Failed to open JSON configuration file %s: %s! " ,
cfg . cfg_file , strerror ( err ) ) ;
goto close_fd ;
}
2025-02-16 12:24:13 +01:00
struct json_tokener * jstok = json_tokener_new ( ) ;
2025-02-16 12:18:36 +01:00
tl . configuration = json_tokener_parse_ex ( jstok , conf_str , length ) ;
2025-02-16 12:24:13 +01:00
free ( conf_str ) ;
2025-02-16 12:18:36 +01:00
if ( jstok - > err ! = json_tokener_success ) {
SOLIDIGM_LOG_WARNING ( " Parsing error on JSON configuration file %s: %s (at offset %d) " ,
cfg . cfg_file ,
json_tokener_error_desc ( jstok - > err ) ,
jstok - > char_offset ) ;
json_tokener_free ( jstok ) ;
err = EINVAL ;
goto close_fd ;
}
json_tokener_free ( jstok ) ;
}
if ( ! cfg . is_input_file ) {
2025-02-16 12:25:41 +01:00
size_t max_data_tx ;
2025-02-16 12:28:30 +01:00
size_t power2 ;
__u8 mdts = 0 ;
2025-02-16 12:25:41 +01:00
err = nvme_get_telemetry_max ( dev_fd ( dev ) , NULL , & max_data_tx ) ;
if ( err < 0 ) {
SOLIDIGM_LOG_WARNING ( " identify_ctrl: %s " ,
nvme_strerror ( errno ) ) ;
goto close_fd ;
} else if ( err > 0 ) {
nvme_show_status ( err ) ;
SOLIDIGM_LOG_WARNING ( " Failed to acquire identify ctrl %d! " , err ) ;
goto close_fd ;
}
2025-02-16 12:28:30 +01:00
power2 = max_data_tx / NVME_LOG_PAGE_PDU_SIZE ;
while ( power2 & & ! ( 1 & power2 ) ) {
power2 > > = 1 ;
mdts + + ;
}
2025-02-16 12:18:36 +01:00
2025-02-16 12:28:30 +01:00
err = sldgm_dynamic_telemetry ( dev_fd ( dev ) , cfg . host_gen , cfg . ctrl_init , true ,
mdts , cfg . data_area , & tl . log , & tl . log_size ) ;
2025-02-16 12:18:36 +01:00
if ( err < 0 ) {
SOLIDIGM_LOG_WARNING ( " get-telemetry-log: %s " ,
nvme_strerror ( errno ) ) ;
goto close_fd ;
} else if ( err > 0 ) {
nvme_show_status ( err ) ;
SOLIDIGM_LOG_WARNING ( " Failed to acquire telemetry log %d! " , err ) ;
goto close_fd ;
}
}
2025-02-16 12:24:13 +01:00
solidigm_telemetry_log_data_areas_parse ( & tl , cfg . data_area ) ;
2025-02-16 12:18:36 +01:00
json_print_object ( tl . root , NULL ) ;
json_free_object ( tl . root ) ;
printf ( " \n " ) ;
close_fd :
if ( ! cfg . is_input_file ) {
/* Redundant close() to make static code analysis happy */
close ( dev - > direct . fd ) ;
dev_close ( dev ) ;
}
ret :
json_free_object ( tl . configuration ) ;
free ( tl . log ) ;
return err ;
}