Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve WriteResidual tests #345

Merged
merged 4 commits into from Feb 9, 2021

Conversation

nastya-nizharadze
Copy link
Contributor

Linux target implementation, TCM, doesn’t pass write residuals tests (Write10Residuals, Write12Residualds, Write16Residuals).

One of the reason why they fail is because TCM doesn’t complete writes that have residuals with GOOD status, instead it replies with CHECK CONDITION and INVALID FIELD IN CDB sense code. The sense code doesn’t seem to be a valid reply but it's legit for a target to reply with CHECK CONDITION as outlined in the discussion on T10, RFC 7143 (11.4.5.1. Field Semantics):

Targets may set the residual count, and initiators may use
   it when the response code is Command Completed at Target (even if the
   status returned is not GOOD)

and FCP-4 (9.4.2 FCP_DATA IUs for read and write operations):

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:
a) process the command normally except that data beyond the FCP_DL count shall not be requested or transferred;
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; or
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.

To allow different replies from different targets, b) from FCP-4 was added as an alternative valid reply. With the PR, libiscsi supports both GOOD and CHECK CONDITION with INVALID FIELD IN COMMAND INFORMATION UNIT.

The tests have also been refactored to make sure that no transfer happens if target replies with INVALID FIELD IN COMMAND INFORMATION UNIT. As a side effect of the refactoring, most of the code duplication was removed.

After refactoring, it turned out that some test cases duplicate each other, so they were removed.

As soon as I send patches in TCM to fix sense key I will attach the link to it here.

Copy link

@roolebo roolebo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was thoroughly reviewed internally and tested with the changes to be sent upstream by @nastya-nizharadze: https://github.com/roolebo/linux/commits/target-write-residuals-v1-feedback

@@ -75,11 +75,15 @@ test_write10_residuals(void)
return;
}
logging(LOG_VERBOSE, "Verify that the target returned SUCCESS");
if (task->status != SCSI_STATUS_GOOD) {
ok = task->status == SCSI_STATUS_GOOD ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please introduce a function or macro instead of repeating this comparison multiple times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @bvanassche,

It is a good practice to separate a refactoring and a changes for the more convenient review. All repetitions are removed in the next commit. Even if I introduce a function or a macro, it will be removed by the next commit. However, if you think that it is really better to remove the duplication in this commit, please tell me and I will do it.

Thanks!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a good practice not to introduce repetitive code. Please introduce a helper function in this commit that returns the result of the following expression:

task->status == SCSI_STATUS_GOOD ||
 (task->status == SCSI_STATUS_CHECK_CONDITION &&
 task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
 task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@nastya-nizharadze
Copy link
Contributor Author

Hi @bvanassche,

Our patches have been added to the 5.12/scsi-staging

Now TCM send ASCQ = 0x03, INVALID FIELD IN COMMAND INFORMATION UNIT for residual write case.

Could you please tell me whether it is really necessary to make the previously requested changes?
Also if there is something else I could do/improve as part of these changes?

Thanks!

unsigned int residuals_kind;
size_t residuals_amount;
bool check_overwrite;
const char log_messages[100];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change the type of the log message from const char [100] into const char* to reduce the space occupied by this data structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

const char log_messages[100];
};

extern bool command_is_implemented;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this global variable? Since it is set but not used, can it be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is needed because if the command is not implemented, testing should end. It is used in test_write10_residuals.c, test_write12_residuals.c and test_write12_residuals.c

if (!command_is_implemented) {
        CU_PASS("WRITE10 is not implemented.");
        return;
}

Comment on lines 21 to 31
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include <CUnit/CUnit.h>

#include "iscsi.h"
#include "iscsi-private.h"
#include "scsi-lowlevel.h"
#include "iscsi-test-cu.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove all include directives except #include <stddef.h> (for size_t).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

int ok;
unsigned int i;
/* testing scenarios */
const struct residuals_test_data write10_residuals[] = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change 'const' into 'static const'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using block_size variable to initialize this structure. Therefore I can not make it static.

int ok;
unsigned int i;
/* testing scenarios */
const struct residuals_test_data write12_residuals[] = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here and in the other source files: please change 'const' into 'static const'.


if (scsi_status != SCSI_STATUS_GOOD) {
logging(LOG_VERBOSE, "Verify that blocks were NOT "
"overwritten and still contains 'a'");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contains -> contain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -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))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are more parentheses in this expression than necessary. I think that ((y) - 1) can be changed into (y) - 1 without changing the result of this expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -75,11 +75,15 @@ test_write10_residuals(void)
return;
}
logging(LOG_VERBOSE, "Verify that the target returned SUCCESS");
if (task->status != SCSI_STATUS_GOOD) {
ok = task->status == SCSI_STATUS_GOOD ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a good practice not to introduce repetitive code. Please introduce a helper function in this commit that returns the result of the following expression:

task->status == SCSI_STATUS_GOOD ||
 (task->status == SCSI_STATUS_CHECK_CONDITION &&
 task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
 task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT

According to the RFC 7143 11.4.5.1:
"Targets may set the residual count,and initiators may use
 it when the response code is Command Completed at Target (even if the
 status returned is not GOOD)."

Therefore valid retuned status may be not only GOOD. Also this check:
task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT
would make Underflow/Overflow response universal for FC/ISCSI.
Comment on lines 85 to 88
memset(scratch, 'b', (tdata->xfer_len * tdata->buf_len));
memset(scratch, 'b', tdata->buf_len);
} else {
memset(scratch, 0xa6, (tdata->xfer_len * tdata->buf_len));
memset(scratch, 0xa6, tdata->buf_len);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these changes perhaps a bug fix for the previous patch in this series? If so, please move these into the previous patch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 25 to 31
size_t cdb_size;
unsigned int xfer_len;
unsigned int buf_len;
unsigned int residuals_kind;
size_t residuals_amount;
bool check_overwrite;
const char *log_messages;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document the meaning of each member of this data structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Looking at test_write10_residuals.c, test_write12_residuals.c and
test_write16_residuals.c tests the similarity of the testing scenario
can be found. There are several EDTL and SPDTL combinations which are
the same for different length write command tests. They form the core
of testing scenario of these commands. There aren't so much differences
in the way of testing these combinations itself either. It is possible
to move the main parameters describing the testing scenario into a
separate structure and move the scenario itself into a separate function.
It will increase the readability and reduce the duplicate code of these tests.
…FCP-4

According to FCP-4 there are several possibilities for target to react on
incorrect data length field value in CDB:

a) process the command normally except that data beyond the FCP_DL count
shall not be requested or transferred;

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;

Add check to support the second one and accept no data write.
Check if the residual data does not owerwrite existing data blocks has now
been added for all testing data to improve the uniformity of test runs,
increase test readability and remove the duplicate testing data records.
@bvanassche bvanassche merged commit 88a46a0 into sahlberg:master Feb 9, 2021
@nastya-nizharadze
Copy link
Contributor Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants