nvme: support for multiple Command Sets Supported and Effects log pages
authorKeith Busch <keith.busch@wdc.com>
Mon, 29 Jun 2020 19:06:40 +0000 (12:06 -0700)
committerChristoph Hellwig <hch@lst.de>
Wed, 8 Jul 2020 14:16:20 +0000 (16:16 +0200)
The Commands Supported and Effects log page was extended with a CSI
field that enables the host to query the log page for each command set
supported. Retrieve this log page for each command set that an attached
namespace supports, and save a pointer to that log in the namespace head.

Reviewed-by: Matias Bjørling <matias.bjorling@wdc.com>
Reviewed-by: Javier González <javier.gonz@samsung.com>
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Daniel Wagner <dwagner@suse.de>
Signed-off-by: Keith Busch <keith.busch@wdc.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/nvme/host/core.c
drivers/nvme/host/hwmon.c
drivers/nvme/host/lightnvm.c
drivers/nvme/host/multipath.c
drivers/nvme/host/nvme.h
include/linux/nvme.h

index 892291dbee641d63dd553893f6c62973c707e0bd..62b2cdc764da3b906d50358a3165a524809606be 100644 (file)
@@ -1370,8 +1370,8 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
        u32 effects = 0;
 
        if (ns) {
-               if (ctrl->effects)
-                       effects = le32_to_cpu(ctrl->effects->iocs[opcode]);
+               if (ns->head->effects)
+                       effects = le32_to_cpu(ns->head->effects->iocs[opcode]);
                if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC))
                        dev_warn(ctrl->device,
                                 "IO command:%02x has unhandled effects:%08x\n",
@@ -2851,7 +2851,7 @@ out_unlock:
        return ret;
 }
 
-int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp,
+int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi,
                void *log, size_t size, u64 offset)
 {
        struct nvme_command c = { };
@@ -2865,27 +2865,55 @@ int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp,
        c.get_log_page.numdu = cpu_to_le16(dwlen >> 16);
        c.get_log_page.lpol = cpu_to_le32(lower_32_bits(offset));
        c.get_log_page.lpou = cpu_to_le32(upper_32_bits(offset));
+       c.get_log_page.csi = csi;
 
        return nvme_submit_sync_cmd(ctrl->admin_q, &c, log, size);
 }
 
-static int nvme_get_effects_log(struct nvme_ctrl *ctrl)
+static struct nvme_cel *nvme_find_cel(struct nvme_ctrl *ctrl, u8 csi)
 {
+       struct nvme_cel *cel, *ret = NULL;
+
+       spin_lock(&ctrl->lock);
+       list_for_each_entry(cel, &ctrl->cels, entry) {
+               if (cel->csi == csi) {
+                       ret = cel;
+                       break;
+               }
+       }
+       spin_unlock(&ctrl->lock);
+
+       return ret;
+}
+
+static int nvme_get_effects_log(struct nvme_ctrl *ctrl, u8 csi,
+                               struct nvme_effects_log **log)
+{
+       struct nvme_cel *cel = nvme_find_cel(ctrl, csi);
        int ret;
 
-       if (!ctrl->effects)
-               ctrl->effects = kzalloc(sizeof(*ctrl->effects), GFP_KERNEL);
+       if (cel)
+               goto out;
 
-       if (!ctrl->effects)
-               return 0;
+       cel = kzalloc(sizeof(*cel), GFP_KERNEL);
+       if (!cel)
+               return -ENOMEM;
 
-       ret = nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_CMD_EFFECTS, 0,
-                       ctrl->effects, sizeof(*ctrl->effects), 0);
+       ret = nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_CMD_EFFECTS, 0, csi,
+                       &cel->log, sizeof(cel->log), 0);
        if (ret) {
-               kfree(ctrl->effects);
-               ctrl->effects = NULL;
+               kfree(cel);
+               return ret;
        }
-       return ret;
+
+       cel->csi = csi;
+
+       spin_lock(&ctrl->lock);
+       list_add_tail(&cel->entry, &ctrl->cels);
+       spin_unlock(&ctrl->lock);
+out:
+       *log = &cel->log;
+       return 0;
 }
 
 /*
@@ -2918,7 +2946,7 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
        }
 
        if (id->lpa & NVME_CTRL_LPA_CMD_EFFECTS_LOG) {
-               ret = nvme_get_effects_log(ctrl);
+               ret = nvme_get_effects_log(ctrl, NVME_CSI_NVM, &ctrl->effects);
                if (ret < 0)
                        goto out_free;
        }
@@ -3551,6 +3579,13 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
                goto out_cleanup_srcu;
        }
 
+       if (head->ids.csi) {
+               ret = nvme_get_effects_log(ctrl, head->ids.csi, &head->effects);
+               if (ret)
+                       goto out_cleanup_srcu;
+       } else
+               head->effects = ctrl->effects;
+
        ret = nvme_mpath_alloc_disk(ctrl, head);
        if (ret)
                goto out_cleanup_srcu;
@@ -3891,8 +3926,8 @@ static void nvme_clear_changed_ns_log(struct nvme_ctrl *ctrl)
         * raced with us in reading the log page, which could cause us to miss
         * updates.
         */
-       error = nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_CHANGED_NS, 0, log,
-                       log_size, 0);
+       error = nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_CHANGED_NS, 0,
+                       NVME_CSI_NVM, log, log_size, 0);
        if (error)
                dev_warn(ctrl->device,
                        "reading changed ns log failed: %d\n", error);
