2025-02-16 12:17:56 +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:25 +01:00
# include "solidigm-util.h"
2025-02-16 12:17:56 +01:00
static int read_file2buffer ( char * file_name , char * * buffer , size_t * length )
{
FILE * fd = fopen ( file_name , " rb " ) ;
if ( ! fd )
2025-04-13 11:50:33 +02:00
return - errno ;
2025-02-16 12:17:56 +01:00
fseek ( fd , 0 , SEEK_END ) ;
size_t length_bytes = ftell ( fd ) ;
fseek ( fd , 0 , SEEK_SET ) ;
* buffer = malloc ( length_bytes ) ;
if ( ! * buffer ) {
fclose ( fd ) ;
2025-04-13 11:50:33 +02:00
return - errno ;
2025-02-16 12:17:56 +01:00
}
* 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 ;
2025-04-13 11:50:33 +02:00
char * binary_file ;
2025-02-16 12:17:56 +01:00
} ;
2025-04-13 11:50:33 +02:00
static void cleanup_json_object ( struct json_object * * jobj_ptr )
{
json_free_object ( * jobj_ptr ) ;
* jobj_ptr = NULL ;
}
2025-02-16 12:17:56 +01:00
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 " ;
2025-04-13 11:50:33 +02:00
const char * sfile = " binary file containing log dump " ;
bool has_binary_file = false ;
_cleanup_nvme_dev_ struct nvme_dev * dev = NULL ;
_cleanup_free_ struct nvme_telemetry_log * tlog = NULL ;
__attribute__ ( ( cleanup ( cleanup_json_object ) ) ) struct json_object * configuration = NULL ;
__attribute__ ( ( cleanup ( cleanup_json_object ) ) ) struct json_object * root =
json_create_object ( ) ;
2025-02-16 12:17:56 +01:00
struct telemetry_log tl = {
2025-04-13 11:50:33 +02:00
. root = root ,
2025-02-16 12:17:56 +01:00
} ;
struct config cfg = {
. host_gen = 1 ,
. ctrl_init = 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 ) ,
2025-04-13 11:50:33 +02:00
OPT_FILE ( " source-file " , ' s ' , & cfg . binary_file , sfile ) ,
OPT_INCR ( " verbose " , ' v ' , & nvme_cfg . verbose , verbose ) ,
2025-02-16 12:17:56 +01:00
OPT_END ( )
} ;
int err = argconfig_parse ( argc , argv , desc , opts ) ;
2025-04-13 11:50:33 +02:00
if ( err ) {
nvme_show_status ( err ) ;
return err ;
}
2025-02-16 12:17:56 +01:00
2025-02-16 12:25:25 +01:00
/* When not selected on the command line, get minimum data area required */
2025-04-13 11:50:33 +02:00
if ( ! argconfig_parse_seen ( opts , " data-area " ) )
cfg . data_area = argconfig_parse_seen ( opts , " config-file " ) ? 3 : 1 ;
has_binary_file = argconfig_parse_seen ( opts , " source-file " ) ;
if ( has_binary_file ) {
// If a binary file is provided, we don't want to open a device.
// GNU getopt() permutes the contents of argv as it scans,
// so that eventually all the nonoptions are at the end.
if ( argc > optind ) {
errno = EINVAL ;
err = - errno ;
nvme_show_status ( err ) ;
return err ;
2025-02-16 12:17:56 +01:00
}
2025-04-13 11:50:33 +02:00
err = read_file2buffer ( cfg . binary_file , ( char * * ) & tlog , & tl . log_size ) ;
2025-02-16 12:17:56 +01:00
} else {
err = parse_and_open ( & dev , argc , argv , desc , opts ) ;
}
2025-04-13 11:50:33 +02:00
if ( err ) {
nvme_show_status ( err ) ;
return err ;
}
2025-02-16 12:17:56 +01:00
if ( cfg . host_gen > 1 ) {
SOLIDIGM_LOG_WARNING ( " Invalid host-generate value '%d' " , cfg . host_gen ) ;
2025-04-13 11:50:33 +02:00
err = - EINVAL ;
nvme_show_status ( err ) ;
return err ;
2025-02-16 12:17:56 +01:00
}
2025-04-13 11:50:33 +02:00
if ( argconfig_parse_seen ( opts , " config-file " ) ) {
_cleanup_free_ char * conf_str = NULL ;
2025-02-16 12:17:56 +01:00
size_t length = 0 ;
err = read_file2buffer ( cfg . cfg_file , & conf_str , & length ) ;
if ( err ) {
2025-04-13 11:50:33 +02:00
nvme_show_status ( err ) ;
return err ;
2025-02-16 12:17:56 +01:00
}
2025-02-16 12:24:03 +01:00
struct json_tokener * jstok = json_tokener_new ( ) ;
2025-02-16 12:17:56 +01:00
2025-04-13 11:50:33 +02:00
configuration = json_tokener_parse_ex ( jstok , conf_str , length ) ;
2025-02-16 12:17:56 +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 ;
2025-04-13 11:50:33 +02:00
return err ;
2025-02-16 12:17:56 +01:00
}
json_tokener_free ( jstok ) ;
2025-04-13 11:50:33 +02:00
tl . configuration = configuration ;
2025-02-16 12:17:56 +01:00
}
2025-04-13 11:50:33 +02:00
if ( ! has_binary_file ) {
2025-02-16 12:25:25 +01:00
size_t max_data_tx ;
2025-02-16 12:28:14 +01:00
size_t power2 ;
__u8 mdts = 0 ;
2025-02-16 12:25:25 +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 ) ) ;
2025-04-13 11:50:33 +02:00
return err ;
2025-02-16 12:25:25 +01:00
} else if ( err > 0 ) {
nvme_show_status ( err ) ;
SOLIDIGM_LOG_WARNING ( " Failed to acquire identify ctrl %d! " , err ) ;
2025-04-13 11:50:33 +02:00
return err ;
2025-02-16 12:25:25 +01:00
}
2025-02-16 12:28:14 +01:00
power2 = max_data_tx / NVME_LOG_PAGE_PDU_SIZE ;
while ( power2 & & ! ( 1 & power2 ) ) {
power2 > > = 1 ;
mdts + + ;
}
2025-02-16 12:17:56 +01:00
2025-02-16 12:28:14 +01:00
err = sldgm_dynamic_telemetry ( dev_fd ( dev ) , cfg . host_gen , cfg . ctrl_init , true ,
2025-04-13 11:50:33 +02:00
mdts , cfg . data_area , & tlog , & tl . log_size ) ;
2025-02-16 12:17:56 +01:00
if ( err < 0 ) {
SOLIDIGM_LOG_WARNING ( " get-telemetry-log: %s " ,
nvme_strerror ( errno ) ) ;
2025-04-13 11:50:33 +02:00
return err ;
2025-02-16 12:17:56 +01:00
} else if ( err > 0 ) {
nvme_show_status ( err ) ;
SOLIDIGM_LOG_WARNING ( " Failed to acquire telemetry log %d! " , err ) ;
2025-04-13 11:50:33 +02:00
return err ;
2025-02-16 12:17:56 +01:00
}
}
2025-04-13 11:50:33 +02:00
tl . log = tlog ;
2025-02-16 12:24:03 +01:00
solidigm_telemetry_log_data_areas_parse ( & tl , cfg . data_area ) ;
2025-02-16 12:17:56 +01:00
json_print_object ( tl . root , NULL ) ;
printf ( " \n " ) ;
return err ;
}