Spdk/test/unit/lib/iscsi/conn.c/conn_ut.c
paul luse a6dbe3721e update Intel copyright notices
per Intel policy to include file commit date using git cmd
below.  The policy does not apply to non-Intel (C) notices.

git log --follow -C90% --format=%ad --date default <file> | tail -1

and then pull just the 4 digit year from the result.

Intel copyrights were not added to files where Intel either had
no contribution ot the contribution lacked substance (ie license
header updates, formatting changes, etc).  Contribution date used
"--follow -C95%" to get the most accurate date.

Note that several files in this patch didn't end the license/(c)
block with a blank comment line so these were added as the vast
majority of files do have this last blank line.  Simply there for
consistency.

Signed-off-by: paul luse <paul.e.luse@intel.com>
Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Community-CI: Mellanox Build Bot
2022-11-10 08:28:53 +00:00

920 lines
27 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
* All rights reserved.
* Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#include "spdk/stdinc.h"
#include "common/lib/test_env.c"
#include "spdk_cunit.h"
#include "iscsi/conn.c"
#include "spdk_internal/mock.h"
#include "unit/lib/json_mock.c"
SPDK_LOG_REGISTER_COMPONENT(iscsi)
DEFINE_STUB(iscsi_get_pdu, struct spdk_iscsi_pdu *,
(struct spdk_iscsi_conn *conn), NULL);
DEFINE_STUB(iscsi_param_eq_val, int,
(struct iscsi_param *params, const char *key, const char *val), 0);
DEFINE_STUB(iscsi_pdu_calc_data_digest, uint32_t, (struct spdk_iscsi_pdu *pdu), 0);
DEFINE_STUB_V(spdk_sock_writev_async,
(struct spdk_sock *sock, struct spdk_sock_request *req));
struct spdk_scsi_lun {
uint8_t reserved;
};
struct spdk_iscsi_globals g_iscsi = {
.MaxLargeDataInPerConnection = DEFAULT_MAX_LARGE_DATAIN_PER_CONNECTION,
};
static TAILQ_HEAD(read_tasks_head, spdk_iscsi_task) g_ut_read_tasks =
TAILQ_HEAD_INITIALIZER(g_ut_read_tasks);
static struct spdk_iscsi_task *g_new_task = NULL;
static ssize_t g_sock_writev_bytes = 0;
DEFINE_STUB(spdk_app_get_shm_id, int, (void), 0);
DEFINE_STUB(spdk_sock_getaddr, int,
(struct spdk_sock *sock, char *saddr, int slen, uint16_t *sport,
char *caddr, int clen, uint16_t *cport),
0);
int
spdk_sock_close(struct spdk_sock **sock)
{
*sock = NULL;
return 0;
}
DEFINE_STUB(spdk_sock_recv, ssize_t,
(struct spdk_sock *sock, void *buf, size_t len), 0);
DEFINE_STUB(spdk_sock_readv, ssize_t,
(struct spdk_sock *sock, struct iovec *iov, int iovcnt), 0);
ssize_t
spdk_sock_writev(struct spdk_sock *sock, struct iovec *iov, int iovcnt)
{
return g_sock_writev_bytes;
}
DEFINE_STUB(spdk_sock_set_recvlowat, int, (struct spdk_sock *s, int nbytes), 0);
DEFINE_STUB(spdk_sock_set_recvbuf, int, (struct spdk_sock *sock, int sz), 0);
DEFINE_STUB(spdk_sock_set_sendbuf, int, (struct spdk_sock *sock, int sz), 0);
DEFINE_STUB(spdk_sock_group_add_sock, int,
(struct spdk_sock_group *group, struct spdk_sock *sock,
spdk_sock_cb cb_fn, void *cb_arg),
0);
DEFINE_STUB(spdk_sock_group_remove_sock, int,
(struct spdk_sock_group *group, struct spdk_sock *sock), 0);
struct spdk_iscsi_task *
iscsi_task_get(struct spdk_iscsi_conn *conn,
struct spdk_iscsi_task *parent,
spdk_scsi_task_cpl cpl_fn)
{
struct spdk_iscsi_task *task;
task = g_new_task;
if (task == NULL) {
return NULL;
}
memset(task, 0, sizeof(*task));
task->scsi.ref = 1;
task->conn = conn;
task->scsi.cpl_fn = cpl_fn;
if (parent) {
parent->scsi.ref++;
task->parent = parent;
task->scsi.dxfer_dir = parent->scsi.dxfer_dir;
task->scsi.transfer_len = parent->scsi.transfer_len;
task->scsi.lun = parent->scsi.lun;
if (conn && (task->scsi.dxfer_dir == SPDK_SCSI_DIR_FROM_DEV)) {
conn->data_in_cnt++;
}
}
return task;
}
void
spdk_scsi_task_put(struct spdk_scsi_task *scsi_task)
{
struct spdk_iscsi_task *task;
CU_ASSERT(scsi_task->ref > 0);
scsi_task->ref--;
task = iscsi_task_from_scsi_task(scsi_task);
if (task->parent) {
spdk_scsi_task_put(&task->parent->scsi);
}
}
DEFINE_STUB(spdk_scsi_dev_get_lun, struct spdk_scsi_lun *,
(struct spdk_scsi_dev *dev, int lun_id), NULL);
DEFINE_STUB(spdk_scsi_dev_get_first_lun, struct spdk_scsi_lun *,
(struct spdk_scsi_dev *dev), NULL);
DEFINE_STUB(spdk_scsi_dev_get_next_lun, struct spdk_scsi_lun *,
(struct spdk_scsi_lun *prev_lun), NULL);
DEFINE_STUB(spdk_scsi_dev_has_pending_tasks, bool,
(const struct spdk_scsi_dev *dev, const struct spdk_scsi_port *initiator_port),
true);
DEFINE_STUB(spdk_scsi_lun_open, int,
(struct spdk_scsi_lun *lun, spdk_scsi_lun_remove_cb_t hotremove_cb,
void *hotremove_ctx, struct spdk_scsi_lun_desc **desc),
0);
DEFINE_STUB_V(spdk_scsi_lun_close, (struct spdk_scsi_lun_desc *desc));
DEFINE_STUB(spdk_scsi_lun_allocate_io_channel, int,
(struct spdk_scsi_lun_desc *desc), 0);
DEFINE_STUB_V(spdk_scsi_lun_free_io_channel, (struct spdk_scsi_lun_desc *desc));
DEFINE_STUB(spdk_scsi_lun_get_id, int, (const struct spdk_scsi_lun *lun), 0);
DEFINE_STUB(spdk_scsi_port_get_name, const char *,
(const struct spdk_scsi_port *port), NULL);
void
spdk_scsi_task_copy_status(struct spdk_scsi_task *dst,
struct spdk_scsi_task *src)
{
dst->status = src->status;
}
DEFINE_STUB_V(spdk_scsi_task_set_data, (struct spdk_scsi_task *task, void *data, uint32_t len));
DEFINE_STUB_V(spdk_scsi_task_process_null_lun, (struct spdk_scsi_task *task));
DEFINE_STUB_V(spdk_scsi_task_process_abort, (struct spdk_scsi_task *task));
DEFINE_STUB_V(iscsi_put_pdu, (struct spdk_iscsi_pdu *pdu));
DEFINE_STUB_V(iscsi_param_free, (struct iscsi_param *params));
DEFINE_STUB(iscsi_conn_params_init, int, (struct iscsi_param **params), 0);
DEFINE_STUB_V(iscsi_clear_all_transfer_task,
(struct spdk_iscsi_conn *conn, struct spdk_scsi_lun *lun,
struct spdk_iscsi_pdu *pdu));
DEFINE_STUB(iscsi_build_iovs, int,
(struct spdk_iscsi_conn *conn, struct iovec *iov, int num_iovs,
struct spdk_iscsi_pdu *pdu, uint32_t *mapped_length),
0);
DEFINE_STUB_V(iscsi_queue_task,
(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task));
DEFINE_STUB_V(iscsi_task_response,
(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task));
DEFINE_STUB_V(iscsi_task_mgmt_response,
(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task));
DEFINE_STUB_V(iscsi_send_nopin, (struct spdk_iscsi_conn *conn));
bool
iscsi_del_transfer_task(struct spdk_iscsi_conn *conn, uint32_t task_tag)
{
struct spdk_iscsi_task *task;
task = TAILQ_FIRST(&conn->active_r2t_tasks);
if (task == NULL || task->tag != task_tag) {
return false;
}
TAILQ_REMOVE(&conn->active_r2t_tasks, task, link);
task->is_r2t_active = false;
iscsi_task_put(task);
return true;
}
DEFINE_STUB(iscsi_handle_incoming_pdus, int, (struct spdk_iscsi_conn *conn), 0);
DEFINE_STUB_V(iscsi_free_sess, (struct spdk_iscsi_sess *sess));
DEFINE_STUB(iscsi_tgt_node_cleanup_luns, int,
(struct spdk_iscsi_conn *conn, struct spdk_iscsi_tgt_node *target),
0);
DEFINE_STUB(iscsi_pdu_calc_header_digest, uint32_t,
(struct spdk_iscsi_pdu *pdu), 0);
DEFINE_STUB(spdk_iscsi_pdu_calc_data_digest, uint32_t,
(struct spdk_iscsi_pdu *pdu), 0);
DEFINE_STUB_V(shutdown_iscsi_conns_done, (void));
static struct spdk_iscsi_task *
ut_conn_task_get(struct spdk_iscsi_task *parent)
{
struct spdk_iscsi_task *task;
task = calloc(1, sizeof(*task));
SPDK_CU_ASSERT_FATAL(task != NULL);
task->scsi.ref = 1;
if (parent) {
task->parent = parent;
parent->scsi.ref++;
}
return task;
}
static void
ut_conn_create_read_tasks(struct spdk_iscsi_task *primary)
{
struct spdk_iscsi_task *subtask;
uint32_t remaining_size = 0;
while (1) {
if (primary->current_data_offset < primary->scsi.transfer_len) {
remaining_size = primary->scsi.transfer_len - primary->current_data_offset;
subtask = ut_conn_task_get(primary);
subtask->scsi.offset = primary->current_data_offset;
subtask->scsi.length = spdk_min(SPDK_BDEV_LARGE_BUF_MAX_SIZE, remaining_size);
subtask->scsi.status = SPDK_SCSI_STATUS_GOOD;
primary->current_data_offset += subtask->scsi.length;
TAILQ_INSERT_TAIL(&g_ut_read_tasks, subtask, link);
}
if (primary->current_data_offset == primary->scsi.transfer_len) {
break;
}
}
}
static void
read_task_split_in_order_case(void)
{
struct spdk_iscsi_task primary = {};
struct spdk_iscsi_task *task, *tmp;
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_sess sess = {};
conn.sess = &sess;
conn.sess->DataSequenceInOrder = true;
primary.scsi.transfer_len = SPDK_BDEV_LARGE_BUF_MAX_SIZE * 8;
TAILQ_INIT(&primary.subtask_list);
primary.current_data_offset = 0;
primary.bytes_completed = 0;
primary.scsi.ref = 1;
ut_conn_create_read_tasks(&primary);
SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_ut_read_tasks));
TAILQ_FOREACH(task, &g_ut_read_tasks, link) {
CU_ASSERT(&primary == iscsi_task_get_primary(task));
process_read_task_completion(&conn, task, &primary);
}
CU_ASSERT(primary.bytes_completed == primary.scsi.transfer_len);
CU_ASSERT(primary.scsi.ref == 0);
TAILQ_FOREACH_SAFE(task, &g_ut_read_tasks, link, tmp) {
CU_ASSERT(task->scsi.ref == 0);
TAILQ_REMOVE(&g_ut_read_tasks, task, link);
free(task);
}
}
static void
read_task_split_reverse_order_case(void)
{
struct spdk_iscsi_task primary = {};
struct spdk_iscsi_task *task, *tmp;
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_sess sess = {};
conn.sess = &sess;
conn.sess->DataSequenceInOrder = true;
primary.scsi.transfer_len = SPDK_BDEV_LARGE_BUF_MAX_SIZE * 8;
TAILQ_INIT(&primary.subtask_list);
primary.current_data_offset = 0;
primary.bytes_completed = 0;
primary.scsi.ref = 1;
ut_conn_create_read_tasks(&primary);
SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_ut_read_tasks));
TAILQ_FOREACH_REVERSE(task, &g_ut_read_tasks, read_tasks_head, link) {
CU_ASSERT(&primary == iscsi_task_get_primary(task));
process_read_task_completion(&conn, task, &primary);
}
CU_ASSERT(primary.bytes_completed == primary.scsi.transfer_len);
CU_ASSERT(primary.scsi.ref == 0);
TAILQ_FOREACH_SAFE(task, &g_ut_read_tasks, link, tmp) {
CU_ASSERT(task->scsi.ref == 0);
TAILQ_REMOVE(&g_ut_read_tasks, task, link);
free(task);
}
}
static void
propagate_scsi_error_status_for_split_read_tasks(void)
{
struct spdk_iscsi_task primary = {};
struct spdk_iscsi_task task1 = {}, task2 = {}, task3 = {}, task4 = {}, task5 = {}, task6 = {};
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_sess sess = {};
conn.sess = &sess;
conn.sess->DataSequenceInOrder = true;
primary.scsi.transfer_len = 512 * 6;
primary.scsi.status = SPDK_SCSI_STATUS_GOOD;
TAILQ_INIT(&primary.subtask_list);
primary.scsi.ref = 7;
task1.scsi.offset = 0;
task1.scsi.length = 512;
task1.scsi.status = SPDK_SCSI_STATUS_GOOD;
task1.scsi.ref = 1;
task1.parent = &primary;
task2.scsi.offset = 512;
task2.scsi.length = 512;
task2.scsi.status = SPDK_SCSI_STATUS_CHECK_CONDITION;
task2.scsi.ref = 1;
task2.parent = &primary;
task3.scsi.offset = 512 * 2;
task3.scsi.length = 512;
task3.scsi.status = SPDK_SCSI_STATUS_GOOD;
task3.scsi.ref = 1;
task3.parent = &primary;
task4.scsi.offset = 512 * 3;
task4.scsi.length = 512;
task4.scsi.status = SPDK_SCSI_STATUS_GOOD;
task4.scsi.ref = 1;
task4.parent = &primary;
task5.scsi.offset = 512 * 4;
task5.scsi.length = 512;
task5.scsi.status = SPDK_SCSI_STATUS_GOOD;
task5.scsi.ref = 1;
task5.parent = &primary;
task6.scsi.offset = 512 * 5;
task6.scsi.length = 512;
task6.scsi.status = SPDK_SCSI_STATUS_GOOD;
task6.scsi.ref = 1;
task6.parent = &primary;
/* task2 has check condition status, and verify if the check condition
* status is propagated to remaining tasks correctly when these tasks complete
* by the following order, task4, task3, task2, task1, primary, task5, and task6.
*/
process_read_task_completion(&conn, &task4, &primary);
process_read_task_completion(&conn, &task3, &primary);
process_read_task_completion(&conn, &task2, &primary);
process_read_task_completion(&conn, &task1, &primary);
process_read_task_completion(&conn, &task5, &primary);
process_read_task_completion(&conn, &task6, &primary);
CU_ASSERT(primary.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task1.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task2.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task3.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task4.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task5.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task6.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(primary.bytes_completed == primary.scsi.transfer_len);
CU_ASSERT(TAILQ_EMPTY(&primary.subtask_list));
CU_ASSERT(primary.scsi.ref == 0);
CU_ASSERT(task1.scsi.ref == 0);
CU_ASSERT(task2.scsi.ref == 0);
CU_ASSERT(task3.scsi.ref == 0);
CU_ASSERT(task4.scsi.ref == 0);
CU_ASSERT(task5.scsi.ref == 0);
CU_ASSERT(task6.scsi.ref == 0);
}
static void
process_non_read_task_completion_test(void)
{
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_task primary = {};
struct spdk_iscsi_task task = {};
TAILQ_INIT(&conn.active_r2t_tasks);
primary.bytes_completed = 0;
primary.scsi.transfer_len = 4096 * 3;
primary.scsi.status = SPDK_SCSI_STATUS_GOOD;
primary.scsi.ref = 1;
TAILQ_INSERT_TAIL(&conn.active_r2t_tasks, &primary, link);
primary.is_r2t_active = true;
primary.tag = 1;
/* First subtask which failed. */
task.scsi.length = 4096;
task.scsi.data_transferred = 4096;
task.scsi.status = SPDK_SCSI_STATUS_CHECK_CONDITION;
task.scsi.ref = 1;
task.parent = &primary;
primary.scsi.ref++;
process_non_read_task_completion(&conn, &task, &primary);
CU_ASSERT(!TAILQ_EMPTY(&conn.active_r2t_tasks));
CU_ASSERT(primary.bytes_completed == 4096);
CU_ASSERT(primary.scsi.data_transferred == 0);
CU_ASSERT(primary.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task.scsi.ref == 0);
CU_ASSERT(primary.scsi.ref == 1);
/* Second subtask which succeeded. */
task.scsi.length = 4096;
task.scsi.data_transferred = 4096;
task.scsi.status = SPDK_SCSI_STATUS_GOOD;
task.scsi.ref = 1;
task.parent = &primary;
primary.scsi.ref++;
process_non_read_task_completion(&conn, &task, &primary);
CU_ASSERT(!TAILQ_EMPTY(&conn.active_r2t_tasks));
CU_ASSERT(primary.bytes_completed == 4096 * 2);
CU_ASSERT(primary.scsi.data_transferred == 4096);
CU_ASSERT(primary.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task.scsi.ref == 0);
CU_ASSERT(primary.scsi.ref == 1);
/* Third and final subtask which succeeded. */
task.scsi.length = 4096;
task.scsi.data_transferred = 4096;
task.scsi.status = SPDK_SCSI_STATUS_GOOD;
task.scsi.ref = 1;
task.parent = &primary;
primary.scsi.ref++;
process_non_read_task_completion(&conn, &task, &primary);
CU_ASSERT(TAILQ_EMPTY(&conn.active_r2t_tasks));
CU_ASSERT(primary.bytes_completed == 4096 * 3);
CU_ASSERT(primary.scsi.data_transferred == 4096 * 2);
CU_ASSERT(primary.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(task.scsi.ref == 0);
CU_ASSERT(primary.scsi.ref == 0);
/* A tricky case that the R2T was already terminated when the last task completed. */
primary.scsi.ref = 0;
primary.bytes_completed = 4096 * 2;
primary.scsi.data_transferred = 4096 * 2;
primary.scsi.transfer_len = 4096 * 3;
primary.scsi.status = SPDK_SCSI_STATUS_CHECK_CONDITION;
primary.is_r2t_active = false;
task.scsi.length = 4096;
task.scsi.data_transferred = 4096;
task.scsi.status = SPDK_SCSI_STATUS_GOOD;
task.scsi.ref = 1;
task.parent = &primary;
primary.scsi.ref++;
process_non_read_task_completion(&conn, &task, &primary);
CU_ASSERT(primary.bytes_completed == 4096 * 3);
CU_ASSERT(primary.scsi.data_transferred == 4096 * 3);
CU_ASSERT(primary.scsi.status == SPDK_SCSI_STATUS_CHECK_CONDITION);
CU_ASSERT(primary.scsi.ref == 0);
}
static bool
dequeue_pdu(void *_head, struct spdk_iscsi_pdu *pdu)
{
TAILQ_HEAD(queued_pdus, spdk_iscsi_pdu) *head = _head;
struct spdk_iscsi_pdu *tmp;
TAILQ_FOREACH(tmp, head, tailq) {
if (tmp == pdu) {
TAILQ_REMOVE(head, tmp, tailq);
return true;
}
}
return false;
}
static bool
dequeue_task(void *_head, struct spdk_iscsi_task *task)
{
TAILQ_HEAD(queued_tasks, spdk_iscsi_task) *head = _head;
struct spdk_iscsi_task *tmp;
TAILQ_FOREACH(tmp, head, link) {
if (tmp == task) {
TAILQ_REMOVE(head, tmp, link);
return true;
}
}
return false;
}
static void
iscsi_conn_pdu_dummy_complete(void *arg)
{
}
static void
free_tasks_on_connection(void)
{
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_pdu pdu1 = {}, pdu2 = {}, pdu3 = {}, pdu4 = {};
struct spdk_iscsi_task task1 = {}, task2 = {}, task3 = {};
struct spdk_scsi_lun lun1 = {}, lun2 = {};
TAILQ_INIT(&conn.write_pdu_list);
TAILQ_INIT(&conn.snack_pdu_list);
TAILQ_INIT(&conn.queued_datain_tasks);
conn.data_in_cnt = g_iscsi.MaxLargeDataInPerConnection;
pdu1.task = &task1;
pdu2.task = &task2;
pdu3.task = &task3;
pdu1.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu2.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu3.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu4.cb_fn = iscsi_conn_pdu_dummy_complete;
task1.scsi.lun = &lun1;
task2.scsi.lun = &lun2;
task1.is_queued = false;
task2.is_queued = false;
task3.is_queued = true;
/* Test conn->write_pdu_list. */
task1.scsi.ref = 1;
task2.scsi.ref = 1;
task3.scsi.ref = 1;
TAILQ_INSERT_TAIL(&conn.write_pdu_list, &pdu1, tailq);
TAILQ_INSERT_TAIL(&conn.write_pdu_list, &pdu2, tailq);
TAILQ_INSERT_TAIL(&conn.write_pdu_list, &pdu3, tailq);
TAILQ_INSERT_TAIL(&conn.write_pdu_list, &pdu4, tailq);
/* Free all PDUs when exiting connection. */
iscsi_conn_free_tasks(&conn);
CU_ASSERT(TAILQ_EMPTY(&conn.write_pdu_list));
CU_ASSERT(task1.scsi.ref == 0);
CU_ASSERT(task2.scsi.ref == 0);
CU_ASSERT(task3.scsi.ref == 0);
/* Test conn->snack_pdu_list */
task1.scsi.ref = 1;
task2.scsi.ref = 1;
task3.scsi.ref = 1;
pdu1.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu2.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu3.cb_fn = iscsi_conn_pdu_dummy_complete;
TAILQ_INSERT_TAIL(&conn.snack_pdu_list, &pdu1, tailq);
TAILQ_INSERT_TAIL(&conn.snack_pdu_list, &pdu2, tailq);
TAILQ_INSERT_TAIL(&conn.snack_pdu_list, &pdu3, tailq);
/* Free all PDUs and associated tasks when exiting connection. */
iscsi_conn_free_tasks(&conn);
CU_ASSERT(!dequeue_pdu(&conn.snack_pdu_list, &pdu1));
CU_ASSERT(!dequeue_pdu(&conn.snack_pdu_list, &pdu2));
CU_ASSERT(!dequeue_pdu(&conn.snack_pdu_list, &pdu3));
CU_ASSERT(task1.scsi.ref == 0);
CU_ASSERT(task2.scsi.ref == 0);
CU_ASSERT(task3.scsi.ref == 0);
/* Test conn->queued_datain_tasks */
task1.scsi.ref = 1;
task2.scsi.ref = 1;
task3.scsi.ref = 1;
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task1, link);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task2, link);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task3, link);
/* Free all tasks which is not queued when exiting connection. */
iscsi_conn_free_tasks(&conn);
CU_ASSERT(!dequeue_task(&conn.queued_datain_tasks, &task1));
CU_ASSERT(!dequeue_task(&conn.queued_datain_tasks, &task2));
CU_ASSERT(dequeue_task(&conn.queued_datain_tasks, &task3));
CU_ASSERT(task1.scsi.ref == 0);
CU_ASSERT(task2.scsi.ref == 0);
CU_ASSERT(task3.scsi.ref == 1);
}
static void
free_tasks_with_queued_datain(void)
{
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_pdu pdu1 = {}, pdu2 = {}, pdu3 = {}, pdu4 = {}, pdu5 = {}, pdu6 = {};
struct spdk_iscsi_task task1 = {}, task2 = {}, task3 = {}, task4 = {}, task5 = {}, task6 = {};
TAILQ_INIT(&conn.write_pdu_list);
TAILQ_INIT(&conn.snack_pdu_list);
TAILQ_INIT(&conn.queued_datain_tasks);
pdu1.task = &task1;
pdu2.task = &task2;
pdu3.task = &task3;
pdu1.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu2.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu3.cb_fn = iscsi_conn_pdu_dummy_complete;
task1.scsi.ref = 1;
task2.scsi.ref = 1;
task3.scsi.ref = 1;
pdu3.bhs.opcode = ISCSI_OP_SCSI_DATAIN;
task3.scsi.offset = 1;
conn.data_in_cnt = 1;
TAILQ_INSERT_TAIL(&conn.write_pdu_list, &pdu1, tailq);
TAILQ_INSERT_TAIL(&conn.write_pdu_list, &pdu2, tailq);
TAILQ_INSERT_TAIL(&conn.write_pdu_list, &pdu3, tailq);
task4.scsi.ref = 1;
task5.scsi.ref = 1;
task6.scsi.ref = 1;
task4.pdu = &pdu4;
task5.pdu = &pdu5;
task6.pdu = &pdu6;
pdu4.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu5.cb_fn = iscsi_conn_pdu_dummy_complete;
pdu6.cb_fn = iscsi_conn_pdu_dummy_complete;
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task4, link);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task5, link);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task6, link);
iscsi_conn_free_tasks(&conn);
CU_ASSERT(TAILQ_EMPTY(&conn.write_pdu_list));
CU_ASSERT(TAILQ_EMPTY(&conn.queued_datain_tasks));
}
static void
abort_queued_datain_task_test(void)
{
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_task task = {}, subtask = {};
struct spdk_iscsi_pdu pdu = {};
struct iscsi_bhs_scsi_req *scsi_req;
int rc;
struct spdk_iscsi_sess sess = {};
conn.sess = &sess;
conn.sess->DataSequenceInOrder = true;
TAILQ_INIT(&conn.queued_datain_tasks);
task.scsi.ref = 1;
task.scsi.dxfer_dir = SPDK_SCSI_DIR_FROM_DEV;
task.pdu = &pdu;
TAILQ_INIT(&task.subtask_list);
scsi_req = (struct iscsi_bhs_scsi_req *)&pdu.bhs;
scsi_req->read_bit = 1;
g_new_task = &subtask;
/* Case1: Queue one task, and this task is not executed */
task.scsi.transfer_len = SPDK_BDEV_LARGE_BUF_MAX_SIZE * 3;
task.scsi.offset = 0;
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task, link);
/* No slots for sub read tasks */
conn.data_in_cnt = g_iscsi.MaxLargeDataInPerConnection;
rc = _iscsi_conn_abort_queued_datain_task(&conn, &task);
CU_ASSERT(rc != 0);
CU_ASSERT(!TAILQ_EMPTY(&conn.queued_datain_tasks));
/* Have slots for sub read tasks */
conn.data_in_cnt = 0;
rc = _iscsi_conn_abort_queued_datain_task(&conn, &task);
CU_ASSERT(rc == 0);
CU_ASSERT(TAILQ_EMPTY(&conn.queued_datain_tasks));
CU_ASSERT(task.current_data_offset == SPDK_BDEV_LARGE_BUF_MAX_SIZE * 3);
CU_ASSERT(task.scsi.ref == 0);
CU_ASSERT(subtask.scsi.offset == 0);
CU_ASSERT(subtask.scsi.length == SPDK_BDEV_LARGE_BUF_MAX_SIZE * 3);
CU_ASSERT(subtask.scsi.ref == 0);
/* Case2: Queue one task, and this task is partially executed */
task.scsi.ref = 1;
task.scsi.transfer_len = SPDK_BDEV_LARGE_BUF_MAX_SIZE * 3;
task.current_data_offset = SPDK_BDEV_LARGE_BUF_MAX_SIZE;
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task, link);
/* No slots for sub read tasks */
conn.data_in_cnt = g_iscsi.MaxLargeDataInPerConnection;
rc = _iscsi_conn_abort_queued_datain_task(&conn, &task);
CU_ASSERT(rc != 0);
CU_ASSERT(!TAILQ_EMPTY(&conn.queued_datain_tasks));
/* have slots for sub read tasks */
conn.data_in_cnt = 0;
rc = _iscsi_conn_abort_queued_datain_task(&conn, &task);
CU_ASSERT(rc == 0);
CU_ASSERT(task.current_data_offset == SPDK_BDEV_LARGE_BUF_MAX_SIZE * 3);
CU_ASSERT(task.scsi.ref == 2);
CU_ASSERT(TAILQ_FIRST(&task.subtask_list) == &subtask);
CU_ASSERT(subtask.scsi.offset == SPDK_BDEV_LARGE_BUF_MAX_SIZE);
CU_ASSERT(subtask.scsi.length == SPDK_BDEV_LARGE_BUF_MAX_SIZE * 2);
CU_ASSERT(subtask.scsi.ref == 1);
g_new_task = NULL;
}
static bool
datain_task_is_queued(struct spdk_iscsi_conn *conn,
struct spdk_iscsi_task *task)
{
struct spdk_iscsi_task *tmp;
TAILQ_FOREACH(tmp, &conn->queued_datain_tasks, link) {
if (tmp == task) {
return true;
}
}
return false;
}
static void
abort_queued_datain_tasks_test(void)
{
struct spdk_iscsi_conn conn = {};
struct spdk_iscsi_task task1 = {}, task2 = {}, task3 = {}, task4 = {}, task5 = {}, task6 = {};
struct spdk_iscsi_task subtask = {};
struct spdk_iscsi_pdu pdu1 = {}, pdu2 = {}, pdu3 = {}, pdu4 = {}, pdu5 = {}, pdu6 = {};
struct spdk_iscsi_pdu mgmt_pdu1 = {}, mgmt_pdu2 = {};
struct spdk_scsi_lun lun1 = {}, lun2 = {};
uint32_t alloc_cmd_sn;
struct iscsi_bhs_scsi_req *scsi_req;
int rc;
struct spdk_iscsi_sess sess = {};
TAILQ_INIT(&conn.queued_datain_tasks);
conn.data_in_cnt = 0;
conn.sess = &sess;
conn.sess->DataSequenceInOrder = true;
g_new_task = &subtask;
alloc_cmd_sn = 88;
pdu1.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
scsi_req = (struct iscsi_bhs_scsi_req *)&pdu1.bhs;
scsi_req->read_bit = 1;
task1.scsi.ref = 1;
task1.current_data_offset = 0;
task1.scsi.transfer_len = 512;
task1.scsi.lun = &lun1;
iscsi_task_set_pdu(&task1, &pdu1);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task1, link);
pdu2.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
scsi_req = (struct iscsi_bhs_scsi_req *)&pdu2.bhs;
scsi_req->read_bit = 1;
task2.scsi.ref = 1;
task2.current_data_offset = 0;
task2.scsi.transfer_len = 512;
task2.scsi.lun = &lun2;
iscsi_task_set_pdu(&task2, &pdu2);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task2, link);
mgmt_pdu1.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
pdu3.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
scsi_req = (struct iscsi_bhs_scsi_req *)&pdu3.bhs;
scsi_req->read_bit = 1;
task3.scsi.ref = 1;
task3.current_data_offset = 0;
task3.scsi.transfer_len = 512;
task3.scsi.lun = &lun1;
iscsi_task_set_pdu(&task3, &pdu3);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task3, link);
pdu4.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
scsi_req = (struct iscsi_bhs_scsi_req *)&pdu4.bhs;
scsi_req->read_bit = 1;
task4.scsi.ref = 1;
task4.current_data_offset = 0;
task4.scsi.transfer_len = 512;
task4.scsi.lun = &lun2;
iscsi_task_set_pdu(&task4, &pdu4);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task4, link);
pdu5.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
scsi_req = (struct iscsi_bhs_scsi_req *)&pdu5.bhs;
scsi_req->read_bit = 1;
task5.scsi.ref = 1;
task5.current_data_offset = 0;
task5.scsi.transfer_len = 512;
task5.scsi.lun = &lun1;
iscsi_task_set_pdu(&task5, &pdu5);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task5, link);
mgmt_pdu2.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
pdu6.cmd_sn = alloc_cmd_sn;
alloc_cmd_sn++;
scsi_req = (struct iscsi_bhs_scsi_req *)&pdu6.bhs;
scsi_req->read_bit = 1;
task6.scsi.ref = 1;
task6.current_data_offset = 0;
task6.scsi.transfer_len = 512;
task6.scsi.lun = &lun2;
iscsi_task_set_pdu(&task6, &pdu6);
TAILQ_INSERT_TAIL(&conn.queued_datain_tasks, &task6, link);
rc = iscsi_conn_abort_queued_datain_tasks(&conn, &lun1, &mgmt_pdu1);
CU_ASSERT(rc == 0);
CU_ASSERT(!datain_task_is_queued(&conn, &task1));
CU_ASSERT(datain_task_is_queued(&conn, &task2));
CU_ASSERT(datain_task_is_queued(&conn, &task3));
CU_ASSERT(datain_task_is_queued(&conn, &task4));
CU_ASSERT(datain_task_is_queued(&conn, &task5));
CU_ASSERT(datain_task_is_queued(&conn, &task6));
rc = iscsi_conn_abort_queued_datain_tasks(&conn, &lun2, &mgmt_pdu2);
CU_ASSERT(rc == 0);
CU_ASSERT(!datain_task_is_queued(&conn, &task2));
CU_ASSERT(datain_task_is_queued(&conn, &task3));
CU_ASSERT(!datain_task_is_queued(&conn, &task4));
CU_ASSERT(datain_task_is_queued(&conn, &task5));
CU_ASSERT(datain_task_is_queued(&conn, &task6));
CU_ASSERT(task1.scsi.ref == 0);
CU_ASSERT(task2.scsi.ref == 0);
CU_ASSERT(task3.scsi.ref == 1);
CU_ASSERT(task4.scsi.ref == 0);
CU_ASSERT(task5.scsi.ref == 1);
CU_ASSERT(task6.scsi.ref == 1);
CU_ASSERT(subtask.scsi.ref == 0);
g_new_task = NULL;
}
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("conn_suite", NULL, NULL);
CU_ADD_TEST(suite, read_task_split_in_order_case);
CU_ADD_TEST(suite, read_task_split_reverse_order_case);
CU_ADD_TEST(suite, propagate_scsi_error_status_for_split_read_tasks);
CU_ADD_TEST(suite, process_non_read_task_completion_test);
CU_ADD_TEST(suite, free_tasks_on_connection);
CU_ADD_TEST(suite, free_tasks_with_queued_datain);
CU_ADD_TEST(suite, abort_queued_datain_task_test);
CU_ADD_TEST(suite, abort_queued_datain_tasks_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;
}