LCOV - code coverage report
Current view: top level - plugins/nvme - nvme-op.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 63.6 % 162 103
Test Date: 2026-01-23 09:12:16 Functions: 100.0 % 4 4
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              : #include <linux/fs.h>
      30              : 
      31              : #include <libnvme.h>
      32              : 
      33              : #include <blockdev/utils.h>
      34              : #include <check_deps.h>
      35              : #include "nvme.h"
      36              : #include "nvme-private.h"
      37              : 
      38              : 
      39              : /**
      40              :  * bd_nvme_device_self_test:
      41              :  * @device: a NVMe controller or namespace device (e.g. `/dev/nvme0`)
      42              :  * @action: self-test action to take.
      43              :  * @error: (out) (nullable): place to store error (if any)
      44              :  *
      45              :  * Initiates or aborts the Device Self-test operation on the controller or a namespace,
      46              :  * distinguished by the @device path specified. In case a controller device
      47              :  * is specified then the self-test operation would include all active namespaces.
      48              :  *
      49              :  * To abort a running operation, pass #BD_NVME_SELF_TEST_ACTION_ABORT as @action.
      50              :  * To retrieve progress of a current running operation, check the self-test log using
      51              :  * bd_nvme_get_self_test_log().
      52              :  *
      53              :  * Returns: %TRUE if the device self-test command was issued successfully,
      54              :  *          %FALSE otherwise with @error set.
      55              :  *
      56              :  * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_MANAGE
      57              :  */
      58           11 : gboolean bd_nvme_device_self_test (const gchar *device, BDNVMESelfTestAction action, GError **error) {
      59              :     int ret;
      60           11 :     struct nvme_dev_self_test_args args = {
      61              :         .args_size = sizeof(args),
      62              :         .result = NULL,
      63              :         .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
      64              :         .nsid = 0xffffffff,
      65              :     };
      66              : 
      67           11 :     switch (action) {
      68            3 :         case BD_NVME_SELF_TEST_ACTION_SHORT:
      69            3 :             args.stc = NVME_DST_STC_SHORT;
      70            3 :             break;
      71            2 :         case BD_NVME_SELF_TEST_ACTION_EXTENDED:
      72            2 :             args.stc = NVME_DST_STC_LONG;
      73            2 :             break;
      74            2 :         case BD_NVME_SELF_TEST_ACTION_VENDOR_SPECIFIC:
      75            2 :             args.stc = NVME_DST_STC_VS;
      76            2 :             break;
      77            2 :         case BD_NVME_SELF_TEST_ACTION_ABORT:
      78            2 :             args.stc = NVME_DST_STC_ABORT;
      79            2 :             break;
      80            2 :         default:
      81            2 :             g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
      82              :                          "Invalid value specified for the self-test action: %d", action);
      83            2 :             return FALSE;
      84              :     }
      85              : 
      86              :     /* open the block device */
      87            9 :     args.fd = _open_dev (device, error);
      88            9 :     if (args.fd < 0)
      89            1 :         return FALSE;
      90              : 
      91              :     /* get Namespace Identifier (NSID) for the @device (NVME_IOCTL_ID) */
      92            8 :     ret = nvme_get_nsid (args.fd, &args.nsid);
      93            8 :     if (ret < 0 && errno == ENOTTY)
      94              :         /* not a block device, assuming controller character device */
      95            4 :         args.nsid = 0xffffffff;
      96            4 :     else if (ret != 0) {
      97            0 :         _nvme_status_to_error (ret, FALSE, error);
      98            0 :         g_prefix_error (error, "Error getting Namespace Identifier (NSID): ");
      99            0 :         close (args.fd);
     100            0 :         return FALSE;
     101              :     }
     102              : 
     103            8 :     ret = nvme_dev_self_test (&args);
     104            8 :     if (ret != 0) {
     105            8 :         _nvme_status_to_error (ret, FALSE, error);
     106            8 :         g_prefix_error (error, "NVMe Device Self-test command error: ");
     107            8 :         close (args.fd);
     108            8 :         return FALSE;
     109              :     }
     110            0 :     close (args.fd);
     111              : 
     112            0 :     return TRUE;
     113              : }
     114              : 
     115              : 
     116              : /* returns 0xff in case of error (the NVMe standard defines total of 16 flba records) */
     117            4 : static __u8 find_lbaf_for_size (int fd, __u32 nsid, guint16 lba_data_size, guint16 metadata_size, GError **error) {
     118              :     int ret;
     119              :     struct nvme_id_ns *ns_info;
     120            4 :     __u8 flbas = 0;
     121              :     guint i;
     122              : 
     123              :     /* TODO: find first attached namespace instead of hardcoding NSID = 1 */
     124            4 :     ns_info = _nvme_alloc (sizeof (struct nvme_id_ns), error);
     125            4 :     if (!ns_info)
     126            0 :         return 0xff;
     127            4 :     ret = nvme_identify_ns (fd, nsid == 0xffffffff ? 1 : nsid, ns_info);
     128            4 :     if (ret != 0) {
     129            0 :         _nvme_status_to_error (ret, FALSE, error);
     130            0 :         g_prefix_error (error, "NVMe Identify Namespace command error: ");
     131            0 :         free (ns_info);
     132            0 :         return 0xff;
     133              :     }
     134              : 
     135              :     /* return currently used lbaf */
     136            4 :     if (lba_data_size == 0) {
     137            2 :         nvme_id_ns_flbas_to_lbaf_inuse (ns_info->flbas, &flbas);
     138            2 :         free (ns_info);
     139            2 :         return flbas;
     140              :     }
     141              : 
     142            4 :     for (i = 0; i <= ns_info->nlbaf + ns_info->nulbaf; i++)
     143            2 :         if (1UL << ns_info->lbaf[i].ds == lba_data_size && GUINT16_FROM_LE (ns_info->lbaf[i].ms) == metadata_size) {
     144            0 :             free (ns_info);
     145            0 :             return i;
     146              :         }
     147              : 
     148            2 :     g_set_error_literal (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
     149              :                          "Couldn't match desired LBA data block size in a device supported LBA format data sizes");
     150            2 :     free (ns_info);
     151            2 :     return 0xff;
     152              : }
     153              : 
     154              : /**
     155              :  * bd_nvme_format:
     156              :  * @device: NVMe namespace or controller device to format (e.g. `/dev/nvme0n1`)
     157              :  * @lba_data_size: desired LBA data size (i.e. a sector size) in bytes or `0` to keep current. See #BDNVMELBAFormat and bd_nvme_get_namespace_info().
     158              :  * @metadata_size: desired metadata size in bytes or `0` for default. See #BDNVMELBAFormat and bd_nvme_get_namespace_info().
     159              :  * @secure_erase: optional secure erase action to take.
     160              :  * @error: (out) (nullable): place to store error (if any)
     161              :  *
     162              :  * Performs low level format of the NVM media, destroying all data and metadata for either
     163              :  * a specific namespace or all attached namespaces to the controller. Use this command
     164              :  * to change LBA sector size. Optional secure erase method can be specified as well.
     165              :  *
     166              :  * Supported LBA data sizes for a given namespace can be listed using the bd_nvme_get_namespace_info()
     167              :  * call. In case of a special value `0` the current LBA format for a given namespace will be
     168              :  * retained. When called on a controller device the first namespace is used as a reference.
     169              :  *
     170              :  * Note that the NVMe controller may define a Format NVM attribute indicating that the format
     171              :  * operation would apply to all namespaces and a format (excluding secure erase) of any
     172              :  * namespace results in a format of all namespaces in the NVM subsystem. In such case and
     173              :  * when @device is a namespace block device the #BD_NVME_ERROR_WOULD_FORMAT_ALL_NS error
     174              :  * is returned to prevent further damage. This is then supposed to be handled by the caller
     175              :  * and bd_nvme_format() is supposed to be called on a controller device instead.
     176              :  *
     177              :  * This call blocks until the format operation has finished. To retrieve progress
     178              :  * of a current running operation, check the namespace info using bd_nvme_get_namespace_info().
     179              :  *
     180              :  * Returns: %TRUE if the format command finished successfully, %FALSE otherwise with @error set.
     181              :  *
     182              :  * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_MANAGE
     183              :  */
     184            5 : gboolean bd_nvme_format (const gchar *device, guint16 lba_data_size, guint16 metadata_size, BDNVMEFormatSecureErase secure_erase, GError **error) {
     185              :     int ret;
     186            5 :     gboolean ctrl_device = FALSE;
     187              :     struct nvme_id_ctrl *ctrl_id;
     188            5 :     struct nvme_format_nvm_args args = {
     189              :         .args_size = sizeof(args),
     190              :         .result = NULL,
     191              :         .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
     192              :         .nsid = 0xffffffff,
     193              :         .mset = NVME_FORMAT_MSET_SEPARATE /* 0 */,
     194              :         .pi = NVME_FORMAT_PI_DISABLE /* 0 */,
     195              :         .pil = NVME_FORMAT_PIL_LAST /* 0 */,
     196              :         .ses = NVME_FORMAT_SES_NONE,
     197              :     };
     198              : 
     199              :     /* open the block device */
     200            5 :     args.fd = _open_dev (device, error);
     201            5 :     if (args.fd < 0)
     202            1 :         return FALSE;
     203              : 
     204            4 :     ret = nvme_get_nsid (args.fd, &args.nsid);
     205            4 :     if (ret < 0 && errno == ENOTTY) {
     206              :         /* not a block device, assuming controller character device */
     207            2 :         args.nsid = 0xffffffff;
     208            2 :         ctrl_device = TRUE;
     209            2 :     } else if (ret != 0) {
     210            0 :         _nvme_status_to_error (ret, FALSE, error);
     211            0 :         g_prefix_error (error, "Error getting Namespace Identifier (NSID): ");
     212            0 :         close (args.fd);
     213            0 :         return FALSE;
     214              :     }
     215              : 
     216              :     /* check the FNA controller bit when formatting a single namespace */
     217            4 :     if (! ctrl_device) {
     218            2 :         ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
     219            2 :         if (!ctrl_id) {
     220            0 :             close (args.fd);
     221            0 :             return FALSE;
     222              :         }
     223            2 :         ret = nvme_identify_ctrl (args.fd, ctrl_id);
     224            2 :         if (ret != 0) {
     225            0 :             _nvme_status_to_error (ret, FALSE, error);
     226            0 :             g_prefix_error (error, "NVMe Identify Controller command error: ");
     227            0 :             close (args.fd);
     228            0 :             free (ctrl_id);
     229            0 :             return FALSE;
     230              :         }
     231              :         /* from nvme-cli:
     232              :          * FNA bit 0 set to 1: all namespaces ... shall be configured with the same
     233              :          * attributes and a format (excluding secure erase) of any namespace results in a
     234              :          * format of all namespaces.
     235              :          */
     236            2 :         if ((ctrl_id->fna & NVME_CTRL_FNA_FMT_ALL_NAMESPACES) == NVME_CTRL_FNA_FMT_ALL_NAMESPACES) {
     237              :             /* tell user that it would format other namespaces and that bd_nvme_format()
     238              :              * should be called on a controller device instead */
     239            0 :             g_set_error_literal (error, BD_NVME_ERROR, BD_NVME_ERROR_WOULD_FORMAT_ALL_NS,
     240              :                          "The NVMe controller indicates it would format all namespaces.");
     241            0 :             close (args.fd);
     242            0 :             free (ctrl_id);
     243            0 :             return FALSE;
     244              :         }
     245            2 :         free (ctrl_id);
     246              :     }
     247              : 
     248              :     /* find out the desired LBA data format index */
     249            4 :     args.lbaf = find_lbaf_for_size (args.fd, args.nsid, lba_data_size, metadata_size, error);
     250            4 :     if (args.lbaf == 0xff) {
     251            2 :         close (args.fd);
     252            2 :         return FALSE;
     253              :     }
     254              : 
     255            2 :     switch (secure_erase) {
     256            0 :         case BD_NVME_FORMAT_SECURE_ERASE_USER_DATA:
     257            0 :             args.ses = NVME_FORMAT_SES_USER_DATA_ERASE;
     258            0 :             break;
     259            0 :         case BD_NVME_FORMAT_SECURE_ERASE_CRYPTO:
     260            0 :             args.ses = NVME_FORMAT_SES_CRYPTO_ERASE;
     261            0 :             break;
     262            2 :         case BD_NVME_FORMAT_SECURE_ERASE_NONE:
     263              :         default:
     264            2 :             args.ses = NVME_FORMAT_SES_NONE;
     265              :     }
     266              : 
     267            2 :     ret = nvme_format_nvm (&args);
     268            2 :     if (ret != 0) {
     269            2 :         _nvme_status_to_error (ret, FALSE, error);
     270            2 :         g_prefix_error (error, "Format NVM command error: ");
     271            2 :         close (args.fd);
     272            2 :         return FALSE;
     273              :     }
     274              : 
     275              :     /* rescan the namespaces if block size has changed */
     276            0 :     if (ctrl_device) {
     277            0 :         if (ioctl (args.fd, NVME_IOCTL_RESCAN) < 0) {
     278            0 :             g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
     279            0 :                          "Failed to rescan namespaces after format: %s", strerror_l (errno, _C_LOCALE));
     280            0 :             close (args.fd);
     281            0 :             return FALSE;
     282              :         }
     283              :     } else {
     284            0 :         if (lba_data_size != 0) {
     285              :             /* from nvme-cli:
     286              :              * If block size has been changed by the format command up there, we should notify it to
     287              :              * kernel blkdev to update its own block size to the given one because blkdev will not
     288              :              * update by itself without re-opening fd.
     289              :              */
     290            0 :             int block_size = lba_data_size;
     291              : 
     292            0 :             if (ioctl (args.fd, BLKBSZSET, &block_size) < 0) {
     293            0 :                 g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
     294            0 :                              "Failed to set block size to %d after format: %s", block_size, strerror_l (errno, _C_LOCALE));
     295            0 :                 close (args.fd);
     296            0 :                 return FALSE;
     297              :             }
     298              : 
     299            0 :             if (ioctl (args.fd, BLKRRPART) < 0) {
     300            0 :                 g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
     301            0 :                              "Failed to re-read partition table after format: %s", strerror_l (errno, _C_LOCALE));
     302            0 :                 close (args.fd);
     303            0 :                 return FALSE;
     304              :             }
     305              :         }
     306              :     }
     307              : 
     308            0 :     close (args.fd);
     309            0 :     return TRUE;
     310              : }
     311              : 
     312              : /**
     313              :  * bd_nvme_sanitize:
     314              :  * @device: NVMe namespace or controller device to format (e.g. `/dev/nvme0n1`)
     315              :  * @action: the sanitize action to perform.
     316              :  * @no_dealloc: instruct the controller to not deallocate the affected media area.
     317              :  * @overwrite_pass_count: number of overwrite passes [1-15] or 0 for the default (16 passes).
     318              :  * @overwrite_pattern: a 32-bit pattern used for the Overwrite sanitize operation.
     319              :  * @overwrite_invert_pattern: invert the overwrite pattern between passes.
     320              :  * @error: (out) (nullable): place to store error (if any)
     321              :  *
     322              :  * Starts a sanitize operation or recovers from a previously failed sanitize operation.
     323              :  * By definition, a sanitize operation alters all user data in the NVM subsystem such
     324              :  * that recovery of any previous user data from any cache, the non-volatile media,
     325              :  * or any Controller Memory Buffer is not possible. The scope of a sanitize operation
     326              :  * is all locations in the NVM subsystem that are able to contain user data, including
     327              :  * caches, Persistent Memory Regions, and unallocated or deallocated areas of the media.
     328              :  *
     329              :  * Once started, a sanitize operation is not able to be aborted and continues after
     330              :  * a Controller Level Reset including across power cycles. Once the sanitize operation
     331              :  * has run the media affected may not be immediately ready for use unless additional
     332              :  * media modification mechanism is run. This is often vendor specific and also depends
     333              :  * on the sanitize method (@action) used. Callers to this sanitize operation should
     334              :  * set @no_dealloc to %TRUE for the added convenience.
     335              :  *
     336              :  * The controller also ignores Critical Warning(s) in the SMART / Health Information
     337              :  * log page (e.g., read only mode) and attempts to complete the sanitize operation requested.
     338              :  *
     339              :  * This call returns immediately and the actual sanitize operation is performed
     340              :  * in the background. Use bd_nvme_get_sanitize_log() to retrieve status and progress
     341              :  * of a running sanitize operation. In case a sanitize operation fails the controller
     342              :  * may restrict its operation until a subsequent sanitize operation is started
     343              :  * (i.e. retried) or an #BD_NVME_SANITIZE_ACTION_EXIT_FAILURE action is used
     344              :  * to acknowledge the failure explicitly.
     345              :  *
     346              :  * The @overwrite_pass_count, @overwrite_pattern and @overwrite_invert_pattern
     347              :  * arguments are only valid when @action is #BD_NVME_SANITIZE_ACTION_OVERWRITE.
     348              :  *
     349              :  * The sanitize operation is set to run under the Allow Unrestricted Sanitize Exit
     350              :  * mode.
     351              :  *
     352              :  * Returns: %TRUE if the format command finished successfully, %FALSE otherwise with @error set.
     353              :  *
     354              :  * Tech category: %BD_NVME_TECH_NVME-%BD_NVME_TECH_MODE_MANAGE
     355              :  */
     356            9 : gboolean bd_nvme_sanitize (const gchar *device, BDNVMESanitizeAction action, gboolean no_dealloc, gint overwrite_pass_count, guint32 overwrite_pattern, gboolean overwrite_invert_pattern, GError **error) {
     357              :     int ret;
     358            9 :     struct nvme_sanitize_nvm_args args = {
     359              :         .args_size = sizeof(args),
     360              :         .result = NULL,
     361              :         .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
     362              :         .ause = TRUE,
     363              :         .owpass = overwrite_pass_count,
     364            9 :         .oipbp = overwrite_invert_pattern,
     365            9 :         .nodas = no_dealloc,
     366              :         .ovrpat = GUINT32_TO_LE (overwrite_pattern),
     367              :     };
     368              : 
     369            9 :     switch (action) {
     370            2 :         case BD_NVME_SANITIZE_ACTION_EXIT_FAILURE:
     371            2 :             args.sanact = NVME_SANITIZE_SANACT_EXIT_FAILURE;
     372            2 :             break;
     373            3 :         case BD_NVME_SANITIZE_ACTION_BLOCK_ERASE:
     374            3 :             args.sanact = NVME_SANITIZE_SANACT_START_BLOCK_ERASE;
     375            3 :             break;
     376            2 :         case BD_NVME_SANITIZE_ACTION_OVERWRITE:
     377            2 :             args.sanact = NVME_SANITIZE_SANACT_START_OVERWRITE;
     378            2 :             break;
     379            2 :         case BD_NVME_SANITIZE_ACTION_CRYPTO_ERASE:
     380            2 :             args.sanact = NVME_SANITIZE_SANACT_START_CRYPTO_ERASE;
     381            2 :             break;
     382            0 :         default:
     383            0 :             g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_INVALID_ARGUMENT,
     384              :                          "Invalid value specified for the sanitize action: %d", action);
     385            0 :             return FALSE;
     386              :     }
     387              : 
     388              :     /* open the block device */
     389            9 :     args.fd = _open_dev (device, error);
     390            9 :     if (args.fd < 0)
     391            1 :         return FALSE;
     392              : 
     393            8 :     ret = nvme_sanitize_nvm (&args);
     394            8 :     if (ret != 0) {
     395            8 :         _nvme_status_to_error (ret, FALSE, error);
     396            8 :         g_prefix_error (error, "Sanitize command error: ");
     397            8 :         close (args.fd);
     398            8 :         return FALSE;
     399              :     }
     400              : 
     401            0 :     close (args.fd);
     402            0 :     return TRUE;
     403              : }
        

Generated by: LCOV version 2.0-1