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: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Change-Id: I5e3939d07cd4f50fe3dcf659a18a9f8fcbe01e07 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/6584 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Changpeng Liu <changpeng.liu@intel.com> Community-CI: Mellanox Build Bot
This commit is contained in:
parent
4af8b24ace
commit
af661f7844
@ -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);
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user