Skip to content

Commit 25a1f7a

Browse files
mikechristiemartinkpetersen
authored andcommitted
scsi: core: Add kunit tests for scsi_check_passthrough()
Add some kunit tests for scsi_check_passthrough() so we can easily make sure we are hitting the cases for which it's difficult to replicate in hardware or even scsi_debug. Signed-off-by: Mike Christie <michael.christie@oracle.com> Link: https://lore.kernel.org/r/20240123002220.129141-20-michael.christie@oracle.com Acked-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent b8c3a7b commit 25a1f7a

File tree

3 files changed

+343
-0
lines changed

3 files changed

+343
-0
lines changed

drivers/scsi/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ config SCSI_PROC_FS
6767

6868
If unsure say Y.
6969

70+
config SCSI_LIB_KUNIT_TEST
71+
tristate "KUnit tests for SCSI Mid Layer's scsi_lib" if !KUNIT_ALL_TESTS
72+
depends on KUNIT
73+
default KUNIT_ALL_TESTS
74+
help
75+
Run SCSI Mid Layer's KUnit tests for scsi_lib.
76+
77+
If unsure say N.
78+
7079
comment "SCSI support type (disk, tape, CD-ROM)"
7180
depends on SCSI
7281

drivers/scsi/scsi_lib.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3434,3 +3434,7 @@ void scsi_build_sense(struct scsi_cmnd *scmd, int desc, u8 key, u8 asc, u8 ascq)
34343434
scmd->result = SAM_STAT_CHECK_CONDITION;
34353435
}
34363436
EXPORT_SYMBOL_GPL(scsi_build_sense);
3437+
3438+
#ifdef CONFIG_SCSI_KUNIT_TEST
3439+
#include "scsi_lib_test.c"
3440+
#endif

