diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/test/unit/lib/scsi | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/test/unit/lib/scsi')
-rw-r--r-- | src/spdk/test/unit/lib/scsi/Makefile | 44 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/dev.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/dev.c/Makefile | 38 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/dev.c/dev_ut.c | 682 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/lun.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/lun.c/Makefile | 38 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/lun.c/lun_ut.c | 750 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi.c/Makefile | 39 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi.c/scsi_ut.c | 69 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi_bdev.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi_bdev.c/Makefile | 38 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi_bdev.c/scsi_bdev_ut.c | 1037 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi_pr.c/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi_pr.c/Makefile | 39 | ||||
-rw-r--r-- | src/spdk/test/unit/lib/scsi/scsi_pr.c/scsi_pr_ut.c | 673 |
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; + +} |