]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
nvmet: implement endurance groups
authorKeith Busch <kbusch@kernel.org>
Fri, 1 Nov 2024 21:46:01 +0000 (14:46 -0700)
committerKeith Busch <kbusch@kernel.org>
Mon, 11 Nov 2024 17:49:49 +0000 (09:49 -0800)
Most of the returned information is just stubbed data. The target must
support these in order to report rotational media. Since this driver
doesn't know any better, each namespace is its own endurance group with
the engid value matching the nsid.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Keith Busch <kbusch@kernel.org>
drivers/nvme/host/core.c
drivers/nvme/target/admin-cmd.c
include/linux/nvme.h

index e9aac07f4c26d36a00e4eba8463e75f591952320..426d4b90ecd7e77a6884c8e3cdcffe8a7bfc510b 100644 (file)
@@ -5001,6 +5001,7 @@ static inline void _nvme_check_size(void)
        BUILD_BUG_ON(sizeof(struct nvme_id_ctrl_nvm) != NVME_IDENTIFY_DATA_SIZE);
        BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64);
        BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
+       BUILD_BUG_ON(sizeof(struct nvme_endurance_group_log) != 512);
        BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64);
        BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64);
        BUILD_BUG_ON(sizeof(struct nvme_feat_host_behavior) != 512);
index f832661a4913e03f33505efcdc371bdf31b021f2..366582f52200ba2d6e7ed1f97d9d04d5d3fd5471 100644 (file)
@@ -88,6 +88,7 @@ static void nvmet_execute_get_supported_log_pages(struct nvmet_req *req)
        logs->lids[NVME_LOG_FW_SLOT] = cpu_to_le32(NVME_LIDS_LSUPP);
        logs->lids[NVME_LOG_CHANGED_NS] = cpu_to_le32(NVME_LIDS_LSUPP);
        logs->lids[NVME_LOG_CMD_EFFECTS] = cpu_to_le32(NVME_LIDS_LSUPP);
+       logs->lids[NVME_LOG_ENDURANCE_GROUP] = cpu_to_le32(NVME_LIDS_LSUPP);
        logs->lids[NVME_LOG_ANA] = cpu_to_le32(NVME_LIDS_LSUPP);
        logs->lids[NVME_LOG_FEATURES] = cpu_to_le32(NVME_LIDS_LSUPP);
        logs->lids[NVME_LOG_RESERVATION] = cpu_to_le32(NVME_LIDS_LSUPP);
@@ -303,6 +304,49 @@ static u32 nvmet_format_ana_group(struct nvmet_req *req, u32 grpid,
        return struct_size(desc, nsids, count);
 }
 
+static void nvmet_execute_get_log_page_endgrp(struct nvmet_req *req)
+{
+       u64 host_reads, host_writes, data_units_read, data_units_written;
+       struct nvme_endurance_group_log *log;
+       u16 status;
+
+       /*
+        * The target driver emulates each endurance group as its own
+        * namespace, reusing the nsid as the endurance group identifier.
+        */
+       req->cmd->common.nsid = cpu_to_le32(le16_to_cpu(
+                                           req->cmd->get_log_page.lsi));
+       status = nvmet_req_find_ns(req);
+       if (status)
+               goto out;
+
+       log = kzalloc(sizeof(*log), GFP_KERNEL);
+       if (!log) {
+               status = NVME_SC_INTERNAL;
+               goto out;
+       }
+
+       if (!req->ns->bdev)
+               goto copy;
+
+       host_reads = part_stat_read(req->ns->bdev, ios[READ]);
+       data_units_read =
+               DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[READ]), 1000);
+       host_writes = part_stat_read(req->ns->bdev, ios[WRITE]);
+       data_units_written =
+               DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[WRITE]), 1000);
+
+       put_unaligned_le64(host_reads, &log->hrc[0]);
+       put_unaligned_le64(data_units_read, &log->dur[0]);
+       put_unaligned_le64(host_writes, &log->hwc[0]);
+       put_unaligned_le64(data_units_written, &log->duw[0]);
+copy:
+       status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log));
+       kfree(log);
+out:
+       nvmet_req_complete(req, status);
+}
+
 static void nvmet_execute_get_log_page_ana(struct nvmet_req *req)
 {
        struct nvme_ana_rsp_hdr hdr = { 0, };
@@ -401,6 +445,8 @@ static void nvmet_execute_get_log_page(struct nvmet_req *req)
                return nvmet_execute_get_log_changed_ns(req);
        case NVME_LOG_CMD_EFFECTS:
                return nvmet_execute_get_log_cmd_effects_ns(req);
+       case NVME_LOG_ENDURANCE_GROUP:
+               return nvmet_execute_get_log_page_endgrp(req);
        case NVME_LOG_ANA:
                return nvmet_execute_get_log_page_ana(req);
        case NVME_LOG_FEATURES:
@@ -535,6 +581,13 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
 
        id->msdbd = ctrl->ops->msdbd;
 
+       /*
+        * Endurance group identifier is 16 bits, so we can't let namespaces
+        * overflow that since we reuse the nsid
+        */
+       BUILD_BUG_ON(NVMET_MAX_NAMESPACES > USHRT_MAX);
+       id->endgidmax = cpu_to_le16(NVMET_MAX_NAMESPACES);
+
        id->anacap = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
        id->anatt = 10; /* random value */
        id->anagrpmax = cpu_to_le32(NVMET_MAX_ANAGRPS);
@@ -628,6 +681,12 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
                        NVME_PR_SUPPORT_EXCLUSIVE_ACCESS_ALL_REGS |
                        NVME_PR_SUPPORT_IEKEY_VER_1_3_DEF;
 
+       /*
+        * Since we don't know any better, every namespace is its own endurance
+        * group.
+        */
+       id->endgid = cpu_to_le16(req->ns->nsid);
+
        memcpy(&id->nguid, &req->ns->nguid, sizeof(id->nguid));
 
        id->lbaf[0].ds = req->ns->blksize_shift;
@@ -653,6 +712,39 @@ out:
        nvmet_req_complete(req, status);
 }
 