drivers/scsi/scsi_lib_test.c

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* KUnit tests for scsi_lib.c.
4+
*
5+
* Copyright (C) 2023, Oracle Corporation
6+
*/
7+
#include <kunit/test.h>
8+
9+
#include <scsi/scsi_proto.h>
10+
#include <scsi/scsi_cmnd.h>
11+
#include <scsi/scsi_device.h>
12+
13+
#define SCSI_LIB_TEST_MAX_ALLOWED 3
14+
#define SCSI_LIB_TEST_TOTAL_MAX_ALLOWED 5
15+
16+
static void scsi_lib_test_multiple_sense(struct kunit *test)
17+
{
18+
struct scsi_failure multiple_sense_failure_defs[] = {
19+
{
20+
.sense = DATA_PROTECT,
21+
.asc = 0x1,
22+
.ascq = 0x1,
23+
.result = SAM_STAT_CHECK_CONDITION,
24+
},
25+
{
26+
.sense = UNIT_ATTENTION,
27+
.asc = 0x11,
28+
.ascq = 0x0,
29+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
30+
.result = SAM_STAT_CHECK_CONDITION,
31+
},
32+
{
33+
.sense = NOT_READY,
34+
.asc = 0x11,
35+
.ascq = 0x22,
36+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
37+
.result = SAM_STAT_CHECK_CONDITION,
38+
},
39+
{
40+
.sense = ABORTED_COMMAND,
41+
.asc = 0x11,
42+
.ascq = SCMD_FAILURE_ASCQ_ANY,
43+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
44+
.result = SAM_STAT_CHECK_CONDITION,
45+
},
46+
{
47+
.sense = HARDWARE_ERROR,
48+
.asc = SCMD_FAILURE_ASC_ANY,
49+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
50+
.result = SAM_STAT_CHECK_CONDITION,
51+
},
52+
{
53+
.sense = ILLEGAL_REQUEST,
54+
.asc = 0x91,
55+
.ascq = 0x36,
56+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
57+
.result = SAM_STAT_CHECK_CONDITION,
58+
},
59+
{}
60+
};
61+
struct scsi_failures failures = {
62+
.failure_definitions = multiple_sense_failure_defs,
63+
};
64+
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
65+
struct scsi_cmnd sc = {
66+
.sense_buffer = sense,
67+
};
68+
int i;
69+
70+
/* Match end of array */
71+
scsi_build_sense(&sc, 0, ILLEGAL_REQUEST, 0x91, 0x36);
72+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
73+
/* Basic match in array */
74+
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x11, 0x0);
75+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
76+
/* No matching sense entry */
77+
scsi_build_sense(&sc, 0, MISCOMPARE, 0x11, 0x11);
78+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
79+
/* Match using SCMD_FAILURE_ASCQ_ANY */
80+
scsi_build_sense(&sc, 0, ABORTED_COMMAND, 0x11, 0x22);
81+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
82+
/* Fail to match */
83+
scsi_build_sense(&sc, 0, ABORTED_COMMAND, 0x22, 0x22);
84+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
85+
/* Match using SCMD_FAILURE_ASC_ANY */
86+
scsi_build_sense(&sc, 0, HARDWARE_ERROR, 0x11, 0x22);
87+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
88+
/* No matching status entry */
89+
sc.result = SAM_STAT_RESERVATION_CONFLICT;
90+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
91+
92+
/* Test hitting allowed limit */
93+
scsi_build_sense(&sc, 0, NOT_READY, 0x11, 0x22);
94+
for (i = 0; i < SCSI_LIB_TEST_MAX_ALLOWED; i++)
95+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
96+
&failures));
97+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
98+
99+
/* reset retries so we can retest */
100+
failures.failure_definitions = multiple_sense_failure_defs;
101+
scsi_failures_reset_retries(&failures);
102+
103+
/* Test no retries allowed */
104+
scsi_build_sense(&sc, 0, DATA_PROTECT, 0x1, 0x1);
105+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
106+
}
107+
108+
static void scsi_lib_test_any_sense(struct kunit *test)
109+
{
110+
struct scsi_failure any_sense_failure_defs[] = {
111+
{
112+
.result = SCMD_FAILURE_SENSE_ANY,
113+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
114+
},
115+
{}
116+
};
117+
struct scsi_failures failures = {
118+
.failure_definitions = any_sense_failure_defs,
119+
};
120+
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
121+
struct scsi_cmnd sc = {
122+
.sense_buffer = sense,
123+
};
124+
125+
/* Match using SCMD_FAILURE_SENSE_ANY */
126+
failures.failure_definitions = any_sense_failure_defs;
127+
scsi_build_sense(&sc, 0, MEDIUM_ERROR, 0x11, 0x22);
128+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
129+
}
130+
131+
static void scsi_lib_test_host(struct kunit *test)
132+
{
133+
struct scsi_failure retryable_host_failure_defs[] = {
134+
{
135+
.result = DID_TRANSPORT_DISRUPTED << 16,
136+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
137+
},
138+
{
139+
.result = DID_TIME_OUT << 16,
140+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
141+
},
142+
{}
143+
};
144+
struct scsi_failures failures = {
145+
.failure_definitions = retryable_host_failure_defs,
146+
};
147+
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
148+
struct scsi_cmnd sc = {
149+
.sense_buffer = sense,
150+
};
151+
152+
/* No matching host byte entry */
153+
failures.failure_definitions = retryable_host_failure_defs;
154+
sc.result = DID_NO_CONNECT << 16;
155+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
156+
/* Matching host byte entry */
157+
sc.result = DID_TIME_OUT << 16;
158+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
159+
}
160+
161+
static void scsi_lib_test_any_failure(struct kunit *test)
162+
{
163+
struct scsi_failure any_failure_defs[] = {
164+
{
165+
.result = SCMD_FAILURE_RESULT_ANY,
166+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
167+
},
168+
{}
169+
};
170+
struct scsi_failures failures = {
171+
.failure_definitions = any_failure_defs,
172+
};
173+
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
174+
struct scsi_cmnd sc = {
175+
.sense_buffer = sense,
176+
};
177+
178+
/* Match SCMD_FAILURE_RESULT_ANY */
179+
failures.failure_definitions = any_failure_defs;
180+
sc.result = DID_TRANSPORT_FAILFAST << 16;
181+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
182+
}
183+
184+
static void scsi_lib_test_any_status(struct kunit *test)
185+
{
186+
struct scsi_failure any_status_failure_defs[] = {
187+
{
188+
.result = SCMD_FAILURE_STAT_ANY,
189+
.allowed = SCSI_LIB_TEST_MAX_ALLOWED,
190+
},
191+
{}
192+
};
193+
struct scsi_failures failures = {
194+
.failure_definitions = any_status_failure_defs,
195+
};
196+
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
197+
struct scsi_cmnd sc = {
198+
.sense_buffer = sense,
199+
};
200+
201+
/* Test any status handling */
202+
failures.failure_definitions = any_status_failure_defs;
203+
sc.result = SAM_STAT_RESERVATION_CONFLICT;
204+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
205+
}
206+
207+
static void scsi_lib_test_total_allowed(struct kunit *test)
208+
{
209+
struct scsi_failure total_allowed_defs[] = {
210+
{
211+
.sense = UNIT_ATTENTION,
212+
.asc = SCMD_FAILURE_ASC_ANY,
213+
.ascq = SCMD_FAILURE_ASCQ_ANY,
214+
.result = SAM_STAT_CHECK_CONDITION,
215+
},
216+
/* Fail all CCs except the UA above */
217+
{
218+
.sense = SCMD_FAILURE_SENSE_ANY,
219+
.result = SAM_STAT_CHECK_CONDITION,
220+
},
221+
/* Retry any other errors not listed above */
222+
{
223+
.result = SCMD_FAILURE_RESULT_ANY,
224+
},
225+
{}
226+
};
227+
struct scsi_failures failures = {
228+
.failure_definitions = total_allowed_defs,
229+
};
230+
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
231+
struct scsi_cmnd sc = {
232+
.sense_buffer = sense,
233+
};
234+
int i;
235+
236+
/* Test total_allowed */
237+
failures.failure_definitions = total_allowed_defs;
238+
scsi_failures_reset_retries(&failures);
239+
failures.total_allowed = SCSI_LIB_TEST_TOTAL_MAX_ALLOWED;
240+
241+
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0);
242+
for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++)
243+
/* Retry since we under the total_allowed limit */
244+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
245+
&failures));
246+
sc.result = DID_TIME_OUT << 16;
247+
/* We have now hit the total_allowed limit so no more retries */
248+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
249+
}
250+
251+
static void scsi_lib_test_mixed_total(struct kunit *test)
252+
{
253+
struct scsi_failure mixed_total_defs[] = {
254+
{
255+
.sense = UNIT_ATTENTION,
256+
.asc = 0x28,
257+
.result = SAM_STAT_CHECK_CONDITION,
258+
},
259+
{
260+
.sense = UNIT_ATTENTION,
261+
.asc = 0x29,
262+
.result = SAM_STAT_CHECK_CONDITION,
263+
},
264+
{
265+
.allowed = 1,
266+
.result = DID_TIME_OUT << 16,
267+
},
268+
{}
269+
};
270+
u8 sense[SCSI_SENSE_BUFFERSIZE] = {};
271+
struct scsi_failures failures = {
272+
.failure_definitions = mixed_total_defs,
273+
};
274+
struct scsi_cmnd sc = {
275+
.sense_buffer = sense,
276+
};
277+
int i;
278+
279+
/*
280+
* Test total_allowed when there is a mix of per failure allowed
281+
* and total_allowed limits.
282+
*/
283+
failures.failure_definitions = mixed_total_defs;
284+
scsi_failures_reset_retries(&failures);
285+
failures.total_allowed = SCSI_LIB_TEST_TOTAL_MAX_ALLOWED;
286+
287+
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0);
288+
for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++)
289+
/* Retry since we under the total_allowed limit */
290+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
291+
&failures));
292+
/* Do not retry since we are now over total_allowed limit */
293+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
294+
295+
scsi_failures_reset_retries(&failures);
296+
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x28, 0x0);
297+
for (i = 0; i < SCSI_LIB_TEST_TOTAL_MAX_ALLOWED; i++)
298+
/* Retry since we under the total_allowed limit */
299+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc,
300+
&failures));
301+
sc.result = DID_TIME_OUT << 16;
302+
/* Retry because this failure has a per failure limit */
303+
KUNIT_EXPECT_EQ(test, -EAGAIN, scsi_check_passthrough(&sc, &failures));
304+
scsi_build_sense(&sc, 0, UNIT_ATTENTION, 0x29, 0x0);
305+
/* total_allowed is now hit so no more retries */
306+
KUNIT_EXPECT_EQ(test, 0, scsi_check_passthrough(&sc, &failures));
307+
}
308+
309+
static void scsi_lib_test_check_passthough(struct kunit *test)
310+
{
311+
scsi_lib_test_multiple_sense(test);
312+
scsi_lib_test_any_sense(test);
313+
scsi_lib_test_host(test);
314+
scsi_lib_test_any_failure(test);
315+
scsi_lib_test_any_status(test);
316+
scsi_lib_test_total_allowed(test);
317+
scsi_lib_test_mixed_total(test);
318+
}
319+
320+
static struct kunit_case scsi_lib_test_cases[] = {
321+
KUNIT_CASE(scsi_lib_test_check_passthough),
322+
{}
323+
};
324+
325+
static struct kunit_suite scsi_lib_test_suite = {
326+
.name = "scsi_lib",
327+
.test_cases = scsi_lib_test_cases,
328+
};
329+
330+
kunit_test_suite(scsi_lib_test_suite);

0 commit comments

Comments
 (0)