Commit af661f78 authored by Shuhei Matsumoto's avatar Shuhei Matsumoto Committed by Tomasz Zawadzki
Browse files

ut/iscsi: Add Data-OUT PDU sequence test cases



Test scenario is as follows.

Some iSCSI initiator sends a Data-OUT PDU sequence such that the size of
the data segment of any Data-OUT PDU is not block size multiples.
Test if such complex Data-OUT PDU sequence is processed correctly.

Desired Data Transfer Length is 5 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2.
Number of Data-OUT PDUs is 4. Length of the data segment of the first two PDUs are
SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 - 4. Length of the data segment of the
third PDU is SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH. Length of the data segment
of the final PDU is SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 + 8.

Three data buffers should be used and three subtasks should be created and submitted.

The test scenario assume that a iscsi_conn_read_data() call could read
the required length of the data and all read lengths are 4 bytes multiples.
The latter is to verify data is copied to the correct offset by using data patterns.

Signed-off-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Change-Id: I5e3939d07cd4f50fe3dcf659a18a9f8fcbe01e07
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/6584


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarChangpeng Liu <changpeng.liu@intel.com>
Community-CI: Mellanox Build Bot
parent 4af8b24a
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -120,8 +120,13 @@ 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(spdk_scsi_dev_queue_task,
	      (struct spdk_scsi_dev *dev, struct spdk_scsi_task *task));
void
spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev, struct spdk_scsi_task *task)
{
	assert(dev->lun[0] != NULL);

	TAILQ_INSERT_TAIL(&dev->lun[0]->tasks, task, scsi_link);
}

DEFINE_STUB(spdk_scsi_dev_find_port_by_id, struct spdk_scsi_port *,
	    (struct spdk_scsi_dev *dev, uint64_t id), NULL);
+216 −0
Original line number Diff line number Diff line
@@ -2124,6 +2124,221 @@ pdu_payload_read_test(void)
	free(mobj2.buf);
}

static void
check_pdu_hdr_handle(struct spdk_iscsi_pdu *pdu, struct spdk_mobj *mobj, uint32_t offset,
		     struct spdk_iscsi_task *primary)
{
	CU_ASSERT(pdu->mobj[0] == mobj);
	CU_ASSERT(pdu->data == NULL || pdu->data == (void *)((uint64_t)mobj->buf + offset));
	CU_ASSERT(primary->mobj == NULL);
}

static void
check_pdu_payload_handle(struct spdk_iscsi_pdu *pdu, struct spdk_iscsi_task *primary,
			 struct spdk_mobj *pdu_mobj0, struct spdk_mobj *pdu_mobj1,
			 struct spdk_mobj *primary_mobj, uint32_t primary_offset)
{
	CU_ASSERT(pdu->mobj[0] == pdu_mobj0);
	CU_ASSERT(pdu->mobj[1] == pdu_mobj1);
	CU_ASSERT(primary->mobj == primary_mobj);
	CU_ASSERT(primary->current_data_offset == primary_offset);
}

static void
check_write_subtask_submit(struct spdk_scsi_lun *lun, struct spdk_mobj *mobj,
			   struct spdk_iscsi_pdu *pdu,
			   int index, uint32_t offset, uint32_t length)
{
	struct spdk_scsi_task *scsi_task;
	struct spdk_iscsi_task *subtask;
	uint32_t *data;
	uint32_t i;

	scsi_task = TAILQ_FIRST(&lun->tasks);
	SPDK_CU_ASSERT_FATAL(scsi_task != NULL);
	TAILQ_REMOVE(&lun->tasks, scsi_task, scsi_link);

	subtask = iscsi_task_from_scsi_task(scsi_task);

	CU_ASSERT(iscsi_task_get_pdu(subtask) == pdu);
	CU_ASSERT(pdu->mobj[index] == mobj);
	CU_ASSERT(subtask->scsi.offset == offset);
	CU_ASSERT(subtask->scsi.length == length);
	CU_ASSERT(subtask->scsi.iovs[0].iov_base == mobj->buf);
	CU_ASSERT(subtask->scsi.iovs[0].iov_len == length);

	data = (uint32_t *)mobj->buf;
	for (i = 0; i < length; i += 4) {
		CU_ASSERT(data[i / 4] == offset + i);
	}

	free(subtask);
}

