Line data Source code
1 : /*
2 : * Copyright (C) 2014-2021 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Author: Tomas Bzatek <tbzatek@redhat.com>
18 : */
19 :
20 : #include <glib.h>
21 : #include <string.h>
22 : #include <stdio.h>
23 : #include <unistd.h>
24 : #include <sys/ioctl.h>
25 : #include <sys/stat.h>
26 : #include <errno.h>
27 : #include <fcntl.h>
28 : #include <malloc.h>
29 :
30 : #include <libnvme.h>
31 :
32 : #include <blockdev/utils.h>
33 : #include <check_deps.h>
34 : #include "nvme.h"
35 : #include "nvme-private.h"
36 :
37 :
38 : /**
39 : * bd_nvme_controller_info_free: (skip)
40 : * @info: (nullable): %BDNVMEControllerInfo to free
41 : *
42 : * Frees @info.
43 : */
44 0 : void bd_nvme_controller_info_free (BDNVMEControllerInfo *info) {
45 0 : if (info == NULL)
46 0 : return;
47 :
48 0 : g_free (info->fguid);
49 0 : g_free (info->subsysnqn);
50 0 : g_free (info->model_number);
51 0 : g_free (info->serial_number);
52 0 : g_free (info->firmware_ver);
53 0 : g_free (info->nvme_ver);
54 0 : g_free (info);
55 : }
56 :
57 : /**
58 : * bd_nvme_controller_info_copy: (skip)
59 : * @info: (nullable): %BDNVMEControllerInfo to copy
60 : *
61 : * Creates a new copy of @info.
62 : */
63 0 : BDNVMEControllerInfo * bd_nvme_controller_info_copy (BDNVMEControllerInfo *info) {
64 : BDNVMEControllerInfo *new_info;
65 :
66 0 : if (info == NULL)
67 0 : return NULL;
68 :
69 0 : new_info = g_new0 (BDNVMEControllerInfo, 1);
70 0 : memcpy (new_info, info, sizeof (BDNVMEControllerInfo));
71 0 : new_info->fguid = g_strdup (info->fguid);
72 0 : new_info->subsysnqn = g_strdup (info->subsysnqn);
73 0 : new_info->model_number = g_strdup (info->model_number);
74 0 : new_info->serial_number = g_strdup (info->serial_number);
75 0 : new_info->firmware_ver = g_strdup (info->firmware_ver);
76 0 : new_info->nvme_ver = g_strdup (info->nvme_ver);
77 :
78 0 : return new_info;
79 : }
80 :
81 : /**
82 : * bd_nvme_lba_format_free: (skip)
83 : * @fmt: (nullable): %BDNVMELBAFormat to free
84 : *
85 : * Frees @fmt.
86 : */
87 0 : void bd_nvme_lba_format_free (BDNVMELBAFormat *fmt) {
88 0 : g_free (fmt);
89 0 : }
90 :
91 : /**
92 : * bd_nvme_lba_format_copy: (skip)
93 : * @fmt: (nullable): %BDNVMELBAFormat to copy
94 : *
95 : * Creates a new copy of @fmt.
96 : */
97 0 : BDNVMELBAFormat * bd_nvme_lba_format_copy (BDNVMELBAFormat *fmt) {
98 : BDNVMELBAFormat *new_fmt;
99 :
100 0 : if (fmt == NULL)
101 0 : return NULL;
102 :
103 0 : new_fmt = g_new0 (BDNVMELBAFormat, 1);
104 0 : new_fmt->data_size = fmt->data_size;
105 0 : new_fmt->metadata_size = fmt->metadata_size;
106 0 : new_fmt->relative_performance = fmt->relative_performance;
107 :
108 0 : return new_fmt;
109 : }
110 :
111 : /**
112 : * bd_nvme_namespace_info_free: (skip)
113 : * @info: (nullable): %BDNVMENamespaceInfo to free
114 : *
115 : * Frees @info.
116 : */
117 0 : void bd_nvme_namespace_info_free (BDNVMENamespaceInfo *info) {
118 : BDNVMELBAFormat **lba_formats;
119 :
120 0 : if (info == NULL)
121 0 : return;
122 :
123 0 : g_free (info->eui64);
124 0 : g_free (info->uuid);
125 0 : g_free (info->nguid);
126 :
127 0 : for (lba_formats = info->lba_formats; lba_formats && *lba_formats; lba_formats++)
128 0 : bd_nvme_lba_format_free (*lba_formats);
129 0 : g_free (info->lba_formats);
130 0 : g_free (info);
131 : }
132 :
133 : /**
134 : * bd_nvme_namespace_info_copy: (skip)
135 : * @info: (nullable): %BDNVMENamespaceInfo to copy
136 : *
137 : * Creates a new copy of @info.
138 : */
139 0 : BDNVMENamespaceInfo * bd_nvme_namespace_info_copy (BDNVMENamespaceInfo *info) {
140 : BDNVMENamespaceInfo *new_info;
141 : BDNVMELBAFormat **lba_formats;
142 : GPtrArray *ptr_array;
143 :
144 0 : if (info == NULL)
145 0 : return NULL;
146 :
147 0 : new_info = g_new0 (BDNVMENamespaceInfo, 1);
148 0 : memcpy (new_info, info, sizeof (BDNVMENamespaceInfo));
149 0 : new_info->eui64 = g_strdup (info->eui64);
150 0 : new_info->uuid = g_strdup (info->uuid);
151 0 : new_info->nguid = g_strdup (info->nguid);
152 :
153 0 : ptr_array = g_ptr_array_new ();
154 0 : for (lba_formats = info->lba_formats; lba_formats && *lba_formats; lba_formats++)
155 0 : g_ptr_array_add (ptr_array, bd_nvme_lba_format_copy (*lba_formats));
156 0 : g_ptr_array_add (ptr_array, NULL);
157 0 : new_info->lba_formats = (BDNVMELBAFormat **) g_ptr_array_free (ptr_array, FALSE);
158 :
159 0 : return new_info;
160 : }
161 :
162 : /**
163 : * bd_nvme_smart_log_free: (skip)
164 : * @log: (nullable): %BDNVMESmartLog to free
165 : *
166 : * Frees @log.
167 : */
168 0 : void bd_nvme_smart_log_free (BDNVMESmartLog *log) {
169 0 : g_free (log);
170 0 : }
171 :
172 : /**
173 : * bd_nvme_smart_log_copy: (skip)
174 : * @log: (nullable): %BDNVMESmartLog to copy
175 : *
176 : * Creates a new copy of @log.
177 : */
178 0 : BDNVMESmartLog * bd_nvme_smart_log_copy (BDNVMESmartLog *log) {
179 : BDNVMESmartLog *new_log;
180 :
181 0 : if (log == NULL)
182 0 : return NULL;
183 :
184 0 : new_log = g_new0 (BDNVMESmartLog, 1);
185 0 : memcpy (new_log, log, sizeof (BDNVMESmartLog));
186 :
187 0 : return new_log;
188 : }
189 :
190 : /**
191 : * bd_nvme_error_log_entry_free: (skip)
192 : * @entry: (nullable): %BDNVMEErrorLogEntry to free
193 : *
194 : * Frees @entry.
195 : */
196 0 : void bd_nvme_error_log_entry_free (BDNVMEErrorLogEntry *entry) {
197 0 : if (entry == NULL)
198 0 : return;
199 :
200 0 : if (entry->command_error)
201 0 : g_error_free (entry->command_error);
202 0 : g_free (entry);
203 : }
204 :
205 : /**
206 : * bd_nvme_error_log_entry_copy: (skip)
207 : * @entry: (nullable): %BDNVMEErrorLogEntry to copy
208 : *
209 : * Creates a new copy of @entry.
210 : */
211 0 : BDNVMEErrorLogEntry * bd_nvme_error_log_entry_copy (BDNVMEErrorLogEntry *entry) {
212 : BDNVMEErrorLogEntry *new_entry;
213 :
214 0 : if (entry == NULL)
215 0 : return NULL;
216 :
217 0 : new_entry = g_new0 (BDNVMEErrorLogEntry, 1);
218 0 : memcpy (new_entry, entry, sizeof (BDNVMEErrorLogEntry));
219 0 : if (entry->command_error)
220 0 : new_entry->command_error = g_error_copy (entry->command_error);
221 :
222 0 : return new_entry;
223 : }
224 :
225 : /**
226 : * bd_nvme_self_test_log_entry_free: (skip)
227 : * @entry: (nullable): %BDNVMESelfTestLogEntry to free
228 : *
229 : * Frees @entry.
230 : */
231 0 : void bd_nvme_self_test_log_entry_free (BDNVMESelfTestLogEntry *entry) {
232 0 : if (entry == NULL)
233 0 : return;
234 :
235 0 : if (entry->status_code_error)
236 0 : g_error_free (entry->status_code_error);
237 0 : g_free (entry);
238 : }
239 :
240 : /**
241 : * bd_nvme_self_test_log_entry_copy: (skip)
242 : * @entry: (nullable): %BDNVMESelfTestLogEntry to copy
243 : *
244 : * Creates a new copy of @entry.
245 : */
246 0 : BDNVMESelfTestLogEntry * bd_nvme_self_test_log_entry_copy (BDNVMESelfTestLogEntry *entry) {
247 : BDNVMESelfTestLogEntry *new_entry;
248 :
249 0 : if (entry == NULL)
250 0 : return NULL;
251 :
252 0 : new_entry = g_new0 (BDNVMESelfTestLogEntry, 1);
253 0 : memcpy (new_entry, entry, sizeof (BDNVMESelfTestLogEntry));
254 0 : if (entry->status_code_error)
255 0 : new_entry->status_code_error = g_error_copy (entry->status_code_error);
256 :
257 0 : return new_entry;
258 : }
259 :
260 : /**
261 : * bd_nvme_self_test_result_to_string:
262 : * @result: A %BDNVMESelfTestResult.
263 : * @error: (out) (optional): place to store error (if any)
264 : *
265 : * Returns: (transfer none): A string representation of @result for use as an identifier string
266 : * or %NULL when the code is unknown.
267 : */
268 10 : const gchar * bd_nvme_self_test_result_to_string (BDNVMESelfTestResult result, GError **error) {
269 : static const gchar * const str[] = {
270 : [BD_NVME_SELF_TEST_RESULT_NO_ERROR] = "success",
271 : [BD_NVME_SELF_TEST_RESULT_ABORTED] = "aborted",
272 : [BD_NVME_SELF_TEST_RESULT_CTRL_RESET] = "ctrl_reset",
273 : [BD_NVME_SELF_TEST_RESULT_NS_REMOVED] = "ns_removed",
274 : [BD_NVME_SELF_TEST_RESULT_ABORTED_FORMAT] = "aborted_format",
275 : [BD_NVME_SELF_TEST_RESULT_FATAL_ERROR] = "fatal_error",
276 : [BD_NVME_SELF_TEST_RESULT_UNKNOWN_SEG_FAIL] = "unknown_seg_fail",
277 : [BD_NVME_SELF_TEST_RESULT_KNOWN_SEG_FAIL] = "known_seg_fail",
278 : [BD_NVME_SELF_TEST_RESULT_ABORTED_UNKNOWN] = "aborted_unknown",
279 : [BD_NVME_SELF_TEST_RESULT_ABORTED_SANITIZE] = "aborted_sanitize"
280 : };
281 :
282 10 : if (result >= G_N_ELEMENTS (str)) {
283 0 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
284 : "Invalid result code %d", result);
285 0 : return NULL;
286 : }
287 :
288 10 : return str[result];
289 : }
290 :
291 : /**
292 : * bd_nvme_self_test_log_free: (skip)
293 : * @log: (nullable): %BDNVMESelfTestLog to free
294 : *
295 : * Frees @log.
296 : */
297 0 : void bd_nvme_self_test_log_free (BDNVMESelfTestLog *log) {
298 : BDNVMESelfTestLogEntry **entries;
299 :
300 0 : if (log == NULL)
301 0 : return;
302 :
303 0 : for (entries = log->entries; entries && *entries; entries++)
304 0 : bd_nvme_self_test_log_entry_free (*entries);
305 0 : g_free (log->entries);
306 0 : g_free (log);
307 : }
308 :
309 : /**
310 : * bd_nvme_self_test_log_copy: (skip)
311 : * @log: (nullable): %BDNVMESelfTestLog to copy
312 : *
313 : * Creates a new copy of @log.
314 : */
315 0 : BDNVMESelfTestLog * bd_nvme_self_test_log_copy (BDNVMESelfTestLog *log) {
316 : BDNVMESelfTestLog *new_log;
317 : BDNVMESelfTestLogEntry **entries;
318 : GPtrArray *ptr_array;
319 :
320 0 : if (log == NULL)
321 0 : return NULL;
322 :
323 0 : new_log = g_new0 (BDNVMESelfTestLog, 1);
324 0 : memcpy (new_log, log, sizeof (BDNVMESelfTestLog));
325 :
326 0 : ptr_array = g_ptr_array_new ();
327 0 : for (entries = log->entries; entries && *entries; entries++)
328 0 : g_ptr_array_add (ptr_array, bd_nvme_self_test_log_entry_copy (*entries));
329 0 : g_ptr_array_add (ptr_array, NULL);
330 0 : new_log->entries = (BDNVMESelfTestLogEntry **) g_ptr_array_free (ptr_array, FALSE);
331 :
332 0 : return new_log;
333 : }
334 :
335 :
336 : /**
337 : * bd_nvme_sanitize_log_free: (skip)
338 : * @log: (nullable): %BDNVMESanitizeLog to free
339 : *
340 : * Frees @log.
341 : */
342 0 : void bd_nvme_sanitize_log_free (BDNVMESanitizeLog *log) {
343 0 : if (log == NULL)
344 0 : return;
345 :
346 0 : g_free (log);
347 : }
348 :
349 : /**
350 : * bd_nvme_sanitize_log_copy: (skip)
351 : * @log: (nullable): %BDNVMESanitizeLog to copy
352 : *
353 : * Creates a new copy of @log.
354 : */
355 0 : BDNVMESanitizeLog * bd_nvme_sanitize_log_copy (BDNVMESanitizeLog *log) {
356 : BDNVMESanitizeLog *new_log;
357 :
358 0 : if (log == NULL)
359 0 : return NULL;
360 :
361 0 : new_log = g_new0 (BDNVMESanitizeLog, 1);
362 0 : memcpy (new_log, log, sizeof (BDNVMESanitizeLog));
363 :
364 0 : return new_log;
365 : }
366 :
367 :
368 : /* can't use real __int128 due to gobject-introspection */
369 12 : static guint64 int128_to_guint64 (__u8 data[16])
370 : {
371 : int i;
372 : __u8 d[16];
373 12 : guint64 result = 0;
374 :
375 : /* endianness conversion */
376 : #if G_BYTE_ORDER == G_BIG_ENDIAN
377 : for (i = 0; i < 16; i++)
378 : d[i] = data[15 - i];
379 : #else
380 12 : memcpy (d, data, sizeof (d));
381 : #endif
382 :
383 : /* FIXME: would overflow */
384 : /* https://github.com/linux-nvme/libnvme/issues/475 */
385 204 : for (i = 0; i < 16; i++) {
386 192 : result *= 256;
387 192 : result += d[15 - i];
388 : }
389 12 : return result;
390 : }
391 :
392 38 : gint _open_dev (const gchar *device, GError **error) {
393 : int fd;
394 :
395 38 : fd = open (device, O_RDONLY);
396 38 : if (fd < 0) {
397 9 : _nvme_status_to_error (-1, FALSE, error);
398 9 : g_prefix_error (error, "Failed to open device '%s': ", device);
399 9 : return -1;
400 : }
401 :
402 29 : return fd;
403 : }
404 :
405 : /* backported from nvme-cli: https://github.com/linux-nvme/nvme-cli/pull/2051 */
406 : #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
407 :
408 19 : void *_nvme_alloc (size_t len, GError **error)
409 : {
410 19 : size_t _len = ROUND_UP (len, 0x1000);
411 : void *p;
412 :
413 19 : if (posix_memalign ((void *) &p, getpagesize (), _len)) {
414 0 : g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
415 : "Memory allocation failed: %m");
416 0 : return NULL;
417 : }
418 :
419 19 : memset (p, 0, _len);
420 19 : return p;
421 : }
422 :
423 2 : static gchar *decode_nvme_rev (guint32 ver) {
424 : guint16 mjr;
425 2 : guint8 mnr, ter = 0;
426 :
427 2 : mjr = ver >> 16;
428 2 : mnr = (ver >> 8) & 0xFF;
429 : /* 'ter' is only valid for >= 1.2.1 */
430 2 : if (mjr >= 2 || mnr >= 2)
431 2 : ter = ver & 0xFF;
432 :
433 2 : if (mjr == 0 && mnr == 0)
434 0 : return NULL;
435 2 : if (ter == 0)
436 2 : return g_strdup_printf ("%u.%u", mjr, mnr);
437 : else
438 0 : return g_strdup_printf ("%u.%u.%u", mjr, mnr, ter);
439 : }
440 :
441 1 : static gchar *_uuid_to_str (unsigned char uuid[NVME_UUID_LEN]) {
442 1 : gchar uuid_buf[NVME_UUID_LEN_STRING] = ZERO_INIT;
443 :
444 1 : if (nvme_uuid_to_string (uuid, uuid_buf) == 0)
445 1 : return g_strdup (uuid_buf);
446 0 : return NULL;
447 : }
448 :
449 4 : static gboolean _nvme_a_is_zero (const __u8 a[], int len) {
450 : int i;
451 :
452 44 : for (i = 0; i < len; i++)
453 41 : if (a[i] > 0)
454 1 : return FALSE;
455 3 : return TRUE;
456 : }
457 :
458 : /**
459 : * bd_nvme_get_controller_info:
460 : * @device: a NVMe controller device (e.g. `/dev/nvme0`)
461 : * @error: (out) (nullable): place to store error (if any)
462 : *
463 : * Retrieves information about the NVMe controller (the Identify Controller command)
464 : * as specified by the @device block device path.
465 : *
466 : * Returns: (transfer full): information about given controller or %NULL in case of an error (with @error set).
467 : *
468 : * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_INFO
469 : */
470 3 : BDNVMEControllerInfo * bd_nvme_get_controller_info (const gchar *device, GError **error) {
471 : int ret;
472 : int fd;
473 : struct nvme_id_ctrl *ctrl_id;
474 : BDNVMEControllerInfo *info;
475 :
476 : /* open the block device */
477 3 : fd = _open_dev (device, error);
478 3 : if (fd < 0)
479 1 : return NULL;
480 :
481 2 : ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
482 2 : if (!ctrl_id)
483 0 : return NULL;
484 : /* send the NVME_IDENTIFY_CNS_CTRL ioctl */
485 2 : ret = nvme_identify_ctrl (fd, ctrl_id);
486 2 : if (ret != 0) {
487 0 : _nvme_status_to_error (ret, FALSE, error);
488 0 : g_prefix_error (error, "NVMe Identify Controller command error: ");
489 0 : close (fd);
490 0 : free (ctrl_id);
491 0 : return NULL;
492 : }
493 2 : close (fd);
494 :
495 2 : info = g_new0 (BDNVMEControllerInfo, 1);
496 2 : if ((ctrl_id->cmic & NVME_CTRL_CMIC_MULTI_PORT) == NVME_CTRL_CMIC_MULTI_PORT)
497 1 : info->features |= BD_NVME_CTRL_FEAT_MULTIPORT;
498 2 : if ((ctrl_id->cmic & NVME_CTRL_CMIC_MULTI_CTRL) == NVME_CTRL_CMIC_MULTI_CTRL)
499 1 : info->features |= BD_NVME_CTRL_FEAT_MULTICTRL;
500 2 : if ((ctrl_id->cmic & NVME_CTRL_CMIC_MULTI_SRIOV) == NVME_CTRL_CMIC_MULTI_SRIOV)
501 0 : info->features |= BD_NVME_CTRL_FEAT_SRIOV;
502 2 : if ((ctrl_id->cmic & NVME_CTRL_CMIC_MULTI_ANA_REPORTING) == NVME_CTRL_CMIC_MULTI_ANA_REPORTING)
503 1 : info->features |= BD_NVME_CTRL_FEAT_ANA_REPORTING;
504 2 : if ((ctrl_id->nvmsr & NVME_CTRL_NVMSR_NVMESD) == NVME_CTRL_NVMSR_NVMESD)
505 0 : info->features |= BD_NVME_CTRL_FEAT_STORAGE_DEVICE;
506 2 : if ((ctrl_id->nvmsr & NVME_CTRL_NVMSR_NVMEE) == NVME_CTRL_NVMSR_NVMEE)
507 0 : info->features |= BD_NVME_CTRL_FEAT_ENCLOSURE;
508 2 : if ((ctrl_id->mec & NVME_CTRL_MEC_PCIEME) == NVME_CTRL_MEC_PCIEME)
509 0 : info->features |= BD_NVME_CTRL_FEAT_MGMT_PCIE;
510 2 : if ((ctrl_id->mec & NVME_CTRL_MEC_SMBUSME) == NVME_CTRL_MEC_SMBUSME)
511 0 : info->features |= BD_NVME_CTRL_FEAT_MGMT_SMBUS;
512 2 : info->pci_vendor_id = GUINT16_FROM_LE (ctrl_id->vid);
513 2 : info->pci_subsys_vendor_id = GUINT16_FROM_LE (ctrl_id->ssvid);
514 2 : info->ctrl_id = GUINT16_FROM_LE (ctrl_id->cntlid);
515 2 : if (!_nvme_a_is_zero (ctrl_id->fguid, sizeof (ctrl_id->fguid)))
516 0 : info->fguid = _uuid_to_str (ctrl_id->fguid);
517 2 : info->model_number = g_strndup (ctrl_id->mn, sizeof (ctrl_id->mn));
518 2 : g_strstrip (info->model_number);
519 2 : info->serial_number = g_strndup (ctrl_id->sn, sizeof (ctrl_id->sn));
520 2 : g_strstrip (info->serial_number);
521 2 : info->firmware_ver = g_strndup (ctrl_id->fr, sizeof (ctrl_id->fr));
522 2 : g_strstrip (info->firmware_ver);
523 2 : info->nvme_ver = decode_nvme_rev (GUINT32_FROM_LE (ctrl_id->ver));
524 : /* TODO: vwci: VPD Write Cycle Information */
525 2 : if ((ctrl_id->oacs & NVME_CTRL_OACS_FORMAT) == NVME_CTRL_OACS_FORMAT)
526 0 : info->features |= BD_NVME_CTRL_FEAT_FORMAT;
527 2 : if ((ctrl_id->oacs & NVME_CTRL_OACS_NS_MGMT) == NVME_CTRL_OACS_NS_MGMT)
528 0 : info->features |= BD_NVME_CTRL_FEAT_NS_MGMT;
529 2 : if ((ctrl_id->oacs & NVME_CTRL_OACS_SELF_TEST) == NVME_CTRL_OACS_SELF_TEST)
530 0 : info->features |= BD_NVME_CTRL_FEAT_SELFTEST;
531 2 : switch (ctrl_id->cntrltype) {
532 1 : case NVME_CTRL_CNTRLTYPE_IO:
533 1 : info->controller_type = BD_NVME_CTRL_TYPE_IO;
534 1 : break;
535 1 : case NVME_CTRL_CNTRLTYPE_DISCOVERY:
536 1 : info->controller_type = BD_NVME_CTRL_TYPE_DISCOVERY;
537 1 : break;
538 0 : case NVME_CTRL_CNTRLTYPE_ADMIN:
539 0 : info->controller_type = BD_NVME_CTRL_TYPE_ADMIN;
540 0 : break;
541 0 : default:
542 0 : info->controller_type = BD_NVME_CTRL_TYPE_UNKNOWN;
543 : }
544 2 : info->hmb_pref_size = GUINT32_FROM_LE (ctrl_id->hmpre) * 4096LL;
545 2 : info->hmb_min_size = GUINT32_FROM_LE (ctrl_id->hmmin) * 4096LL;
546 2 : info->size_total = int128_to_guint64 (ctrl_id->tnvmcap);
547 2 : info->size_unalloc = int128_to_guint64 (ctrl_id->unvmcap);
548 2 : info->selftest_ext_time = GUINT16_FROM_LE (ctrl_id->edstt);
549 : /* TODO: lpa: Log Page Attributes - NVME_CTRL_LPA_PERSETENT_EVENT: Persistent Event log */
550 2 : if ((ctrl_id->dsto & NVME_CTRL_DSTO_ONE_DST) == NVME_CTRL_DSTO_ONE_DST)
551 0 : info->features |= BD_NVME_CTRL_FEAT_SELFTEST_SINGLE;
552 2 : if ((ctrl_id->sanicap & NVME_CTRL_SANICAP_CES) == NVME_CTRL_SANICAP_CES)
553 0 : info->features |= BD_NVME_CTRL_FEAT_SANITIZE_CRYPTO;
554 2 : if ((ctrl_id->sanicap & NVME_CTRL_SANICAP_BES) == NVME_CTRL_SANICAP_BES)
555 0 : info->features |= BD_NVME_CTRL_FEAT_SANITIZE_BLOCK;
556 2 : if ((ctrl_id->sanicap & NVME_CTRL_SANICAP_OWS) == NVME_CTRL_SANICAP_OWS)
557 0 : info->features |= BD_NVME_CTRL_FEAT_SANITIZE_OVERWRITE;
558 : /* struct nvme_id_ctrl.nn: If the &struct nvme_id_ctrl.mnan field is cleared to 0h,
559 : * then the struct nvme_id_ctrl.nn field also indicates the maximum number of namespaces
560 : * supported by the NVM subsystem.
561 : */
562 2 : info->num_namespaces = GUINT32_FROM_LE (ctrl_id->mnan) == 0 ? GUINT32_FROM_LE (ctrl_id->nn) : GUINT32_FROM_LE (ctrl_id->mnan);
563 2 : if ((ctrl_id->fna & NVME_CTRL_FNA_FMT_ALL_NAMESPACES) == NVME_CTRL_FNA_FMT_ALL_NAMESPACES)
564 0 : info->features |= BD_NVME_CTRL_FEAT_FORMAT_ALL_NS;
565 2 : if ((ctrl_id->fna & NVME_CTRL_FNA_SEC_ALL_NAMESPACES) == NVME_CTRL_FNA_SEC_ALL_NAMESPACES)
566 0 : info->features |= BD_NVME_CTRL_FEAT_SECURE_ERASE_ALL_NS;
567 2 : if ((ctrl_id->fna & NVME_CTRL_FNA_CRYPTO_ERASE) == NVME_CTRL_FNA_CRYPTO_ERASE)
568 0 : info->features |= BD_NVME_CTRL_FEAT_SECURE_ERASE_CRYPTO;
569 : /* TODO: enum nvme_id_ctrl_oncs: NVME_CTRL_ONCS_WRITE_UNCORRECTABLE, NVME_CTRL_ONCS_WRITE_ZEROES... */
570 : /* TODO: nwpc: Namespace Write Protection Capabilities */
571 2 : info->subsysnqn = g_strndup (ctrl_id->subnqn, sizeof (ctrl_id->subnqn));
572 2 : g_strstrip (info->subsysnqn);
573 :
574 2 : free (ctrl_id);
575 2 : return info;
576 : }
577 :
578 :
579 : /**
580 : * bd_nvme_get_namespace_info:
581 : * @device: a NVMe namespace device (e.g. `/dev/nvme0n1`)
582 : * @error: (out) (nullable): place to store error (if any)
583 : *
584 : * Retrieves information about the NVMe namespace (the Identify Namespace command)
585 : * as specified by the @device block device path.
586 : *
587 : * Returns: (transfer full): information about given namespace or %NULL in case of an error (with @error set).
588 : *
589 : * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_INFO
590 : */
591 3 : BDNVMENamespaceInfo *bd_nvme_get_namespace_info (const gchar *device, GError **error) {
592 : int ret;
593 : int ret_ctrl;
594 3 : int ret_desc = -1;
595 3 : int ret_ns_ind = -1;
596 : int fd;
597 3 : __u32 nsid = 0;
598 : struct nvme_id_ctrl *ctrl_id;
599 : struct nvme_id_ns *ns_info;
600 3 : struct nvme_id_independent_id_ns *ns_info_ind = NULL;
601 3 : struct nvme_ns_id_desc *descs = NULL;
602 : guint8 flbas;
603 : guint i;
604 : guint len;
605 : BDNVMENamespaceInfo *info;
606 : GPtrArray *ptr_array;
607 :
608 : /* open the block device */
609 3 : fd = _open_dev (device, error);
610 3 : if (fd < 0)
611 1 : return NULL;
612 :
613 : /* get Namespace Identifier (NSID) for the @device (NVME_IOCTL_ID) */
614 2 : ret = nvme_get_nsid (fd, &nsid);
615 2 : if (ret != 0) {
616 1 : _nvme_status_to_error (ret, FALSE, error);
617 1 : g_prefix_error (error, "Error getting Namespace Identifier (NSID): ");
618 1 : close (fd);
619 1 : return NULL;
620 : }
621 :
622 : /* send the NVME_IDENTIFY_CNS_NS ioctl */
623 1 : ns_info = _nvme_alloc (sizeof (struct nvme_id_ns), error);
624 1 : if (!ns_info) {
625 0 : close (fd);
626 0 : return NULL;
627 : }
628 1 : ret = nvme_identify_ns (fd, nsid, ns_info);
629 1 : if (ret != 0) {
630 0 : _nvme_status_to_error (ret, FALSE, error);
631 0 : g_prefix_error (error, "NVMe Identify Namespace command error: ");
632 0 : close (fd);
633 0 : free (ns_info);
634 0 : return NULL;
635 : }
636 :
637 : /* send the NVME_IDENTIFY_CNS_CTRL ioctl */
638 1 : ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
639 1 : if (!ctrl_id) {
640 0 : close (fd);
641 0 : free (ns_info);
642 0 : return NULL;
643 : }
644 1 : ret_ctrl = nvme_identify_ctrl (fd, ctrl_id);
645 :
646 : /* send the NVME_IDENTIFY_CNS_NS_DESC_LIST ioctl, NVMe 1.3 */
647 1 : if (ret_ctrl == 0 && GUINT32_FROM_LE (ctrl_id->ver) >= 0x10300) {
648 1 : descs = _nvme_alloc (NVME_IDENTIFY_DATA_SIZE, NULL);
649 1 : if (descs != NULL)
650 1 : ret_desc = nvme_identify_ns_descs (fd, nsid, descs);
651 : }
652 :
653 : /* send the NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS ioctl, NVMe 2.0 */
654 1 : if (ret_ctrl == 0 && GUINT32_FROM_LE (ctrl_id->ver) >= 0x20000) {
655 1 : ns_info_ind = _nvme_alloc (sizeof (struct nvme_id_independent_id_ns), NULL);
656 1 : if (ns_info_ind != NULL)
657 1 : ret_ns_ind = nvme_identify_independent_identify_ns (fd, nsid, ns_info_ind);
658 : }
659 1 : close (fd);
660 :
661 1 : info = g_new0 (BDNVMENamespaceInfo, 1);
662 1 : info->nsid = nsid;
663 1 : info->nsize = GUINT64_FROM_LE (ns_info->nsze);
664 1 : info->ncap = GUINT64_FROM_LE (ns_info->ncap);
665 1 : info->nuse = GUINT64_FROM_LE (ns_info->nuse);
666 1 : if ((ns_info->nsfeat & NVME_NS_FEAT_THIN) == NVME_NS_FEAT_THIN)
667 0 : info->features |= BD_NVME_NS_FEAT_THIN;
668 1 : if ((ns_info->nmic & NVME_NS_NMIC_SHARED) == NVME_NS_NMIC_SHARED)
669 1 : info->features |= BD_NVME_NS_FEAT_MULTIPATH_SHARED;
670 1 : if ((ns_info->fpi & NVME_NS_FPI_SUPPORTED) == NVME_NS_FPI_SUPPORTED)
671 0 : info->features |= BD_NVME_NS_FEAT_FORMAT_PROGRESS;
672 1 : info->format_progress_remaining = ns_info->fpi & NVME_NS_FPI_REMAINING;
673 : /* TODO: what the ns_info->nvmcap really stands for? */
674 1 : info->write_protected = (ns_info->nsattr & NVME_NS_NSATTR_WRITE_PROTECTED) == NVME_NS_NSATTR_WRITE_PROTECTED;
675 :
676 1 : if (ret_desc == 0) {
677 2 : for (i = 0; i < NVME_IDENTIFY_DATA_SIZE; i += len) {
678 2 : struct nvme_ns_id_desc *d = descs + i;
679 :
680 2 : if (!d->nidl)
681 1 : break;
682 1 : len = d->nidl + sizeof (*d);
683 :
684 1 : switch (d->nidt) {
685 0 : case NVME_NIDT_EUI64:
686 0 : g_free (info->eui64);
687 0 : info->eui64 = g_malloc0 (d->nidl * 2 + 1);
688 0 : for (i = 0; i < d->nidl; i++)
689 0 : snprintf (info->eui64 + i * 2, 3, "%02x", d->nid[i]);
690 0 : break;
691 0 : case NVME_NIDT_NGUID:
692 0 : g_free (info->nguid);
693 0 : info->nguid = g_malloc0 (d->nidl * 2 + 1);
694 0 : for (i = 0; i < d->nidl; i++)
695 0 : snprintf (info->nguid + i * 2, 3, "%02x", d->nid[i]);
696 0 : break;
697 1 : case NVME_NIDT_UUID:
698 1 : g_free (info->uuid);
699 1 : info->uuid = _uuid_to_str (d->nid);
700 1 : break;
701 0 : case NVME_NIDT_CSI:
702 : /* unused */
703 0 : break;
704 : }
705 : }
706 : }
707 :
708 1 : if (info->nguid == NULL && !_nvme_a_is_zero (ns_info->nguid, sizeof (ns_info->nguid))) {
709 1 : info->nguid = g_malloc0 (sizeof (ns_info->nguid) * 2 + 1);
710 17 : for (i = 0; i < sizeof (ns_info->nguid); i++)
711 16 : snprintf (info->nguid + i * 2, 3, "%02x", ns_info->nguid[i]);
712 : }
713 1 : if (info->eui64 == NULL && !_nvme_a_is_zero (ns_info->eui64, sizeof (ns_info->eui64))) {
714 0 : info->eui64 = g_malloc0 (sizeof (ns_info->eui64) * 2 + 1);
715 0 : for (i = 0; i < sizeof (ns_info->eui64); i++)
716 0 : snprintf (info->eui64 + i * 2, 3, "%02x", ns_info->eui64[i]);
717 : }
718 1 : if (ret_ns_ind == 0) {
719 1 : if ((ns_info_ind->nsfeat & 1 << 4) == 1 << 4)
720 0 : info->features |= BD_NVME_NS_FEAT_ROTATIONAL;
721 : }
722 :
723 : /* translate the LBA Format array */
724 1 : ptr_array = g_ptr_array_new ();
725 1 : nvme_id_ns_flbas_to_lbaf_inuse (ns_info->flbas, &flbas);
726 2 : for (i = 0; i <= ns_info->nlbaf + ns_info->nulbaf; i++) {
727 1 : BDNVMELBAFormat *lbaf = g_new0 (BDNVMELBAFormat, 1);
728 1 : lbaf->data_size = 1 << ns_info->lbaf[i].ds;
729 1 : lbaf->metadata_size = GUINT16_FROM_LE (ns_info->lbaf[i].ms);
730 1 : lbaf->relative_performance = ns_info->lbaf[i].rp + 1;
731 1 : g_ptr_array_add (ptr_array, lbaf);
732 1 : if (i == flbas) {
733 1 : info->current_lba_format.data_size = lbaf->data_size;
734 1 : info->current_lba_format.metadata_size = lbaf->metadata_size;
735 1 : info->current_lba_format.relative_performance = lbaf->relative_performance;
736 : }
737 : }
738 1 : g_ptr_array_add (ptr_array, NULL); /* trailing NULL element */
739 1 : info->lba_formats = (BDNVMELBAFormat **) g_ptr_array_free (ptr_array, FALSE);
740 :
741 1 : free (ctrl_id);
742 1 : free (ns_info);
743 1 : free (ns_info_ind);
744 1 : free (descs);
745 1 : return info;
746 : }
747 :
748 :
749 : /**
750 : * bd_nvme_get_smart_log:
751 : * @device: a NVMe controller device (e.g. `/dev/nvme0`)
752 : * @error: (out) (nullable): place to store error (if any)
753 : *
754 : * Retrieves drive SMART and general health information (Log Identifier `02h`).
755 : * The information provided is over the life of the controller and is retained across power cycles.
756 : *
757 : * Returns: (transfer full): health log data or %NULL in case of an error (with @error set).
758 : *
759 : * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_INFO
760 : */
761 2 : BDNVMESmartLog * bd_nvme_get_smart_log (const gchar *device, GError **error) {
762 : int ret;
763 : int ret_identify;
764 : int fd;
765 : struct nvme_id_ctrl *ctrl_id;
766 : struct nvme_smart_log *smart_log;
767 : BDNVMESmartLog *log;
768 : guint i;
769 :
770 : /* open the block device */
771 2 : fd = _open_dev (device, error);
772 2 : if (fd < 0)
773 1 : return NULL;
774 :
775 : /* send the NVME_IDENTIFY_CNS_CTRL ioctl */
776 1 : ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
777 1 : if (!ctrl_id) {
778 0 : close (fd);
779 0 : return NULL;
780 : }
781 1 : ret_identify = nvme_identify_ctrl (fd, ctrl_id);
782 1 : if (ret_identify != 0) {
783 0 : _nvme_status_to_error (ret_identify, FALSE, error);
784 0 : g_prefix_error (error, "NVMe Identify Controller command error: ");
785 0 : close (fd);
786 0 : free (ctrl_id);
787 0 : return NULL;
788 : }
789 :
790 : /* send the NVME_LOG_LID_SMART ioctl */
791 1 : smart_log = _nvme_alloc (sizeof (struct nvme_smart_log), error);
792 1 : if (!smart_log) {
793 0 : close (fd);
794 0 : free (ctrl_id);
795 0 : return NULL;
796 : }
797 1 : ret = nvme_get_log_smart (fd, NVME_NSID_ALL, FALSE /* rae */, smart_log);
798 1 : if (ret != 0) {
799 0 : _nvme_status_to_error (ret, FALSE, error);
800 0 : g_prefix_error (error, "NVMe Get Log Page - SMART / Health Information Log command error: ");
801 0 : close (fd);
802 0 : free (ctrl_id);
803 0 : free (smart_log);
804 0 : return NULL;
805 : }
806 1 : close (fd);
807 :
808 1 : log = g_new0 (BDNVMESmartLog, 1);
809 1 : if ((smart_log->critical_warning & NVME_SMART_CRIT_SPARE) == NVME_SMART_CRIT_SPARE)
810 0 : log->critical_warning |= BD_NVME_SMART_CRITICAL_WARNING_SPARE;
811 1 : if ((smart_log->critical_warning & NVME_SMART_CRIT_TEMPERATURE) == NVME_SMART_CRIT_TEMPERATURE)
812 0 : log->critical_warning |= BD_NVME_SMART_CRITICAL_WARNING_TEMPERATURE;
813 1 : if ((smart_log->critical_warning & NVME_SMART_CRIT_DEGRADED) == NVME_SMART_CRIT_DEGRADED)
814 0 : log->critical_warning |= BD_NVME_SMART_CRITICAL_WARNING_DEGRADED;
815 1 : if ((smart_log->critical_warning & NVME_SMART_CRIT_MEDIA) == NVME_SMART_CRIT_MEDIA)
816 0 : log->critical_warning |= BD_NVME_SMART_CRITICAL_WARNING_READONLY;
817 1 : if ((smart_log->critical_warning & NVME_SMART_CRIT_VOLATILE_MEMORY) == NVME_SMART_CRIT_VOLATILE_MEMORY)
818 0 : log->critical_warning |= BD_NVME_SMART_CRITICAL_WARNING_VOLATILE_MEM;
819 1 : if ((smart_log->critical_warning & NVME_SMART_CRIT_PMR_RO) == NVME_SMART_CRIT_PMR_RO)
820 0 : log->critical_warning |= BD_NVME_SMART_CRITICAL_WARNING_PMR_READONLY;
821 1 : log->avail_spare = smart_log->avail_spare;
822 1 : log->spare_thresh = smart_log->spare_thresh;
823 1 : log->percent_used = smart_log->percent_used;
824 1 : log->total_data_read = int128_to_guint64 (smart_log->data_units_read) * 1000 * 512;
825 1 : log->total_data_written = int128_to_guint64 (smart_log->data_units_written) * 1000 * 512;
826 1 : log->ctrl_busy_time = int128_to_guint64 (smart_log->ctrl_busy_time);
827 1 : log->power_cycles = int128_to_guint64 (smart_log->power_cycles);
828 1 : log->power_on_hours = int128_to_guint64 (smart_log->power_on_hours);
829 1 : log->unsafe_shutdowns = int128_to_guint64 (smart_log->unsafe_shutdowns);
830 1 : log->media_errors = int128_to_guint64 (smart_log->media_errors);
831 1 : log->num_err_log_entries = int128_to_guint64 (smart_log->num_err_log_entries);
832 :
833 1 : log->temperature = (smart_log->temperature[1] << 8) | smart_log->temperature[0];
834 9 : for (i = 0; i < G_N_ELEMENTS (smart_log->temp_sensor); i++)
835 8 : log->temp_sensors[i] = GUINT16_FROM_LE (smart_log->temp_sensor[i]);
836 1 : log->warning_temp_time = GUINT32_FROM_LE (smart_log->warning_temp_time);
837 1 : log->critical_temp_time = GUINT32_FROM_LE (smart_log->critical_comp_time);
838 :
839 1 : if (ret_identify == 0) {
840 1 : log->wctemp = GUINT16_FROM_LE (ctrl_id->wctemp);
841 1 : log->cctemp = GUINT16_FROM_LE (ctrl_id->cctemp);
842 : }
843 :
844 : /* FIXME: intentionally not providing Host Controlled Thermal Management attributes
845 : * at the moment (an optional NVMe feature), along with intentionally not providing
846 : * Power State attributes. Subject to re-evaluation in the future.
847 : */
848 :
849 1 : free (ctrl_id);
850 1 : free (smart_log);
851 1 : return log;
852 : }
853 :
854 :
855 : /**
856 : * bd_nvme_get_error_log_entries:
857 : * @device: a NVMe controller device (e.g. `/dev/nvme0`)
858 : * @error: (out) (nullable): place to store error (if any)
859 : *
860 : * Retrieves Error Information Log (Log Identifier `01h`) entries, used to describe
861 : * extended error information for a command that completed with error or to report
862 : * an error that is not specific to a particular command. This log is global to the
863 : * controller. The ordering of the entries is based on the time when the error
864 : * occurred, with the most recent error being returned as the first log entry.
865 : * As the number of entries is typically limited by the drive implementation, only
866 : * most recent entries are provided.
867 : *
868 : * Returns: (transfer full) (array zero-terminated=1): null-terminated list
869 : * of error entries or %NULL in case of an error (with @error set).
870 : *
871 : * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_INFO
872 : */
873 2 : BDNVMEErrorLogEntry ** bd_nvme_get_error_log_entries (const gchar *device, GError **error) {
874 : int ret;
875 : int fd;
876 : guint elpe;
877 : struct nvme_id_ctrl *ctrl_id;
878 : struct nvme_error_log_page *err_log;
879 : GPtrArray *ptr_array;
880 : guint i;
881 :
882 : /* open the block device */
883 2 : fd = _open_dev (device, error);
884 2 : if (fd < 0)
885 1 : return NULL;
886 :
887 : /* find out the maximum number of error log entries as reported by the controller */
888 1 : ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
889 1 : if (!ctrl_id) {
890 0 : close (fd);
891 0 : return NULL;
892 : }
893 1 : ret = nvme_identify_ctrl (fd, ctrl_id);
894 1 : if (ret != 0) {
895 0 : _nvme_status_to_error (ret, FALSE, error);
896 0 : g_prefix_error (error, "NVMe Identify Controller command error: ");
897 0 : close (fd);
898 0 : free (ctrl_id);
899 0 : return NULL;
900 : }
901 :
902 1 : elpe = ctrl_id->elpe + 1;
903 1 : free (ctrl_id);
904 :
905 : /* send the NVME_LOG_LID_ERROR ioctl */
906 1 : err_log = _nvme_alloc (sizeof (struct nvme_error_log_page) * elpe, error);
907 1 : if (!err_log) {
908 0 : close (fd);
909 0 : return NULL;
910 : }
911 1 : ret = nvme_get_log_error (fd, elpe, FALSE /* rae */, err_log);
912 1 : if (ret != 0) {
913 0 : _nvme_status_to_error (ret, FALSE, error);
914 0 : g_prefix_error (error, "NVMe Get Log Page - Error Information Log Entry command error: ");
915 0 : close (fd);
916 0 : free (err_log);
917 0 : return NULL;
918 : }
919 1 : close (fd);
920 :
921 : /* parse the log */
922 1 : ptr_array = g_ptr_array_new ();
923 129 : for (i = 0; i < elpe; i++) {
924 128 : if (GUINT64_FROM_LE (err_log[i].error_count) > 0) {
925 : BDNVMEErrorLogEntry *entry;
926 :
927 0 : entry = g_new0 (BDNVMEErrorLogEntry, 1);
928 0 : entry->error_count = GUINT64_FROM_LE (err_log[i].error_count);
929 0 : entry->command_id = err_log[i].cmdid;
930 0 : entry->command_specific = GUINT64_FROM_LE (err_log[i].cs);
931 0 : entry->command_status = GUINT16_FROM_LE (err_log[i].status_field) >> 1;
932 0 : _nvme_status_to_error (GUINT16_FROM_LE (err_log[i].status_field) >> 1, FALSE, &entry->command_error);
933 0 : entry->lba = GUINT64_FROM_LE (err_log[i].lba);
934 0 : entry->nsid = err_log[i].nsid;
935 0 : entry->transport_type = err_log[i].trtype;
936 : /* not providing Transport Type Specific Information here on purpose */
937 :
938 0 : g_ptr_array_add (ptr_array, entry);
939 : }
940 : }
941 1 : g_ptr_array_add (ptr_array, NULL); /* trailing NULL element */
942 1 : free (err_log);
943 :
944 1 : return (BDNVMEErrorLogEntry **) g_ptr_array_free (ptr_array, FALSE);
945 : }
946 :
947 :
948 : /**
949 : * bd_nvme_get_self_test_log:
950 : * @device: a NVMe controller device (e.g. `/dev/nvme0`)
951 : * @error: (out) (nullable): place to store error (if any)
952 : *
953 : * Retrieves drive self-test log (Log Identifier `06h`). Provides the status of a self-test operation
954 : * in progress and the percentage complete of that operation, along with the results of the last
955 : * 20 device self-test operations.
956 : *
957 : * Returns: (transfer full): self-test log data or %NULL in case of an error (with @error set).
958 : *
959 : * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_INFO
960 : */
961 2 : BDNVMESelfTestLog * bd_nvme_get_self_test_log (const gchar *device, GError **error) {
962 : int ret;
963 : int fd;
964 : struct nvme_self_test_log *self_test_log;
965 : BDNVMESelfTestLog *log;
966 : GPtrArray *ptr_array;
967 : guint i;
968 :
969 : /* open the block device */
970 2 : fd = _open_dev (device, error);
971 2 : if (fd < 0)
972 1 : return NULL;
973 :
974 : /* send the NVME_LOG_LID_DEVICE_SELF_TEST ioctl */
975 1 : self_test_log = _nvme_alloc (sizeof (struct nvme_self_test_log), error);
976 1 : if (!self_test_log) {
977 0 : close (fd);
978 0 : return NULL;
979 : }
980 1 : ret = nvme_get_log_device_self_test (fd, self_test_log);
981 1 : if (ret != 0) {
982 1 : _nvme_status_to_error (ret, FALSE, error);
983 1 : g_prefix_error (error, "NVMe Get Log Page - Device Self-test Log command error: ");
984 1 : close (fd);
985 1 : free (self_test_log);
986 1 : return NULL;
987 : }
988 0 : close (fd);
989 :
990 0 : log = g_new0 (BDNVMESelfTestLog, 1);
991 0 : switch (self_test_log->current_operation & NVME_ST_CURR_OP_MASK) {
992 0 : case NVME_ST_CURR_OP_NOT_RUNNING:
993 0 : log->current_operation = BD_NVME_SELF_TEST_ACTION_NOT_RUNNING;
994 0 : break;
995 0 : case NVME_ST_CURR_OP_SHORT:
996 0 : log->current_operation = BD_NVME_SELF_TEST_ACTION_SHORT;
997 0 : break;
998 0 : case NVME_ST_CURR_OP_EXTENDED:
999 0 : log->current_operation = BD_NVME_SELF_TEST_ACTION_EXTENDED;
1000 0 : break;
1001 0 : case NVME_ST_CURR_OP_VS:
1002 : case NVME_ST_CURR_OP_RESERVED:
1003 : default:
1004 0 : log->current_operation = BD_NVME_SELF_TEST_ACTION_VENDOR_SPECIFIC;
1005 : }
1006 0 : if ((self_test_log->current_operation & NVME_ST_CURR_OP_MASK) > 0)
1007 0 : log->current_operation_completion = self_test_log->completion & NVME_ST_CURR_OP_CMPL_MASK;
1008 :
1009 0 : ptr_array = g_ptr_array_new ();
1010 0 : for (i = 0; i < NVME_LOG_ST_MAX_RESULTS; i++) {
1011 : BDNVMESelfTestLogEntry *entry;
1012 : guint8 dsts;
1013 : guint8 code;
1014 :
1015 0 : dsts = self_test_log->result[i].dsts & NVME_ST_RESULT_MASK;
1016 0 : code = self_test_log->result[i].dsts >> NVME_ST_CODE_SHIFT;
1017 0 : if (dsts == NVME_ST_RESULT_NOT_USED)
1018 0 : continue;
1019 :
1020 0 : entry = g_new0 (BDNVMESelfTestLogEntry, 1);
1021 0 : switch (dsts) {
1022 0 : case NVME_ST_RESULT_NO_ERR:
1023 0 : entry->result = BD_NVME_SELF_TEST_RESULT_NO_ERROR;
1024 0 : break;
1025 0 : case NVME_ST_RESULT_ABORTED:
1026 0 : entry->result = BD_NVME_SELF_TEST_RESULT_ABORTED;
1027 0 : break;
1028 0 : case NVME_ST_RESULT_CLR:
1029 0 : entry->result = BD_NVME_SELF_TEST_RESULT_CTRL_RESET;
1030 0 : break;
1031 0 : case NVME_ST_RESULT_NS_REMOVED:
1032 0 : entry->result = BD_NVME_SELF_TEST_RESULT_NS_REMOVED;
1033 0 : break;
1034 0 : case NVME_ST_RESULT_ABORTED_FORMAT:
1035 0 : entry->result = BD_NVME_SELF_TEST_RESULT_ABORTED_FORMAT;
1036 0 : break;
1037 0 : case NVME_ST_RESULT_FATAL_ERR:
1038 0 : entry->result = BD_NVME_SELF_TEST_RESULT_FATAL_ERROR;
1039 0 : break;
1040 0 : case NVME_ST_RESULT_UNKNOWN_SEG_FAIL:
1041 0 : entry->result = BD_NVME_SELF_TEST_RESULT_UNKNOWN_SEG_FAIL;
1042 0 : break;
1043 0 : case NVME_ST_RESULT_KNOWN_SEG_FAIL:
1044 0 : entry->result = BD_NVME_SELF_TEST_RESULT_KNOWN_SEG_FAIL;
1045 0 : break;
1046 0 : case NVME_ST_RESULT_ABORTED_UNKNOWN:
1047 0 : entry->result = BD_NVME_SELF_TEST_RESULT_ABORTED_UNKNOWN;
1048 0 : break;
1049 0 : case NVME_ST_RESULT_ABORTED_SANITIZE:
1050 0 : entry->result = BD_NVME_SELF_TEST_RESULT_ABORTED_SANITIZE;
1051 0 : break;
1052 0 : default:
1053 0 : bd_utils_log_format (BD_UTILS_LOG_WARNING, "Unhandled self-test log entry result code: %d", dsts);
1054 0 : g_free (entry);
1055 0 : continue;
1056 : }
1057 0 : switch (code) {
1058 0 : case NVME_ST_CODE_SHORT:
1059 0 : entry->action = BD_NVME_SELF_TEST_ACTION_SHORT;
1060 0 : break;
1061 0 : case NVME_ST_CODE_EXTENDED:
1062 0 : entry->action = BD_NVME_SELF_TEST_ACTION_EXTENDED;
1063 0 : break;
1064 0 : case NVME_ST_CODE_VS:
1065 : case NVME_ST_CODE_RESERVED:
1066 0 : entry->action = BD_NVME_SELF_TEST_ACTION_VENDOR_SPECIFIC;
1067 0 : break;
1068 0 : default:
1069 0 : bd_utils_log_format (BD_UTILS_LOG_WARNING, "Unhandled self-test log entry action code: %d", code);
1070 0 : entry->action = BD_NVME_SELF_TEST_ACTION_VENDOR_SPECIFIC;
1071 : }
1072 0 : entry->segment = self_test_log->result[i].seg;
1073 0 : entry->power_on_hours = GUINT64_FROM_LE (self_test_log->result[i].poh);
1074 0 : if (self_test_log->result[i].vdi & NVME_ST_VALID_DIAG_INFO_NSID)
1075 0 : entry->nsid = GUINT32_FROM_LE (self_test_log->result[i].nsid);
1076 0 : if (self_test_log->result[i].vdi & NVME_ST_VALID_DIAG_INFO_FLBA)
1077 0 : entry->failing_lba = GUINT64_FROM_LE (self_test_log->result[i].flba);
1078 0 : if ((self_test_log->result[i].vdi & NVME_ST_VALID_DIAG_INFO_SC) &&
1079 0 : (self_test_log->result[i].vdi & NVME_ST_VALID_DIAG_INFO_SCT))
1080 0 : _nvme_status_to_error ((self_test_log->result[i].sct & 7) << 8 | self_test_log->result[i].sc,
1081 : FALSE, &entry->status_code_error);
1082 :
1083 0 : g_ptr_array_add (ptr_array, entry);
1084 : }
1085 0 : g_ptr_array_add (ptr_array, NULL);
1086 0 : log->entries = (BDNVMESelfTestLogEntry **) g_ptr_array_free (ptr_array, FALSE);
1087 0 : free (self_test_log);
1088 :
1089 0 : return log;
1090 : }
1091 :
1092 :
1093 : /**
1094 : * bd_nvme_get_sanitize_log:
1095 : * @device: a NVMe controller device (e.g. `/dev/nvme0`)
1096 : * @error: (out) (nullable): place to store error (if any)
1097 : *
1098 : * Retrieves the drive sanitize status log (Log Identifier `81h`) that includes information
1099 : * about the most recent sanitize operation and the sanitize operation time estimates.
1100 : *
1101 : * As advised in the NVMe specification whitepaper the host should limit polling
1102 : * to retrieve progress of a running sanitize operations (e.g. to at most once every
1103 : * several minutes) to avoid interfering with the progress of the sanitize operation itself.
1104 : *
1105 : * Returns: (transfer full): sanitize log data or %NULL in case of an error (with @error set).
1106 : *
1107 : * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_INFO
1108 : */
1109 3 : BDNVMESanitizeLog * bd_nvme_get_sanitize_log (const gchar *device, GError **error) {
1110 : int ret;
1111 : int fd;
1112 : struct nvme_sanitize_log_page *sanitize_log;
1113 : BDNVMESanitizeLog *log;
1114 : __u16 sstat;
1115 :
1116 : /* open the block device */
1117 3 : fd = _open_dev (device, error);
1118 3 : if (fd < 0)
1119 1 : return NULL;
1120 :
1121 : /* send the NVME_LOG_LID_SANITIZE ioctl */
1122 2 : sanitize_log = _nvme_alloc (sizeof (struct nvme_sanitize_log_page), error);
1123 2 : if (!sanitize_log) {
1124 0 : close (fd);
1125 0 : return NULL;
1126 : }
1127 2 : ret = nvme_get_log_sanitize (fd, FALSE /* rae */, sanitize_log);
1128 2 : if (ret != 0) {
1129 2 : _nvme_status_to_error (ret, FALSE, error);
1130 2 : g_prefix_error (error, "NVMe Get Log Page - Sanitize Status Log command error: ");
1131 2 : close (fd);
1132 2 : free (sanitize_log);
1133 2 : return NULL;
1134 : }
1135 0 : close (fd);
1136 :
1137 0 : log = g_new0 (BDNVMESanitizeLog, 1);
1138 0 : log->sanitize_progress = 0;
1139 0 : sstat = GUINT16_FROM_LE (sanitize_log->sstat);
1140 0 : if ((sstat & NVME_SANITIZE_SSTAT_STATUS_MASK) == NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS)
1141 0 : log->sanitize_progress = ((gdouble) GUINT16_FROM_LE (sanitize_log->sprog) * 100) / 0x10000;
1142 0 : log->global_data_erased = sstat & NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED;
1143 0 : log->overwrite_passes = (sstat >> NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT) & NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK;
1144 :
1145 0 : switch (sstat & NVME_SANITIZE_SSTAT_STATUS_MASK) {
1146 0 : case NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS:
1147 0 : log->sanitize_status = BD_NVME_SANITIZE_STATUS_SUCCESS;
1148 0 : break;
1149 0 : case NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS:
1150 0 : log->sanitize_status = BD_NVME_SANITIZE_STATUS_IN_PROGRESS;
1151 0 : break;
1152 0 : case NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED:
1153 0 : log->sanitize_status = BD_NVME_SANITIZE_STATUS_FAILED;
1154 0 : break;
1155 0 : case NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS:
1156 0 : log->sanitize_status = BD_NVME_SANITIZE_STATUS_SUCCESS_NO_DEALLOC;
1157 0 : break;
1158 0 : case NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED:
1159 : default:
1160 0 : log->sanitize_status = BD_NVME_SANITIZE_STATUS_NEVER_SANITIZED;
1161 0 : break;
1162 : }
1163 :
1164 0 : log->time_for_overwrite = (GUINT32_FROM_LE (sanitize_log->eto) == 0xffffffff) ? -1 : (gint64) GUINT32_FROM_LE (sanitize_log->eto);
1165 0 : log->time_for_block_erase = (GUINT32_FROM_LE (sanitize_log->etbe) == 0xffffffff) ? -1 : (gint64) GUINT32_FROM_LE (sanitize_log->etbe);
1166 0 : log->time_for_crypto_erase = (GUINT32_FROM_LE (sanitize_log->etce) == 0xffffffff) ? -1 : (gint64) GUINT32_FROM_LE (sanitize_log->etce);
1167 0 : log->time_for_overwrite_nd = (GUINT32_FROM_LE (sanitize_log->etond) == 0xffffffff) ? -1 : (gint64) GUINT32_FROM_LE (sanitize_log->etond);
1168 0 : log->time_for_block_erase_nd = (GUINT32_FROM_LE (sanitize_log->etbend) == 0xffffffff) ? -1 : (gint64) GUINT32_FROM_LE (sanitize_log->etbend);
1169 0 : log->time_for_crypto_erase_nd = (GUINT32_FROM_LE (sanitize_log->etcend) == 0xffffffff) ? -1 : (gint64) GUINT32_FROM_LE (sanitize_log->etcend);
1170 :
1171 0 : free (sanitize_log);
1172 0 : return log;
1173 : }
|