Skip to content

Commit 266b652

Browse files
committed
nvmet: implement endurance groups
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 <[email protected]> Signed-off-by: Keith Busch <[email protected]>
1 parent 81ee2f2 commit 266b652

File tree

3 files changed

+123
-2
lines changed

3 files changed

+123
-2
lines changed

drivers/nvme/host/core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5001,6 +5001,7 @@ static inline void _nvme_check_size(void)
50015001
BUILD_BUG_ON(sizeof(struct nvme_id_ctrl_nvm) != NVME_IDENTIFY_DATA_SIZE);
50025002
BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64);
50035003
BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
5004+
BUILD_BUG_ON(sizeof(struct nvme_endurance_group_log) != 512);
50045005
BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64);
50055006
BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64);
50065007
BUILD_BUG_ON(sizeof(struct nvme_feat_host_behavior) != 512);

drivers/nvme/target/admin-cmd.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ static void nvmet_execute_get_supported_log_pages(struct nvmet_req *req)
8888
logs->lids[NVME_LOG_FW_SLOT] = cpu_to_le32(NVME_LIDS_LSUPP);
8989
logs->lids[NVME_LOG_CHANGED_NS] = cpu_to_le32(NVME_LIDS_LSUPP);
9090
logs->lids[NVME_LOG_CMD_EFFECTS] = cpu_to_le32(NVME_LIDS_LSUPP);
91+
logs->lids[NVME_LOG_ENDURANCE_GROUP] = cpu_to_le32(NVME_LIDS_LSUPP);
9192
logs->lids[NVME_LOG_ANA] = cpu_to_le32(NVME_LIDS_LSUPP);
9293
logs->lids[NVME_LOG_FEATURES] = cpu_to_le32(NVME_LIDS_LSUPP);
9394
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,
303304
return struct_size(desc, nsids, count);
304305
}
305306

307+
static void nvmet_execute_get_log_page_endgrp(struct nvmet_req *req)
308+
{
309+
u64 host_reads, host_writes, data_units_read, data_units_written;
310+
struct nvme_endurance_group_log *log;
311+
u16 status;
312+
313+
/*
314+
* The target driver emulates each endurance group as its own
315+
* namespace, reusing the nsid as the endurance group identifier.
316+
*/
317+
req->cmd->common.nsid = cpu_to_le32(le16_to_cpu(
318+
req->cmd->get_log_page.lsi));
319+
status = nvmet_req_find_ns(req);
320+
if (status)
321+
goto out;
322+
323+
log = kzalloc(sizeof(*log), GFP_KERNEL);
324+
if (!log) {
325+
status = NVME_SC_INTERNAL;
326+
goto out;
327+
}
328+
329+
if (!req->ns->bdev)
330+
goto copy;
331+
332+
host_reads = part_stat_read(req->ns->bdev, ios[READ]);
333+
data_units_read =
334+
DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[READ]), 1000);
335+
host_writes = part_stat_read(req->ns->bdev, ios[WRITE]);
336+
data_units_written =
337+
DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[WRITE]), 1000);
338+
339+
put_unaligned_le64(host_reads, &log->hrc[0]);
340+
put_unaligned_le64(data_units_read, &log->dur[0]);
341+
put_unaligned_le64(host_writes, &log->hwc[0]);
342+
put_unaligned_le64(data_units_written, &log->duw[0]);
343+
copy:
344+
status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log));
345+
kfree(log);
346+
out:
347+
nvmet_req_complete(req, status);
348+
}
349+
306350
static void nvmet_execute_get_log_page_ana(struct nvmet_req *req)
307351
{
308352
struct nvme_ana_rsp_hdr hdr = { 0, };
@@ -401,6 +445,8 @@ static void nvmet_execute_get_log_page(struct nvmet_req *req)
401445
return nvmet_execute_get_log_changed_ns(req);
402446
case NVME_LOG_CMD_EFFECTS:
403447
return nvmet_execute_get_log_cmd_effects_ns(req);
448+
case NVME_LOG_ENDURANCE_GROUP:
449+
return nvmet_execute_get_log_page_endgrp(req);
404450
case NVME_LOG_ANA:
405451
return nvmet_execute_get_log_page_ana(req);
406452
case NVME_LOG_FEATURES:
@@ -535,6 +581,13 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
535581

536582
id->msdbd = ctrl->ops->msdbd;
537583

584+
/*
585+
* Endurance group identifier is 16 bits, so we can't let namespaces
586+
* overflow that since we reuse the nsid
587+
*/
588+
BUILD_BUG_ON(NVMET_MAX_NAMESPACES > USHRT_MAX);
589+
id->endgidmax = cpu_to_le16(NVMET_MAX_NAMESPACES);
590+
538591
id->anacap = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
539592
id->anatt = 10; /* random value */
540593
id->anagrpmax = cpu_to_le32(NVMET_MAX_ANAGRPS);
@@ -628,6 +681,12 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
628681
NVME_PR_SUPPORT_EXCLUSIVE_ACCESS_ALL_REGS |
629682
NVME_PR_SUPPORT_IEKEY_VER_1_3_DEF;
630683

684+
/*
685+
* Since we don't know any better, every namespace is its own endurance
686+
* group.
687+
*/
688+
id->endgid = cpu_to_le16(req->ns->nsid);
689+
631690
memcpy(&id->nguid, &req->ns->nguid, sizeof(id->nguid));
632691

633692
id->lbaf[0].ds = req->ns->blksize_shift;
@@ -653,6 +712,39 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
653712
nvmet_req_complete(req, status);
654713
}
655714

