diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 6f9ecb4948f4b948ce47cd1e921a1794b5562b60..2cad357c80d26d5ba6de69ee31113833771ad8e4 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -178,6 +178,13 @@ enum nvme_quirks { * Align dma pool segment size to 512 bytes */ NVME_QUIRK_DMAPOOL_ALIGN_512 = (1 << 22), + + /* + * In terms of security, certain NVMe controllers deactivate the NVMe + * namespace after initialization, requiring it to be reactivated + * once the reset is completed. + */ + NVME_QUIRK_ACTIVATE_NS = (1 << 23), }; static inline char *nvme_quirk_name(enum nvme_quirks q) @@ -229,6 +236,8 @@ static inline char *nvme_quirk_name(enum nvme_quirks q) return "broken_msi"; case NVME_QUIRK_DMAPOOL_ALIGN_512: return "dmapool_align_512"; + case NVME_QUIRK_ACTIVATE_NS: + return "activate_ns"; } return "unknown"; diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index a535d0011c0beae2bf3d1c88e721ca489a628f83..cd1483dc117dd83e6ba434abc485f56c8fa4054d 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "trace.h" #include "nvme.h" @@ -288,6 +289,18 @@ struct nvme_descriptor_pools { struct dma_pool *small; }; +enum nvme_activation_check_mode { + NVME_ACTIVATION_CHECK_MODE_CRC32 = 1 << 0, +}; + +struct nvme_activation_info { + /* + * The kernel does not care the details of activation, + * only care its size. + */ + u8 rsvd[4096]; +}; + /* * Represents an NVM Express device. Each nvme_dev is a PCI function. */ @@ -334,6 +347,11 @@ struct nvme_dev { unsigned int nr_allocated_queues; unsigned int nr_write_queues; unsigned int nr_poll_queues; + + struct nvme_activation_info activation_info; + u32 activation_check_crc32; + int activation_result; + u64 activation_count; struct nvme_descriptor_pools descriptor_pools[]; }; @@ -2774,6 +2792,36 @@ static ssize_t hmb_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(hmb); +static ssize_t activation_info_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nvme_dev *ndev = to_nvme_dev(dev_get_drvdata(dev)); + struct nvme_ctrl *ctrl = &ndev->ctrl; + + if (ctrl->quirks & NVME_QUIRK_ACTIVATE_NS && + count == sizeof(struct nvme_activation_info)) { + memcpy(&ndev->activation_info, buf, count); + ndev->activation_check_crc32 = crc32c(0, + &ndev->activation_info, count); + return count; + } + dev_warn(dev, "activation set fail, count:%lu, activate ns quirk:%lu\n", + count, ctrl->quirks & NVME_QUIRK_ACTIVATE_NS); + return -EINVAL; +} +static DEVICE_ATTR_WO(activation_info); + +static ssize_t activation_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_dev *ndev = to_nvme_dev(dev_get_drvdata(dev)); + + return sysfs_emit(buf, "activation_result:0x%.4x\nactivation_count:%llu\n", + ndev->activation_result, ndev->activation_count); +} +static DEVICE_ATTR_RO(activation_status); + static umode_t nvme_pci_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { @@ -2790,6 +2838,12 @@ static umode_t nvme_pci_attrs_are_visible(struct kobject *kobj, if (a == &dev_attr_hmb.attr && !ctrl->hmpre) return 0; + if (a == &dev_attr_activation_info.attr || + a == &dev_attr_activation_status.attr) { + if (!(ctrl->quirks & NVME_QUIRK_ACTIVATE_NS)) + return 0; + } + return a->mode; } @@ -2798,6 +2852,8 @@ static struct attribute *nvme_pci_attrs[] = { &dev_attr_cmbloc.attr, &dev_attr_cmbsz.attr, &dev_attr_hmb.attr, + &dev_attr_activation_info.attr, + &dev_attr_activation_status.attr, NULL, }; @@ -3350,6 +3406,33 @@ static void nvme_pci_free_ctrl(struct nvme_ctrl *ctrl) kfree(dev); } +static void nvme_activate_ns(struct nvme_dev *ndev) +{ + struct nvme_ctrl *ctrl = &ndev->ctrl; + struct nvme_command c = {0}; + int ret; + + if (!(ctrl->quirks & NVME_QUIRK_ACTIVATE_NS)) + return; + + if (!ndev->activation_check_crc32) + return; + + dev_info(ctrl->device, "activate ns start\n"); + c.features.opcode = nvme_admin_set_features; + c.features.fid = cpu_to_le32(NVME_FEAT_VENDOR_ACTIVATE_NS); + c.features.dword11 = cpu_to_le32(NVME_ACTIVATION_CHECK_MODE_CRC32); + c.features.dword12 = cpu_to_le32(ndev->activation_check_crc32); + + ret = __nvme_submit_sync_cmd(ndev->ctrl.admin_q, &c, NULL, + &ndev->activation_info, sizeof(struct nvme_activation_info), + NVME_QID_ANY, 0); + + ndev->activation_result = ret; + ndev->activation_count++; + dev_info(ctrl->device, "activate ns result:0x%.4x\n", ret); +} + static void nvme_reset_work(struct work_struct *work) { struct nvme_dev *dev = @@ -3407,6 +3490,8 @@ static void nvme_reset_work(struct work_struct *work) nvme_update_attrs(dev); + nvme_activate_ns(dev); + result = nvme_setup_io_queues(dev); if (result) goto out; @@ -4236,6 +4321,10 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_DMA_ADDRESS_BITS_48, }, { PCI_DEVICE(PCI_VENDOR_ID_AMAZON, 0xcd02), .driver_data = NVME_QUIRK_DMA_ADDRESS_BITS_48, }, + { PCI_DEVICE(PCI_VENDOR_ID_ALIBABA, 0x500e), /* ALIBABA Elastic SSD(ESSD) */ + .driver_data = NVME_QUIRK_ACTIVATE_NS}, + { PCI_DEVICE(PCI_VENDOR_ID_MELLANOX, 0x6001), /* Mellanox BF3 */ + .driver_data = NVME_QUIRK_ACTIVATE_NS}, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001), /* * Fix for the Apple controller found in the MacBook8,1 and diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 041f30931a908aeafa1dba60ec8ce1c1cec32992..77fd11979d3b5e17a41d928e4a7c11200180a7e4 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -1401,6 +1401,7 @@ enum { NVME_FEAT_RESV_PERSIST = 0x83, NVME_FEAT_WRITE_PROTECT = 0x84, NVME_FEAT_VENDOR_START = 0xC0, + NVME_FEAT_VENDOR_ACTIVATE_NS = 0xC0, NVME_FEAT_VENDOR_END = 0xFF, NVME_LOG_SUPPORTED = 0x00, NVME_LOG_ERROR = 0x01,