+static void nvmet_execute_identify_endgrp_list(struct nvmet_req *req)
+{
+       u16 min_endgid = le16_to_cpu(req->cmd->identify.cnssid);
+       static const int buf_size = NVME_IDENTIFY_DATA_SIZE;
+       struct nvmet_ctrl *ctrl = req->sq->ctrl;
+       struct nvmet_ns *ns;
+       unsigned long idx;
+       __le16 *list;
+       u16 status;
+       int i = 1;
+
+       list = kzalloc(buf_size, GFP_KERNEL);
+       if (!list) {
+               status = NVME_SC_INTERNAL;
+               goto out;
+       }
+
+       xa_for_each(&ctrl->subsys->namespaces, idx, ns) {
+               if (ns->nsid <= min_endgid)
+                       continue;
+
+               list[i++] = cpu_to_le16(ns->nsid);
+               if (i == buf_size / sizeof(__le16))
+                       break;
+       }
+
+       list[0] = cpu_to_le16(i - 1);
+       status = nvmet_copy_to_sgl(req, 0, list, buf_size);
+       kfree(list);
+out:
+       nvmet_req_complete(req, status);
+}
+
 static void nvmet_execute_identify_nslist(struct nvmet_req *req, bool match_css)
 {
        static const int buf_size = NVME_IDENTIFY_DATA_SIZE;
@@ -825,6 +917,9 @@ static void nvmet_execute_identify(struct nvmet_req *req)
        case NVME_ID_CNS_NS_ACTIVE_LIST_CS:
                nvmet_execute_identify_nslist(req, true);
                return;
+       case NVME_ID_CNS_ENDGRP_LIST:
+               nvmet_execute_identify_endgrp_list(req);
+               return;
        }
 
        pr_debug("unhandled identify cns %d on qid %d\n",
index 31d7ec6d8b93511469026367f748c2546b39e4ea..6d5b4299a1b2dca4e5d6df14d4b4682b3307a8f1 100644 (file)
@@ -327,7 +327,8 @@ struct nvme_id_ctrl {
        __le32                  sanicap;
        __le32                  hmminds;
        __le16                  hmmaxd;
-       __u8                    rsvd338[4];
+       __le16                  nvmsetidmax;
+       __le16                  endgidmax;
        __u8                    anatt;
        __u8                    anacap;
        __le32                  anagrpmax;
@@ -531,6 +532,7 @@ enum {
        NVME_ID_CNS_SCNDRY_CTRL_LIST    = 0x15,
        NVME_ID_CNS_NS_GRANULARITY      = 0x16,
        NVME_ID_CNS_UUID_LIST           = 0x17,
+       NVME_ID_CNS_ENDGRP_LIST         = 0x19,
 };
 
 enum {
@@ -618,6 +620,28 @@ enum {
        NVME_NIDT_CSI           = 0x04,
 };
 
+struct nvme_endurance_group_log {
+       __u8    egcw;
+       __u8    egfeat;
+       __u8    rsvd2;
+       __u8    avsp;
+       __u8    avspt;
+       __u8    pused;
+       __le16  did;
+       __u8    rsvd8[24];
+       __u8    ee[16];
+       __u8    dur[16];
+       __u8    duw[16];
+       __u8    muw[16];
+       __u8    hrc[16];
+       __u8    hwc[16];
+       __u8    mdie[16];
+       __u8    neile[16];
+       __u8    tegcap[16];
+       __u8    uegcap[16];
+       __u8    rsvd192[320];
+};
+
 struct nvme_smart_log {
        __u8                    critical_warning;
        __u8                    temperature[2];
@@ -1302,7 +1326,8 @@ struct nvme_identify {
        __u8                    cns;
        __u8                    rsvd3;
        __le16                  ctrlid;
-       __u8                    rsvd11[3];
+       __le16                  cnssid;
+       __u8                    rsvd11;
        __u8                    csi;
        __u32                   rsvd12[4];
 };