summaryrefslogtreecommitdiffstats
path: root/src/spdk/test/nvme/sgl/sgl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/test/nvme/sgl/sgl.c')
-rw-r--r--src/spdk/test/nvme/sgl/sgl.c542
1 files changed, 542 insertions, 0 deletions
diff --git a/src/spdk/test/nvme/sgl/sgl.c b/src/spdk/test/nvme/sgl/sgl.c
new file mode 100644
index 00000000..ccff652c
--- /dev/null
+++ b/src/spdk/test/nvme/sgl/sgl.c
@@ -0,0 +1,542 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/nvme.h"
+#include "spdk/env.h"
+#include "spdk/util.h"
+
+#define MAX_DEVS 64
+
+#define MAX_IOVS 128
+
+#define DATA_PATTERN 0x5A
+
+#define BASE_LBA_START 0x100000
+
+struct dev {
+ struct spdk_nvme_ctrlr *ctrlr;
+ char name[SPDK_NVMF_TRADDR_MAX_LEN + 1];
+};
+
+static struct dev devs[MAX_DEVS];
+static int num_devs = 0;
+
+#define foreach_dev(iter) \
+ for (iter = devs; iter - devs < num_devs; iter++)
+
+static int io_complete_flag = 0;
+
+struct sgl_element {
+ void *base;
+ size_t offset;
+ size_t len;
+};
+
+struct io_request {
+ uint32_t current_iov_index;
+ uint32_t current_iov_bytes_left;
+ struct sgl_element iovs[MAX_IOVS];
+ uint32_t nseg;
+ uint32_t misalign;
+};
+
+static void nvme_request_reset_sgl(void *cb_arg, uint32_t sgl_offset)
+{
+ uint32_t i;
+ uint32_t offset = 0;
+ struct sgl_element *iov;
+ struct io_request *req = (struct io_request *)cb_arg;
+
+ for (i = 0; i < req->nseg; i++) {
+ iov = &req->iovs[i];
+ offset += iov->len;
+ if (offset > sgl_offset) {
+ break;
+ }
+ }
+ req->current_iov_index = i;
+ req->current_iov_bytes_left = offset - sgl_offset;
+ return;
+}
+
+static int nvme_request_next_sge(void *cb_arg, void **address, uint32_t *length)
+{
+ struct io_request *req = (struct io_request *)cb_arg;
+ struct sgl_element *iov;
+
+ if (req->current_iov_index >= req->nseg) {
+ *length = 0;
+ *address = NULL;
+ return 0;
+ }
+
+ iov = &req->iovs[req->current_iov_index];
+
+ if (req->current_iov_bytes_left) {
+ *address = iov->base + iov->offset + iov->len - req->current_iov_bytes_left;
+ *length = req->current_iov_bytes_left;
+ req->current_iov_bytes_left = 0;
+ } else {
+ *address = iov->base + iov->offset;
+ *length = iov->len;
+ }
+
+ req->current_iov_index++;
+
+ return 0;
+}
+
+static void
+io_complete(void *ctx, const struct spdk_nvme_cpl *cpl)
+{
+ if (spdk_nvme_cpl_is_error(cpl)) {
+ io_complete_flag = 2;
+ } else {
+ io_complete_flag = 1;
+ }
+}
+
+static void build_io_request_0(struct io_request *req)
+{
+ req->nseg = 1;
+
+ req->iovs[0].base = spdk_dma_zmalloc(0x800, 4, NULL);
+ req->iovs[0].len = 0x800;
+}
+
+static void build_io_request_1(struct io_request *req)
+{
+ req->nseg = 1;
+
+ /* 512B for 1st sge */
+ req->iovs[0].base = spdk_dma_zmalloc(0x200, 0x200, NULL);
+ req->iovs[0].len = 0x200;
+}
+
+static void build_io_request_2(struct io_request *req)
+{
+ req->nseg = 1;
+
+ /* 256KB for 1st sge */
+ req->iovs[0].base = spdk_dma_zmalloc(0x40000, 0x1000, NULL);
+ req->iovs[0].len = 0x40000;
+}
+
+static void build_io_request_3(struct io_request *req)
+{
+ req->nseg = 3;
+
+ /* 2KB for 1st sge, make sure the iov address start at 0x800 boundary,
+ * and end with 0x1000 boundary */
+ req->iovs[0].base = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
+ req->iovs[0].offset = 0x800;
+ req->iovs[0].len = 0x800;
+
+ /* 4KB for 2th sge */
+ req->iovs[1].base = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
+ req->iovs[1].len = 0x1000;
+
+ /* 12KB for 3th sge */
+ req->iovs[2].base = spdk_dma_zmalloc(0x3000, 0x1000, NULL);
+ req->iovs[2].len = 0x3000;
+}
+
+static void build_io_request_4(struct io_request *req)
+{
+ uint32_t i;
+
+ req->nseg = 32;
+
+ /* 4KB for 1st sge */
+ req->iovs[0].base = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
+ req->iovs[0].len = 0x1000;
+
+ /* 8KB for the rest 31 sge */
+ for (i = 1; i < req->nseg; i++) {
+ req->iovs[i].base = spdk_dma_zmalloc(0x2000, 0x1000, NULL);
+ req->iovs[i].len = 0x2000;
+ }
+}
+
+static void build_io_request_5(struct io_request *req)
+{
+ req->nseg = 1;
+
+ /* 8KB for 1st sge */
+ req->iovs[0].base = spdk_dma_zmalloc(0x2000, 0x1000, NULL);
+ req->iovs[0].len = 0x2000;
+}
+
+static void build_io_request_6(struct io_request *req)
+{
+ req->nseg = 2;
+
+ /* 4KB for 1st sge */
+ req->iovs[0].base = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
+ req->iovs[0].len = 0x1000;
+
+ /* 4KB for 2st sge */
+ req->iovs[1].base = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
+ req->iovs[1].len = 0x1000;
+}
+
+static void build_io_request_7(struct io_request *req)
+{
+ uint8_t *base;
+
+ req->nseg = 1;
+
+ /*
+ * Create a 64KB sge, but ensure it is *not* aligned on a 4KB
+ * boundary. This is valid for single element buffers with PRP.
+ */
+ base = spdk_dma_zmalloc(0x11000, 0x1000, NULL);
+ req->misalign = 64;
+ req->iovs[0].base = base + req->misalign;
+ req->iovs[0].len = 0x10000;
+}
+
+static void build_io_request_8(struct io_request *req)
+{
+ req->nseg = 2;
+
+ /*
+ * 1KB for 1st sge, make sure the iov address does not start and end
+ * at 0x1000 boundary
+ */
+ req->iovs[0].base = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
+ req->iovs[0].offset = 0x400;
+ req->iovs[0].len = 0x400;
+
+ /*
+ * 1KB for 1st sge, make sure the iov address does not start and end
+ * at 0x1000 boundary
+ */
+ req->iovs[1].base = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
+ req->iovs[1].offset = 0x400;
+ req->iovs[1].len = 0x400;
+}
+
+static void build_io_request_9(struct io_request *req)
+{
+ /*
+ * Check if mixed PRP complaint and not complaint requests are handled
+ * properly by splitting them into subrequests.
+ * Construct buffers with following theme:
+ */
+ const size_t req_len[] = { 2048, 4096, 2048, 4096, 2048, 1024 };
+ const size_t req_off[] = { 0x800, 0x0, 0x0, 0x100, 0x800, 0x800 };
+ struct sgl_element *iovs = req->iovs;
+ uint32_t i;
+ req->nseg = SPDK_COUNTOF(req_len);
+ assert(SPDK_COUNTOF(req_len) == SPDK_COUNTOF(req_off));
+
+ for (i = 0; i < req->nseg; i++) {
+ iovs[i].base = spdk_dma_zmalloc(req_off[i] + req_len[i], 0x4000, NULL);
+ iovs[i].offset = req_off[i];
+ iovs[i].len = req_len[i];
+ }
+}
+
+static void build_io_request_10(struct io_request *req)
+{
+ /*
+ * Test the case where we have a valid PRP list, but the first and last
+ * elements are not exact multiples of the logical block size.
+ */
+ const size_t req_len[] = { 4004, 4096, 92 };
+ const size_t req_off[] = { 0x5c, 0x0, 0x0 };
+ struct sgl_element *iovs = req->iovs;
+ uint32_t i;
+ req->nseg = SPDK_COUNTOF(req_len);
+ assert(SPDK_COUNTOF(req_len) == SPDK_COUNTOF(req_off));
+
+ for (i = 0; i < req->nseg; i++) {
+ iovs[i].base = spdk_dma_zmalloc(req_off[i] + req_len[i], 0x4000, NULL);
+ iovs[i].offset = req_off[i];
+ iovs[i].len = req_len[i];
+ }
+}
+
+static void build_io_request_11(struct io_request *req)
+{
+ /* This test case focuses on the last element not starting on a page boundary. */
+ const size_t req_len[] = { 512, 512 };
+ const size_t req_off[] = { 0xe00, 0x800 };
+ struct sgl_element *iovs = req->iovs;
+ uint32_t i;
+ req->nseg = SPDK_COUNTOF(req_len);
+ assert(SPDK_COUNTOF(req_len) == SPDK_COUNTOF(req_off));
+
+ for (i = 0; i < req->nseg; i++) {
+ iovs[i].base = spdk_dma_zmalloc(req_off[i] + req_len[i], 0x4000, NULL);
+ iovs[i].offset = req_off[i];
+ iovs[i].len = req_len[i];
+ }
+}
+
+typedef void (*nvme_build_io_req_fn_t)(struct io_request *req);
+
+static void
+free_req(struct io_request *req)
+{
+ uint32_t i;
+
+ if (req == NULL) {
+ return;
+ }
+
+ for (i = 0; i < req->nseg; i++) {
+ spdk_dma_free(req->iovs[i].base - req->misalign);
+ }
+
+ spdk_dma_free(req);
+}
+
+static int
+writev_readv_tests(struct dev *dev, nvme_build_io_req_fn_t build_io_fn, const char *test_name)
+{
+ int rc = 0;
+ uint32_t len, lba_count;
+ uint32_t i, j, nseg, remainder;
+ char *buf;
+
+ struct io_request *req;
+ struct spdk_nvme_ns *ns;
+ struct spdk_nvme_qpair *qpair;
+ const struct spdk_nvme_ns_data *nsdata;
+
+ ns = spdk_nvme_ctrlr_get_ns(dev->ctrlr, 1);
+ if (!ns) {
+ fprintf(stderr, "Null namespace\n");
+ return 0;
+ }
+ nsdata = spdk_nvme_ns_get_data(ns);
+ if (!nsdata || !spdk_nvme_ns_get_sector_size(ns)) {
+ fprintf(stderr, "Empty nsdata or wrong sector size\n");
+ return 0;
+ }
+
+ if (spdk_nvme_ns_get_flags(ns) & SPDK_NVME_NS_DPS_PI_SUPPORTED) {
+ return 0;
+ }
+
+ req = spdk_dma_zmalloc(sizeof(*req), 0, NULL);
+ if (!req) {
+ fprintf(stderr, "Allocate request failed\n");
+ return 0;
+ }
+
+ /* IO parameters setting */
+ build_io_fn(req);
+
+ len = 0;
+ for (i = 0; i < req->nseg; i++) {
+ struct sgl_element *sge = &req->iovs[i];
+
+ len += sge->len;
+ }
+
+ lba_count = len / spdk_nvme_ns_get_sector_size(ns);
+ remainder = len % spdk_nvme_ns_get_sector_size(ns);
+ if (!lba_count || remainder || (BASE_LBA_START + lba_count > (uint32_t)nsdata->nsze)) {
+ fprintf(stderr, "%s: %s Invalid IO length parameter\n", dev->name, test_name);
+ free_req(req);
+ return 0;
+ }
+
+ qpair = spdk_nvme_ctrlr_alloc_io_qpair(dev->ctrlr, NULL, 0);
+ if (!qpair) {
+ free_req(req);
+ return -1;
+ }
+
+ nseg = req->nseg;
+ for (i = 0; i < nseg; i++) {
+ memset(req->iovs[i].base + req->iovs[i].offset, DATA_PATTERN, req->iovs[i].len);
+ }
+
+ rc = spdk_nvme_ns_cmd_writev(ns, qpair, BASE_LBA_START, lba_count,
+ io_complete, req, 0,
+ nvme_request_reset_sgl,
+ nvme_request_next_sge);
+
+ if (rc != 0) {
+ fprintf(stderr, "%s: %s writev failed\n", dev->name, test_name);
+ spdk_nvme_ctrlr_free_io_qpair(qpair);
+ free_req(req);
+ return -1;
+ }
+
+ io_complete_flag = 0;
+
+ while (!io_complete_flag) {
+ spdk_nvme_qpair_process_completions(qpair, 1);
+ }
+
+ if (io_complete_flag != 1) {
+ fprintf(stderr, "%s: %s writev failed\n", dev->name, test_name);
+ spdk_nvme_ctrlr_free_io_qpair(qpair);
+ free_req(req);
+ return -1;
+ }
+
+ /* reset completion flag */
+ io_complete_flag = 0;
+
+ for (i = 0; i < nseg; i++) {
+ memset(req->iovs[i].base + req->iovs[i].offset, 0, req->iovs[i].len);
+ }
+
+ rc = spdk_nvme_ns_cmd_readv(ns, qpair, BASE_LBA_START, lba_count,
+ io_complete, req, 0,
+ nvme_request_reset_sgl,
+ nvme_request_next_sge);
+
+ if (rc != 0) {
+ fprintf(stderr, "%s: %s readv failed\n", dev->name, test_name);
+ spdk_nvme_ctrlr_free_io_qpair(qpair);
+ free_req(req);
+ return -1;
+ }
+
+ while (!io_complete_flag) {
+ spdk_nvme_qpair_process_completions(qpair, 1);
+ }
+
+ if (io_complete_flag != 1) {
+ fprintf(stderr, "%s: %s readv failed\n", dev->name, test_name);
+ spdk_nvme_ctrlr_free_io_qpair(qpair);
+ free_req(req);
+ return -1;
+ }
+
+ for (i = 0; i < nseg; i++) {
+ buf = (char *)req->iovs[i].base + req->iovs[i].offset;
+ for (j = 0; j < req->iovs[i].len; j++) {
+ if (buf[j] != DATA_PATTERN) {
+ fprintf(stderr, "%s: %s write/read success, but memcmp Failed\n", dev->name, test_name);
+ spdk_nvme_ctrlr_free_io_qpair(qpair);
+ free_req(req);
+ return -1;
+ }
+ }
+ }
+
+ fprintf(stdout, "%s: %s test passed\n", dev->name, test_name);
+ spdk_nvme_ctrlr_free_io_qpair(qpair);
+ free_req(req);
+ return rc;
+}
+
+static bool
+probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
+ struct spdk_nvme_ctrlr_opts *opts)
+{
+ printf("Attaching to %s\n", trid->traddr);
+
+ return true;
+}
+
+static void
+attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
+ struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
+{
+ struct dev *dev;
+
+ /* add to dev list */
+ dev = &devs[num_devs++];
+
+ dev->ctrlr = ctrlr;
+
+ snprintf(dev->name, sizeof(dev->name), "%s",
+ trid->traddr);
+
+ printf("Attached to %s\n", dev->name);
+}
+
+int main(int argc, char **argv)
+{
+ struct dev *iter;
+ int rc, i;
+ struct spdk_env_opts opts;
+
+ spdk_env_opts_init(&opts);
+ opts.name = "nvme_sgl";
+ opts.core_mask = "0x1";
+ opts.shm_id = 0;
+ if (spdk_env_init(&opts) < 0) {
+ fprintf(stderr, "Unable to initialize SPDK env\n");
+ return 1;
+ }
+
+ printf("NVMe Readv/Writev Request test\n");
+
+ if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL) != 0) {
+ fprintf(stderr, "nvme_probe() failed\n");
+ exit(1);
+ }
+
+ rc = 0;
+ foreach_dev(iter) {
+#define TEST(x) writev_readv_tests(iter, x, #x)
+ if (TEST(build_io_request_0)
+ || TEST(build_io_request_1)
+ || TEST(build_io_request_2)
+ || TEST(build_io_request_3)
+ || TEST(build_io_request_4)
+ || TEST(build_io_request_5)
+ || TEST(build_io_request_6)
+ || TEST(build_io_request_7)
+ || TEST(build_io_request_8)
+ || TEST(build_io_request_9)
+ || TEST(build_io_request_10)
+ || TEST(build_io_request_11)) {
+#undef TEST
+ rc = 1;
+ printf("%s: failed sgl tests\n", iter->name);
+ }
+ }
+
+ printf("Cleaning up...\n");
+
+ for (i = 0; i < num_devs; i++) {
+ struct dev *dev = &devs[i];
+
+ spdk_nvme_detach(dev->ctrlr);
+ }
+
+ return rc;
+}