// SPDX-License-Identifier: GPL-2.0 /* * KUnit tests for scsi_lib.c. * * Copyright (C) 2023, Oracle Corporation */ #include #include #include #include #define SCSI_LIB_TEST_MAX_ALLOWED 3 #define SCSI_LIB_TEST_TOTAL_MAX_ALLOWED 5 static void scsi_lib_test_multiple_sense(struct kunit *test) { struct scsi_failure multiple_sense_failure_defs[] = { { .sense = DATA_PROTECT, .asc = 0x1, .ascq = 0x1, .result = SAM_STAT_CHECK_CONDITION, }, { .sense = UNIT_ATTENTION, .asc = 0x11, .ascq = 0x0, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, .result = SAM_STAT_CHECK_CONDITION, }, { .sense = NOT_READY, .asc = 0x11, .ascq = 0x22, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, .result = SAM_STAT_CHECK_CONDITION, }, { .sense = ABORTED_COMMAND, .asc = 0x11, .ascq = SCMD_FAILURE_ASCQ_ANY, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, .result = SAM_STAT_CHECK_CONDITION, }, { .sense = HARDWARE_ERROR, .asc = SCMD_FAILURE_ASC_ANY, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, .result = SAM_STAT_CHECK_CONDITION, }, { .sense = ILLEGAL_REQUEST, .asc = 0x91, .ascq = 0x36, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, .result = SAM_STAT_CHECK_CONDITION, }, {} }; struct scsi_failures failures = { .failure_definitions = multiple_sense_failure_defs, }; u8 sense[SCSI_SENSE_BUFFERSIZE] = {}; struct scsi_cmnd sc = { .sense_buffer = sense, }; int i; /* Match end of array */ scsi_build_sense(&sc, 0, ILLEGAL_REQUEST, 0x91, 0x36); KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); /* Basic match in array */ scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x11, 0x0); KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); /* No matching sense entry */ scsi_build_sense(&sc, 0, MISCOMPARE, 0x11, 0x11); KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); /* Match using SCMD_FAILURE_ASCQ_ANY */ scsi_build_sense(&sc, 0, ABORTED_COMMAND, 0x11, 0x22); KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); /* Fail to match */ scsi_build_sense(&sc, 0, ABORTED_COMMAND, 0x22, 0x22); KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); /* Match using SCMD_FAILURE_ASC_ANY */ scsi_build_sense(&sc, 0, HARDWARE_ERROR, 0x11, 0x22); KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); /* No matching status entry */ sc.result = SAM_STAT_RESERVATION_CONFLICT; KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); /* Test hitting allowed limit */ scsi_build_sense(&sc, 0, NOT_READY, 0x11, 0x22); for (i = 0; i < SCSI_LIB_TEST_MAX_ALLOWED; i++) KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); /* reset retries so we can retest */ failures.failure_definitions = multiple_sense_failure_defs; scsi_failures_reset_retries(&failures); /* Test no retries allowed */ scsi_build_sense(&sc, 0, DATA_PROTECT, 0x1, 0x1); KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); } static void scsi_lib_test_any_sense(struct kunit *test) { struct scsi_failure any_sense_failure_defs[] = { { .result = SCMD_FAILURE_SENSE_ANY, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, }, {} }; struct scsi_failures failures = { .failure_definitions = any_sense_failure_defs, }; u8 sense[SCSI_SENSE_BUFFERSIZE] = {}; struct scsi_cmnd sc = { .sense_buffer = sense, }; /* Match using SCMD_FAILURE_SENSE_ANY */ failures.failure_definitions = any_sense_failure_defs; scsi_build_sense(&sc, 0, MEDIUM_ERROR, 0x11, 0x22); KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); } static void scsi_lib_test_host(struct kunit *test) { struct scsi_failure retryable_host_failure_defs[] = { { .result = DID_TRANSPORT_DISRUPTED << 16, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, }, { .result = DID_TIME_OUT << 16, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, }, {} }; struct scsi_failures failures = { .failure_definitions = retryable_host_failure_defs, }; u8 sense[SCSI_SENSE_BUFFERSIZE] = {}; struct scsi_cmnd sc = { .sense_buffer = sense, }; /* No matching host byte entry */ failures.failure_definitions = retryable_host_failure_defs; sc.result = DID_NO_CONNECT << 16; KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); /* Matching host byte entry */ sc.result = DID_TIME_OUT << 16; KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); } static void scsi_lib_test_any_failure(struct kunit *test) { struct scsi_failure any_failure_defs[] = { { .result = SCMD_FAILURE_RESULT_ANY, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, }, {} }; struct scsi_failures failures = { .failure_definitions = any_failure_defs, }; u8 sense[SCSI_SENSE_BUFFERSIZE] = {}; struct scsi_cmnd sc = { .sense_buffer = sense, }; /* Match SCMD_FAILURE_RESULT_ANY */ failures.failure_definitions = any_failure_defs; sc.result = DID_TRANSPORT_FAILFAST << 16; KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); } static void scsi_lib_test_any_status(struct kunit *test) { struct scsi_failure any_status_failure_defs[] = { { .result = SCMD_FAILURE_STAT_ANY, .allowed = SCSI_LIB_TEST_MAX_ALLOWED, }, {} }; struct scsi_failures failures = { .failure_definitions = any_status_failure_defs, }; u8 sense[SCSI_SENSE_BUFFERSIZE] = {}; struct scsi_cmnd sc = { .sense_buffer = sense, }; /* Test any status handling */ failures.failure_definitions = any_status_failure_defs; sc.result = SAM_STAT_RESERVATION_CONFLICT; KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); } static void scsi_lib_test_total_allowed(struct kunit *test) { struct scsi_failure total_allowed_defs[] = { { .sense = UNIT_ATTENTION, .asc = SCMD_FAILURE_ASC_ANY, .ascq = SCMD_FAILURE_ASCQ_ANY, .result = SAM_STAT_CHECK_CONDITION, }, /* Fail all CCs except the UA above */ { .sense = SCMD_FAILURE_SENSE_ANY, .result = SAM_STAT_CHECK_CONDITION, }, /* Retry any other errors not listed above */ { .result = SCMD_FAILURE_RESULT_ANY, }, {} }; struct scsi_failures failures = { .failure_definitions = total_allowed_defs, }; u8 sense[SCSI_SENSE_BUFFERSIZE] = {}; struct scsi_cmnd sc = { .sense_buffer = sense, }; int i; /* Test total_allowed */ failures.failure_definitions = total_allowed_defs; scsi_failures_reset_retries(&failures); failures.total_allowed = SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0); for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++) /* Retry since we under the total_allowed limit */ KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); sc.result = DID_TIME_OUT << 16; /* We have now hit the total_allowed limit so no more retries */ KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); } static void scsi_lib_test_mixed_total(struct kunit *test) { struct scsi_failure mixed_total_defs[] = { { .sense = UNIT_ATTENTION, .asc = 0x28, .result = SAM_STAT_CHECK_CONDITION, }, { .sense = UNIT_ATTENTION, .asc = 0x29, .result = SAM_STAT_CHECK_CONDITION, }, { .allowed = 1, .result = DID_TIME_OUT << 16, }, {} }; u8 sense[SCSI_SENSE_BUFFERSIZE] = {}; struct scsi_failures failures = { .failure_definitions = mixed_total_defs, }; struct scsi_cmnd sc = { .sense_buffer = sense, }; int i; /* * Test total_allowed when there is a mix of per failure allowed * and total_allowed limits. */ failures.failure_definitions = mixed_total_defs; scsi_failures_reset_retries(&failures); failures.total_allowed = SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0); for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++) /* Retry since we under the total_allowed limit */ KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); /* Do not retry since we are now over total_allowed limit */ KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); scsi_failures_reset_retries(&failures); scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0); for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++) /* Retry since we under the total_allowed limit */ KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); sc.result = DID_TIME_OUT << 16; /* Retry because this failure has a per failure limit */ KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures)); scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x29, 0x0); /* total_allowed is now hit so no more retries */ KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures)); } static void scsi_lib_test_check_passthough(struct kunit *test) { scsi_lib_test_multiple_sense(test); scsi_lib_test_any_sense(test); scsi_lib_test_host(test); scsi_lib_test_any_failure(test); scsi_lib_test_any_status(test); scsi_lib_test_total_allowed(test); scsi_lib_test_mixed_total(test); } static struct kunit_case scsi_lib_test_cases[] = { KUNIT_CASE(scsi_lib_test_check_passthough), {} }; static struct kunit_suite scsi_lib_test_suite = { .name = "scsi_lib", .test_cases = scsi_lib_test_cases, }; kunit_test_suite(scsi_lib_test_suite);