Based on Linux nvme sanitize command and kernel driver, add the storage low level block erase. This operation erases the entire device, any user-specified size smaller than the storage size will result in an error. Signed-off-by: Renaud Barbier <renaud.barbier@xxxxxxxxxx> --- drivers/nvme/host/core.c | 82 ++++++++++++++++++++++++++++++++++++++++ drivers/nvme/host/pci.c | 2 + include/nvme/types.h | 38 +++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 include/nvme/types.h diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 33a592caeb..fcc2167763 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include <common.h> +#include <nvme/types.h> #include "nvme.h" @@ -64,6 +65,72 @@ nvme_set_features(struct nvme_ctrl *dev, unsigned fid, unsigned dword11, return ret; } +static int +nvme_sanitize_poll_log(struct nvme_ctrl *ctrl) +{ + struct nvme_sanitize_log_page *sanitize_log; + int log_size = sizeof(*sanitize_log); + uint64_t start = get_time_ns(); + struct nvme_command c = { 0 }; + uint32_t numd = (log_size >> 2) - 1; + uint16_t numdl = numd & 0xffff; + int ret; + + sanitize_log = kmalloc(log_size, GFP_KERNEL); + if (!sanitize_log) + return -ENOMEM; + + memset(sanitize_log, 0, log_size); + + c.common.opcode = nvme_admin_get_log_page; + c.common.cdw10[0] = (numdl << 16) | NVME_LOG_LID_SANITIZE; + /* Poll the sanitize log for operation completion */ + do { + ret = nvme_submit_sync_cmd(ctrl, &c, sanitize_log, log_size); + if (ret) { + dev_err(ctrl->dev, "Retrieving log failed\n"); + goto out; + } + + if ((sanitize_log->sstat & 0x3) == 3) { + dev_err(ctrl->dev, "Erase failed\n"); + ret = -EIO; + goto out; + } else if (sanitize_log->sstat & 1) { + ret = 0; + goto out; + } + sanitize_log->sstat = 0; + + mdelay(1000); + } while (!is_timeout(start, 30 * SECOND)); + ret = -ETIME; +out: + if (sanitize_log) + free(sanitize_log); + return ret; +} + +/* Perform a low level block erase of the whole NVME device */ +static int +nvme_sanitize_nvm(struct nvme_ctrl *ctrl) +{ + struct nvme_command c = { 0 }; + int ret; + + /* Input arguments based on what the kernel sets up for the cmd */ + c.common.opcode = nvme_admin_sanitize_nvm; + c.common.cdw10[0] = NVME_SANITIZE_SANACT_START_BLOCK_ERASE; + ret = nvme_submit_sync_cmd(ctrl, &c, NULL, 0); + if (ret) { + dev_err(ctrl->dev, + "Failed to trigger the sanitize block erase command\n"); + return -EINVAL; + } + + return nvme_sanitize_poll_log(ctrl); +} + int nvme_set_queue_count(struct nvme_ctrl *ctrl, int *count) { u32 q_count = (*count - 1) | ((*count - 1) << 16); @@ -324,11 +391,26 @@ static int __maybe_unused nvme_block_device_flush(struct block_device *blk) 0, 0, NVME_QID_IO); } +static int __maybe_unused +nvme_block_device_erase(struct block_device *blk, sector_t block, + blkcnt_t num_blocks) +{ + struct nvme_ns *ns = to_nvme_ns(blk); + + if (block != 0 || num_blocks != blk->num_blocks) { + dev_err(ns->ctrl->dev, "The whole device must be erased\n"); + return -ENOSYS; + } + + return nvme_sanitize_nvm(ns->ctrl); +} + static struct block_device_ops nvme_block_device_ops = { .read = nvme_block_device_read, #ifdef CONFIG_BLOCK_WRITE .write = nvme_block_device_write, .flush = nvme_block_device_flush, + .erase = nvme_block_device_erase, #endif }; diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index e74a43b9aa..543be9a235 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -400,10 +400,12 @@ static int nvme_pci_submit_sync_cmd(struct nvme_ctrl *ctrl, case nvme_admin_create_cq: case nvme_admin_delete_sq: case nvme_admin_delete_cq: + case nvme_admin_sanitize_nvm: case nvme_admin_set_features: dma_dir = DMA_TO_DEVICE; break; case nvme_admin_identify: + case nvme_admin_get_log_page: dma_dir = DMA_FROM_DEVICE; break; default: diff --git a/include/nvme/types.h b/include/nvme/types.h new file mode 100644 index 0000000000..7b5aad1ca5 --- /dev/null +++ b/include/nvme/types.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: Keith Busch <keith.busch@xxxxxxx> + * Chaitanya Kulkarni <chaitanya.kulkarni@xxxxxxx> + */ +#ifndef _LIBNVME_H +#define _LIBNVME_H + +enum nvme_sanitize_sanact { + NVME_SANITIZE_SANACT_EXIT_FAILURE = 1, + NVME_SANITIZE_SANACT_START_BLOCK_ERASE = 2, + NVME_SANITIZE_SANACT_START_OVERWRITE = 3, + NVME_SANITIZE_SANACT_START_CRYPTO_ERASE = 4, + NVME_SANITIZE_SANACT_EXIT_MEDIA_VERIF = 5, +}; + +struct nvme_sanitize_log_page { + __le16 sprog; + __le16 sstat; + __le32 scdw10; + __le32 eto; + __le32 etbe; + __le32 etce; + __le32 etond; + __le32 etbend; + __le32 etcend; + __le32 etpvds; + __u8 ssi; + __u8 rsvd37[475]; +}; + +enum nvme_cmd_get_log_lid { + NVME_LOG_LID_SANITIZE = 0x81, +}; +#endif /* _LIBNVME_TYPES_H */ -- 2.34.1