static void
data_out_pdu_sequence_test(void)
{
	struct spdk_scsi_lun lun = { .tasks = TAILQ_HEAD_INITIALIZER(lun.tasks), };
	struct spdk_scsi_dev dev = { .lun[0] = &lun,    };
	struct spdk_iscsi_sess sess = {
		.session_type = SESSION_TYPE_NORMAL,
		.MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH,
	};
	struct spdk_iscsi_conn conn = {
		.full_feature = true,
		.state = ISCSI_CONN_STATE_RUNNING,
		.sess = &sess,
		.dev = &dev,
		.active_r2t_tasks = TAILQ_HEAD_INITIALIZER(conn.active_r2t_tasks),
	};
	struct spdk_iscsi_task primary = {};
	struct spdk_iscsi_pdu pdu = {};
	struct spdk_mobj mobj1 = {}, mobj2 = {}, mobj3 = {};
	struct iscsi_bhs_data_out *data_reqh;
	int rc;

	mobj1.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH));
	SPDK_CU_ASSERT_FATAL(mobj1.buf != NULL);

	mobj2.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH));
	SPDK_CU_ASSERT_FATAL(mobj2.buf != NULL);

	mobj3.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH));
	SPDK_CU_ASSERT_FATAL(mobj3.buf != NULL);

	/* Test scenario is as follows.
	 *
	 * Some iSCSI initiator sends a Data-OUT PDU sequence such that the size of
	 * the data segment of any Data-OUT PDU is not block size multiples.
	 * Test if such complex Data-OUT PDU sequence is processed correctly.
	 *
	 * Desired Data Transfer Length is 5 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2.
	 * Number of Data-OUT PDUs is 4. Length of the data segment of the first two PDUs are
	 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 - 4. Length of the data segment of the
	 * third PDU is SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH. Length of the data segment
	 * of the final PDU is SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 + 8.
	 *
	 * Three data buffers should be used and three subtasks should be created and submitted.
	 *
	 * The test scenario assume that a iscsi_conn_read_data() call could read
	 * the required length of the data and all read lengths are 4 bytes multiples.
	 * The latter is to verify data is copied to the correct offset by using data patterns.
	 */

	primary.scsi.transfer_len = 5 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2;
	primary.desired_data_transfer_length = primary.scsi.transfer_len;
	TAILQ_INSERT_TAIL(&conn.active_r2t_tasks, &primary, link);
	conn.pending_r2t = 1;
	g_conn_read_len = 0;

	/* The 1st Data-OUT PDU */
	data_reqh = (struct iscsi_bhs_data_out *)&pdu.bhs;
	pdu.bhs.opcode = ISCSI_OP_SCSI_DATAOUT;
	pdu.data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 - 4;

	rc = iscsi_pdu_hdr_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_hdr_handle(&pdu, NULL, 0, &primary);

	MOCK_SET(spdk_mempool_get, &mobj1);

	rc = iscsi_pdu_payload_read(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_read(&pdu, &mobj1, rc, 0, 0);

	rc = iscsi_pdu_payload_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_handle(&pdu, &primary, NULL, NULL, &mobj1, 0);

	/* The 2nd Data-OUT PDU */
	memset(&pdu, 0, sizeof(pdu));
	pdu.bhs.opcode = ISCSI_OP_SCSI_DATAOUT;
	pdu.data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 - 4;
	to_be32(&data_reqh->data_sn, 1);
	to_be32(&data_reqh->buffer_offset, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 - 4);

	rc = iscsi_pdu_hdr_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_hdr_handle(&pdu, &mobj1, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 - 4, &primary);

	rc = iscsi_pdu_payload_read(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_read(&pdu, &mobj1, rc, 0, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 - 4);

	rc = iscsi_pdu_payload_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_handle(&pdu, &primary, NULL, NULL, &mobj1, 0);

	/* The 3rd Data-OUT PDU */
	memset(&pdu, 0, sizeof(pdu));
	pdu.bhs.opcode = ISCSI_OP_SCSI_DATAOUT;
	pdu.data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH;
	to_be32(&data_reqh->data_sn, 2);
	to_be32(&data_reqh->buffer_offset, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH - 8);

	rc = iscsi_pdu_hdr_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_hdr_handle(&pdu, &mobj1, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH - 8, &primary);

	rc = iscsi_pdu_payload_read(&conn, &pdu);
	CU_ASSERT(rc > 0);
	check_pdu_payload_read(&pdu, &mobj1, rc, 0, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH - 8);

	MOCK_SET(spdk_mempool_get, &mobj2);

	rc = iscsi_pdu_payload_read(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_read(&pdu, &mobj2, rc, 1, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);

	rc = iscsi_pdu_payload_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_handle(&pdu, &primary, &mobj1, NULL, &mobj2,
				 SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);

	check_write_subtask_submit(&lun, &mobj1, &pdu, 0, 0, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);

	/* The 4th and final Data-OUT PDU */
	memset(&pdu, 0, sizeof(pdu));
	pdu.bhs.opcode = ISCSI_OP_SCSI_DATAOUT;
	pdu.data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2 + 8;
	data_reqh->flags |= ISCSI_FLAG_FINAL;
	to_be32(&data_reqh->data_sn, 3);
	to_be32(&data_reqh->buffer_offset, 2 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH - 8);

	rc = iscsi_pdu_hdr_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_hdr_handle(&pdu, &mobj2, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH - 8, &primary);

	rc = iscsi_pdu_payload_read(&conn, &pdu);
	CU_ASSERT(rc > 0);
	check_pdu_payload_read(&pdu, &mobj2, rc, 0, 2 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH - 8);

	MOCK_SET(spdk_mempool_get, &mobj3);

	rc = iscsi_pdu_payload_read(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_read(&pdu, &mobj3, rc, 1, 2 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);

	rc = iscsi_pdu_payload_handle(&conn, &pdu);
	CU_ASSERT(rc == 0);
	check_pdu_payload_handle(&pdu, &primary, &mobj2, &mobj3, NULL,
				 5 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2);

	check_write_subtask_submit(&lun, &mobj2, &pdu, 0, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH,
				   SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);

	check_write_subtask_submit(&lun, &mobj3, &pdu, 1, 2 * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH,
				   SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH / 2);

	CU_ASSERT(TAILQ_EMPTY(&lun.tasks));

	MOCK_CLEAR(spdk_mempool_get);

	free(mobj1.buf);
	free(mobj2.buf);
	free(mobj3.buf);
}

int
main(int argc, char **argv)
{
@@ -2157,6 +2372,7 @@ main(int argc, char **argv)
	CU_ADD_TEST(suite, pdu_hdr_op_data_test);
	CU_ADD_TEST(suite, empty_text_with_cbit_test);
	CU_ADD_TEST(suite, pdu_payload_read_test);
	CU_ADD_TEST(suite, data_out_pdu_sequence_test);

	CU_basic_set_mode(CU_BRM_VERBOSE);
	CU_basic_run_tests();