xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_extsmart.c (revision f97471398e082e41a83d1e6879c77893cca0a4f4)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2026 Oxide Computer Company
14  */
15 
16 /*
17  * Many vendors have followed the same layout for their Extended SMART data
18  * which usually is a 1 byte id, 1 byte normalized value, and 6ish bytes of
19  * data. This file contains the field data for printing those and the
20  * corresponding logs.
21  */
22 
23 #include <sys/stddef.h>
24 #include <sys/sysmacros.h>
25 #include <sys/nvme/kioxia.h>
26 #include <sys/nvme/solidigm.h>
27 #include <sys/nvme/wdc.h>
28 
29 #include "nvmeadm.h"
30 
31 #define	EXTSMART_F(f)	\
32 	.nf_off = offsetof(solidigm_smart_ent_t, sse_##f), \
33 	.nf_len = sizeof (((solidigm_smart_ent_t *)NULL)->sse_##f)
34 
35 static const nvmeadm_field_t extsmart_percent_fields[] = { {
36 	EXTSMART_F(type),
37 	.nf_short = "id",
38 	.nf_desc = "Identifier",
39 	.nf_type = NVMEADM_FT_HEX
40 }, {
41 	EXTSMART_F(norm),
42 	.nf_short = "norm",
43 	.nf_desc = "Normalized",
44 	.nf_type = NVMEADM_FT_PERCENT
45 }, {
46 	EXTSMART_F(raw),
47 	.nf_short = "raw",
48 	.nf_desc = "Raw",
49 	.nf_type = NVMEADM_FT_HEX
50 } };
51 
52 /*
53  * All fields are just printed in hex.
54  */
55 static const nvmeadm_field_t extsmart_hex_fields[] = { {
56 	EXTSMART_F(type),
57 	.nf_short = "id",
58 	.nf_desc = "Identifier",
59 	.nf_type = NVMEADM_FT_HEX
60 }, {
61 	EXTSMART_F(norm),
62 	.nf_short = "norm",
63 	.nf_desc = "Normalized",
64 	.nf_type = NVMEADM_FT_HEX
65 }, {
66 	EXTSMART_F(raw),
67 	.nf_short = "raw",
68 	.nf_desc = "Raw",
69 	.nf_type = NVMEADM_FT_HEX
70 } };
71 
72 static const nvmeadm_field_t extsmart_wl_fields[] = { {
73 	EXTSMART_F(type),
74 	.nf_short = "id",
75 	.nf_desc = "Identifier",
76 	.nf_type = NVMEADM_FT_HEX
77 }, {
78 	EXTSMART_F(norm),
79 	.nf_short = "norm",
80 	.nf_desc = "Normalized",
81 	.nf_type = NVMEADM_FT_PERCENT
82 }, {
83 	.nf_off = offsetof(solidigm_smart_ent_t, sse_raw[0]),
84 	.nf_len = 2,
85 	.nf_short = "min",
86 	.nf_desc = "Minimum Erase Cycles",
87 	.nf_type = NVMEADM_FT_HEX
88 }, {
89 	.nf_off = offsetof(solidigm_smart_ent_t, sse_raw[2]),
90 	.nf_len = 2,
91 	.nf_short = "max",
92 	.nf_desc = "Maximum Erase Cycles",
93 	.nf_type = NVMEADM_FT_HEX
94 }, {
95 	.nf_off = offsetof(solidigm_smart_ent_t, sse_raw[4]),
96 	.nf_len = 2,
97 	.nf_short = "avg",
98 	.nf_desc = "Average Erase Cycles",
99 	.nf_type = NVMEADM_FT_HEX
100 } };
101 
102 static const nvmeadm_field_t extsmart_32mio_fields[] = { {
103 	EXTSMART_F(type),
104 	.nf_short = "id",
105 	.nf_desc = "Identifier",
106 	.nf_type = NVMEADM_FT_HEX
107 }, {
108 	EXTSMART_F(norm),
109 	.nf_short = "norm",
110 	.nf_desc = "Normalized",
111 	.nf_type = NVMEADM_FT_HEX
112 }, {
113 	EXTSMART_F(raw),
114 	.nf_short = "raw",
115 	.nf_desc = "Raw",
116 	.nf_type = NVMEADM_FT_BYTES,
117 	.nf_addend = { .nfa_shift = 25 }
118 } };
119 
120 static const nvmeadm_field_t extsmart_rawpct_fields[] = { {
121 	EXTSMART_F(type),
122 	.nf_short = "id",
123 	.nf_desc = "Identifier",
124 	.nf_type = NVMEADM_FT_HEX
125 }, {
126 	EXTSMART_F(norm),
127 	.nf_short = "norm",
128 	.nf_desc = "Normalized",
129 	.nf_type = NVMEADM_FT_HEX
130 }, {
131 	EXTSMART_F(raw),
132 	.nf_short = "raw",
133 	.nf_desc = "Raw",
134 	.nf_type = NVMEADM_FT_PERCENT,
135 } };
136 
137 static const nvmeadm_field_t extsmart_rawmin_fields[] = { {
138 	EXTSMART_F(type),
139 	.nf_short = "id",
140 	.nf_desc = "Identifier",
141 	.nf_type = NVMEADM_FT_HEX
142 }, {
143 	EXTSMART_F(norm),
144 	.nf_short = "norm",
145 	.nf_desc = "Normalized",
146 	.nf_type = NVMEADM_FT_HEX
147 }, {
148 	EXTSMART_F(raw),
149 	.nf_short = "raw",
150 	.nf_desc = "Raw",
151 	.nf_type = NVMEADM_FT_UNIT,
152 	.nf_addend = { .nfa_unit = "min" }
153 } };
154 
155 static const nvmeadm_field_t extsmart_rawhour_fields[] = { {
156 	EXTSMART_F(type),
157 	.nf_short = "id",
158 	.nf_desc = "Identifier",
159 	.nf_type = NVMEADM_FT_HEX
160 }, {
161 	EXTSMART_F(norm),
162 	.nf_short = "norm",
163 	.nf_desc = "Normalized",
164 	.nf_type = NVMEADM_FT_HEX
165 }, {
166 	EXTSMART_F(raw),
167 	.nf_short = "raw",
168 	.nf_desc = "Raw",
169 	.nf_type = NVMEADM_FT_UNIT,
170 	.nf_addend = { .nfa_unit = "hours" }
171 } };
172 
173 static const nvmeadm_field_t extsmart_therm_fields[] = { {
174 	EXTSMART_F(type),
175 	.nf_short = "id",
176 	.nf_desc = "Identifier",
177 	.nf_type = NVMEADM_FT_HEX
178 }, {
179 	EXTSMART_F(norm),
180 	.nf_short = "norm",
181 	.nf_desc = "Normalized",
182 	.nf_type = NVMEADM_FT_HEX
183 }, {
184 	.nf_off = offsetof(solidigm_smart_ent_t, sse_raw[0]),
185 	.nf_len = 1,
186 	.nf_short = "status",
187 	.nf_desc = "Throttle Status",
188 	.nf_type = NVMEADM_FT_PERCENT
189 }, {
190 	.nf_off = offsetof(solidigm_smart_ent_t, sse_raw[1]),
191 	.nf_len = 4,
192 	.nf_short = "count",
193 	.nf_desc = "Throttle Count",
194 	.nf_type = NVMEADM_FT_HEX
195 } };
196 
197 #define	WDC_F_SMART(f)	\
198 	.nf_off = offsetof(wdc_vul_sn65x_smart_t, sm_##f), \
199 	.nf_len = sizeof (((wdc_vul_sn65x_smart_t *)NULL)->sm_##f)
200 
201 static const nvmeadm_field_t wdc_vul_cusmart_fields[] = { {
202 	WDC_F_SMART(prog_fail),
203 	.nf_short = "pfc",
204 	.nf_desc = "Program Fail Count",
205 	NVMEADM_F_FIELDS(extsmart_percent_fields)
206 }, {
207 	WDC_F_SMART(erase_fail),
208 	.nf_short = "efc",
209 	.nf_desc = "Erase Fail Count",
210 	NVMEADM_F_FIELDS(extsmart_percent_fields)
211 }, {
212 	WDC_F_SMART(wear_level),
213 	.nf_short = "wl",
214 	.nf_desc = "Wear Leveling",
215 	NVMEADM_F_FIELDS(extsmart_wl_fields)
216 }, {
217 	WDC_F_SMART(e2e_edet),
218 	.nf_short = "e2e",
219 	.nf_desc = "End-to-End Error Detection Count",
220 	NVMEADM_F_FIELDS(extsmart_hex_fields)
221 }, {
222 	WDC_F_SMART(crc_err),
223 	.nf_short = "crc",
224 	.nf_desc = "CRC Error Count",
225 	NVMEADM_F_FIELDS(extsmart_hex_fields)
226 }, {
227 	WDC_F_SMART(timed_wear),
228 	.nf_short = "twmw",
229 	.nf_desc = "Timed Workload Media Wear",
230 	NVMEADM_F_FIELDS(extsmart_hex_fields)
231 }, {
232 	WDC_F_SMART(timed_read),
233 	.nf_short = "twhr",
234 	.nf_desc = "Timed Workload Host Reads",
235 	NVMEADM_F_FIELDS(extsmart_rawpct_fields)
236 }, {
237 	WDC_F_SMART(timed_timer),
238 	.nf_short = "twt",
239 	.nf_desc = "Timed Workload Timer",
240 	NVMEADM_F_FIELDS(extsmart_rawmin_fields)
241 }, {
242 	WDC_F_SMART(therm_throt),
243 	.nf_short = "tthrot",
244 	.nf_desc = "Thermal Throttle",
245 	NVMEADM_F_FIELDS(extsmart_therm_fields)
246 }, {
247 	WDC_F_SMART(retry_buf_over),
248 	.nf_short = "rboc",
249 	.nf_desc = "Retry Buffer Overflow Count",
250 	NVMEADM_F_FIELDS(extsmart_hex_fields)
251 }, {
252 	WDC_F_SMART(pll_lock_loss),
253 	.nf_short = "pllll",
254 	.nf_desc = "PLL Lock Loss Count",
255 	NVMEADM_F_FIELDS(extsmart_hex_fields)
256 }, {
257 	WDC_F_SMART(nand_write),
258 	.nf_short = "nbw",
259 	.nf_desc = "NAND Bytes Written",
260 	NVMEADM_F_FIELDS(extsmart_32mio_fields)
261 }, {
262 	WDC_F_SMART(host_write),
263 	.nf_short = "hbw",
264 	.nf_desc = "Host Bytes Written",
265 	NVMEADM_F_FIELDS(extsmart_32mio_fields)
266 } };
267 
268 const nvmeadm_log_field_info_t wdc_vul_cusmart_field_info = {
269 	.nlfi_log = "wdc/cusmart",
270 	.nlfi_fields = wdc_vul_cusmart_fields,
271 	.nlfi_nfields = ARRAY_SIZE(wdc_vul_cusmart_fields),
272 	.nlfi_min = sizeof (wdc_vul_sn65x_smart_t)
273 };
274 
275 
276 #define	KIOXIA_F_SMART(f)	\
277 	.nf_off = offsetof(kioxia_vul_cd8_smart_t, cds_##f), \
278 	.nf_len = sizeof (((kioxia_vul_cd8_smart_t *)NULL)->cds_##f)
279 
280 static const nvmeadm_field_t kioxia_vul_extsmart_fields[] = { {
281 	KIOXIA_F_SMART(prog_fail),
282 	.nf_short = "pfc",
283 	.nf_desc = "Program Fail Count",
284 	NVMEADM_F_FIELDS(extsmart_percent_fields)
285 }, {
286 	KIOXIA_F_SMART(erase_fail),
287 	.nf_short = "efc",
288 	.nf_desc = "Erase Fail Count",
289 	NVMEADM_F_FIELDS(extsmart_percent_fields)
290 }, {
291 	KIOXIA_F_SMART(wear_level),
292 	.nf_short = "wl",
293 	.nf_desc = "Wear Leveling",
294 	NVMEADM_F_FIELDS(extsmart_wl_fields)
295 }, {
296 	KIOXIA_F_SMART(e2e_det),
297 	.nf_short = "e2e",
298 	.nf_desc = "End-to-End Error Detection Count",
299 	NVMEADM_F_FIELDS(extsmart_hex_fields)
300 }, {
301 	KIOXIA_F_SMART(crc_error),
302 	.nf_short = "crc",
303 	.nf_desc = "CRC Error Count",
304 	NVMEADM_F_FIELDS(extsmart_hex_fields)
305 }, {
306 	KIOXIA_F_SMART(nand_write),
307 	.nf_short = "nbw",
308 	.nf_desc = "NAND Bytes Written",
309 	NVMEADM_F_FIELDS(extsmart_32mio_fields)
310 }, {
311 	KIOXIA_F_SMART(host_write),
312 	.nf_short = "hbw",
313 	.nf_desc = "Host Bytes Written",
314 	NVMEADM_F_FIELDS(extsmart_32mio_fields)
315 }, {
316 	/*
317 	 * The remaining fields (other than host bytes read) are duplicates from
318 	 * the normal NVMe Health log so we use the standard's name and short
319 	 * values.
320 	 */
321 	KIOXIA_F_SMART(crit_warn),
322 	.nf_short = "cw",
323 	.nf_desc = "Device Critical Warning",
324 	NVMEADM_F_FIELDS(extsmart_hex_fields)
325 }, {
326 	KIOXIA_F_SMART(host_read),
327 	.nf_short = "hbr",
328 	.nf_desc = "Host Bytes Read",
329 	NVMEADM_F_FIELDS(extsmart_32mio_fields)
330 }, {
331 	KIOXIA_F_SMART(comp_temp),
332 	.nf_short = "ctemp",
333 	.nf_desc = "Composite Temperature",
334 	NVMEADM_F_FIELDS(extsmart_hex_fields)
335 }, {
336 	KIOXIA_F_SMART(life_used),
337 	.nf_short = "pused",
338 	.nf_desc = "Percentage Used",
339 	NVMEADM_F_FIELDS(extsmart_percent_fields)
340 }, {
341 	KIOXIA_F_SMART(power_cycles),
342 	.nf_short = "pwrc",
343 	.nf_desc = "Power Cycles",
344 	NVMEADM_F_FIELDS(extsmart_hex_fields)
345 }, {
346 	KIOXIA_F_SMART(power_hours),
347 	.nf_short = "poh",
348 	.nf_desc = "Power On Hours",
349 	NVMEADM_F_FIELDS(extsmart_rawhour_fields)
350 }, {
351 	KIOXIA_F_SMART(unsafe_shut),
352 	.nf_short = "upl",
353 	.nf_desc = "Unexpected Power Losses",
354 	NVMEADM_F_FIELDS(extsmart_hex_fields)
355 } };
356 
357 const nvmeadm_log_field_info_t kioxia_vul_extsmart_field_info = {
358 	.nlfi_log = "kioxia/extsmart",
359 	.nlfi_fields = kioxia_vul_extsmart_fields,
360 	.nlfi_nfields = ARRAY_SIZE(kioxia_vul_extsmart_fields),
361 	.nlfi_min = sizeof (kioxia_vul_cd8_smart_t)
362 };
363