diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 4c29c83..f93b801 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -40,6 +40,9 @@ extern "C" { #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) +#endif #define ISCSI_RAW_HEADER_SIZE 48 #define ISCSI_DIGEST_SIZE 4 diff --git a/test-tool/test_write10_residuals.c b/test-tool/test_write10_residuals.c index 2c4c207..67ea3ff 100644 --- a/test-tool/test_write10_residuals.c +++ b/test-tool/test_write10_residuals.c @@ -32,24 +32,21 @@ test_write10_residuals(void) { /* testing scenarios */ const struct residuals_test_data write10_residuals[] = { - /* cdb_size, xfer_len, buf_len, residuals_kind, residuals_amount, check_overwrite */ - {10, 1, 0, SCSI_RESIDUAL_OVERFLOW, block_size, false, + /* cdb_size, xfer_len, buf_len, residuals_kind, residuals_amount */ + {10, 1, 0, SCSI_RESIDUAL_OVERFLOW, block_size, "Try writing one block but with iSCSI expected transfer length==0"}, - {10, 1, 10000, SCSI_RESIDUAL_UNDERFLOW, 10000 - block_size, false, + {10, 1, 2 * block_size, SCSI_RESIDUAL_UNDERFLOW, block_size, + "Try writing one block but set iSCSI EDTL to 2 blocks"}, + + {10, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, + "Try writing two blocks but set iSCSI EDTL to 1 block"}, + + {10, 1, 10000, SCSI_RESIDUAL_UNDERFLOW, 10000 - block_size, "Try writing one block but with iSCSI expected transfer length==10000"}, - {10, 1, 200, SCSI_RESIDUAL_OVERFLOW, block_size - 200, false, + {10, 1, 200, SCSI_RESIDUAL_OVERFLOW, block_size - 200, "Try writing one block but with iSCSI expected transfer length==200"}, - - {10, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, false, - "Try writing two blocks but iSCSI expected one block transfer length"}, - - {10, 1, 2 * block_size, SCSI_RESIDUAL_UNDERFLOW, block_size, true, - "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"}, - - {10, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, true, - "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"}, }; unsigned int i = 0; diff --git a/test-tool/test_write12_residuals.c b/test-tool/test_write12_residuals.c index bb27808..23ae0c9 100644 --- a/test-tool/test_write12_residuals.c +++ b/test-tool/test_write12_residuals.c @@ -32,24 +32,21 @@ test_write12_residuals(void) { /* testing scenarios */ const struct residuals_test_data write12_residuals[] = { - /* cdb_size, xfer_len, buf_len, residuals_kind, residuals_amount, check_overwrite */ - {12, 1, 0, SCSI_RESIDUAL_OVERFLOW, block_size, false, + /* cdb_size, xfer_len, buf_len, residuals_kind, residuals_amount */ + {12, 1, 0, SCSI_RESIDUAL_OVERFLOW, block_size, "Try writing one block but with iSCSI expected transfer length==0"}, - {12, 1, 10000, SCSI_RESIDUAL_UNDERFLOW, 10000 - block_size, false, + {12, 1, 2 * block_size, SCSI_RESIDUAL_UNDERFLOW, block_size, + "Try writing one block but set iSCSI EDTL to 2 blocks"}, + + {12, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, + "Try writing two blocks but set iSCSI EDTL to 1 block"}, + + {12, 1, 10000, SCSI_RESIDUAL_UNDERFLOW, 10000 - block_size, "Try writing one block but with iSCSI expected transfer length==10000"}, - {12, 1, 200, SCSI_RESIDUAL_OVERFLOW, block_size - 200, false, + {12, 1, 200, SCSI_RESIDUAL_OVERFLOW, block_size - 200, "Try writing one block but with iSCSI expected transfer length==200"}, - - {12, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, false, - "Try writing two blocks but iSCSI expected one block transfer length"}, - - {12, 1, 2 * block_size, SCSI_RESIDUAL_UNDERFLOW, block_size, true, - "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"}, - - {12, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, true, - "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"}, }; unsigned int i = 0; diff --git a/test-tool/test_write16_residuals.c b/test-tool/test_write16_residuals.c index c4a7139..b3f6979 100644 --- a/test-tool/test_write16_residuals.c +++ b/test-tool/test_write16_residuals.c @@ -32,24 +32,21 @@ test_write16_residuals(void) { /* testing scenarios */ const struct residuals_test_data write16_residuals[] = { - /* cdb_size, xfer_len, buf_len, residuals_kind, residuals_amount, check_overwrite */ - {16, 1, 0, SCSI_RESIDUAL_OVERFLOW, block_size, false, + /* cdb_size, xfer_len, buf_len, residuals_kind, residuals_amount */ + {16, 1, 0, SCSI_RESIDUAL_OVERFLOW, block_size, "Try writing one block but with iSCSI expected transfer length==0"}, - {16, 1, 10000, SCSI_RESIDUAL_UNDERFLOW, 10000 - block_size, false, + {16, 1, 2 * block_size, SCSI_RESIDUAL_UNDERFLOW, block_size, + "Try writing one block but set iSCSI EDTL to 2 blocks"}, + + {16, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, + "Try writing two blocks but set iSCSI EDTL to 1 block"}, + + {16, 1, 10000, SCSI_RESIDUAL_UNDERFLOW, 10000 - block_size, "Try writing one block but with iSCSI expected transfer length==10000"}, - {16, 1, 200, SCSI_RESIDUAL_OVERFLOW, block_size - 200, false, + {16, 1, 200, SCSI_RESIDUAL_OVERFLOW, block_size - 200, "Try writing one block but with iSCSI expected transfer length==200"}, - - {16, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, false, - "Try writing two blocks but iSCSI expected one block transfer length"}, - - {16, 1, 2 * block_size, SCSI_RESIDUAL_UNDERFLOW, block_size, true, - "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"}, - - {16, 2, block_size, SCSI_RESIDUAL_OVERFLOW, block_size, true, - "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"}, }; unsigned int i = 0; diff --git a/test-tool/test_write_residuals.c b/test-tool/test_write_residuals.c index 976db3b..cdfa031 100644 --- a/test-tool/test_write_residuals.c +++ b/test-tool/test_write_residuals.c @@ -35,10 +35,13 @@ write_residuals_test(const struct residuals_test_data *tdata) { struct iscsi_data data; struct scsi_task *task_ret; + struct task_status status; int ok; - int scsi_status; + unsigned int expected_write_size; + unsigned int max_len; unsigned int xfer_len_byte = 8; unsigned int i; + unsigned int transfer_length; unsigned int scsi_opcode_write = SCSI_OPCODE_WRITE10; const char *residual = tdata->residuals_kind == SCSI_RESIDUAL_OVERFLOW ? "overflow" : "underflow"; @@ -57,36 +60,42 @@ write_residuals_test(const struct residuals_test_data *tdata) break; } - if (tdata->check_overwrite) { - logging(LOG_VERBOSE, "Write two blocks of 'a'"); - memset(scratch, 'a', (2 * block_size)); + if (tdata->xfer_len * block_size > tdata->buf_len) /* SPDTL > EDTL */ { + /* Transfer has to be truncated up to EDTL */ + expected_write_size = tdata->buf_len; + max_len = (tdata->xfer_len * block_size); + } else /* SPDTL <= EDTL */ { + /* Transfer has to be truncated up to SPDTL */ + expected_write_size = (tdata->xfer_len * block_size); + max_len = tdata->buf_len; + } + transfer_length = DIV_ROUND_UP(max_len, block_size) * block_size; - switch (tdata->cdb_size) { - case 10: - WRITE10(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); - break; - case 12: - WRITE12(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); - break; - case 16: - WRITE16(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); - break; - } + logging(LOG_VERBOSE, "Write %ld block(s) of 'a'", transfer_length / block_size); + memset(scratch, 'a', transfer_length); - logging(LOG_VERBOSE, "Write %u block(s) of 'b' but set iSCSI EDTL to %u block(s).", - tdata->xfer_len, - tdata->xfer_len % 2 + 1); + switch (tdata->cdb_size) { + case 10: + WRITE10(sd, 0, transfer_length, block_size, 0, 0, 0, 0, 0, + scratch, EXPECT_STATUS_GOOD); + break; + case 12: + WRITE12(sd, 0, transfer_length, block_size, 0, 0, 0, 0, 0, + scratch, EXPECT_STATUS_GOOD); + break; + case 16: + WRITE16(sd, 0, transfer_length, block_size, 0, 0, 0, 0, 0, + scratch, EXPECT_STATUS_GOOD); + break; } task = malloc(sizeof(*task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(*task)); - if (tdata->check_overwrite) { - memset(scratch, 'b', tdata->buf_len); - } else { - memset(scratch, 0xa6, tdata->buf_len); - } + logging(LOG_VERBOSE, "Write 'b' with the transfer length " + "being set to %d block(s)", tdata->xfer_len); + memset(scratch, 'b', tdata->buf_len); task->cdb[0] = scsi_opcode_write; task->cdb[xfer_len_byte] = tdata->xfer_len; @@ -113,7 +122,11 @@ write_residuals_test(const struct residuals_test_data *tdata) logging(LOG_VERBOSE, "Verify that target returns SUCCESS or INVALID " "FIELD IN INFORMATION UNIT"); - scsi_status = task->status; + + status.status = task->status; + status.sense.key = task->sense.key; + status.sense.ascq = task->sense.ascq; + ok = task->status == SCSI_STATUS_GOOD || (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && @@ -142,36 +155,65 @@ write_residuals_test(const struct residuals_test_data *tdata) scsi_free_scsi_task(task); task = NULL; - if (!tdata->check_overwrite) { - return; - } - - logging(LOG_VERBOSE, "Read the two blocks"); + logging(LOG_VERBOSE, "Read %ld block(s)", transfer_length / block_size); switch (tdata->cdb_size) { case 10: - READ10(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, - scratch, EXPECT_STATUS_GOOD); + READ10(sd, NULL, 0, transfer_length, block_size, + 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); break; case 12: - READ12(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, - scratch, EXPECT_STATUS_GOOD); + READ12(sd, NULL, 0, transfer_length, block_size, + 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); break; case 16: - READ16(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, - scratch, EXPECT_STATUS_GOOD); + READ16(sd, NULL, 0, transfer_length, block_size, + 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); break; } - /* According to FCP target could transfer no data and return - CHECK CONDITION status with the sense key set to - ILLEGAL REQUEST and the additional sense code set to - INVALID FIELD IN COMMAND INFORMATION UNIT; this check prevent - false assert*/ + /* According to FCP-4: + If the command requested that data beyond the length specified by + the FCP_DL field be transferred, then the device server shall set + the FCP_RESID_OVER bit (see 9.5.8) to one in the FCP_RSP IU and: - if (scsi_status != SCSI_STATUS_GOOD) { - logging(LOG_VERBOSE, "Verify that blocks were NOT " - "overwritten and still contain 'a'"); - for (i = 0; i < 2 * block_size; i++) { + a) process the command normally except that data beyond the FCP_DL + count shall not be requested or transferred; */ + + if (status.status == SCSI_STATUS_GOOD) { + + switch (tdata->residuals_kind) { + case SCSI_RESIDUAL_OVERFLOW: + logging(LOG_VERBOSE, "Verify that if iSCSI EDTL < SCSI TL " + "then we only write iSCSI EDTL amount of data"); + break; + case SCSI_RESIDUAL_UNDERFLOW: + logging(LOG_VERBOSE, "Verify that if iSCSI EDTL > SCSI TL " + "then we only write SCSI TL amount of data"); + break; + } + + logging(LOG_VERBOSE, "Verify that the first %d bytes were " + "changed to 'b'", expected_write_size); + for (i = 0; i < expected_write_size; i++) { + if (scratch[i] != 'b') { + logging(LOG_NORMAL, "Blocks did not contain " + "expected 'b'"); + CU_FAIL("Blocks was not written correctly"); + break; + } + } + + /* b) transfer no data and return CHECK CONDITION status with the sense + key set to ILLEGAL REQUEST and the additional sense code set to + INVALID FIELD IN COMMAND INFORMATION UNIT; */ + + } else if (status.status == SCSI_STATUS_CHECK_CONDITION && + status.sense.key == SCSI_SENSE_ILLEGAL_REQUEST && + status.sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT) { + + logging(LOG_VERBOSE, "Verify that first %d bytes were NOT " + "overwritten and still contain 'a'", expected_write_size); + for (i = 0; i < expected_write_size; i++) { if (scratch[i] != 'a') { logging(LOG_NORMAL, "Blocks were overwritten " "and no longer contain 'a'"); @@ -179,26 +221,24 @@ write_residuals_test(const struct residuals_test_data *tdata) break; } } - return; } - logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); - for (i = 0; i < block_size; i++) { - if (scratch[i] != 'b') { - logging(LOG_NORMAL, "First block did not contain " - "expected 'b'"); - CU_FAIL("Block was not written correctly"); - break; - } - } + /* c) may transfer data and return CHECK CONDITION status with the + sense key set to ABORTED COMMAND and the additional sense code + set to INVALID FIELD IN COMMAND INFORMATION UNIT. - logging(LOG_VERBOSE, "Verify that the second block was NOT " - "overwritten and still contains 'a'"); - for (i = block_size; i < 2 * block_size; i++) { + (not implemented yet) */ + + /* Regardless of the executed target scenario, data beyond expected + truncation point should not be overwritten */ + + logging(LOG_VERBOSE, "Verify that the last %ld bytes were NOT " + "overwritten and still contain 'a'", tdata->residuals_amount); + for (i = expected_write_size; i < max_len; i++) { if (scratch[i] != 'a') { - logging(LOG_NORMAL, "Second block was overwritten " + logging(LOG_NORMAL, "Data was overwritten " "and no longer contain 'a'"); - CU_FAIL("Second block was incorrectly overwritten"); + CU_FAIL("Data was incorrectly overwritten"); break; } } diff --git a/test-tool/test_write_residuals.h b/test-tool/test_write_residuals.h index 3c38140..96c8736 100644 --- a/test-tool/test_write_residuals.h +++ b/test-tool/test_write_residuals.h @@ -34,11 +34,14 @@ struct residuals_test_data { size_t residuals_amount; /* The amount of residual data in bytes */ - bool check_overwrite; /* Whether the test checks for overwrite or not */ - const char *log_messages; /* Test case description */ }; +struct task_status { + int status; + struct scsi_sense sense; +}; + extern bool command_is_implemented; extern void write_residuals_test (const struct residuals_test_data *tdata);