LCOV - code coverage report
Current view: top level - plugins/nvme - nvme-info.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 43.1 % 620 267
Test Date: 2026-01-23 09:12:16 Functions: 44.8 % 29 13
Legend: Lines: hit not hit

            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              : }
        

Generated by: LCOV version 2.0-1