[PATCH v2] nvmet: add support reading with offset from ANA log
Daniel Wagner
dwagner at suse.de
Mon Jan 10 06:05:39 PST 2022
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 at suse.de>
---
v2:
- fixed lenght calculation and limit response lenght
- some more testing with KASAN enabled
v1:
- https://lore.kernel.org/linux-nvme/20211229155302.16789-1-dwagner@suse.de/
drivers/nvme/target/admin-cmd.c | 41 +++++++++++++++++++--------------
1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 6fb24746de06..c4f1bbb9fb8a 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -263,35 +263,42 @@ 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 */
- size_t len;
+ u64 offset = nvmet_get_log_page_offset(req->cmd);
+ u32 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;
+ len = sizeof(*hdr);
+ 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;
+ len += nvmet_format_ana_group(req, grpid, desc);
ngrps++;
}
for ( ; grpid <= NVMET_MAX_ANAGRPS; grpid++) {
@@ -299,15 +306,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,
+ min(len, 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.34.1
More information about the Linux-nvme
mailing list