[PATCH] nvmet: add support reading with offset from ANA log

From: Daniel Wagner
Date: Wed Dec 29 2021 - 10:53:07 EST


Add support to read with offsets from ANA log buffer.

The controller claims to support extended data for the Get Log Page
command (including extended Number of Dwords and Log Page Offset 2
fields):

lpa : 0x7
[2:2] : 0x1 Extended data for Get Log Page Supported
[1:1] : 0x1 Command Effects Log Page Supported
[0:0] : 0x1 SMART/Health Log Page per NS Supported

Signed-off-by: Daniel Wagner <dwagner@xxxxxxx>
---
drivers/nvme/target/admin-cmd.c | 37 +++++++++++++++++++--------------
1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 6fb24746de06..7c8806f477e2 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -263,35 +263,40 @@ static u32 nvmet_format_ana_group(struct nvmet_req *req, u32 grpid,
desc->nnsids = cpu_to_le32(count);
desc->chgcnt = cpu_to_le64(nvmet_ana_chgcnt);
desc->state = req->port->ana_state[grpid];
- memset(desc->rsvd17, 0, sizeof(desc->rsvd17));
return struct_size(desc, nsids, count);
}

static void nvmet_execute_get_log_page_ana(struct nvmet_req *req)
{
- struct nvme_ana_rsp_hdr hdr = { 0, };
+ struct nvme_ana_rsp_hdr *hdr;
struct nvme_ana_group_desc *desc;
- size_t offset = sizeof(struct nvme_ana_rsp_hdr); /* start beyond hdr */
+ u64 offset = nvmet_get_log_page_offset(req->cmd);
size_t len;
+ void *buffer;
u32 grpid;
u16 ngrps = 0;
u16 status;

+ if (offset & 0x3) {
+ req->error_loc =
+ offsetof(struct nvme_get_log_page_command, lpo);
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ goto out;
+ }
+
status = NVME_SC_INTERNAL;
- desc = kmalloc(struct_size(desc, nsids, NVMET_MAX_NAMESPACES),
- GFP_KERNEL);
- if (!desc)
+ len = sizeof(*hdr) + struct_size(desc, nsids, NVMET_MAX_NAMESPACES);
+ buffer = kzalloc(len, GFP_KERNEL);
+ if (!buffer)
goto out;
+ hdr = buffer;
+ desc = buffer + sizeof(*hdr);

down_read(&nvmet_ana_sem);
for (grpid = 1; grpid <= NVMET_MAX_ANAGRPS; grpid++) {
if (!nvmet_ana_group_enabled[grpid])
continue;
- len = nvmet_format_ana_group(req, grpid, desc);
- status = nvmet_copy_to_sgl(req, offset, desc, len);
- if (status)
- break;
- offset += len;
+ nvmet_format_ana_group(req, grpid, desc);
ngrps++;
}
for ( ; grpid <= NVMET_MAX_ANAGRPS; grpid++) {
@@ -299,15 +304,15 @@ static void nvmet_execute_get_log_page_ana(struct nvmet_req *req)
ngrps++;
}

- hdr.chgcnt = cpu_to_le64(nvmet_ana_chgcnt);
- hdr.ngrps = cpu_to_le16(ngrps);
+ hdr->chgcnt = cpu_to_le64(nvmet_ana_chgcnt);
+ hdr->ngrps = cpu_to_le16(ngrps);
nvmet_clear_aen_bit(req, NVME_AEN_BIT_ANA_CHANGE);
up_read(&nvmet_ana_sem);

- kfree(desc);
+ status = nvmet_copy_to_sgl(req, 0, buffer + offset,
+ nvmet_get_log_page_len(req->cmd));

- /* copy the header last once we know the number of groups */
- status = nvmet_copy_to_sgl(req, 0, &hdr, sizeof(hdr));
+ kfree(buffer);
out:
nvmet_req_complete(req, status);
}
--
2.29.2