789 lines
37 KiB
C
789 lines
37 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-only
|
||
|
/* Copyright (c) 2022 Meta Platforms, Inc.
|
||
|
*
|
||
|
* Authors: Arthur Shau <arthurshau@fb.com>,
|
||
|
* Wei Zhang <wzhang@fb.com>,
|
||
|
* Venkat Ramesh <venkatraghavan@fb.com>
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <errno.h>
|
||
|
#include <limits.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "nvme.h"
|
||
|
#include "libnvme.h"
|
||
|
#include "plugin.h"
|
||
|
#include "linux/types.h"
|
||
|
#include "nvme-print.h"
|
||
|
|
||
|
#define CREATE_CMD
|
||
|
#include "ocp-nvme.h"
|
||
|
|
||
|
/* C0 SCAO Log Page */
|
||
|
#define C0_SMART_CLOUD_ATTR_LEN 0x200
|
||
|
#define C0_SMART_CLOUD_ATTR_OPCODE 0xC0
|
||
|
#define C0_GUID_LENGTH 16
|
||
|
#define C0_ACTIVE_BUCKET_TIMER_INCREMENT 5
|
||
|
#define C0_ACTIVE_THRESHOLD_INCREMENT 5
|
||
|
#define C0_MINIMUM_WINDOW_INCREMENT 100
|
||
|
|
||
|
static __u8 scao_guid[C0_GUID_LENGTH] = { 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF,
|
||
|
0xF2, 0xA4, 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF };
|
||
|
|
||
|
/* C3 Latency Monitor Log Page */
|
||
|
#define C3_LATENCY_MON_LOG_BUF_LEN 0x200
|
||
|
#define C3_LATENCY_MON_OPCODE 0xC3
|
||
|
#define C3_LATENCY_MON_VERSION 0x0001
|
||
|
#define C3_GUID_LENGTH 16
|
||
|
static __u8 lat_mon_guid[C3_GUID_LENGTH] = { 0x92, 0x7a, 0xc0, 0x8c, 0xd0, 0x84,
|
||
|
0x6c, 0x9c, 0x70, 0x43, 0xe6, 0xd4, 0x58, 0x5e, 0xd4, 0x85 };
|
||
|
|
||
|
#define READ 0
|
||
|
#define WRITE 1
|
||
|
#define TRIM 2
|
||
|
#define RESERVED 3
|
||
|
|
||
|
typedef enum {
|
||
|
SCAO_PMUW = 0, /* Physical media units written */
|
||
|
SCAO_PMUR = 16, /* Physical media units read */
|
||
|
SCAO_BUNBR = 32, /* Bad user nand blocks raw */
|
||
|
SCAO_BUNBN = 38, /* Bad user nand blocks normalized */
|
||
|
SCAO_BSNBR = 40, /* Bad system nand blocks raw */
|
||
|
SCAO_BSNBN = 46, /* Bad system nand blocks normalized */
|
||
|
SCAO_XRC = 48, /* XOR recovery count */
|
||
|
SCAO_UREC = 56, /* Uncorrectable read error count */
|
||
|
SCAO_SEEC = 64, /* Soft ecc error count */
|
||
|
SCAO_EECE = 72, /* End to end corrected errors */
|
||
|
SCAO_EEDC = 76, /* End to end detected errors */
|
||
|
SCAO_SDPU = 80, /* System data percent used */
|
||
|
SCAO_RFSC = 81, /* Refresh counts */
|
||
|
SCAO_MXUDEC = 88, /* Max User data erase counts */
|
||
|
SCAO_MNUDEC = 92, /* Min User data erase counts */
|
||
|
SCAO_NTTE = 96, /* Number of Thermal throttling events */
|
||
|
SCAO_CTS = 97, /* Current throttling status */
|
||
|
SCAO_EVF = 98, /* Errata Version Field */
|
||
|
SCAO_PVF = 99, /* Point Version Field */
|
||
|
SCAO_MIVF = 101, /* Minor Version Field */
|
||
|
SCAO_MAVF = 103, /* Major Version Field */
|
||
|
SCAO_PCEC = 104, /* PCIe correctable error count */
|
||
|
SCAO_ICS = 112, /* Incomplete shutdowns */
|
||
|
SCAO_PFB = 120, /* Percent free blocks */
|
||
|
SCAO_CPH = 128, /* Capacitor health */
|
||
|
SCAO_NEV = 130, /* NVMe Errata Version */
|
||
|
SCAO_UIO = 136, /* Unaligned I/O */
|
||
|
SCAO_SVN = 144, /* Security Version Number */
|
||
|
SCAO_NUSE = 152, /* NUSE - Namespace utilization */
|
||
|
SCAO_PSC = 160, /* PLP start count */
|
||
|
SCAO_EEST = 176, /* Endurance estimate */
|
||
|
SCAO_PLRC = 192, /* PCIe Link Retraining Count */
|
||
|
SCAO_LPV = 494, /* Log page version */
|
||
|
SCAO_LPG = 496, /* Log page GUID */
|
||
|
} SMART_CLOUD_ATTRIBUTE_OFFSETS;
|
||
|
|
||
|
struct __attribute__((__packed__)) ssd_latency_monitor_log {
|
||
|
__u8 feature_status; /* 0x00 */
|
||
|
__u8 rsvd1; /* 0x01 */
|
||
|
__le16 active_bucket_timer; /* 0x02 */
|
||
|
__le16 active_bucket_timer_threshold; /* 0x04 */
|
||
|
__u8 active_threshold_a; /* 0x06 */
|
||
|
__u8 active_threshold_b; /* 0x07 */
|
||
|
__u8 active_threshold_c; /* 0x08 */
|
||
|
__u8 active_threshold_d; /* 0x09 */
|
||
|
__le16 active_latency_config; /* 0x0A */
|
||
|
__u8 active_latency_min_window; /* 0x0C */
|
||
|
__u8 rsvd2[0x13]; /* 0x0D */
|
||
|
|
||
|
__le32 active_bucket_counter[4][4] ; /* 0x20 - 0x5F */
|
||
|
__le64 active_latency_timestamp[4][3]; /* 0x60 - 0xBF */
|
||
|
__le16 active_measured_latency[4][3]; /* 0xC0 - 0xD7 */
|
||
|
__le16 active_latency_stamp_units; /* 0xD8 */
|
||
|
__u8 rsvd3[0x16]; /* 0xDA */
|
||
|
|
||
|
__le32 static_bucket_counter[4][4] ; /* 0xF0 - 0x12F */
|
||
|
__le64 static_latency_timestamp[4][3]; /* 0x130 - 0x18F */
|
||
|
__le16 static_measured_latency[4][3]; /* 0x190 - 0x1A7 */
|
||
|
__le16 static_latency_stamp_units; /* 0x1A8 */
|
||
|
__u8 rsvd4[0x16]; /* 0x1AA */
|
||
|
|
||
|
__le16 debug_log_trigger_enable; /* 0x1C0 */
|
||
|
__le16 debug_log_measured_latency; /* 0x1C2 */
|
||
|
__le64 debug_log_latency_stamp; /* 0x1C4 */
|
||
|
__le16 debug_log_ptr; /* 0x1CC */
|
||
|
__le16 debug_log_counter_trigger; /* 0x1CE */
|
||
|
__u8 debug_log_stamp_units; /* 0x1D0 */
|
||
|
__u8 rsvd5[0x1D]; /* 0x1D1 */
|
||
|
|
||
|
__le16 log_page_version; /* 0x1EE */
|
||
|
__u8 log_page_guid[0x10]; /* 0x1F0 */
|
||
|
};
|
||
|
|
||
|
static long double int128_to_double(__u8 *data)
|
||
|
{
|
||
|
int i;
|
||
|
long double result = 0;
|
||
|
|
||
|
for (i = 0; i < 16; i++) {
|
||
|
result *= 256;
|
||
|
result += data[15 - i];
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int convert_ts(time_t time, char *ts_buf)
|
||
|
{
|
||
|
struct tm gmTimeInfo;
|
||
|
time_t time_Human, time_ms;
|
||
|
char buf[80];
|
||
|
|
||
|
time_Human = time/1000;
|
||
|
time_ms = time % 1000;
|
||
|
|
||
|
gmtime_r((const time_t *)&time_Human, &gmTimeInfo);
|
||
|
|
||
|
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &gmTimeInfo);
|
||
|
sprintf(ts_buf, "%s.%03ld GMT", buf, time_ms);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void ocp_print_C0_log_normal(void *data)
|
||
|
{
|
||
|
__u8 *log_data = (__u8*)data;
|
||
|
uint16_t smart_log_ver = 0;
|
||
|
|
||
|
printf("SMART Cloud Attributes :- \n");
|
||
|
|
||
|
printf(" Physical media units written - %"PRIu64" %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF),
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
|
||
|
printf(" Physical media units read - %"PRIu64" %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF),
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
|
||
|
printf(" Bad user nand blocks - Raw %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF));
|
||
|
printf(" Bad user nand blocks - Normalized %d\n",
|
||
|
(uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN]));
|
||
|
printf(" Bad system nand blocks - Raw %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF));
|
||
|
printf(" Bad system nand blocks - Normalized %d\n",
|
||
|
(uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN]));
|
||
|
printf(" XOR recovery count %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC]));
|
||
|
printf(" Uncorrectable read error count %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC]));
|
||
|
printf(" Soft ecc error count %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC]));
|
||
|
printf(" End to end corrected errors %"PRIu32"\n",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE]));
|
||
|
printf(" End to end detected errors %"PRIu32"\n",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC]));
|
||
|
printf(" System data percent used %d\n",
|
||
|
(__u8)log_data[SCAO_SDPU]);
|
||
|
printf(" Refresh counts %"PRIu64"\n",
|
||
|
(uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF));
|
||
|
printf(" Max User data erase counts %"PRIu32"\n",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC]));
|
||
|
printf(" Min User data erase counts %"PRIu32"\n",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC]));
|
||
|
printf(" Number of Thermal throttling events %d\n",
|
||
|
(__u8)log_data[SCAO_NTTE]);
|
||
|
printf(" Current throttling status 0x%x\n",
|
||
|
(__u8)log_data[SCAO_CTS]);
|
||
|
printf(" PCIe correctable error count %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC]));
|
||
|
printf(" Incomplete shutdowns %"PRIu32"\n",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS]));
|
||
|
printf(" Percent free blocks %d\n",
|
||
|
(__u8)log_data[SCAO_PFB]);
|
||
|
printf(" Capacitor health %"PRIu16"\n",
|
||
|
(uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH]));
|
||
|
printf(" Unaligned I/O %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO]));
|
||
|
printf(" Security Version Number %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN]));
|
||
|
printf(" NUSE - Namespace utilization %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE]));
|
||
|
printf(" PLP start count %.0Lf\n",
|
||
|
int128_to_double(&log_data[SCAO_PSC]));
|
||
|
printf(" Endurance estimate %.0Lf\n",
|
||
|
int128_to_double(&log_data[SCAO_EEST]));
|
||
|
smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]);
|
||
|
printf(" Log page version %"PRIu16"\n",smart_log_ver);
|
||
|
printf(" Log page GUID 0x");
|
||
|
printf("%"PRIx64"%"PRIx64"\n",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]),
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG]));
|
||
|
if(smart_log_ver > 2) {
|
||
|
printf(" Errata Version Field %d\n",
|
||
|
(__u8)log_data[SCAO_EVF]);
|
||
|
printf(" Point Version Field %"PRIu16"\n",
|
||
|
(uint16_t)log_data[SCAO_PVF]);
|
||
|
printf(" Minor Version Field %"PRIu16"\n",
|
||
|
(uint16_t)log_data[SCAO_MIVF]);
|
||
|
printf(" Major Version Field %d\n",
|
||
|
(__u8)log_data[SCAO_MAVF]);
|
||
|
printf(" NVMe Errata Version %d\n",
|
||
|
(__u8)log_data[SCAO_NEV]);
|
||
|
printf(" PCIe Link Retraining Count %"PRIu64"\n",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC]));
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
static void ocp_print_C0_log_json(void *data)
|
||
|
{
|
||
|
__u8 *log_data = (__u8*)data;
|
||
|
struct json_object *root;
|
||
|
struct json_object *pmuw;
|
||
|
struct json_object *pmur;
|
||
|
uint16_t smart_log_ver = 0;
|
||
|
|
||
|
root = json_create_object();
|
||
|
pmuw = json_create_object();
|
||
|
pmur = json_create_object();
|
||
|
|
||
|
json_object_add_value_uint64(pmuw, "hi",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF));
|
||
|
json_object_add_value_uint64(pmuw, "lo",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
|
||
|
json_object_add_value_object(root, "Physical media units written", pmuw);
|
||
|
json_object_add_value_uint64(pmur, "hi",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF));
|
||
|
json_object_add_value_uint64(pmur, "lo",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
|
||
|
json_object_add_value_object(root, "Physical media units read", pmur);
|
||
|
json_object_add_value_uint64(root, "Bad user nand blocks - Raw",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF));
|
||
|
json_object_add_value_uint(root, "Bad user nand blocks - Normalized",
|
||
|
(uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN]));
|
||
|
json_object_add_value_uint64(root, "Bad system nand blocks - Raw",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF));
|
||
|
json_object_add_value_uint(root, "Bad system nand blocks - Normalized",
|
||
|
(uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN]));
|
||
|
json_object_add_value_uint64(root, "XOR recovery count",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC]));
|
||
|
json_object_add_value_uint64(root, "Uncorrectable read error count",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC]));
|
||
|
json_object_add_value_uint64(root, "Soft ecc error count",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC]));
|
||
|
json_object_add_value_uint(root, "End to end corrected errors",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE]));
|
||
|
json_object_add_value_uint(root, "End to end detected errors",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC]));
|
||
|
json_object_add_value_uint(root, "System data percent used",
|
||
|
(__u8)log_data[SCAO_SDPU]);
|
||
|
json_object_add_value_uint64(root, "Refresh counts",
|
||
|
(uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF));
|
||
|
json_object_add_value_uint(root, "Max User data erase counts",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC]));
|
||
|
json_object_add_value_uint(root, "Min User data erase counts",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC]));
|
||
|
json_object_add_value_uint(root, "Number of Thermal throttling events",
|
||
|
(__u8)log_data[SCAO_NTTE]);
|
||
|
json_object_add_value_uint(root, "Current throttling status",
|
||
|
(__u8)log_data[SCAO_CTS]);
|
||
|
json_object_add_value_uint64(root, "PCIe correctable error count",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC]));
|
||
|
json_object_add_value_uint(root, "Incomplete shutdowns",
|
||
|
(uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS]));
|
||
|
json_object_add_value_uint(root, "Percent free blocks",
|
||
|
(__u8)log_data[SCAO_PFB]);
|
||
|
json_object_add_value_uint(root, "Capacitor health",
|
||
|
(uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH]));
|
||
|
json_object_add_value_uint64(root, "Unaligned I/O",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO]));
|
||
|
json_object_add_value_uint64(root, "Security Version Number",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN]));
|
||
|
json_object_add_value_uint64(root, "NUSE - Namespace utilization",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE]));
|
||
|
json_object_add_value_uint(root, "PLP start count",
|
||
|
int128_to_double(&log_data[SCAO_PSC]));
|
||
|
json_object_add_value_uint64(root, "Endurance estimate",
|
||
|
int128_to_double(&log_data[SCAO_EEST]));
|
||
|
smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]);
|
||
|
json_object_add_value_uint(root, "Log page version", smart_log_ver);
|
||
|
char guid[40];
|
||
|
memset((void*)guid, 0, 40);
|
||
|
sprintf((char*)guid, "0x%"PRIx64"%"PRIx64"",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]),
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG]));
|
||
|
json_object_add_value_string(root, "Log page GUID", guid);
|
||
|
if(smart_log_ver > 2){
|
||
|
json_object_add_value_uint(root, "Errata Version Field",
|
||
|
(__u8)log_data[SCAO_EVF]);
|
||
|
json_object_add_value_uint(root, "Point Version Field",
|
||
|
(uint16_t)log_data[SCAO_PVF]);
|
||
|
json_object_add_value_uint(root, "Minor Version Field",
|
||
|
(uint16_t)log_data[SCAO_MIVF]);
|
||
|
json_object_add_value_uint(root, "Major Version Field",
|
||
|
(__u8)log_data[SCAO_MAVF]);
|
||
|
json_object_add_value_uint(root, "NVMe Errata Version",
|
||
|
(__u8)log_data[SCAO_NEV]);
|
||
|
json_object_add_value_uint(root, "PCIe Link Retraining Count",
|
||
|
(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC]));
|
||
|
}
|
||
|
json_print_object(root, NULL);
|
||
|
printf("\n");
|
||
|
json_free_object(root);
|
||
|
}
|
||
|
|
||
|
static int get_c0_log_page(int fd, char *format)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int fmt = -1;
|
||
|
__u8 *data;
|
||
|
int i;
|
||
|
|
||
|
fmt = validate_output_format(format);
|
||
|
if (fmt < 0) {
|
||
|
fprintf(stderr, "ERROR : OCP : invalid output format\n");
|
||
|
return fmt;
|
||
|
}
|
||
|
|
||
|
if ((data = (__u8 *) malloc(sizeof(__u8) * C0_SMART_CLOUD_ATTR_LEN)) == NULL) {
|
||
|
fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
memset(data, 0, sizeof (__u8) * C0_SMART_CLOUD_ATTR_LEN);
|
||
|
|
||
|
ret = nvme_get_log_simple(fd, C0_SMART_CLOUD_ATTR_OPCODE,
|
||
|
C0_SMART_CLOUD_ATTR_LEN, data);
|
||
|
|
||
|
if (strcmp(format, "json"))
|
||
|
fprintf(stderr, "NVMe Status:%s(%x)\n",
|
||
|
nvme_status_to_string(ret, false), ret);
|
||
|
|
||
|
if (ret == 0) {
|
||
|
|
||
|
/* check log page guid */
|
||
|
/* Verify GUID matches */
|
||
|
for (i=0; i<16; i++) {
|
||
|
if (scao_guid[i] != data[SCAO_LPG + i]) {
|
||
|
fprintf(stderr, "ERROR : OCP : Unknown GUID in C0 Log Page data\n");
|
||
|
int j;
|
||
|
fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
|
||
|
for (j = 0; j<16; j++) {
|
||
|
fprintf(stderr, "%x", scao_guid[j]);
|
||
|
}
|
||
|
fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
|
||
|
for (j = 0; j<16; j++) {
|
||
|
fprintf(stderr, "%x", data[SCAO_LPG + j]);
|
||
|
}
|
||
|
fprintf(stderr, "\n");
|
||
|
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* print the data */
|
||
|
if (!data) {
|
||
|
fprintf(stderr, "ERROR : OCP : Invalid buffer to read 0xC0 log\n");
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
switch (fmt) {
|
||
|
case NORMAL:
|
||
|
ocp_print_C0_log_normal(data);
|
||
|
break;
|
||
|
case JSON:
|
||
|
ocp_print_C0_log_json(data);
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
fprintf(stderr, "ERROR : OCP : Unable to read C0 data from buffer\n");
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
free(data);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int ocp_smart_add_log(int argc, char **argv, struct command *cmd,
|
||
|
struct plugin *plugin)
|
||
|
{
|
||
|
const char *desc = "Retrieve latency monitor log data.";
|
||
|
int fd;
|
||
|
int ret = 0;
|
||
|
|
||
|
struct config {
|
||
|
char *output_format;
|
||
|
};
|
||
|
|
||
|
struct config cfg = {
|
||
|
.output_format = "normal",
|
||
|
};
|
||
|
|
||
|
OPT_ARGS(opts) = {
|
||
|
OPT_FMT("output-format", 'o', &cfg.output_format, "output Format: normal|json"),
|
||
|
OPT_END()
|
||
|
};
|
||
|
|
||
|
fd = parse_and_open(argc, argv, desc, opts);
|
||
|
if (fd < 0)
|
||
|
return fd;
|
||
|
|
||
|
ret = get_c0_log_page(fd, cfg.output_format);
|
||
|
if (ret)
|
||
|
fprintf(stderr, "ERROR : OCP : Failure reading the C0 Log Page, ret = %d\n",
|
||
|
ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int ocp_print_C3_log_normal(int fd, struct ssd_latency_monitor_log *log_data)
|
||
|
{
|
||
|
printf("-Latency Monitor/C3 Log Page Data- \n");
|
||
|
printf(" Controller : %s\n", devicename);
|
||
|
int i, j;
|
||
|
int pos = 0;
|
||
|
char ts_buf[128];
|
||
|
|
||
|
printf(" Feature Status 0x%x \n",
|
||
|
log_data->feature_status);
|
||
|
printf(" Active Bucket Timer %d min \n",
|
||
|
C0_ACTIVE_BUCKET_TIMER_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_bucket_timer));
|
||
|
printf(" Active Bucket Timer Threshold %d min \n",
|
||
|
C0_ACTIVE_BUCKET_TIMER_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_bucket_timer_threshold));
|
||
|
printf(" Active Threshold A %d ms \n",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_a+1));
|
||
|
printf(" Active Threshold B %d ms \n",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_b+1));
|
||
|
printf(" Active Threshold C %d ms \n",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_c+1));
|
||
|
printf(" Active Threshold D %d ms \n",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_d+1));
|
||
|
printf(" Active Latency Minimum Window %d ms \n",
|
||
|
C0_MINIMUM_WINDOW_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_latency_min_window));
|
||
|
printf(" Active Latency Stamp Units %d \n",
|
||
|
le16_to_cpu(log_data->active_latency_stamp_units));
|
||
|
printf(" Static Latency Stamp Units %d \n",
|
||
|
le16_to_cpu(log_data->static_latency_stamp_units));
|
||
|
printf(" Debug Log Trigger Enable %d \n",
|
||
|
le16_to_cpu(log_data->debug_log_trigger_enable));
|
||
|
|
||
|
printf(" Read Write Deallocate/Trim \n");
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
printf(" Active Latency Mode: Bucket %d %27d %27d %27d \n",
|
||
|
i,
|
||
|
log_data->active_latency_config & (1 << pos),
|
||
|
log_data->active_latency_config & (1 << pos),
|
||
|
log_data->active_latency_config & (1 << pos));
|
||
|
}
|
||
|
printf("\n");
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
printf(" Active Bucket Counter: Bucket %d %27d %27d %27d \n",
|
||
|
i,
|
||
|
le32_to_cpu(log_data->active_bucket_counter[i][READ]),
|
||
|
le32_to_cpu(log_data->active_bucket_counter[i][WRITE]),
|
||
|
le32_to_cpu(log_data->active_bucket_counter[i][TRIM]));
|
||
|
}
|
||
|
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
|
||
|
i,
|
||
|
le16_to_cpu(log_data->active_measured_latency[i][READ]),
|
||
|
le16_to_cpu(log_data->active_measured_latency[i][WRITE]),
|
||
|
le16_to_cpu(log_data->active_measured_latency[i][TRIM]));
|
||
|
}
|
||
|
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
printf(" Active Latency Time Stamp: Bucket %d ", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1)
|
||
|
printf(" N/A ");
|
||
|
else {
|
||
|
convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf);
|
||
|
printf("%s ", ts_buf);
|
||
|
}
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
printf(" Static Bucket Counter: Bucket %d %27d %27d %27d \n",
|
||
|
i,
|
||
|
le32_to_cpu(log_data->static_bucket_counter[i][READ]),
|
||
|
le32_to_cpu(log_data->static_bucket_counter[i][WRITE]),
|
||
|
le32_to_cpu(log_data->static_bucket_counter[i][TRIM]));
|
||
|
}
|
||
|
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
|
||
|
i,
|
||
|
le16_to_cpu(log_data->static_measured_latency[i][READ]),
|
||
|
le16_to_cpu(log_data->static_measured_latency[i][WRITE]),
|
||
|
le16_to_cpu(log_data->static_measured_latency[i][TRIM]));
|
||
|
}
|
||
|
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
printf(" Static Latency Time Stamp: Bucket %d ", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1)
|
||
|
printf(" N/A ");
|
||
|
else {
|
||
|
convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf);
|
||
|
printf("%s ", ts_buf);
|
||
|
}
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data)
|
||
|
{
|
||
|
int i, j;
|
||
|
int pos = 0;
|
||
|
char buf[128];
|
||
|
char ts_buf[128];
|
||
|
char *operation[3] = {"Read", "Write", "Trim"};
|
||
|
struct json_object *root;
|
||
|
root = json_create_object();
|
||
|
|
||
|
json_object_add_value_uint(root, "Feature Status",
|
||
|
log_data->feature_status);
|
||
|
json_object_add_value_uint(root, "Active Bucket Timer",
|
||
|
C0_ACTIVE_BUCKET_TIMER_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_bucket_timer));
|
||
|
json_object_add_value_uint(root, "Active Bucket Timer Threshold",
|
||
|
C0_ACTIVE_BUCKET_TIMER_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_bucket_timer_threshold));
|
||
|
json_object_add_value_uint(root, "Active Threshold A",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_a+1));
|
||
|
json_object_add_value_uint(root, "Active Threshold B",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_b+1));
|
||
|
json_object_add_value_uint(root, "Active Threshold C",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_c+1));
|
||
|
json_object_add_value_uint(root, "Active Threshold D",
|
||
|
C0_ACTIVE_THRESHOLD_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_threshold_d+1));
|
||
|
json_object_add_value_uint(root, "Active Lantency Minimum Window",
|
||
|
C0_MINIMUM_WINDOW_INCREMENT *
|
||
|
le16_to_cpu(log_data->active_latency_min_window));
|
||
|
json_object_add_value_uint(root, "Active Latency Stamp Units",
|
||
|
le16_to_cpu(log_data->active_latency_stamp_units));
|
||
|
json_object_add_value_uint(root, "Static Latency Stamp Units",
|
||
|
le16_to_cpu(log_data->static_latency_stamp_units));
|
||
|
json_object_add_value_uint(root, "Debug Log Trigger Enable",
|
||
|
le16_to_cpu(log_data->debug_log_trigger_enable));
|
||
|
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
struct json_object *bucket;
|
||
|
bucket = json_create_object();
|
||
|
sprintf(buf, "Active Latency Mode: Bucket %d", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
json_object_add_value_uint(bucket, operation[j],
|
||
|
log_data->active_latency_config & (1 << pos));
|
||
|
}
|
||
|
json_object_add_value_object(root, buf, bucket);
|
||
|
}
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
struct json_object *bucket;
|
||
|
bucket = json_create_object();
|
||
|
sprintf(buf, "Active Bucket Counter: Bucket %d", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
json_object_add_value_uint(bucket, operation[j],
|
||
|
le32_to_cpu(log_data->active_bucket_counter[i][j]));
|
||
|
}
|
||
|
json_object_add_value_object(root, buf, bucket);
|
||
|
}
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
struct json_object *bucket;
|
||
|
bucket = json_create_object();
|
||
|
sprintf(buf, "Active Measured Latency: Bucket %d", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
json_object_add_value_uint(bucket, operation[j],
|
||
|
le16_to_cpu(log_data->active_measured_latency[i][j]));
|
||
|
}
|
||
|
json_object_add_value_object(root, buf, bucket);
|
||
|
}
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
struct json_object *bucket;
|
||
|
bucket = json_create_object();
|
||
|
sprintf(buf, "Active Latency Time Stamp: Bucket %d", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1)
|
||
|
json_object_add_value_string(bucket, operation[j], "NA");
|
||
|
else {
|
||
|
convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf);
|
||
|
json_object_add_value_string(bucket, operation[j], ts_buf);
|
||
|
}
|
||
|
}
|
||
|
json_object_add_value_object(root, buf, bucket);
|
||
|
}
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
struct json_object *bucket;
|
||
|
bucket = json_create_object();
|
||
|
sprintf(buf, "Static Bucket Counter: Bucket %d", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
json_object_add_value_uint(bucket, operation[j],
|
||
|
le32_to_cpu(log_data->static_bucket_counter[i][j]));
|
||
|
}
|
||
|
json_object_add_value_object(root, buf, bucket);
|
||
|
}
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
struct json_object *bucket;
|
||
|
bucket = json_create_object();
|
||
|
sprintf(buf, "Static Measured Latency: Bucket %d", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
json_object_add_value_uint(bucket, operation[j],
|
||
|
le16_to_cpu(log_data->static_measured_latency[i][j]));
|
||
|
}
|
||
|
json_object_add_value_object(root, buf, bucket);
|
||
|
}
|
||
|
for (i = 0; i <= 3; i++) {
|
||
|
struct json_object *bucket;
|
||
|
bucket = json_create_object();
|
||
|
sprintf(buf, "Static Latency Time Stamp: Bucket %d", i);
|
||
|
for (j = 0; j <= 2; j++) {
|
||
|
if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1)
|
||
|
json_object_add_value_string(bucket, operation[j], "NA");
|
||
|
else {
|
||
|
convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf);
|
||
|
json_object_add_value_string(bucket, operation[j], ts_buf);
|
||
|
}
|
||
|
}
|
||
|
json_object_add_value_object(root, buf, bucket);
|
||
|
}
|
||
|
|
||
|
json_print_object(root, NULL);
|
||
|
printf("\n");
|
||
|
|
||
|
json_free_object(root);
|
||
|
}
|
||
|
|
||
|
static int get_c3_log_page(int fd, char *format)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int fmt = -1;
|
||
|
__u8 *data;
|
||
|
int i;
|
||
|
struct ssd_latency_monitor_log *log_data;
|
||
|
|
||
|
fmt = validate_output_format(format);
|
||
|
if (fmt < 0) {
|
||
|
fprintf(stderr, "ERROR : OCP : invalid output format\n");
|
||
|
return fmt;
|
||
|
}
|
||
|
|
||
|
if ((data = (__u8 *) malloc(sizeof(__u8) * C3_LATENCY_MON_LOG_BUF_LEN)) == NULL) {
|
||
|
fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
memset(data, 0, sizeof (__u8) * C3_LATENCY_MON_LOG_BUF_LEN);
|
||
|
|
||
|
ret = nvme_get_log_simple(fd, C3_LATENCY_MON_OPCODE,
|
||
|
C3_LATENCY_MON_LOG_BUF_LEN, data);
|
||
|
|
||
|
if (strcmp(format, "json"))
|
||
|
fprintf(stderr,
|
||
|
"NVMe Status:%s(%x)\n",
|
||
|
nvme_status_to_string(ret, false),
|
||
|
ret);
|
||
|
|
||
|
if (ret == 0) {
|
||
|
log_data = (struct ssd_latency_monitor_log*)data;
|
||
|
|
||
|
/* check log page version */
|
||
|
if (log_data->log_page_version != C3_LATENCY_MON_VERSION) {
|
||
|
fprintf(stderr,
|
||
|
"ERROR : OCP : invalid latency monitor version\n");
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
/* check log page guid */
|
||
|
/* Verify GUID matches */
|
||
|
for (i=0; i<16; i++) {
|
||
|
if (lat_mon_guid[i] != log_data->log_page_guid[i]) {
|
||
|
fprintf(stderr,"ERROR : OCP : Unknown GUID in C3 Log Page data\n");
|
||
|
int j;
|
||
|
fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
|
||
|
for (j = 0; j<16; j++) {
|
||
|
fprintf(stderr, "%x", lat_mon_guid[j]);
|
||
|
}
|
||
|
fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
|
||
|
for (j = 0; j<16; j++) {
|
||
|
fprintf(stderr, "%x", log_data->log_page_guid[j]);
|
||
|
}
|
||
|
fprintf(stderr, "\n");
|
||
|
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* print the data */
|
||
|
if (!log_data) {
|
||
|
fprintf(stderr,
|
||
|
"ERROR : OCP : Invalid C3 log data buffer\n");
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
switch (fmt) {
|
||
|
case NORMAL:
|
||
|
ocp_print_C3_log_normal(fd, log_data);
|
||
|
break;
|
||
|
case JSON:
|
||
|
ocp_print_C3_log_json(log_data);
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
fprintf(stderr,
|
||
|
"ERROR : OCP : Unable to read C3 data from buffer\n");
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
free(data);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int ocp_latency_monitor_log(int argc, char **argv, struct command *command,
|
||
|
struct plugin *plugin)
|
||
|
{
|
||
|
const char *desc = "Retrieve latency monitor log data.";
|
||
|
int fd;
|
||
|
int ret = 0;
|
||
|
|
||
|
struct config {
|
||
|
char *output_format;
|
||
|
};
|
||
|
|
||
|
struct config cfg = {
|
||
|
.output_format = "normal",
|
||
|
};
|
||
|
|
||
|
OPT_ARGS(opts) = {
|
||
|
OPT_FMT("output-format", 'o', &cfg.output_format,
|
||
|
"output Format: normal|json"),
|
||
|
OPT_END()
|
||
|
};
|
||
|
|
||
|
fd = parse_and_open(argc, argv, desc, opts);
|
||
|
if (fd < 0)
|
||
|
return fd;
|
||
|
|
||
|
ret = get_c3_log_page(fd, cfg.output_format);
|
||
|
if (ret)
|
||
|
fprintf(stderr,
|
||
|
"ERROR : OCP : Failure reading the C3 Log Page, ret = %d\n",
|
||
|
ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|