715+
static void nvmet_execute_identify_endgrp_list(struct nvmet_req *req)
716+
{
717+
u16 min_endgid = le16_to_cpu(req->cmd->identify.cnssid);
718+
static const int buf_size = NVME_IDENTIFY_DATA_SIZE;
719+
struct nvmet_ctrl *ctrl = req->sq->ctrl;
720+
struct nvmet_ns *ns;
721+
unsigned long idx;
722+
__le16 *list;
723+
u16 status;
724+
int i = 1;
725+
726+
list = kzalloc(buf_size, GFP_KERNEL);
727+
if (!list) {
728+
status = NVME_SC_INTERNAL;
729+
goto out;
730+
}
731+
732+
xa_for_each(&ctrl->subsys->namespaces, idx, ns) {
733+
if (ns->nsid <= min_endgid)
734+
continue;
735+
736+
list[i++] = cpu_to_le16(ns->nsid);
737+
if (i == buf_size / sizeof(__le16))
738+
break;
739+
}
740+
741+
list[0] = cpu_to_le16(i - 1);
742+
status = nvmet_copy_to_sgl(req, 0, list, buf_size);
743+
kfree(list);
744+
out:
745+
nvmet_req_complete(req, status);
746+
}
747+
656748
static void nvmet_execute_identify_nslist(struct nvmet_req *req, bool match_css)
657749
{
658750
static const int buf_size = NVME_IDENTIFY_DATA_SIZE;
@@ -825,6 +917,9 @@ static void nvmet_execute_identify(struct nvmet_req *req)
825917
case NVME_ID_CNS_NS_ACTIVE_LIST_CS:
826918
nvmet_execute_identify_nslist(req, true);
827919
return;
920+
case NVME_ID_CNS_ENDGRP_LIST:
921+
nvmet_execute_identify_endgrp_list(req);
922+
return;
828923
}
829924

830925
pr_debug("unhandled identify cns %d on qid %d\n",

include/linux/nvme.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ struct nvme_id_ctrl {
327327
__le32 sanicap;
328328
__le32 hmminds;
329329
__le16 hmmaxd;
330-
__u8 rsvd338[4];
330+
__le16 nvmsetidmax;
331+
__le16 endgidmax;
331332
__u8 anatt;
332333
__u8 anacap;
333334
__le32 anagrpmax;
@@ -531,6 +532,7 @@ enum {
531532
NVME_ID_CNS_SCNDRY_CTRL_LIST = 0x15,
532533
NVME_ID_CNS_NS_GRANULARITY = 0x16,
533534
NVME_ID_CNS_UUID_LIST = 0x17,
535+
NVME_ID_CNS_ENDGRP_LIST = 0x19,
534536
};
535537

536538
enum {
@@ -618,6 +620,28 @@ enum {
618620
NVME_NIDT_CSI = 0x04,
619621
};
620622

623+
struct nvme_endurance_group_log {
624+
__u8 egcw;
625+
__u8 egfeat;
626+
__u8 rsvd2;
627+
__u8 avsp;
628+
__u8 avspt;
629+
__u8 pused;
630+
__le16 did;
631+
__u8 rsvd8[24];
632+
__u8 ee[16];
633+
__u8 dur[16];
634+
__u8 duw[16];
635+
__u8 muw[16];
636+
__u8 hrc[16];
637+
__u8 hwc[16];
638+
__u8 mdie[16];
639+
__u8 neile[16];
640+
__u8 tegcap[16];
641+
__u8 uegcap[16];
642+
__u8 rsvd192[320];
643+
};
644+
621645
struct nvme_smart_log {
622646
__u8 critical_warning;
623647
__u8 temperature[2];
@@ -1302,7 +1326,8 @@ struct nvme_identify {
13021326
__u8 cns;
13031327
__u8 rsvd3;
13041328
__le16 ctrlid;
1305-
__u8 rsvd11[3];
1329+
__le16 cnssid;
1330+
__u8 rsvd11;
13061331
__u8 csi;
13071332
__u32 rsvd12[4];
13081333
};

0 commit comments

Comments
 (0)