[PATCH 1/7] nvmet: add support of Compare command

Dmitry Bogdanov d.bogdanov at yadro.com
Wed Sep 11 23:42:53 PDT 2024


Report that Compare command is supported.
Make a common function for comparision of datas from the command and
from the backstore.

Signed-off-by: Dmitry Bogdanov <d.bogdanov at yadro.com>
---
 drivers/nvme/target/admin-cmd.c |  4 ++-
 drivers/nvme/target/core.c      | 55 +++++++++++++++++++++++++++++++++
 drivers/nvme/target/nvmet.h     |  2 ++
 3 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index d64480f01f4a..e555b9efebfb 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -176,6 +176,7 @@ static void nvmet_get_cmd_effects_nvm(struct nvme_effects_log *log)
 	log->iocs[nvme_cmd_read] =
 	log->iocs[nvme_cmd_flush] =
 	log->iocs[nvme_cmd_dsm]	=
+	log->iocs[nvme_cmd_compare] =
 		cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
 	log->iocs[nvme_cmd_write] =
 	log->iocs[nvme_cmd_write_zeroes] =
@@ -433,7 +434,8 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
 	id->nn = cpu_to_le32(NVMET_MAX_NAMESPACES);
 	id->mnan = cpu_to_le32(NVMET_MAX_NAMESPACES);
 	id->oncs = cpu_to_le16(NVME_CTRL_ONCS_DSM |
-			NVME_CTRL_ONCS_WRITE_ZEROES);
+			NVME_CTRL_ONCS_WRITE_ZEROES |
+			NVME_CTRL_ONCS_COMPARE);
 
 	/* XXX: don't report vwc if the underlying device is write through */
 	id->vwc = NVME_CTRL_VWC_PRESENT;
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index ed2424f8a396..b3194d66f7dc 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -751,6 +751,59 @@ static void nvmet_set_error(struct nvmet_req *req, u16 status)
 	req->cqe->status |= cpu_to_le16(1 << 14);
 }
 
+int nvmet_compare_sg(struct nvmet_req *req)
+{
+	unsigned int data_len = nvmet_rw_data_len(req);
+	unsigned int offset = 0;
+	struct scatterlist *sg;
+	unsigned int len;
+	u8 *datap = NULL;
+	u8 *buf = NULL;
+	int ret = 0;
+	int i;
+
+	buf = kmalloc(data_len, GFP_ATOMIC);
+	if (!buf)
+		return NVME_SC_INTERNAL;
+
+	/* Copy READ data to the buffer */
+	ret = nvmet_copy_from_sgl(req, 0, buf, data_len);
+	if (ret)
+		goto error;
+
+	/* Compare on-disk data with the data provided by the initiator */
+	for_each_sg(req->cmp_sg, sg, req->sg_cnt, i) {
+		datap = kmap_atomic(sg_page(sg));
+		if (!datap) {
+			ret = NVME_SC_INTERNAL;
+			goto error;
+		}
+
+		len = min_t(unsigned int, sg->length, data_len);
+
+		if (memcmp(datap, buf + offset, len) != 0) {
+			ret = NVME_SC_COMPARE_FAILED;
+			goto error;
+		}
+
+		kunmap_atomic(datap);
+
+		offset += len;
+		if (offset == data_len)
+			break;
+	}
+
+	kfree(buf);
+
+	return 0;
+error:
+	if (datap)
+		kunmap_atomic(datap);
+
+	kfree(buf);
+	return ret;
+}
+
 static void __nvmet_req_complete(struct nvmet_req *req, u16 status)
 {
 	struct nvmet_ns *ns = req->ns;
@@ -887,6 +940,7 @@ static inline u16 nvmet_io_cmd_check_access(struct nvmet_req *req)
 		switch (req->cmd->common.opcode) {
 		case nvme_cmd_read:
 		case nvme_cmd_flush:
+		case nvme_cmd_compare:
 			break;
 		default:
 			return NVME_SC_NS_WRITE_PROTECTED;
@@ -953,6 +1007,7 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
 	req->sq = sq;
 	req->ops = ops;
 	req->sg = NULL;
+	req->cmp_sg = NULL;
 	req->metadata_sg = NULL;
 	req->sg_cnt = 0;
 	req->metadata_sg_cnt = 0;
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 190f55e6d753..8a26b8b4dc63 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -373,6 +373,7 @@ struct nvmet_req {
 	struct nvmet_ns		*ns;
 	struct scatterlist	*sg;
 	struct scatterlist	*metadata_sg;
+	struct scatterlist	*cmp_sg;
 	struct bio_vec		inline_bvec[NVMET_MAX_INLINE_BIOVEC];
 	union {
 		struct {
@@ -602,6 +603,7 @@ void nvmet_bdev_ns_revalidate(struct nvmet_ns *ns);
 void nvmet_file_ns_revalidate(struct nvmet_ns *ns);
 bool nvmet_ns_revalidate(struct nvmet_ns *ns);
 u16 blk_to_nvme_status(struct nvmet_req *req, blk_status_t blk_sts);
+int nvmet_compare_sg(struct nvmet_req *req);
 
 bool nvmet_bdev_zns_enable(struct nvmet_ns *ns);
 void nvmet_execute_identify_ctrl_zns(struct nvmet_req *req);
-- 
2.25.1




More information about the Linux-nvme mailing list