@@ -4036,8 +4071,8 @@ static void nvme_get_fw_slot_info(struct nvme_ctrl *ctrl)
        if (!log)
                return;
 
-       if (nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_FW_SLOT, 0, log,
-                       sizeof(*log), 0))
+       if (nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_FW_SLOT, 0, NVME_CSI_NVM,
+                       log, sizeof(*log), 0))
                dev_warn(ctrl->device, "Get FW SLOT INFO log error\n");
        kfree(log);
 }
@@ -4174,11 +4209,16 @@ static void nvme_free_ctrl(struct device *dev)
        struct nvme_ctrl *ctrl =
                container_of(dev, struct nvme_ctrl, ctrl_device);
        struct nvme_subsystem *subsys = ctrl->subsys;
+       struct nvme_cel *cel, *next;
 
        if (subsys && ctrl->instance != subsys->instance)
                ida_simple_remove(&nvme_instance_ida, ctrl->instance);
 
-       kfree(ctrl->effects);
+       list_for_each_entry_safe(cel, next, &ctrl->cels, entry) {
+               list_del(&cel->entry);
+               kfree(cel);
+       }
+
        nvme_mpath_uninit(ctrl);
        __free_page(ctrl->discard_page);
 
@@ -4209,6 +4249,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
        spin_lock_init(&ctrl->lock);
        mutex_init(&ctrl->scan_lock);
        INIT_LIST_HEAD(&ctrl->namespaces);
+       INIT_LIST_HEAD(&ctrl->cels);
        init_rwsem(&ctrl->namespaces_rwsem);
        ctrl->dev = dev;
        ctrl->ops = ops;
index 2e6477ed420f6e6e0035159c5337438e9c78deda..23ba8bf678ae488dee366a0cbcaa065da7555659 100644 (file)
@@ -62,7 +62,7 @@ static int nvme_hwmon_get_smart_log(struct nvme_hwmon_data *data)
        int ret;
 
        ret = nvme_get_log(data->ctrl, NVME_NSID_ALL, NVME_LOG_SMART, 0,
-                          &data->log, sizeof(data->log), 0);
+                          NVME_CSI_NVM, &data->log, sizeof(data->log), 0);
 
        return ret <= 0 ? ret : -EIO;
 }
index 69608755d4154309d7834f1560ed359b61fee349..8e562d0f2c3014533dd2c38ceb03f5525a562665 100644 (file)
@@ -593,8 +593,8 @@ static int nvme_nvm_get_chk_meta(struct nvm_dev *ndev,
                dev_meta_off = dev_meta;
 
                ret = nvme_get_log(ctrl, ns->head->ns_id,
-                               NVME_NVM_LOG_REPORT_CHUNK, 0, dev_meta, len,
-                               offset);
+                               NVME_NVM_LOG_REPORT_CHUNK, 0, NVME_CSI_NVM,
+                               dev_meta, len, offset);
                if (ret) {
                        dev_err(ctrl->device, "Get REPORT CHUNK log error\n");
                        break;
index 5a37a595411e8101d0177a24fb693d56fda848c2..74bad4e3d3778523b429977291ab791cf70d0eb6 100644 (file)
@@ -527,7 +527,7 @@ static int nvme_read_ana_log(struct nvme_ctrl *ctrl)
        int error;
 
        mutex_lock(&ctrl->ana_lock);
-       error = nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_ANA, 0,
+       error = nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_ANA, 0, NVME_CSI_NVM,
                        ctrl->ana_log_buf, ctrl->ana_log_size, 0);
        if (error) {
                dev_warn(ctrl->device, "Failed to get ANA log: %d\n", error);
index 5573159f714d402ebe85e1aee741e1a02f4c5b5d..fe9424c7097fd91f59916c9097adeb033c5be1ad 100644 (file)
@@ -191,6 +191,12 @@ struct nvme_fault_inject {
 #endif
 };
 
+struct nvme_cel {
+       struct list_head        entry;
+       struct nvme_effects_log log;
+       u8                      csi;
+};
+
 struct nvme_ctrl {
        bool comp_seen;
        enum nvme_ctrl_state state;
@@ -257,6 +263,7 @@ struct nvme_ctrl {
        unsigned long quirks;
        struct nvme_id_power_state psd[32];
        struct nvme_effects_log *effects;
+       struct list_head cels;
        struct work_struct scan_work;
        struct work_struct async_event_work;
        struct delayed_work ka_work;
@@ -359,6 +366,7 @@ struct nvme_ns_head {
        struct kref             ref;
        bool                    shared;
        int                     instance;
+       struct nvme_effects_log *effects;
 #ifdef CONFIG_NVME_MULTIPATH
        struct gendisk          *disk;
        struct bio_list         requeue_list;
@@ -561,7 +569,7 @@ int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl);
 int nvme_try_sched_reset(struct nvme_ctrl *ctrl);
 int nvme_delete_ctrl(struct nvme_ctrl *ctrl);
 
-int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp,
+int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi,
                void *log, size_t size, u64 offset);
 
 extern const struct attribute_group *nvme_ns_id_attr_groups[];
index 81ffe52475059989b0b62f08342558b84a210a48..95cd03e240a180b32403e74485d45fb983ec25ad 100644 (file)
@@ -1101,7 +1101,9 @@ struct nvme_get_log_page_command {
                };
                __le64 lpo;
        };
-       __u32                   rsvd14[2];
+       __u8                    rsvd14[3];
+       __u8                    csi;
+       __u32                   rsvd15;
 };
 
 struct nvme_directive_cmd {