summaryrefslogtreecommitdiffstats
path: root/src/spdk/test/unit/lib/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/test/unit/lib/scsi')
-rw-r--r--src/spdk/test/unit/lib/scsi/Makefile44
-rw-r--r--src/spdk/test/unit/lib/scsi/dev.c/.gitignore1
-rw-r--r--src/spdk/test/unit/lib/scsi/dev.c/Makefile38
-rw-r--r--src/spdk/test/unit/lib/scsi/dev.c/dev_ut.c682
-rw-r--r--src/spdk/test/unit/lib/scsi/lun.c/.gitignore1
-rw-r--r--src/spdk/test/unit/lib/scsi/lun.c/Makefile38
-rw-r--r--src/spdk/test/unit/lib/scsi/lun.c/lun_ut.c750
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi.c/.gitignore1
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi.c/Makefile39
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi.c/scsi_ut.c69
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi_bdev.c/.gitignore1
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi_bdev.c/Makefile38
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi_bdev.c/scsi_bdev_ut.c1037
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi_pr.c/.gitignore1
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi_pr.c/Makefile39
-rw-r--r--src/spdk/test/unit/lib/scsi/scsi_pr.c/scsi_pr_ut.c673
16 files changed, 3452 insertions, 0 deletions
diff --git a/src/spdk/test/unit/lib/scsi/Makefile b/src/spdk/test/unit/lib/scsi/Makefile
new file mode 100644
index 000000000..8044d3f4e
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/Makefile
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..)
+include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
+
+DIRS-y = dev.c lun.c scsi.c scsi_bdev.c scsi_pr.c
+
+.PHONY: all clean $(DIRS-y)
+
+all: $(DIRS-y)
+clean: $(DIRS-y)
+
+include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk
diff --git a/src/spdk/test/unit/lib/scsi/dev.c/.gitignore b/src/spdk/test/unit/lib/scsi/dev.c/.gitignore
new file mode 100644
index 000000000..e325086bb
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/dev.c/.gitignore
@@ -0,0 +1 @@
+dev_ut
diff --git a/src/spdk/test/unit/lib/scsi/dev.c/Makefile b/src/spdk/test/unit/lib/scsi/dev.c/Makefile
new file mode 100644
index 000000000..983b3bc9e
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/dev.c/Makefile
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..)
+
+TEST_FILE = dev_ut.c
+
+include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk
diff --git a/src/spdk/test/unit/lib/scsi/dev.c/dev_ut.c b/src/spdk/test/unit/lib/scsi/dev.c/dev_ut.c
new file mode 100644
index 000000000..f738011fb
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/dev.c/dev_ut.c
@@ -0,0 +1,682 @@
+/*-
+ * 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 "CUnit/Basic.h"
+#include "spdk_cunit.h"
+
+#include "spdk/util.h"
+
+#include "scsi/dev.c"
+#include "scsi/port.c"
+
+#include "spdk_internal/mock.h"
+
+/* Unit test bdev mockup */
+struct spdk_bdev {
+ char name[100];
+};
+
+static struct spdk_bdev g_bdevs[] = {
+ {"malloc0"},
+ {"malloc1"},
+};
+
+static struct spdk_scsi_port *g_initiator_port_with_pending_tasks = NULL;
+static struct spdk_scsi_port *g_initiator_port_with_pending_mgmt_tasks = NULL;
+
+const char *
+spdk_bdev_get_name(const struct spdk_bdev *bdev)
+{
+ return bdev->name;
+}
+
+static struct spdk_scsi_task *
+spdk_get_task(uint32_t *owner_task_ctr)
+{
+ struct spdk_scsi_task *task;
+
+ task = calloc(1, sizeof(*task));
+ if (!task) {
+ return NULL;
+ }
+
+ return task;
+}
+
+void
+spdk_scsi_task_put(struct spdk_scsi_task *task)
+{
+ free(task);
+}
+
+struct spdk_scsi_lun *scsi_lun_construct(struct spdk_bdev *bdev,
+ void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
+ void *hotremove_ctx)
+{
+ struct spdk_scsi_lun *lun;
+
+ lun = calloc(1, sizeof(struct spdk_scsi_lun));
+ SPDK_CU_ASSERT_FATAL(lun != NULL);
+
+ lun->bdev = bdev;
+
+ return lun;
+}
+
+void
+scsi_lun_destruct(struct spdk_scsi_lun *lun)
+{
+ free(lun);
+}
+
+struct spdk_bdev *
+spdk_bdev_get_by_name(const char *bdev_name)
+{
+ size_t i;
+
+ for (i = 0; i < SPDK_COUNTOF(g_bdevs); i++) {
+ if (strcmp(bdev_name, g_bdevs[i].name) == 0) {
+ return &g_bdevs[i];
+ }
+ }
+
+ return NULL;
+}
+
+DEFINE_STUB_V(scsi_lun_execute_mgmt_task,
+ (struct spdk_scsi_lun *lun, struct spdk_scsi_task *task));
+
+DEFINE_STUB_V(scsi_lun_execute_task,
+ (struct spdk_scsi_lun *lun, struct spdk_scsi_task *task));
+
+DEFINE_STUB(scsi_lun_allocate_io_channel, int,
+ (struct spdk_scsi_lun *lun), 0);
+
+DEFINE_STUB_V(scsi_lun_free_io_channel, (struct spdk_scsi_lun *lun));
+
+bool
+scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun,
+ const struct spdk_scsi_port *initiator_port)
+{
+ return (g_initiator_port_with_pending_mgmt_tasks == initiator_port);
+}
+
+bool
+scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun,
+ const struct spdk_scsi_port *initiator_port)
+{
+ return (g_initiator_port_with_pending_tasks == initiator_port);
+}
+
+static void
+dev_destruct_null_dev(void)
+{
+ /* pass null for the dev */
+ spdk_scsi_dev_destruct(NULL, NULL, NULL);
+}
+
+static void
+dev_destruct_zero_luns(void)
+{
+ struct spdk_scsi_dev dev = { .is_allocated = 1 };
+
+ /* No luns attached to the dev */
+
+ /* free the dev */
+ spdk_scsi_dev_destruct(&dev, NULL, NULL);
+}
+
+static void
+dev_destruct_null_lun(void)
+{
+ struct spdk_scsi_dev dev = { .is_allocated = 1 };
+
+ /* pass null for the lun */
+ dev.lun[0] = NULL;
+
+ /* free the dev */
+ spdk_scsi_dev_destruct(&dev, NULL, NULL);
+}
+
+static void
+dev_destruct_success(void)
+{
+ struct spdk_scsi_dev dev = { .is_allocated = 1 };
+ int rc;
+
+ /* dev with a single lun */
+ rc = spdk_scsi_dev_add_lun(&dev, "malloc0", 0, NULL, NULL);
+
+ CU_ASSERT(rc == 0);
+
+ /* free the dev */
+ spdk_scsi_dev_destruct(&dev, NULL, NULL);
+
+}
+
+static void
+dev_construct_num_luns_zero(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[1] = {};
+ int lun_id_list[1] = { 0 };
+
+ dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 0,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ /* dev should be null since we passed num_luns = 0 */
+ CU_ASSERT_TRUE(dev == NULL);
+}
+
+static void
+dev_construct_no_lun_zero(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[1] = {};
+ int lun_id_list[1] = { 0 };
+
+ lun_id_list[0] = 1;
+
+ dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ /* dev should be null since no LUN0 was specified (lun_id_list[0] = 1) */
+ CU_ASSERT_TRUE(dev == NULL);
+}
+
+static void
+dev_construct_null_lun(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[1] = {};
+ int lun_id_list[1] = { 0 };
+
+ dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ /* dev should be null since no LUN0 was specified (lun_list[0] = NULL) */
+ CU_ASSERT_TRUE(dev == NULL);
+}
+
+static void
+dev_construct_name_too_long(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[1] = {"malloc0"};
+ int lun_id_list[1] = { 0 };
+ char name[SPDK_SCSI_DEV_MAX_NAME + 1 + 1];
+
+ /* Try to construct a dev with a name that is one byte longer than allowed. */
+ memset(name, 'x', sizeof(name) - 1);
+ name[sizeof(name) - 1] = '\0';
+
+ dev = spdk_scsi_dev_construct(name, bdev_name_list, lun_id_list, 1,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ CU_ASSERT(dev == NULL);
+}
+
+static void
+dev_construct_success(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[1] = {"malloc0"};
+ int lun_id_list[1] = { 0 };
+
+ dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ /* Successfully constructs and returns a dev */
+ CU_ASSERT_TRUE(dev != NULL);
+
+ /* free the dev */
+ spdk_scsi_dev_destruct(dev, NULL, NULL);
+}
+
+static void
+dev_construct_success_lun_zero_not_first(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[2] = {"malloc1", "malloc0"};
+ int lun_id_list[2] = { 1, 0 };
+
+ dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 2,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ /* Successfully constructs and returns a dev */
+ CU_ASSERT_TRUE(dev != NULL);
+
+ /* free the dev */
+ spdk_scsi_dev_destruct(dev, NULL, NULL);
+}
+
+static void
+dev_queue_mgmt_task_success(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[1] = {"malloc0"};
+ int lun_id_list[1] = { 0 };
+ struct spdk_scsi_task *task;
+
+ dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ /* Successfully constructs and returns a dev */
+ CU_ASSERT_TRUE(dev != NULL);
+
+ task = spdk_get_task(NULL);
+
+ task->function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+ spdk_scsi_dev_queue_mgmt_task(dev, task);
+
+ spdk_scsi_task_put(task);
+
+ spdk_scsi_dev_destruct(dev, NULL, NULL);
+}
+
+static void
+dev_queue_task_success(void)
+{
+ struct spdk_scsi_dev *dev;
+ const char *bdev_name_list[1] = {"malloc0"};
+ int lun_id_list[1] = { 0 };
+ struct spdk_scsi_task *task;
+
+ dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+
+ /* Successfully constructs and returns a dev */
+ CU_ASSERT_TRUE(dev != NULL);
+
+ task = spdk_get_task(NULL);
+
+ spdk_scsi_dev_queue_task(dev, task);
+
+ spdk_scsi_task_put(task);
+
+ spdk_scsi_dev_destruct(dev, NULL, NULL);
+}
+
+static void
+dev_stop_success(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ struct spdk_scsi_task *task;
+ struct spdk_scsi_task *task_mgmt;
+
+ task = spdk_get_task(NULL);
+
+ spdk_scsi_dev_queue_task(&dev, task);
+
+ task_mgmt = spdk_get_task(NULL);
+
+ /* Enqueue the tasks into dev->task_mgmt_submit_queue */
+ task->function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+ spdk_scsi_dev_queue_mgmt_task(&dev, task_mgmt);
+
+ spdk_scsi_task_put(task);
+ spdk_scsi_task_put(task_mgmt);
+}
+
+static void
+dev_add_port_max_ports(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ const char *name;
+ int id, rc;
+
+ /* dev is set to SPDK_SCSI_DEV_MAX_PORTS */
+ dev.num_ports = SPDK_SCSI_DEV_MAX_PORTS;
+ name = "Name of Port";
+ id = 1;
+
+ rc = spdk_scsi_dev_add_port(&dev, id, name);
+
+ /* returns -1; since the dev already has maximum
+ * number of ports (SPDK_SCSI_DEV_MAX_PORTS) */
+ CU_ASSERT_TRUE(rc < 0);
+}
+
+static void
+dev_add_port_construct_failure1(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ const int port_name_length = SPDK_SCSI_PORT_MAX_NAME_LENGTH + 2;
+ char name[port_name_length];
+ uint64_t id;
+ int rc;
+
+ dev.num_ports = 1;
+ /* Set the name such that the length exceeds SPDK_SCSI_PORT_MAX_NAME_LENGTH
+ * SPDK_SCSI_PORT_MAX_NAME_LENGTH = 256 */
+ memset(name, 'a', port_name_length - 1);
+ name[port_name_length - 1] = '\0';
+ id = 1;
+
+ rc = spdk_scsi_dev_add_port(&dev, id, name);
+
+ /* returns -1; since the length of the name exceeds
+ * SPDK_SCSI_PORT_MAX_NAME_LENGTH */
+ CU_ASSERT_TRUE(rc < 0);
+}
+
+static void
+dev_add_port_construct_failure2(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ const char *name;
+ uint64_t id;
+ int rc;
+
+ dev.num_ports = 1;
+ name = "Name of Port";
+ id = 1;
+
+ /* Initialize port[0] to be valid and its index is set to 1 */
+ dev.port[0].id = id;
+ dev.port[0].is_used = 1;
+
+ rc = spdk_scsi_dev_add_port(&dev, id, name);
+
+ /* returns -1; since the dev already has a port whose index to be 1 */
+ CU_ASSERT_TRUE(rc < 0);
+}
+
+static void
+dev_add_port_success1(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ const char *name;
+ int id, rc;
+
+ dev.num_ports = 1;
+ name = "Name of Port";
+ id = 1;
+
+ rc = spdk_scsi_dev_add_port(&dev, id, name);
+
+ /* successfully adds a port */
+ CU_ASSERT_EQUAL(rc, 0);
+ /* Assert num_ports has been incremented to 2 */
+ CU_ASSERT_EQUAL(dev.num_ports, 2);
+}
+
+static void
+dev_add_port_success2(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ const char *name;
+ uint64_t id;
+ int rc;
+
+ dev.num_ports = 1;
+ name = "Name of Port";
+ id = 1;
+ /* set id of invalid port[0] to 1. This must be ignored */
+ dev.port[0].id = id;
+ dev.port[0].is_used = 0;
+
+ rc = spdk_scsi_dev_add_port(&dev, id, name);
+
+ /* successfully adds a port */
+ CU_ASSERT_EQUAL(rc, 0);
+ /* Assert num_ports has been incremented to 1 */
+ CU_ASSERT_EQUAL(dev.num_ports, 2);
+}
+
+static void
+dev_add_port_success3(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ const char *name;
+ uint64_t add_id;
+ int rc;
+
+ dev.num_ports = 1;
+ name = "Name of Port";
+ dev.port[0].id = 1;
+ dev.port[0].is_used = 1;
+ add_id = 2;
+
+ /* Add a port with id = 2 */
+ rc = spdk_scsi_dev_add_port(&dev, add_id, name);
+
+ /* successfully adds a port */
+ CU_ASSERT_EQUAL(rc, 0);
+ /* Assert num_ports has been incremented to 2 */
+ CU_ASSERT_EQUAL(dev.num_ports, 2);
+}
+
+static void
+dev_find_port_by_id_num_ports_zero(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ struct spdk_scsi_port *rp_port;
+ uint64_t id;
+
+ dev.num_ports = 0;
+ id = 1;
+
+ rp_port = spdk_scsi_dev_find_port_by_id(&dev, id);
+
+ /* returns null; since dev's num_ports is 0 */
+ CU_ASSERT_TRUE(rp_port == NULL);
+}
+
+static void
+dev_find_port_by_id_id_not_found_failure(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ struct spdk_scsi_port *rp_port;
+ const char *name;
+ int rc;
+ uint64_t id, find_id;
+
+ id = 1;
+ dev.num_ports = 1;
+ name = "Name of Port";
+ find_id = 2;
+
+ /* Add a port with id = 1 */
+ rc = spdk_scsi_dev_add_port(&dev, id, name);
+
+ CU_ASSERT_EQUAL(rc, 0);
+
+ /* Find port with id = 2 */
+ rp_port = spdk_scsi_dev_find_port_by_id(&dev, find_id);
+
+ /* returns null; failed to find port specified by id = 2 */
+ CU_ASSERT_TRUE(rp_port == NULL);
+}
+
+static void
+dev_find_port_by_id_success(void)
+{
+ struct spdk_scsi_dev dev = { 0 };
+ struct spdk_scsi_port *rp_port;
+ const char *name;
+ int rc;
+ uint64_t id;
+
+ id = 1;
+ dev.num_ports = 1;
+ name = "Name of Port";
+
+ /* Add a port */
+ rc = spdk_scsi_dev_add_port(&dev, id, name);
+
+ CU_ASSERT_EQUAL(rc, 0);
+
+ /* Find port by the same id as the one added above */
+ rp_port = spdk_scsi_dev_find_port_by_id(&dev, id);
+
+ /* Successfully found port specified by id */
+ CU_ASSERT_TRUE(rp_port != NULL);
+ if (rp_port != NULL) {
+ /* Assert the found port's id and name are same as
+ * the port added. */
+ CU_ASSERT_EQUAL(rp_port->id, 1);
+ CU_ASSERT_STRING_EQUAL(rp_port->name, "Name of Port");
+ }
+}
+
+static void
+dev_add_lun_bdev_not_found(void)
+{
+ int rc;
+ struct spdk_scsi_dev dev = {0};
+
+ rc = spdk_scsi_dev_add_lun(&dev, "malloc2", 0, NULL, NULL);
+
+ SPDK_CU_ASSERT_FATAL(dev.lun[0] == NULL);
+ CU_ASSERT_NOT_EQUAL(rc, 0);
+}
+
+static void
+dev_add_lun_no_free_lun_id(void)
+{
+ int rc;
+ int i;
+ struct spdk_scsi_dev dev = {0};
+ struct spdk_scsi_lun lun;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ dev.lun[i] = &lun;
+ }
+
+ rc = spdk_scsi_dev_add_lun(&dev, "malloc0", -1, NULL, NULL);
+
+ CU_ASSERT_NOT_EQUAL(rc, 0);
+}
+
+static void
+dev_add_lun_success1(void)
+{
+ int rc;
+ struct spdk_scsi_dev dev = {0};
+
+ rc = spdk_scsi_dev_add_lun(&dev, "malloc0", -1, NULL, NULL);
+
+ CU_ASSERT_EQUAL(rc, 0);
+
+ spdk_scsi_dev_destruct(&dev, NULL, NULL);
+}
+
+static void
+dev_add_lun_success2(void)
+{
+ int rc;
+ struct spdk_scsi_dev dev = {0};
+
+ rc = spdk_scsi_dev_add_lun(&dev, "malloc0", 0, NULL, NULL);
+
+ CU_ASSERT_EQUAL(rc, 0);
+
+ spdk_scsi_dev_destruct(&dev, NULL, NULL);
+}
+
+static void
+dev_check_pending_tasks(void)
+{
+ struct spdk_scsi_dev dev = {};
+ struct spdk_scsi_lun lun = {};
+ struct spdk_scsi_port initiator_port = {};
+
+ g_initiator_port_with_pending_tasks = NULL;
+ g_initiator_port_with_pending_mgmt_tasks = NULL;
+
+ CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == false);
+
+ dev.lun[SPDK_SCSI_DEV_MAX_LUN - 1] = &lun;
+
+ CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == true);
+ CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, &initiator_port) == false);
+
+ g_initiator_port_with_pending_tasks = &initiator_port;
+ CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == true);
+ CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, &initiator_port) == true);
+
+ g_initiator_port_with_pending_tasks = NULL;
+ g_initiator_port_with_pending_mgmt_tasks = &initiator_port;
+ CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == true);
+ CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, &initiator_port) == true);
+}
+
+int
+main(int argc, char **argv)
+{
+ CU_pSuite suite = NULL;
+ unsigned int num_failures;
+
+ CU_set_error_action(CUEA_ABORT);
+ CU_initialize_registry();
+
+ suite = CU_add_suite("dev_suite", NULL, NULL);
+
+ CU_ADD_TEST(suite, dev_destruct_null_dev);
+ CU_ADD_TEST(suite, dev_destruct_zero_luns);
+ CU_ADD_TEST(suite, dev_destruct_null_lun);
+ CU_ADD_TEST(suite, dev_destruct_success);
+ CU_ADD_TEST(suite, dev_construct_num_luns_zero);
+ CU_ADD_TEST(suite, dev_construct_no_lun_zero);
+ CU_ADD_TEST(suite, dev_construct_null_lun);
+ CU_ADD_TEST(suite, dev_construct_name_too_long);
+ CU_ADD_TEST(suite, dev_construct_success);
+ CU_ADD_TEST(suite, dev_construct_success_lun_zero_not_first);
+ CU_ADD_TEST(suite, dev_queue_mgmt_task_success);
+ CU_ADD_TEST(suite, dev_queue_task_success);
+ CU_ADD_TEST(suite, dev_stop_success);
+ CU_ADD_TEST(suite, dev_add_port_max_ports);
+ CU_ADD_TEST(suite, dev_add_port_construct_failure1);
+ CU_ADD_TEST(suite, dev_add_port_construct_failure2);
+ CU_ADD_TEST(suite, dev_add_port_success1);
+ CU_ADD_TEST(suite, dev_add_port_success2);
+ CU_ADD_TEST(suite, dev_add_port_success3);
+ CU_ADD_TEST(suite, dev_find_port_by_id_num_ports_zero);
+ CU_ADD_TEST(suite, dev_find_port_by_id_id_not_found_failure);
+ CU_ADD_TEST(suite, dev_find_port_by_id_success);
+ CU_ADD_TEST(suite, dev_add_lun_bdev_not_found);
+ CU_ADD_TEST(suite, dev_add_lun_no_free_lun_id);
+ CU_ADD_TEST(suite, dev_add_lun_success1);
+ CU_ADD_TEST(suite, dev_add_lun_success2);
+ CU_ADD_TEST(suite, dev_check_pending_tasks);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ num_failures = CU_get_number_of_failures();
+ CU_cleanup_registry();
+
+ return num_failures;
+}
diff --git a/src/spdk/test/unit/lib/scsi/lun.c/.gitignore b/src/spdk/test/unit/lib/scsi/lun.c/.gitignore
new file mode 100644
index 000000000..89bd2aaf1
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/lun.c/.gitignore
@@ -0,0 +1 @@
+lun_ut
diff --git a/src/spdk/test/unit/lib/scsi/lun.c/Makefile b/src/spdk/test/unit/lib/scsi/lun.c/Makefile
new file mode 100644
index 000000000..95e179fe5
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/lun.c/Makefile
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..)
+
+TEST_FILE = lun_ut.c
+
+include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk
diff --git a/src/spdk/test/unit/lib/scsi/lun.c/lun_ut.c b/src/spdk/test/unit/lib/scsi/lun.c/lun_ut.c
new file mode 100644
index 000000000..4efa8e364
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/lun.c/lun_ut.c
@@ -0,0 +1,750 @@
+/*-
+ * 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_cunit.h"
+
+#include "scsi/task.c"
+#include "scsi/lun.c"
+
+#include "spdk_internal/mock.h"
+/* These unit tests aren't multithreads, but we need to allocate threads since
+ * the lun.c code will register pollers.
+ */
+#include "common/lib/ut_multithread.c"
+
+/* Unit test bdev mockup */
+struct spdk_bdev {
+ int x;
+};
+
+SPDK_LOG_REGISTER_COMPONENT("scsi", SPDK_LOG_SCSI)
+
+struct spdk_scsi_globals g_spdk_scsi;
+
+static bool g_lun_execute_fail = false;
+static int g_lun_execute_status = SPDK_SCSI_TASK_PENDING;
+static uint32_t g_task_count = 0;
+
+struct spdk_trace_histories *g_trace_histories;
+
+DEFINE_STUB_V(_spdk_trace_record,
+ (uint64_t tsc, uint16_t tpoint_id, uint16_t poller_id,
+ uint32_t size, uint64_t object_id, uint64_t arg1));
+
+static void
+spdk_lun_ut_cpl_task(struct spdk_scsi_task *task)
+{
+ SPDK_CU_ASSERT_FATAL(g_task_count > 0);
+ g_task_count--;
+}
+
+static void
+spdk_lun_ut_free_task(struct spdk_scsi_task *task)
+{
+}
+
+static void
+ut_init_task(struct spdk_scsi_task *task)
+{
+ memset(task, 0, sizeof(*task));
+ spdk_scsi_task_construct(task, spdk_lun_ut_cpl_task,
+ spdk_lun_ut_free_task);
+ g_task_count++;
+}
+
+void
+spdk_bdev_free_io(struct spdk_bdev_io *bdev_io)
+{
+ CU_ASSERT(0);
+}
+
+DEFINE_STUB(spdk_bdev_open, int,
+ (struct spdk_bdev *bdev, bool write, spdk_bdev_remove_cb_t remove_cb,
+ void *remove_ctx, struct spdk_bdev_desc **desc),
+ 0);
+
+DEFINE_STUB_V(spdk_bdev_close, (struct spdk_bdev_desc *desc));
+
+DEFINE_STUB(spdk_bdev_get_name, const char *,
+ (const struct spdk_bdev *bdev), "test");
+
+DEFINE_STUB_V(spdk_scsi_dev_queue_mgmt_task,
+ (struct spdk_scsi_dev *dev, struct spdk_scsi_task *task));
+
+DEFINE_STUB_V(spdk_scsi_dev_delete_lun,
+ (struct spdk_scsi_dev *dev, struct spdk_scsi_lun *lun));
+
+DEFINE_STUB(scsi_pr_check, int, (struct spdk_scsi_task *task), 0);
+DEFINE_STUB(scsi2_reserve_check, int, (struct spdk_scsi_task *task), 0);
+
+void
+bdev_scsi_reset(struct spdk_scsi_task *task)
+{
+ task->status = SPDK_SCSI_STATUS_GOOD;
+ task->response = SPDK_SCSI_TASK_MGMT_RESP_SUCCESS;
+
+ scsi_lun_complete_reset_task(task->lun, task);
+}
+
+int
+bdev_scsi_execute(struct spdk_scsi_task *task)
+{
+ if (g_lun_execute_fail) {
+ return -EINVAL;
+ } else {
+ task->status = SPDK_SCSI_STATUS_GOOD;
+
+ if (g_lun_execute_status == SPDK_SCSI_TASK_PENDING) {
+ return g_lun_execute_status;
+ } else if (g_lun_execute_status == SPDK_SCSI_TASK_COMPLETE) {
+ return g_lun_execute_status;
+ } else {
+ return 0;
+ }
+ }
+}
+
+DEFINE_STUB(spdk_bdev_get_io_channel, struct spdk_io_channel *,
+ (struct spdk_bdev_desc *desc), NULL);
+
+static struct spdk_scsi_lun *lun_construct(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_bdev bdev;
+
+ lun = scsi_lun_construct(&bdev, NULL, NULL);
+
+ SPDK_CU_ASSERT_FATAL(lun != NULL);
+ return lun;
+}
+
+static void
+lun_destruct(struct spdk_scsi_lun *lun)
+{
+ /* LUN will defer its removal if there are any unfinished tasks */
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&lun->tasks));
+
+ scsi_lun_destruct(lun);
+}
+
+static void
+lun_task_mgmt_execute_abort_task_not_supported(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task = { 0 };
+ struct spdk_scsi_task mgmt_task = { 0 };
+ struct spdk_scsi_port initiator_port = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+ uint8_t cdb[6] = { 0 };
+
+ lun = lun_construct();
+ lun->dev = &dev;
+
+ ut_init_task(&mgmt_task);
+ mgmt_task.lun = lun;
+ mgmt_task.initiator_port = &initiator_port;
+ mgmt_task.function = SPDK_SCSI_TASK_FUNC_ABORT_TASK;
+
+ /* Params to add regular task to the lun->tasks */
+ ut_init_task(&task);
+ task.lun = lun;
+ task.cdb = cdb;
+
+ scsi_lun_execute_task(lun, &task);
+
+ /* task should now be on the tasks list */
+ CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
+
+ scsi_lun_execute_mgmt_task(lun, &mgmt_task);
+
+ /* task abort is not supported */
+ CU_ASSERT(mgmt_task.response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED);
+
+ /* task is still on the tasks list */
+ CU_ASSERT_EQUAL(g_task_count, 1);
+
+ scsi_lun_complete_task(lun, &task);
+ CU_ASSERT_EQUAL(g_task_count, 0);
+
+ lun_destruct(lun);
+}
+
+static void
+lun_task_mgmt_execute_abort_task_all_not_supported(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task = { 0 };
+ struct spdk_scsi_task mgmt_task = { 0 };
+ struct spdk_scsi_port initiator_port = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+ uint8_t cdb[6] = { 0 };
+
+ lun = lun_construct();
+ lun->dev = &dev;
+
+ ut_init_task(&mgmt_task);
+ mgmt_task.lun = lun;
+ mgmt_task.initiator_port = &initiator_port;
+ mgmt_task.function = SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET;
+
+ /* Params to add regular task to the lun->tasks */
+ ut_init_task(&task);
+ task.initiator_port = &initiator_port;
+ task.lun = lun;
+ task.cdb = cdb;
+
+ scsi_lun_execute_task(lun, &task);
+
+ /* task should now be on the tasks list */
+ CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
+
+ scsi_lun_execute_mgmt_task(lun, &mgmt_task);
+
+ /* task abort is not supported */
+ CU_ASSERT(mgmt_task.response == SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED);
+
+ /* task is still on the tasks list */
+ CU_ASSERT_EQUAL(g_task_count, 1);
+
+ scsi_lun_complete_task(lun, &task);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+
+ lun_destruct(lun);
+}
+
+static void
+lun_task_mgmt_execute_lun_reset(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task mgmt_task = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+
+ lun = lun_construct();
+ lun->dev = &dev;
+
+ ut_init_task(&mgmt_task);
+ mgmt_task.lun = lun;
+ mgmt_task.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+
+ scsi_lun_execute_mgmt_task(lun, &mgmt_task);
+
+ /* Returns success */
+ CU_ASSERT_EQUAL(mgmt_task.status, SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT_EQUAL(mgmt_task.response, SPDK_SCSI_TASK_MGMT_RESP_SUCCESS);
+
+ lun_destruct(lun);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_task_mgmt_execute_invalid_case(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task mgmt_task = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+
+ lun = lun_construct();
+ lun->dev = &dev;
+
+ ut_init_task(&mgmt_task);
+ mgmt_task.function = 5;
+
+ /* Pass an invalid value to the switch statement */
+ scsi_lun_execute_mgmt_task(lun, &mgmt_task);
+
+ /* function code is invalid */
+ CU_ASSERT_EQUAL(mgmt_task.response, SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED);
+
+ lun_destruct(lun);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_append_task_null_lun_task_cdb_spc_inquiry(void)
+{
+ struct spdk_scsi_task task = { 0 };
+ uint8_t cdb[6] = { 0 };
+
+ ut_init_task(&task);
+ task.cdb = cdb;
+ task.cdb[0] = SPDK_SPC_INQUIRY;
+ /* alloc_len >= 4096 */
+ task.cdb[3] = 0xFF;
+ task.cdb[4] = 0xFF;
+ task.lun = NULL;
+
+ spdk_scsi_task_process_null_lun(&task);
+
+ CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_GOOD);
+
+ spdk_scsi_task_put(&task);
+
+ /* spdk_scsi_task_process_null_lun() does not call cpl_fn */
+ CU_ASSERT_EQUAL(g_task_count, 1);
+ g_task_count = 0;
+}
+
+static void
+lun_append_task_null_lun_alloc_len_lt_4096(void)
+{
+ struct spdk_scsi_task task = { 0 };
+ uint8_t cdb[6] = { 0 };
+
+ ut_init_task(&task);
+ task.cdb = cdb;
+ task.cdb[0] = SPDK_SPC_INQUIRY;
+ /* alloc_len < 4096 */
+ task.cdb[3] = 0;
+ task.cdb[4] = 0;
+ /* alloc_len is set to a minimal value of 4096
+ * Hence, buf of size 4096 is allocated */
+ spdk_scsi_task_process_null_lun(&task);
+
+ CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_GOOD);
+
+ spdk_scsi_task_put(&task);
+
+ /* spdk_scsi_task_process_null_lun() does not call cpl_fn */
+ CU_ASSERT_EQUAL(g_task_count, 1);
+ g_task_count = 0;
+}
+
+static void
+lun_append_task_null_lun_not_supported(void)
+{
+ struct spdk_scsi_task task = { 0 };
+ uint8_t cdb[6] = { 0 };
+
+ ut_init_task(&task);
+ task.cdb = cdb;
+ task.lun = NULL;
+
+ spdk_scsi_task_process_null_lun(&task);
+
+ CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION);
+ /* LUN not supported; task's data transferred should be 0 */
+ CU_ASSERT_EQUAL(task.data_transferred, 0);
+
+ /* spdk_scsi_task_process_null_lun() does not call cpl_fn */
+ CU_ASSERT_EQUAL(g_task_count, 1);
+ g_task_count = 0;
+}
+
+static void
+lun_execute_scsi_task_pending(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+
+ lun = lun_construct();
+
+ ut_init_task(&task);
+ task.lun = lun;
+ lun->dev = &dev;
+
+ g_lun_execute_fail = false;
+ g_lun_execute_status = SPDK_SCSI_TASK_PENDING;
+
+ /* the tasks list should still be empty since it has not been
+ executed yet
+ */
+ CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
+
+ scsi_lun_execute_task(lun, &task);
+
+ /* Assert the task has been successfully added to the tasks queue */
+ CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
+
+ /* task is still on the tasks list */
+ CU_ASSERT_EQUAL(g_task_count, 1);
+
+ /* Need to complete task so LUN might be removed right now */
+ scsi_lun_complete_task(lun, &task);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+
+ lun_destruct(lun);
+}
+
+static void
+lun_execute_scsi_task_complete(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+
+ lun = lun_construct();
+
+ ut_init_task(&task);
+ task.lun = lun;
+ lun->dev = &dev;
+
+ g_lun_execute_fail = false;
+ g_lun_execute_status = SPDK_SCSI_TASK_COMPLETE;
+
+ /* the tasks list should still be empty since it has not been
+ executed yet
+ */
+ CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
+
+ scsi_lun_execute_task(lun, &task);
+
+ /* Assert the task has not been added to the tasks queue */
+ CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
+
+ lun_destruct(lun);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_destruct_success(void)
+{
+ struct spdk_scsi_lun *lun;
+
+ lun = lun_construct();
+
+ scsi_lun_destruct(lun);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_construct_null_ctx(void)
+{
+ struct spdk_scsi_lun *lun;
+
+ lun = scsi_lun_construct(NULL, NULL, NULL);
+
+ /* lun should be NULL since we passed NULL for the ctx pointer. */
+ CU_ASSERT(lun == NULL);
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_construct_success(void)
+{
+ struct spdk_scsi_lun *lun = lun_construct();
+
+ lun_destruct(lun);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_reset_task_wait_scsi_task_complete(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task = { 0 };
+ struct spdk_scsi_task mgmt_task = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+
+ lun = lun_construct();
+ lun->dev = &dev;
+
+ ut_init_task(&task);
+ task.lun = lun;
+
+ g_lun_execute_fail = false;
+ g_lun_execute_status = SPDK_SCSI_TASK_PENDING;
+
+ ut_init_task(&mgmt_task);
+ mgmt_task.lun = lun;
+ mgmt_task.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+
+ /* Execute the task but it is still in the task list. */
+ scsi_lun_execute_task(lun, &task);
+
+ CU_ASSERT(TAILQ_EMPTY(&lun->pending_tasks));
+ CU_ASSERT(!TAILQ_EMPTY(&lun->tasks));
+
+ /* Execute the reset task */
+ scsi_lun_execute_mgmt_task(lun, &mgmt_task);
+
+ /* The reset task should be on the submitted mgmt task list and
+ * a poller is created because the task prior to the reset task is pending.
+ */
+ CU_ASSERT(!TAILQ_EMPTY(&lun->mgmt_tasks));
+ CU_ASSERT(lun->reset_poller != NULL);
+
+ /* Execute the poller to check if the task prior to the reset task complete. */
+ scsi_lun_reset_check_outstanding_tasks(&mgmt_task);
+
+ CU_ASSERT(!TAILQ_EMPTY(&lun->mgmt_tasks));
+ CU_ASSERT(lun->reset_poller != NULL);
+
+ /* Complete the task. */
+ scsi_lun_complete_task(lun, &task);
+
+ CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
+
+ /* Execute the poller to check if the task prior to the reset task complete. */
+ scsi_lun_reset_check_outstanding_tasks(&mgmt_task);
+
+ CU_ASSERT(TAILQ_EMPTY(&lun->mgmt_tasks));
+ CU_ASSERT(lun->reset_poller == NULL);
+ CU_ASSERT_EQUAL(mgmt_task.status, SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT_EQUAL(mgmt_task.response, SPDK_SCSI_TASK_MGMT_RESP_SUCCESS);
+
+ lun_destruct(lun);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_reset_task_suspend_scsi_task(void)
+{
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task = { 0 };
+ struct spdk_scsi_task mgmt_task = { 0 };
+ struct spdk_scsi_dev dev = { 0 };
+
+ lun = lun_construct();
+ lun->dev = &dev;
+
+ ut_init_task(&task);
+ task.lun = lun;
+
+ g_lun_execute_fail = false;
+ g_lun_execute_status = SPDK_SCSI_TASK_COMPLETE;
+
+ ut_init_task(&mgmt_task);
+ mgmt_task.lun = lun;
+ mgmt_task.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+
+ /* Append a reset task to the pending mgmt task list. */
+ scsi_lun_append_mgmt_task(lun, &mgmt_task);
+
+ CU_ASSERT(!TAILQ_EMPTY(&lun->pending_mgmt_tasks));
+
+ /* Execute the task but it is on the pending task list. */
+ scsi_lun_execute_task(lun, &task);
+
+ CU_ASSERT(!TAILQ_EMPTY(&lun->pending_tasks));
+
+ /* Execute the reset task. The task will be executed then. */
+ _scsi_lun_execute_mgmt_task(lun);
+
+ CU_ASSERT(TAILQ_EMPTY(&lun->mgmt_tasks));
+ CU_ASSERT(lun->reset_poller == NULL);
+ CU_ASSERT_EQUAL(mgmt_task.status, SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT_EQUAL(mgmt_task.response, SPDK_SCSI_TASK_MGMT_RESP_SUCCESS);
+
+ CU_ASSERT(TAILQ_EMPTY(&lun->pending_tasks));
+ CU_ASSERT(TAILQ_EMPTY(&lun->tasks));
+
+ lun_destruct(lun);
+
+ CU_ASSERT_EQUAL(g_task_count, 0);
+}
+
+static void
+lun_check_pending_tasks_only_for_specific_initiator(void)
+{
+ struct spdk_bdev bdev = {};
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task1 = {};
+ struct spdk_scsi_task task2 = {};
+ struct spdk_scsi_port initiator_port1 = {};
+ struct spdk_scsi_port initiator_port2 = {};
+ struct spdk_scsi_port initiator_port3 = {};
+
+ lun = scsi_lun_construct(&bdev, NULL, NULL);
+
+ task1.initiator_port = &initiator_port1;
+ task2.initiator_port = &initiator_port2;
+
+ TAILQ_INSERT_TAIL(&lun->tasks, &task1, scsi_link);
+ TAILQ_INSERT_TAIL(&lun->tasks, &task2, scsi_link);
+ CU_ASSERT(scsi_lun_has_outstanding_tasks(lun) == true);
+ CU_ASSERT(_scsi_lun_has_pending_tasks(lun) == false);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, NULL) == true);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, &initiator_port1) == true);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, &initiator_port2) == true);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, &initiator_port3) == false);
+ TAILQ_REMOVE(&lun->tasks, &task1, scsi_link);
+ TAILQ_REMOVE(&lun->tasks, &task2, scsi_link);
+ CU_ASSERT(_scsi_lun_has_pending_tasks(lun) == false);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, NULL) == false);
+
+ TAILQ_INSERT_TAIL(&lun->pending_tasks, &task1, scsi_link);
+ TAILQ_INSERT_TAIL(&lun->pending_tasks, &task2, scsi_link);
+ CU_ASSERT(scsi_lun_has_outstanding_tasks(lun) == false);
+ CU_ASSERT(_scsi_lun_has_pending_tasks(lun) == true);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, NULL) == true);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, &initiator_port1) == true);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, &initiator_port2) == true);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, &initiator_port3) == false);
+ TAILQ_REMOVE(&lun->pending_tasks, &task1, scsi_link);
+ TAILQ_REMOVE(&lun->pending_tasks, &task2, scsi_link);
+ CU_ASSERT(_scsi_lun_has_pending_tasks(lun) == false);
+ CU_ASSERT(scsi_lun_has_pending_tasks(lun, NULL) == false);
+
+ TAILQ_INSERT_TAIL(&lun->mgmt_tasks, &task1, scsi_link);
+ TAILQ_INSERT_TAIL(&lun->mgmt_tasks, &task2, scsi_link);
+ CU_ASSERT(scsi_lun_has_outstanding_mgmt_tasks(lun) == true);
+ CU_ASSERT(_scsi_lun_has_pending_mgmt_tasks(lun) == false);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, NULL) == true);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, &initiator_port1) == true);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, &initiator_port2) == true);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, &initiator_port3) == false);
+ TAILQ_REMOVE(&lun->mgmt_tasks, &task1, scsi_link);
+ TAILQ_REMOVE(&lun->mgmt_tasks, &task2, scsi_link);
+ CU_ASSERT(_scsi_lun_has_pending_mgmt_tasks(lun) == false);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, NULL) == false);
+
+ TAILQ_INSERT_TAIL(&lun->pending_mgmt_tasks, &task1, scsi_link);
+ TAILQ_INSERT_TAIL(&lun->pending_mgmt_tasks, &task2, scsi_link);
+ CU_ASSERT(_scsi_lun_has_pending_mgmt_tasks(lun) == true);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, NULL) == true);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, &initiator_port1) == true);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, &initiator_port2) == true);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, &initiator_port3) == false);
+ TAILQ_REMOVE(&lun->pending_mgmt_tasks, &task1, scsi_link);
+ TAILQ_REMOVE(&lun->pending_mgmt_tasks, &task2, scsi_link);
+ CU_ASSERT(_scsi_lun_has_pending_mgmt_tasks(lun) == false);
+ CU_ASSERT(scsi_lun_has_pending_mgmt_tasks(lun, NULL) == false);
+
+ scsi_lun_remove(lun);
+}
+
+static void
+abort_pending_mgmt_tasks_when_lun_is_removed(void)
+{
+ struct spdk_bdev bdev = {};
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_task task1, task2, task3;
+
+ lun = scsi_lun_construct(&bdev, NULL, NULL);
+
+ /* Normal case */
+ ut_init_task(&task1);
+ ut_init_task(&task2);
+ ut_init_task(&task3);
+ task1.lun = lun;
+ task2.lun = lun;
+ task3.lun = lun;
+ task1.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+ task2.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+ task3.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+
+ CU_ASSERT(g_task_count == 3);
+
+ scsi_lun_append_mgmt_task(lun, &task1);
+ scsi_lun_append_mgmt_task(lun, &task2);
+ scsi_lun_append_mgmt_task(lun, &task3);
+
+ CU_ASSERT(!TAILQ_EMPTY(&lun->pending_mgmt_tasks));
+
+ _scsi_lun_execute_mgmt_task(lun);
+
+ CU_ASSERT(TAILQ_EMPTY(&lun->pending_mgmt_tasks));
+ CU_ASSERT(TAILQ_EMPTY(&lun->mgmt_tasks));
+ CU_ASSERT(g_task_count == 0);
+ CU_ASSERT(task1.response == SPDK_SCSI_TASK_MGMT_RESP_SUCCESS);
+ CU_ASSERT(task2.response == SPDK_SCSI_TASK_MGMT_RESP_SUCCESS);
+ CU_ASSERT(task3.response == SPDK_SCSI_TASK_MGMT_RESP_SUCCESS);
+
+ /* LUN hotplug case */
+ ut_init_task(&task1);
+ ut_init_task(&task2);
+ ut_init_task(&task3);
+ task1.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+ task2.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+ task3.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
+
+ CU_ASSERT(g_task_count == 3);
+
+ scsi_lun_append_mgmt_task(lun, &task1);
+ scsi_lun_append_mgmt_task(lun, &task2);
+ scsi_lun_append_mgmt_task(lun, &task3);
+
+ CU_ASSERT(!TAILQ_EMPTY(&lun->pending_mgmt_tasks));
+
+ lun->removed = true;
+
+ _scsi_lun_execute_mgmt_task(lun);
+
+ CU_ASSERT(TAILQ_EMPTY(&lun->pending_mgmt_tasks));
+ CU_ASSERT(TAILQ_EMPTY(&lun->mgmt_tasks));
+ CU_ASSERT(g_task_count == 0);
+ CU_ASSERT(task1.response == SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN);
+ CU_ASSERT(task2.response == SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN);
+ CU_ASSERT(task3.response == SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN);
+
+ scsi_lun_remove(lun);
+}
+
+int
+main(int argc, char **argv)
+{
+ CU_pSuite suite = NULL;
+ unsigned int num_failures;
+
+ CU_set_error_action(CUEA_ABORT);
+ CU_initialize_registry();
+
+ suite = CU_add_suite("lun_suite", NULL, NULL);
+
+ CU_ADD_TEST(suite, lun_task_mgmt_execute_abort_task_not_supported);
+ CU_ADD_TEST(suite, lun_task_mgmt_execute_abort_task_all_not_supported);
+ CU_ADD_TEST(suite, lun_task_mgmt_execute_lun_reset);
+ CU_ADD_TEST(suite, lun_task_mgmt_execute_invalid_case);
+ CU_ADD_TEST(suite, lun_append_task_null_lun_task_cdb_spc_inquiry);
+ CU_ADD_TEST(suite, lun_append_task_null_lun_alloc_len_lt_4096);
+ CU_ADD_TEST(suite, lun_append_task_null_lun_not_supported);
+ CU_ADD_TEST(suite, lun_execute_scsi_task_pending);
+ CU_ADD_TEST(suite, lun_execute_scsi_task_complete);
+ CU_ADD_TEST(suite, lun_destruct_success);
+ CU_ADD_TEST(suite, lun_construct_null_ctx);
+ CU_ADD_TEST(suite, lun_construct_success);
+ CU_ADD_TEST(suite, lun_reset_task_wait_scsi_task_complete);
+ CU_ADD_TEST(suite, lun_reset_task_suspend_scsi_task);
+ CU_ADD_TEST(suite, lun_check_pending_tasks_only_for_specific_initiator);
+ CU_ADD_TEST(suite, abort_pending_mgmt_tasks_when_lun_is_removed);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ allocate_threads(1);
+ set_thread(0);
+ CU_basic_run_tests();
+ free_threads();
+ num_failures = CU_get_number_of_failures();
+ CU_cleanup_registry();
+ return num_failures;
+}
diff --git a/src/spdk/test/unit/lib/scsi/scsi.c/.gitignore b/src/spdk/test/unit/lib/scsi/scsi.c/.gitignore
new file mode 100644
index 000000000..99a7db2b1
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi.c/.gitignore
@@ -0,0 +1 @@
+scsi_ut
diff --git a/src/spdk/test/unit/lib/scsi/scsi.c/Makefile b/src/spdk/test/unit/lib/scsi/scsi.c/Makefile
new file mode 100644
index 000000000..2ed249227
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi.c/Makefile
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..)
+
+SPDK_LIB_LIST = trace
+TEST_FILE = scsi_ut.c
+
+include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk
diff --git a/src/spdk/test/unit/lib/scsi/scsi.c/scsi_ut.c b/src/spdk/test/unit/lib/scsi/scsi.c/scsi_ut.c
new file mode 100644
index 000000000..430ff96b0
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi.c/scsi_ut.c
@@ -0,0 +1,69 @@
+/*-
+ * 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/scsi.h"
+
+#include "spdk_cunit.h"
+
+#include "scsi/scsi.c"
+
+static void
+scsi_init(void)
+{
+ int rc;
+
+ rc = spdk_scsi_init();
+ CU_ASSERT_EQUAL(rc, 0);
+}
+
+int
+main(int argc, char **argv)
+{
+ CU_pSuite suite = NULL;
+ unsigned int num_failures;
+
+ CU_set_error_action(CUEA_ABORT);
+ CU_initialize_registry();
+
+ suite = CU_add_suite("scsi_suite", NULL, NULL);
+
+ CU_ADD_TEST(suite, scsi_init);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ num_failures = CU_get_number_of_failures();
+ CU_cleanup_registry();
+ return num_failures;
+}
diff --git a/src/spdk/test/unit/lib/scsi/scsi_bdev.c/.gitignore b/src/spdk/test/unit/lib/scsi/scsi_bdev.c/.gitignore
new file mode 100644
index 000000000..8f1ecc12c
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi_bdev.c/.gitignore
@@ -0,0 +1 @@
+scsi_bdev_ut
diff --git a/src/spdk/test/unit/lib/scsi/scsi_bdev.c/Makefile b/src/spdk/test/unit/lib/scsi/scsi_bdev.c/Makefile
new file mode 100644
index 000000000..66a4119bb
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi_bdev.c/Makefile
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..)
+
+TEST_FILE = scsi_bdev_ut.c
+
+include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk
diff --git a/src/spdk/test/unit/lib/scsi/scsi_bdev.c/scsi_bdev_ut.c b/src/spdk/test/unit/lib/scsi/scsi_bdev.c/scsi_bdev_ut.c
new file mode 100644
index 000000000..4e64f7071
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi_bdev.c/scsi_bdev_ut.c
@@ -0,0 +1,1037 @@
+/*-
+ * 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 "scsi/task.c"
+#include "scsi/scsi_bdev.c"
+#include "common/lib/test_env.c"
+
+#include "spdk_cunit.h"
+
+#include "spdk_internal/mock.h"
+
+SPDK_LOG_REGISTER_COMPONENT("scsi", SPDK_LOG_SCSI)
+
+struct spdk_scsi_globals g_spdk_scsi;
+
+static uint64_t g_test_bdev_num_blocks;
+
+TAILQ_HEAD(, spdk_bdev_io) g_bdev_io_queue;
+int g_scsi_cb_called = 0;
+
+TAILQ_HEAD(, spdk_bdev_io_wait_entry) g_io_wait_queue;
+bool g_bdev_io_pool_full = false;
+
+bool
+spdk_bdev_io_type_supported(struct spdk_bdev *bdev, enum spdk_bdev_io_type io_type)
+{
+ abort();
+ return false;
+}
+
+DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *bdev_io));
+
+DEFINE_STUB(spdk_bdev_get_name, const char *,
+ (const struct spdk_bdev *bdev), "test");
+
+DEFINE_STUB(spdk_bdev_get_block_size, uint32_t,
+ (const struct spdk_bdev *bdev), 512);
+
+DEFINE_STUB(spdk_bdev_get_md_size, uint32_t,
+ (const struct spdk_bdev *bdev), 8);
+
+DEFINE_STUB(spdk_bdev_is_md_interleaved, bool,
+ (const struct spdk_bdev *bdev), false);
+
+DEFINE_STUB(spdk_bdev_get_data_block_size, uint32_t,
+ (const struct spdk_bdev *bdev), 512);
+
+uint64_t
+spdk_bdev_get_num_blocks(const struct spdk_bdev *bdev)
+{
+ return g_test_bdev_num_blocks;
+}
+
+DEFINE_STUB(spdk_bdev_get_product_name, const char *,
+ (const struct spdk_bdev *bdev), "test product");
+
+DEFINE_STUB(spdk_bdev_has_write_cache, bool,
+ (const struct spdk_bdev *bdev), false);
+
+DEFINE_STUB(spdk_bdev_get_dif_type, enum spdk_dif_type,
+ (const struct spdk_bdev *bdev), SPDK_DIF_DISABLE);
+
+DEFINE_STUB(spdk_bdev_is_dif_head_of_md, bool,
+ (const struct spdk_bdev *bdev), false);
+
+DEFINE_STUB(spdk_bdev_is_dif_check_enabled, bool,
+ (const struct spdk_bdev *bdev, enum spdk_dif_check_type check_type), false);
+
+DEFINE_STUB(scsi_pr_out, int, (struct spdk_scsi_task *task,
+ uint8_t *cdb, uint8_t *data, uint16_t data_len), 0);
+
+DEFINE_STUB(scsi_pr_in, int, (struct spdk_scsi_task *task, uint8_t *cdb,
+ uint8_t *data, uint16_t data_len), 0);
+
+DEFINE_STUB(scsi2_reserve, int, (struct spdk_scsi_task *task, uint8_t *cdb), 0);
+DEFINE_STUB(scsi2_release, int, (struct spdk_scsi_task *task), 0);
+
+void
+scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
+{
+ g_scsi_cb_called++;
+}
+
+DEFINE_STUB_V(scsi_lun_complete_reset_task,
+ (struct spdk_scsi_lun *lun, struct spdk_scsi_task *task));
+
+DEFINE_STUB(spdk_scsi_lun_id_int_to_fmt, uint64_t, (int lun_id), 0);
+
+static void
+ut_put_task(struct spdk_scsi_task *task)
+{
+ if (task->alloc_len) {
+ free(task->iov.iov_base);
+ }
+
+ task->iov.iov_base = NULL;
+ task->iov.iov_len = 0;
+ task->alloc_len = 0;
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue));
+}
+
+static void
+ut_init_task(struct spdk_scsi_task *task)
+{
+ memset(task, 0xFF, sizeof(*task));
+ task->iov.iov_base = NULL;
+ task->iovs = &task->iov;
+ task->iovcnt = 1;
+ task->alloc_len = 0;
+ task->dxfer_dir = SPDK_SCSI_DIR_NONE;
+}
+
+void
+spdk_bdev_io_get_scsi_status(const struct spdk_bdev_io *bdev_io,
+ int *sc, int *sk, int *asc, int *ascq)
+{
+ switch (bdev_io->internal.status) {
+ case SPDK_BDEV_IO_STATUS_SUCCESS:
+ *sc = SPDK_SCSI_STATUS_GOOD;
+ *sk = SPDK_SCSI_SENSE_NO_SENSE;
+ *asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ *ascq = SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
+ break;
+ case SPDK_BDEV_IO_STATUS_SCSI_ERROR:
+ *sc = bdev_io->internal.error.scsi.sc;
+ *sk = bdev_io->internal.error.scsi.sk;
+ *asc = bdev_io->internal.error.scsi.asc;
+ *ascq = bdev_io->internal.error.scsi.ascq;
+ break;
+ default:
+ *sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
+ *sk = SPDK_SCSI_SENSE_ABORTED_COMMAND;
+ *asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
+ *ascq = SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
+ break;
+ }
+}
+
+void
+spdk_bdev_io_get_iovec(struct spdk_bdev_io *bdev_io, struct iovec **iovp, int *iovcntp)
+{
+ *iovp = NULL;
+ *iovcntp = 0;
+}
+
+static void
+ut_bdev_io_flush(void)
+{
+ struct spdk_bdev_io *bdev_io;
+ struct spdk_bdev_io_wait_entry *entry;
+
+ while (!TAILQ_EMPTY(&g_bdev_io_queue) || !TAILQ_EMPTY(&g_io_wait_queue)) {
+ while (!TAILQ_EMPTY(&g_bdev_io_queue)) {
+ bdev_io = TAILQ_FIRST(&g_bdev_io_queue);
+ TAILQ_REMOVE(&g_bdev_io_queue, bdev_io, internal.link);
+ bdev_io->internal.cb(bdev_io, true, bdev_io->internal.caller_ctx);
+ free(bdev_io);
+ }
+
+ while (!TAILQ_EMPTY(&g_io_wait_queue)) {
+ entry = TAILQ_FIRST(&g_io_wait_queue);
+ TAILQ_REMOVE(&g_io_wait_queue, entry, link);
+ entry->cb_fn(entry->cb_arg);
+ }
+ }
+}
+
+static int
+_spdk_bdev_io_op(spdk_bdev_io_completion_cb cb, void *cb_arg)
+{
+ struct spdk_bdev_io *bdev_io;
+
+ if (g_bdev_io_pool_full) {
+ g_bdev_io_pool_full = false;
+ return -ENOMEM;
+ }
+
+ bdev_io = calloc(1, sizeof(*bdev_io));
+ SPDK_CU_ASSERT_FATAL(bdev_io != NULL);
+ bdev_io->internal.status = SPDK_BDEV_IO_STATUS_SUCCESS;
+ bdev_io->internal.cb = cb;
+ bdev_io->internal.caller_ctx = cb_arg;
+
+ TAILQ_INSERT_TAIL(&g_bdev_io_queue, bdev_io, internal.link);
+
+ return 0;
+}
+
+int
+spdk_bdev_readv_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
+ struct iovec *iov, int iovcnt,
+ uint64_t offset_blocks, uint64_t num_blocks,
+ spdk_bdev_io_completion_cb cb, void *cb_arg)
+{
+ return _spdk_bdev_io_op(cb, cb_arg);
+}
+
+int
+spdk_bdev_writev_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
+ struct iovec *iov, int iovcnt,
+ uint64_t offset_blocks, uint64_t num_blocks,
+ spdk_bdev_io_completion_cb cb, void *cb_arg)
+{
+ return _spdk_bdev_io_op(cb, cb_arg);
+}
+
+int
+spdk_bdev_unmap_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
+ uint64_t offset_blocks, uint64_t num_blocks,
+ spdk_bdev_io_completion_cb cb, void *cb_arg)
+{
+ return _spdk_bdev_io_op(cb, cb_arg);
+}
+
+int
+spdk_bdev_reset(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
+ spdk_bdev_io_completion_cb cb, void *cb_arg)
+{
+ return _spdk_bdev_io_op(cb, cb_arg);
+}
+
+int
+spdk_bdev_flush_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
+ uint64_t offset_blocks, uint64_t num_blocks,
+ spdk_bdev_io_completion_cb cb, void *cb_arg)
+{
+ return _spdk_bdev_io_op(cb, cb_arg);
+}
+
+int
+spdk_bdev_queue_io_wait(struct spdk_bdev *bdev, struct spdk_io_channel *ch,
+ struct spdk_bdev_io_wait_entry *entry)
+{
+ TAILQ_INSERT_TAIL(&g_io_wait_queue, entry, link);
+ return 0;
+}
+
+int
+spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md_size,
+ bool md_interleave, bool dif_loc, enum spdk_dif_type dif_type, uint32_t dif_flags,
+ uint32_t init_ref_tag, uint16_t apptag_mask, uint16_t app_tag,
+ uint32_t data_offset, uint16_t guard_seed)
+{
+ ctx->init_ref_tag = init_ref_tag;
+ ctx->ref_tag_offset = data_offset / 512;
+ return 0;
+}
+
+/*
+ * This test specifically tests a mode select 6 command from the
+ * Windows SCSI compliance test that caused SPDK to crash.
+ */
+static void
+mode_select_6_test(void)
+{
+ struct spdk_bdev bdev;
+ struct spdk_scsi_task task;
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_dev dev;
+ char cdb[16];
+ char data[24];
+ int rc;
+
+ ut_init_task(&task);
+
+ cdb[0] = 0x15;
+ cdb[1] = 0x11;
+ cdb[2] = 0x00;
+ cdb[3] = 0x00;
+ cdb[4] = 0x18;
+ cdb[5] = 0x00;
+ task.cdb = cdb;
+
+ snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test");
+ lun.bdev = &bdev;
+ lun.dev = &dev;
+ task.lun = &lun;
+
+ memset(data, 0, sizeof(data));
+ data[4] = 0x08;
+ data[5] = 0x02;
+ spdk_scsi_task_set_data(&task, data, sizeof(data));
+
+ rc = bdev_scsi_execute(&task);
+
+ CU_ASSERT_EQUAL(rc, 0);
+
+ ut_put_task(&task);
+}
+
+/*
+ * This test specifically tests a mode select 6 command which
+ * contains no mode pages.
+ */
+static void
+mode_select_6_test2(void)
+{
+ struct spdk_bdev bdev;
+ struct spdk_scsi_task task;
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_dev dev;
+ char cdb[16];
+ int rc;
+
+ ut_init_task(&task);
+
+ cdb[0] = 0x15;
+ cdb[1] = 0x00;
+ cdb[2] = 0x00;
+ cdb[3] = 0x00;
+ cdb[4] = 0x00;
+ cdb[5] = 0x00;
+ task.cdb = cdb;
+
+ snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test");
+ lun.bdev = &bdev;
+ lun.dev = &dev;
+ task.lun = &lun;
+
+ rc = bdev_scsi_execute(&task);
+
+ CU_ASSERT_EQUAL(rc, 0);
+
+ ut_put_task(&task);
+}
+
+/*
+ * This test specifically tests a mode sense 6 command which
+ * return all subpage 00h mode pages.
+ */
+static void
+mode_sense_6_test(void)
+{
+ struct spdk_bdev bdev;
+ struct spdk_scsi_task task;
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_dev dev;
+ char cdb[12];
+ unsigned char *data;
+ int rc;
+ unsigned char mode_data_len = 0;
+ unsigned char medium_type = 0;
+ unsigned char dev_specific_param = 0;
+ unsigned char blk_descriptor_len = 0;
+
+ memset(&bdev, 0, sizeof(struct spdk_bdev));
+ ut_init_task(&task);
+ memset(cdb, 0, sizeof(cdb));
+
+ cdb[0] = 0x1A;
+ cdb[2] = 0x3F;
+ cdb[4] = 0xFF;
+ task.cdb = cdb;
+
+ snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test");
+ lun.bdev = &bdev;
+ lun.dev = &dev;
+ task.lun = &lun;
+
+ rc = bdev_scsi_execute(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ data = task.iovs[0].iov_base;
+ mode_data_len = data[0];
+ medium_type = data[1];
+ dev_specific_param = data[2];
+ blk_descriptor_len = data[3];
+
+ CU_ASSERT(mode_data_len >= 11);
+ CU_ASSERT_EQUAL(medium_type, 0);
+ CU_ASSERT_EQUAL(dev_specific_param, 0);
+ CU_ASSERT_EQUAL(blk_descriptor_len, 8);
+
+ ut_put_task(&task);
+}
+
+/*
+ * This test specifically tests a mode sense 10 command which
+ * return all subpage 00h mode pages.
+ */
+static void
+mode_sense_10_test(void)
+{
+ struct spdk_bdev bdev;
+ struct spdk_scsi_task task;
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_dev dev;
+ char cdb[12];
+ unsigned char *data;
+ int rc;
+ unsigned short mode_data_len = 0;
+ unsigned char medium_type = 0;
+ unsigned char dev_specific_param = 0;
+ unsigned short blk_descriptor_len = 0;
+
+ memset(&bdev, 0, sizeof(struct spdk_bdev));
+ ut_init_task(&task);
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x5A;
+ cdb[2] = 0x3F;
+ cdb[8] = 0xFF;
+ task.cdb = cdb;
+
+ snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test");
+ lun.bdev = &bdev;
+ lun.dev = &dev;
+ task.lun = &lun;
+
+ rc = bdev_scsi_execute(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ data = task.iovs[0].iov_base;
+ mode_data_len = ((data[0] << 8) + data[1]);
+ medium_type = data[2];
+ dev_specific_param = data[3];
+ blk_descriptor_len = ((data[6] << 8) + data[7]);
+
+ CU_ASSERT(mode_data_len >= 14);
+ CU_ASSERT_EQUAL(medium_type, 0);
+ CU_ASSERT_EQUAL(dev_specific_param, 0);
+ CU_ASSERT_EQUAL(blk_descriptor_len, 8);
+
+ ut_put_task(&task);
+}
+
+/*
+ * This test specifically tests a scsi inquiry command from the
+ * Windows SCSI compliance test that failed to return the
+ * expected SCSI error sense code.
+ */
+static void
+inquiry_evpd_test(void)
+{
+ struct spdk_bdev bdev;
+ struct spdk_scsi_task task;
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_dev dev;
+ char cdb[6];
+ int rc;
+
+ ut_init_task(&task);
+
+ cdb[0] = 0x12;
+ cdb[1] = 0x00; /* EVPD = 0 */
+ cdb[2] = 0xff; /* PageCode non-zero */
+ cdb[3] = 0x00;
+ cdb[4] = 0xff;
+ cdb[5] = 0x00;
+ task.cdb = cdb;
+
+ snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test");
+ lun.bdev = &bdev;
+ lun.dev = &dev;
+ task.lun = &lun;
+
+ rc = bdev_scsi_execute(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION);
+ CU_ASSERT_EQUAL(task.sense_data[2] & 0xf, SPDK_SCSI_SENSE_ILLEGAL_REQUEST);
+ CU_ASSERT_EQUAL(task.sense_data[12], 0x24);
+ CU_ASSERT_EQUAL(task.sense_data[13], 0x0);
+
+ ut_put_task(&task);
+}
+
+/*
+ * This test is to verify specific return data for a standard scsi inquiry
+ * command: Version
+ */
+static void
+inquiry_standard_test(void)
+{
+ struct spdk_bdev bdev = { .blocklen = 512 };
+ struct spdk_scsi_task task;
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_dev dev;
+ char cdb[6];
+ char *data;
+ struct spdk_scsi_cdb_inquiry_data *inq_data;
+ int rc;
+
+ ut_init_task(&task);
+
+ cdb[0] = 0x12;
+ cdb[1] = 0x00; /* EVPD = 0 */
+ cdb[2] = 0x00; /* PageCode zero - requesting standard inquiry */
+ cdb[3] = 0x00;
+ cdb[4] = 0xff; /* Indicate data size used by conformance test */
+ cdb[5] = 0x00;
+ task.cdb = cdb;
+
+ snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test");
+ lun.bdev = &bdev;
+ lun.dev = &dev;
+ task.lun = &lun;
+
+ rc = bdev_scsi_execute(&task);
+
+ data = task.iovs[0].iov_base;
+ inq_data = (struct spdk_scsi_cdb_inquiry_data *)&data[0];
+
+ CU_ASSERT_EQUAL(inq_data->version, SPDK_SPC_VERSION_SPC3);
+ CU_ASSERT_EQUAL(rc, 0);
+
+ ut_put_task(&task);
+}
+
+static void
+_inquiry_overflow_test(uint8_t alloc_len)
+{
+ struct spdk_bdev bdev = { .blocklen = 512 };
+ struct spdk_scsi_task task;
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_dev dev;
+ uint8_t cdb[6];
+ int rc;
+ /* expects a 4K internal data buffer */
+ char data[4096], data_compare[4096];
+
+ ut_init_task(&task);
+
+ cdb[0] = 0x12;
+ cdb[1] = 0x00; /* EVPD = 0 */
+ cdb[2] = 0x00; /* PageCode zero - requesting standard inquiry */
+ cdb[3] = 0x00;
+ cdb[4] = alloc_len; /* Indicate data size used by conformance test */
+ cdb[5] = 0x00;
+ task.cdb = cdb;
+
+ snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test");
+ lun.bdev = &bdev;
+ lun.dev = &dev;
+ task.lun = &lun;
+
+ memset(data, 0, sizeof(data));
+ memset(data_compare, 0, sizeof(data_compare));
+
+ spdk_scsi_task_set_data(&task, data, sizeof(data));
+
+ rc = bdev_scsi_execute(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ CU_ASSERT_EQUAL(memcmp(data + alloc_len, data_compare + alloc_len, sizeof(data) - alloc_len), 0);
+ CU_ASSERT(task.data_transferred <= alloc_len);
+
+ ut_put_task(&task);
+}
+
+static void
+inquiry_overflow_test(void)
+{
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ _inquiry_overflow_test(i);
+ }
+}
+
+static void
+scsi_name_padding_test(void)
+{
+ char name[SPDK_SCSI_DEV_MAX_NAME + 1];
+ char buf[SPDK_SCSI_DEV_MAX_NAME + 1];
+ int written, i;
+
+ /* case 1 */
+ memset(name, '\0', sizeof(name));
+ memset(name, 'x', 251);
+ written = bdev_scsi_pad_scsi_name(buf, name);
+
+ CU_ASSERT(written == 252);
+ CU_ASSERT(buf[250] == 'x');
+ CU_ASSERT(buf[251] == '\0');
+
+ /* case 2: */
+ memset(name, '\0', sizeof(name));
+ memset(name, 'x', 252);
+ written = bdev_scsi_pad_scsi_name(buf, name);
+
+ CU_ASSERT(written == 256);
+ CU_ASSERT(buf[251] == 'x');
+ for (i = 252; i < 256; i++) {
+ CU_ASSERT(buf[i] == '\0');
+ }
+
+ /* case 3 */
+ memset(name, '\0', sizeof(name));
+ memset(name, 'x', 255);
+ written = bdev_scsi_pad_scsi_name(buf, name);
+
+ CU_ASSERT(written == 256);
+ CU_ASSERT(buf[254] == 'x');
+ CU_ASSERT(buf[255] == '\0');
+}
+
+/*
+ * This test is to verify specific error translation from bdev to scsi.
+ */
+static void
+task_complete_test(void)
+{
+ struct spdk_scsi_task task;
+ struct spdk_bdev_io bdev_io = {};
+ struct spdk_scsi_lun lun;
+
+ ut_init_task(&task);
+
+ TAILQ_INIT(&lun.tasks);
+ TAILQ_INSERT_TAIL(&lun.tasks, &task, scsi_link);
+ task.lun = &lun;
+
+ bdev_io.internal.status = SPDK_BDEV_IO_STATUS_SUCCESS;
+ bdev_scsi_task_complete_cmd(&bdev_io, bdev_io.internal.status, &task);
+ CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+
+ bdev_io.internal.status = SPDK_BDEV_IO_STATUS_SCSI_ERROR;
+ bdev_io.internal.error.scsi.sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
+ bdev_io.internal.error.scsi.sk = SPDK_SCSI_SENSE_HARDWARE_ERROR;
+ bdev_io.internal.error.scsi.asc = SPDK_SCSI_ASC_WARNING;
+ bdev_io.internal.error.scsi.ascq = SPDK_SCSI_ASCQ_POWER_LOSS_EXPECTED;
+ bdev_scsi_task_complete_cmd(&bdev_io, bdev_io.internal.status, &task);
+ CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION);
+ CU_ASSERT_EQUAL(task.sense_data[2] & 0xf, SPDK_SCSI_SENSE_HARDWARE_ERROR);
+ CU_ASSERT_EQUAL(task.sense_data[12], SPDK_SCSI_ASC_WARNING);
+ CU_ASSERT_EQUAL(task.sense_data[13], SPDK_SCSI_ASCQ_POWER_LOSS_EXPECTED);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+
+ bdev_io.internal.status = SPDK_BDEV_IO_STATUS_FAILED;
+ bdev_scsi_task_complete_cmd(&bdev_io, bdev_io.internal.status, &task);
+ CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION);
+ CU_ASSERT_EQUAL(task.sense_data[2] & 0xf, SPDK_SCSI_SENSE_ABORTED_COMMAND);
+ CU_ASSERT_EQUAL(task.sense_data[12], SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
+ CU_ASSERT_EQUAL(task.sense_data[13], SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+
+ ut_put_task(&task);
+}
+
+static void
+lba_range_test(void)
+{
+ struct spdk_bdev bdev = { .blocklen = 512 };
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_task task;
+ uint8_t cdb[16];
+ int rc;
+
+ lun.bdev = &bdev;
+
+ ut_init_task(&task);
+ task.lun = &lun;
+ task.lun->bdev_desc = NULL;
+ task.lun->io_channel = NULL;
+ task.cdb = cdb;
+
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x88; /* READ (16) */
+
+ /* Test block device size of 4 blocks */
+ g_test_bdev_num_blocks = 4;
+
+ /* LBA = 0, length = 1 (in range) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 1); /* transfer length */
+ task.transfer_len = 1 * 512;
+ task.offset = 0;
+ task.length = 1 * 512;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == 0xFF);
+ SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue));
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+
+ /* LBA = 4, length = 1 (LBA out of range) */
+ to_be64(&cdb[2], 4); /* LBA */
+ to_be32(&cdb[10], 1); /* transfer length */
+ task.transfer_len = 1 * 512;
+ task.offset = 0;
+ task.length = 1 * 512;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE);
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
+ CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE);
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue));
+
+ /* LBA = 0, length = 4 (in range, max valid size) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 4); /* transfer length */
+ task.transfer_len = 4 * 512;
+ task.status = 0xFF;
+ task.offset = 0;
+ task.length = 1 * 512;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == 0xFF);
+ SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue));
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+
+ /* LBA = 0, length = 5 (LBA in range, length beyond end of bdev) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 5); /* transfer length */
+ task.transfer_len = 5 * 512;
+ task.offset = 0;
+ task.length = 1 * 512;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE);
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
+ CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE);
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue));
+
+ ut_put_task(&task);
+}
+
+static void
+xfer_len_test(void)
+{
+ struct spdk_bdev bdev = { .blocklen = 512 };
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_task task;
+ uint8_t cdb[16];
+ int rc;
+
+ lun.bdev = &bdev;
+
+ ut_init_task(&task);
+ task.lun = &lun;
+ task.lun->bdev_desc = NULL;
+ task.lun->io_channel = NULL;
+ task.cdb = cdb;
+
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x88; /* READ (16) */
+
+ /* Test block device size of 512 MiB */
+ g_test_bdev_num_blocks = 512 * 1024 * 1024;
+
+ /* 1 block */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 1); /* transfer length */
+ task.transfer_len = 1 * 512;
+ task.offset = 0;
+ task.length = 1 * 512;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == 0xFF);
+ SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue));
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+
+ /* max transfer length (as reported in block limits VPD page) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], SPDK_WORK_BLOCK_SIZE / 512); /* transfer length */
+ task.transfer_len = SPDK_WORK_BLOCK_SIZE;
+ task.status = 0xFF;
+ task.offset = 0;
+ task.length = 1 * 512;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == 0xFF);
+ SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue));
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+
+ /* max transfer length plus one block (invalid) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], SPDK_WORK_BLOCK_SIZE / 512 + 1); /* transfer length */
+ task.transfer_len = SPDK_WORK_BLOCK_SIZE + 512;
+ task.offset = 0;
+ task.length = 1 * 512;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE);
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
+ CU_ASSERT((task.sense_data[2] & 0xf) == SPDK_SCSI_SENSE_ILLEGAL_REQUEST);
+ CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB);
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue));
+
+ /* zero transfer length (valid) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 0); /* transfer length */
+ task.transfer_len = 0;
+ task.offset = 0;
+ task.length = 0;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE);
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(task.data_transferred == 0);
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue));
+
+ /* zero transfer length past end of disk (invalid) */
+ to_be64(&cdb[2], g_test_bdev_num_blocks); /* LBA */
+ to_be32(&cdb[10], 0); /* transfer length */
+ task.transfer_len = 0;
+ task.offset = 0;
+ task.length = 0;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE);
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
+ CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE);
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue));
+
+ ut_put_task(&task);
+}
+
+static void
+_xfer_test(bool bdev_io_pool_full)
+{
+ struct spdk_bdev bdev = { .blocklen = 512 };
+ struct spdk_scsi_lun lun;
+ struct spdk_scsi_task task;
+ uint8_t cdb[16];
+ char data[4096];
+ int rc;
+
+ lun.bdev = &bdev;
+
+ /* Test block device size of 512 MiB */
+ g_test_bdev_num_blocks = 512 * 1024 * 1024;
+
+ /* Read 1 block */
+ ut_init_task(&task);
+ task.lun = &lun;
+ task.lun->bdev_desc = NULL;
+ task.lun->io_channel = NULL;
+ task.cdb = cdb;
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x88; /* READ (16) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 1); /* transfer length */
+ task.transfer_len = 1 * 512;
+ task.offset = 0;
+ task.length = 1 * 512;
+ g_bdev_io_pool_full = bdev_io_pool_full;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == 0xFF);
+
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+ ut_put_task(&task);
+
+ /* Write 1 block */
+ ut_init_task(&task);
+ task.lun = &lun;
+ task.cdb = cdb;
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x8a; /* WRITE (16) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 1); /* transfer length */
+ task.transfer_len = 1 * 512;
+ task.offset = 0;
+ task.length = 1 * 512;
+ g_bdev_io_pool_full = bdev_io_pool_full;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == 0xFF);
+
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+ ut_put_task(&task);
+
+ /* Unmap 5 blocks using 2 descriptors */
+ ut_init_task(&task);
+ task.lun = &lun;
+ task.cdb = cdb;
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x42; /* UNMAP */
+ to_be16(&data[7], 2); /* 2 parameters in list */
+ memset(data, 0, sizeof(data));
+ to_be16(&data[2], 32); /* 2 descriptors */
+ to_be64(&data[8], 1); /* LBA 1 */
+ to_be32(&data[16], 2); /* 2 blocks */
+ to_be64(&data[24], 10); /* LBA 10 */
+ to_be32(&data[32], 3); /* 3 blocks */
+ spdk_scsi_task_set_data(&task, data, sizeof(data));
+ task.status = SPDK_SCSI_STATUS_GOOD;
+ g_bdev_io_pool_full = bdev_io_pool_full;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+ ut_put_task(&task);
+
+ /* Flush 1 block */
+ ut_init_task(&task);
+ task.lun = &lun;
+ task.cdb = cdb;
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x91; /* SYNCHRONIZE CACHE (16) */
+ to_be64(&cdb[2], 0); /* LBA */
+ to_be32(&cdb[10], 1); /* 1 blocks */
+ g_bdev_io_pool_full = bdev_io_pool_full;
+ rc = bdev_scsi_execute(&task);
+ CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING);
+ CU_ASSERT(task.status == 0xFF);
+
+ ut_bdev_io_flush();
+ CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD);
+ CU_ASSERT(g_scsi_cb_called == 1);
+ g_scsi_cb_called = 0;
+ SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue));
+
+ ut_put_task(&task);
+}
+
+static void
+xfer_test(void)
+{
+ _xfer_test(false);
+ _xfer_test(true);
+}
+
+static void
+get_dif_ctx_test(void)
+{
+ struct spdk_bdev bdev = {};
+ struct spdk_scsi_task task = {};
+ struct spdk_dif_ctx dif_ctx = {};
+ uint8_t cdb[16];
+ bool ret;
+
+ cdb[0] = SPDK_SBC_READ_6;
+ cdb[1] = 0x12;
+ cdb[2] = 0x34;
+ cdb[3] = 0x50;
+ task.cdb = cdb;
+ task.offset = 0x6 * 512;
+
+ ret = bdev_scsi_get_dif_ctx(&bdev, &task, &dif_ctx);
+ CU_ASSERT(ret == true);
+ CU_ASSERT(dif_ctx.init_ref_tag + dif_ctx.ref_tag_offset == 0x123456);
+
+ cdb[0] = SPDK_SBC_WRITE_12;
+ to_be32(&cdb[2], 0x12345670);
+ task.offset = 0x8 * 512;
+
+ ret = bdev_scsi_get_dif_ctx(&bdev, &task, &dif_ctx);
+ CU_ASSERT(ret == true);
+ CU_ASSERT(dif_ctx.init_ref_tag + dif_ctx.ref_tag_offset == 0x12345678);
+
+ cdb[0] = SPDK_SBC_WRITE_16;
+ to_be64(&cdb[2], 0x0000000012345670);
+ task.offset = 0x8 * 512;
+
+ ret = bdev_scsi_get_dif_ctx(&bdev, &task, &dif_ctx);
+ CU_ASSERT(ret == true);
+ CU_ASSERT(dif_ctx.init_ref_tag + dif_ctx.ref_tag_offset == 0x12345678);
+}
+
+int
+main(int argc, char **argv)
+{
+ CU_pSuite suite = NULL;
+ unsigned int num_failures;
+
+ TAILQ_INIT(&g_bdev_io_queue);
+ TAILQ_INIT(&g_io_wait_queue);
+
+ CU_set_error_action(CUEA_ABORT);
+ CU_initialize_registry();
+
+ suite = CU_add_suite("translation_suite", NULL, NULL);
+
+ CU_ADD_TEST(suite, mode_select_6_test);
+ CU_ADD_TEST(suite, mode_select_6_test2);
+ CU_ADD_TEST(suite, mode_sense_6_test);
+ CU_ADD_TEST(suite, mode_sense_10_test);
+ CU_ADD_TEST(suite, inquiry_evpd_test);
+ CU_ADD_TEST(suite, inquiry_standard_test);
+ CU_ADD_TEST(suite, inquiry_overflow_test);
+ CU_ADD_TEST(suite, task_complete_test);
+ CU_ADD_TEST(suite, lba_range_test);
+ CU_ADD_TEST(suite, xfer_len_test);
+ CU_ADD_TEST(suite, xfer_test);
+ CU_ADD_TEST(suite, scsi_name_padding_test);
+ CU_ADD_TEST(suite, get_dif_ctx_test);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ num_failures = CU_get_number_of_failures();
+ CU_cleanup_registry();
+ return num_failures;
+}
diff --git a/src/spdk/test/unit/lib/scsi/scsi_pr.c/.gitignore b/src/spdk/test/unit/lib/scsi/scsi_pr.c/.gitignore
new file mode 100644
index 000000000..9655d812e
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi_pr.c/.gitignore
@@ -0,0 +1 @@
+scsi_pr_ut
diff --git a/src/spdk/test/unit/lib/scsi/scsi_pr.c/Makefile b/src/spdk/test/unit/lib/scsi/scsi_pr.c/Makefile
new file mode 100644
index 000000000..22be734ae
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi_pr.c/Makefile
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..)
+include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
+
+TEST_FILE = scsi_pr_ut.c
+
+include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk
diff --git a/src/spdk/test/unit/lib/scsi/scsi_pr.c/scsi_pr_ut.c b/src/spdk/test/unit/lib/scsi/scsi_pr.c/scsi_pr_ut.c
new file mode 100644
index 000000000..993277036
--- /dev/null
+++ b/src/spdk/test/unit/lib/scsi/scsi_pr.c/scsi_pr_ut.c
@@ -0,0 +1,673 @@
+/*-
+ * 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 "scsi/port.c"
+#include "scsi/scsi_pr.c"
+
+#include "spdk_cunit.h"
+
+#include "spdk_internal/mock.h"
+
+SPDK_LOG_REGISTER_COMPONENT("scsi", SPDK_LOG_SCSI)
+
+void
+spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
+ int asc, int ascq)
+{
+ task->status = sc;
+}
+
+/*
+ * Reservation Unit Test Configuration
+ *
+ * -------- -------- -------
+ * | Host A | | Host B | | Host C|
+ * -------- -------- -------
+ * | | |
+ * ------ ------ ------
+ * |Port A| |Port B| |Port C|
+ * ------ ------ ------
+ * \ | /
+ * \ | /
+ * \ | /
+ * ------------------------
+ * | Target Node 1 Port 0 |
+ * ------------------------
+ * |
+ * ----------------------------------
+ * | Target Node |
+ * ----------------------------------
+ * |
+ * -----
+ * |LUN 0|
+ * -----
+ *
+ */
+
+static struct spdk_scsi_lun g_lun;
+static struct spdk_scsi_port g_i_port_a;
+static struct spdk_scsi_port g_i_port_b;
+static struct spdk_scsi_port g_i_port_c;
+static struct spdk_scsi_port g_t_port_0;
+
+static void
+ut_lun_deinit(void)
+{
+ struct spdk_scsi_pr_registrant *reg, *tmp;
+
+ TAILQ_FOREACH_SAFE(reg, &g_lun.reg_head, link, tmp) {
+ TAILQ_REMOVE(&g_lun.reg_head, reg, link);
+ free(reg);
+ }
+ g_lun.reservation.rtype = 0;
+ g_lun.reservation.crkey = 0;
+ g_lun.reservation.holder = NULL;
+ g_lun.pr_generation = 0;
+}
+
+static void
+ut_port_init(void)
+{
+ int rc;
+
+ /* g_i_port_a */
+ rc = scsi_port_construct(&g_i_port_a, 0xa, 0,
+ "iqn.2016-06.io.spdk:fe5aacf7420a,i,0x00023d00000a");
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ spdk_scsi_port_set_iscsi_transport_id(&g_i_port_a,
+ "iqn.2016-06.io.spdk:fe5aacf7420a", 0x00023d00000a);
+ /* g_i_port_b */
+ rc = scsi_port_construct(&g_i_port_b, 0xb, 0,
+ "iqn.2016-06.io.spdk:fe5aacf7420b,i,0x00023d00000b");
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ spdk_scsi_port_set_iscsi_transport_id(&g_i_port_b,
+ "iqn.2016-06.io.spdk:fe5aacf7420b", 0x00023d00000b);
+ /* g_i_port_c */
+ rc = scsi_port_construct(&g_i_port_c, 0xc, 0,
+ "iqn.2016-06.io.spdk:fe5aacf7420c,i,0x00023d00000c");
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ spdk_scsi_port_set_iscsi_transport_id(&g_i_port_c,
+ "iqn.2016-06.io.spdk:fe5aacf7420c", 0x00023d00000c);
+ /* g_t_port_0 */
+ rc = scsi_port_construct(&g_t_port_0, 0x0, 1,
+ "iqn.2016-06.io.spdk:fe5aacf74200,t,0x00023d000000");
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ spdk_scsi_port_set_iscsi_transport_id(&g_t_port_0,
+ "iqn.2016-06.io.spdk:fe5aacf74200", 0x00023d000000);
+}
+
+static void
+ut_lun_init(void)
+{
+ TAILQ_INIT(&g_lun.reg_head);
+}
+
+static void
+ut_init_reservation_test(void)
+{
+ ut_lun_init();
+ ut_port_init();
+ ut_lun_init();
+}
+
+static void
+ut_deinit_reservation_test(void)
+{
+ ut_lun_deinit();
+}
+
+/* Host A: register with key 0xa.
+ * Host B: register with key 0xb.
+ * Host C: register with key 0xc.
+ */
+static void
+test_build_registrants(void)
+{
+ struct spdk_scsi_pr_registrant *reg;
+ struct spdk_scsi_task task = {0};
+ uint32_t gen;
+ int rc;
+
+ task.lun = &g_lun;
+ task.target_port = &g_t_port_0;
+
+ gen = g_lun.pr_generation;
+
+ /* I_T nexus: Initiator Port A to Target Port 0 */
+ task.initiator_port = &g_i_port_a;
+ /* Test Case: Host A registers with a new key */
+ task.status = 0;
+ rc = scsi_pr_out_register(&task, SPDK_SCSI_PR_OUT_REGISTER,
+ 0x0, 0xa1, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_a, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg != NULL);
+ SPDK_CU_ASSERT_FATAL(reg->rkey == 0xa1);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 1);
+
+ /* Test Case: Host A replaces with a new key */
+ task.status = 0;
+ rc = scsi_pr_out_register(&task, SPDK_SCSI_PR_OUT_REGISTER,
+ 0xa1, 0xa, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_a, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg != NULL);
+ SPDK_CU_ASSERT_FATAL(reg->rkey == 0xa);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 2);
+
+ /* Test Case: Host A replaces with a new key, reservation conflict is expected */
+ task.status = 0;
+ rc = scsi_pr_out_register(&task, SPDK_SCSI_PR_OUT_REGISTER,
+ 0xa1, 0xdead, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_a, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg != NULL);
+ SPDK_CU_ASSERT_FATAL(reg->rkey == 0xa);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 2);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+
+ /* I_T nexus: Initiator Port B to Target Port 0 */
+ task.initiator_port = &g_i_port_b;
+ /* Test Case: Host B registers with a new key */
+ task.status = 0;
+ rc = scsi_pr_out_register(&task, SPDK_SCSI_PR_OUT_REGISTER,
+ 0x0, 0xb, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_b, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg != NULL);
+ SPDK_CU_ASSERT_FATAL(reg->rkey == 0xb);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 3);
+
+ /* I_T nexus: Initiator Port C to Target Port 0 */
+ task.initiator_port = &g_i_port_c;
+ /* Test Case: Host C registers with a new key */
+ task.status = 0;
+ rc = scsi_pr_out_register(&task, SPDK_SCSI_PR_OUT_REGISTER,
+ 0x0, 0xc, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_c, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg != NULL);
+ SPDK_CU_ASSERT_FATAL(reg->rkey == 0xc);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 4);
+}
+
+static void
+test_reservation_register(void)
+{
+ ut_init_reservation_test();
+
+ test_build_registrants();
+
+ ut_deinit_reservation_test();
+}
+
+static void
+test_reservation_reserve(void)
+{
+ struct spdk_scsi_pr_registrant *reg;
+ struct spdk_scsi_task task = {0};
+ uint32_t gen;
+ int rc;
+
+ task.lun = &g_lun;
+ task.target_port = &g_t_port_0;
+
+ ut_init_reservation_test();
+ test_build_registrants();
+
+ gen = g_lun.pr_generation;
+
+ task.initiator_port = &g_i_port_a;
+ task.status = 0;
+ /* Test Case: Host A acquires the reservation */
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE,
+ 0xa, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xa);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen);
+
+ /* Test Case: Host B acquires the reservation, reservation
+ * conflict is expected.
+ */
+ task.initiator_port = &g_i_port_b;
+ task.status = 0;
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE,
+ 0xb, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xa);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen);
+
+ /* Test Case: Host A unregister with reservation */
+ task.initiator_port = &g_i_port_a;
+ task.status = 0;
+ rc = scsi_pr_out_register(&task, SPDK_SCSI_PR_OUT_REGISTER,
+ 0xa, 0, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 1);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_a, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg == NULL);
+
+ /* Test Case: Host B acquires the reservation */
+ task.initiator_port = &g_i_port_b;
+ task.status = 0;
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS,
+ 0xb, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 1);
+
+ /* Test Case: Host C acquires the reservation with invalid type */
+ task.initiator_port = &g_i_port_c;
+ task.status = 0;
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE,
+ 0xc, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 1);
+
+ /* Test Case: Host C acquires the reservation, all registrants type */
+ task.status = 0;
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS,
+ 0xc, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen + 1);
+
+ ut_deinit_reservation_test();
+}
+
+static void
+test_reservation_preempt_non_all_regs(void)
+{
+ struct spdk_scsi_pr_registrant *reg;
+ struct spdk_scsi_task task = {0};
+ uint32_t gen;
+ int rc;
+
+ task.lun = &g_lun;
+ task.target_port = &g_t_port_0;
+
+ ut_init_reservation_test();
+ test_build_registrants();
+
+ task.initiator_port = &g_i_port_a;
+ task.status = 0;
+ gen = g_lun.pr_generation;
+ /* Host A acquires the reservation */
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY,
+ 0xa, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xa);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen);
+
+ /* Test Case: Host B premmpts Host A, Check condition is expected
+ * for zeroed service action reservation key */
+ task.initiator_port = &g_i_port_b;
+ task.status = 0;
+ rc = scsi_pr_out_preempt(&task, SPDK_SCSI_PR_OUT_PREEMPT,
+ SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY,
+ 0xb, 0);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
+
+ /* Test Case: Host B preempts Host A, Host A is unregisted */
+ task.status = 0;
+ gen = g_lun.pr_generation;
+ rc = scsi_pr_out_preempt(&task, SPDK_SCSI_PR_OUT_PREEMPT,
+ SPDK_SCSI_PR_WRITE_EXCLUSIVE,
+ 0xb, 0xa);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xb);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation > gen);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_a, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg == NULL);
+
+ /* Test Case: Host B preempts itself */
+ task.status = 0;
+ gen = g_lun.pr_generation;
+ rc = scsi_pr_out_preempt(&task, SPDK_SCSI_PR_OUT_PREEMPT,
+ SPDK_SCSI_PR_WRITE_EXCLUSIVE,
+ 0xb, 0xb);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xb);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation > gen);
+
+ /* Test Case: Host B preempts itself and remove registrants */
+ task.status = 0;
+ gen = g_lun.pr_generation;
+ rc = scsi_pr_out_preempt(&task, SPDK_SCSI_PR_OUT_PREEMPT,
+ SPDK_SCSI_PR_WRITE_EXCLUSIVE,
+ 0xb, 0xc);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xb);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_c, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg == NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation > gen);
+
+ ut_deinit_reservation_test();
+}
+
+static void
+test_reservation_preempt_all_regs(void)
+{
+ struct spdk_scsi_pr_registrant *reg;
+ struct spdk_scsi_task task = {0};
+ uint32_t gen;
+ int rc;
+
+ task.lun = &g_lun;
+ task.target_port = &g_t_port_0;
+
+ ut_init_reservation_test();
+ test_build_registrants();
+
+ /* Test Case: No reservation yet, Host B removes Host C's registrant */
+ task.initiator_port = &g_i_port_b;
+ task.status = 0;
+ gen = g_lun.pr_generation;
+ rc = scsi_pr_out_preempt(&task, SPDK_SCSI_PR_OUT_PREEMPT,
+ SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY,
+ 0xb, 0xc);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_c, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg == NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation > gen);
+
+ task.initiator_port = &g_i_port_a;
+ task.status = 0;
+ gen = g_lun.pr_generation;
+ /* Host A acquires the reservation */
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS,
+ 0xa, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation == gen);
+
+ /* Test Case: Host B removes Host A's registrant and preempt */
+ task.initiator_port = &g_i_port_b;
+ task.status = 0;
+ gen = g_lun.pr_generation;
+ rc = scsi_pr_out_preempt(&task, SPDK_SCSI_PR_OUT_PREEMPT,
+ SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS,
+ 0xb, 0x0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_a, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg == NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
+ SPDK_CU_ASSERT_FATAL(g_lun.pr_generation > gen);
+
+ ut_deinit_reservation_test();
+}
+
+static void
+test_reservation_cmds_conflict(void)
+{
+ struct spdk_scsi_pr_registrant *reg;
+ struct spdk_scsi_task task = {0};
+ uint8_t cdb[32];
+ int rc;
+
+ task.lun = &g_lun;
+ task.target_port = &g_t_port_0;
+ task.cdb = cdb;
+
+ ut_init_reservation_test();
+ test_build_registrants();
+
+ /* Host A acquires the reservation */
+ task.initiator_port = &g_i_port_a;
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY,
+ 0xa, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xa);
+
+ /* Remove Host B registrant */
+ task.initiator_port = &g_i_port_b;
+ task.status = 0;
+ rc = scsi_pr_out_register(&task, SPDK_SCSI_PR_OUT_REGISTER,
+ 0xb, 0, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ reg = scsi_pr_get_registrant(&g_lun, &g_i_port_b, &g_t_port_0);
+ SPDK_CU_ASSERT_FATAL(reg == NULL);
+
+ /* Test Case: Host B sends Read/Write commands,
+ * reservation conflict is expected.
+ */
+ task.cdb[0] = SPDK_SBC_READ_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ task.cdb[0] = SPDK_SBC_WRITE_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+
+ /* Test Case: Host C sends Read/Write commands */
+ task.initiator_port = &g_i_port_c;
+ task.cdb[0] = SPDK_SBC_READ_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ task.cdb[0] = SPDK_SBC_WRITE_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ /* Host A preempts itself with SPDK_SCSI_PR_EXCLUSIVE_ACCESS */
+ task.initiator_port = &g_i_port_a;
+ rc = scsi_pr_out_preempt(&task, SPDK_SCSI_PR_OUT_PREEMPT,
+ SPDK_SCSI_PR_EXCLUSIVE_ACCESS,
+ 0xa, 0xa);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xa);
+
+ /* Test Case: Host C sends Read/Write commands */
+ task.initiator_port = &g_i_port_c;
+ task.cdb[0] = SPDK_SBC_READ_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+ task.cdb[0] = SPDK_SBC_WRITE_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+
+ /* Test Case: Host B sends Read/Write commands */
+ task.initiator_port = &g_i_port_b;
+ task.cdb[0] = SPDK_SBC_READ_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+ task.cdb[0] = SPDK_SBC_WRITE_10;
+ task.status = 0;
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+
+ ut_deinit_reservation_test();
+}
+
+static void
+test_scsi2_reserve_release(void)
+{
+ struct spdk_scsi_task task = {0};
+ uint8_t cdb[32] = {};
+ int rc;
+
+ task.lun = &g_lun;
+ task.target_port = &g_t_port_0;
+ task.cdb = cdb;
+
+ ut_init_reservation_test();
+
+ /* Test Case: SPC2 RESERVE from Host A */
+ task.initiator_port = &g_i_port_a;
+ task.cdb[0] = SPDK_SPC2_RESERVE_10;
+ rc = scsi2_reserve(&task, task.cdb);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.holder != NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.flags == SCSI_SPC2_RESERVE);
+
+ /* Test Case: READ command from Host B */
+ task.initiator_port = &g_i_port_b;
+ task.cdb[0] = SPDK_SBC_READ_10;
+ task.status = 0;
+ rc = scsi2_reserve_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc < 0);
+ SPDK_CU_ASSERT_FATAL(task.status == SPDK_SCSI_STATUS_RESERVATION_CONFLICT);
+
+ /* Test Case: SPDK_SPC2_RELEASE10 command from Host B */
+ task.initiator_port = &g_i_port_b;
+ task.cdb[0] = SPDK_SPC2_RELEASE_10;
+ task.status = 0;
+ rc = scsi2_reserve_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ rc = scsi2_release(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.holder == NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.flags == 0);
+
+ /* Test Case: SPC2 RESERVE from Host B */
+ task.initiator_port = &g_i_port_b;
+ task.cdb[0] = SPDK_SPC2_RESERVE_10;
+ rc = scsi2_reserve(&task, task.cdb);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.holder != NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.flags == SCSI_SPC2_RESERVE);
+
+ /* Test Case: READ command from Host B */
+ task.initiator_port = &g_i_port_b;
+ task.cdb[0] = SPDK_SBC_READ_10;
+ rc = scsi2_reserve_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ /* Test Case: SPDK_SPC2_RELEASE10 command from Host A */
+ task.initiator_port = &g_i_port_a;
+ task.cdb[0] = SPDK_SPC2_RELEASE_10;
+
+ rc = scsi2_release(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.holder == NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.flags == 0);
+
+ ut_deinit_reservation_test();
+}
+
+static void
+test_pr_with_scsi2_reserve_release(void)
+{
+ struct spdk_scsi_task task = {0};
+ uint8_t cdb[32] = {};
+ int rc;
+
+ task.lun = &g_lun;
+ task.target_port = &g_t_port_0;
+ task.cdb = cdb;
+
+ ut_init_reservation_test();
+ test_build_registrants();
+
+ task.initiator_port = &g_i_port_a;
+ task.status = 0;
+ /* Test Case: Host A acquires the reservation */
+ rc = scsi_pr_out_reserve(&task, SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY,
+ 0xa, 0, 0, 0);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.crkey == 0xa);
+
+ /* Test Case: SPDK_SPC2_RESERVE_10 command from Host B */
+ task.initiator_port = &g_i_port_b;
+ task.cdb[0] = SPDK_SPC2_RESERVE_10;
+ /* SPC2 RESERVE/RELEASE will pass to scsi2_reserve/release */
+ rc = scsi_pr_check(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+
+ /* do nothing with PR but have good status */
+ rc = scsi2_reserve(&task, task.cdb);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.holder != NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY);
+
+ rc = scsi2_release(&task);
+ SPDK_CU_ASSERT_FATAL(rc == 0);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.holder != NULL);
+ SPDK_CU_ASSERT_FATAL(g_lun.reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY);
+
+ ut_deinit_reservation_test();
+}
+
+int
+main(int argc, char **argv)
+{
+ CU_pSuite suite = NULL;
+ unsigned int num_failures;
+
+ CU_set_error_action(CUEA_ABORT);
+ CU_initialize_registry();
+
+ suite = CU_add_suite("reservation_suite", NULL, NULL);
+ CU_ADD_TEST(suite, test_reservation_register);
+ CU_ADD_TEST(suite, test_reservation_reserve);
+ CU_ADD_TEST(suite, test_reservation_preempt_non_all_regs);
+ CU_ADD_TEST(suite, test_reservation_preempt_all_regs);
+ CU_ADD_TEST(suite, test_reservation_cmds_conflict);
+ CU_ADD_TEST(suite, test_scsi2_reserve_release);
+ CU_ADD_TEST(suite, test_pr_with_scsi2_reserve_release);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ num_failures = CU_get_number_of_failures();
+ CU_cleanup_registry();
+ return num_failures;
+
+}