1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 /*
17 * Command-line utility for injecting SCSI transport errors in SD targets.
18 * It utilizes fault injection framework, so make sure both SD and MPTSAS
19 * drivers support it, or some IOCTLs may fail.
20 */
21
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <strings.h>
31 #include <stdarg.h>
32 #include <sys/types.h>
33
34 #define SDIOC ('T'<<8)
35 #define SDIOCSTART (SDIOC|1)
36 #define SDIOCSTOP (SDIOC|2)
37 #define SDIOCPUSH (SDIOC|7)
38 #define SDIOCRETRIEVE (SDIOC|8)
39 #define SDIOCRUN (SDIOC|9)
40 #define SDIOCINSERTTRAN (SDIOC|0xA)
41
42 enum sd_fi_tran_cmd {
43 /*
44 * Reject this command instead of sending it the hardware.
45 * Results in immediate rejecting the packet by the HBA with the TRAN_BUSY
46 * reason.
47 */
48 SD_FLTINJ_CMD_BUSY,
49
50 /*
51 * Time-out this command.
52 * Results in putting the command to a stashed queue in the HBA, returning
53 * TRAN_ACCEPT to the target driver and calling its completion
54 * handler with the reason set to CMD_TIMEOUT, when command timeout expires
55 */
56 SD_FLTINJ_CMD_TIMEOUT
57 };
58
59 struct sd_fi_tran {
60 enum sd_fi_tran_cmd tran_cmd;
61 };
62
63 void
64 usage(void)
65 {
66 printf("diskfltinj -c command [-s] [-f] [-r] [-a] <devpath>\n");
67 printf(" -c: Apply fault injection command. The following commands");
68 printf(" are supported:\n");
69 printf(" reject Reject command immediately\n");
70 printf(" timeout Cause command timeout expiration\n");
71 printf(" -s: Start a new fault injection session.\n");
72 printf(" -f: Reset current fault injection session.\n");
73 printf(" -r: Run fault injection.\n");
74 printf(" -a: Access target device (read one sector).\n");
75 printf(" -v: Verbose mode.\n");
76 exit(1);
77 }
78
79 int verbose = 0;
80
81 void
82 v_output(const char *fmt, ...)
83 {
84 if (verbose) {
85 va_list args;
86 va_start(args, fmt);
87 vfprintf(stderr, fmt, args);
88 va_end(args);
89 }
90 }
91
92 int
93 main(int argc, char **argv)
94 {
95 int fd = 0;
96 unsigned char buf[512];
97 struct sd_fi_tran tr_err;
98 int cmd = -1, opt;
99 int do_access = 0;
100 int start_inj = 0;
101 char *devpath = NULL;
102 int stop_inj = 0;
103 int run_inj = 0;
104
105 while ((opt = getopt(argc, argv, "asvfrc:")) != -1) {
106 switch (opt) {
107 case 'v':
108 verbose = 1;
109 break;
110 case 'a':
111 do_access = 1;
112 break;
113 case 's':
114 start_inj = 1;
115 break;
116 case 'r':
117 run_inj = 1;
118 break;
119 case 'f':
120 stop_inj = 1;
121 break;
122 case 'c':
123 if (strcmp(optarg, "reject") == 0) {
124 cmd = SD_FLTINJ_CMD_BUSY;
125 } else if (strcmp(optarg, "timeout") == 0) {
126 cmd = SD_FLTINJ_CMD_TIMEOUT;
127 } else {
128 usage();
129 }
130 break;
131 default:
132 usage();
133 }
134 }
135
136 /* Handle device name. */
137 if ((argc - optind) == 1) {
138 devpath = argv[optind];
139 } else {
140 usage();
141 }
142
143 /* Action must be specified. */
144 if (cmd == -1 && !stop_inj && !run_inj && !start_inj) {
145 usage();
146 }
147
148 /* Open device. */
149 printf("Target device: %s\n", devpath);
150 if ((fd = open(devpath, O_RDONLY|O_NDELAY)) == -1) {
151 perror("open");
152 exit(1);
153 }
154
155 /* Start new fault injection session. */
156 if (start_inj) {
157 v_output("Starting a new fault injection session.\n");
158 if (ioctl(fd, SDIOCSTART, NULL) == -1) {
159 perror("ioctl SDIOCSTART");
160 exit(1);
161 }
162 }
163
164 /* Inject a command. */
165 if (cmd != -1) {
166 tr_err.tran_cmd = cmd;
167
168 v_output("Injecting fault.\n");
169 if (ioctl(fd, SDIOCINSERTTRAN, &tr_err) == -1) {
170 perror("ioctl SDIOCINSERTTRAN");
171 exit(1);
172 }
173
174 if (ioctl(fd, SDIOCPUSH, NULL) == -1) {
175 perror("ioctl SDIOCPUSH");
176 exit(1);
177 }
178 }
179
180 /* Run injection sequence. */
181 if (run_inj) {
182 v_output("Activating fault injection sequence.\n");
183 if (ioctl(fd, SDIOCRUN, NULL) == -1) {
184 perror("ioctl SDIOCRUN");
185 exit(1);
186 }
187 }
188
189 /* Access the device, if requested. */
190 if (do_access) {
191 v_output("Accessing target device.\n");
192 read(fd, buf, sizeof (buf));
193 }
194
195 /* Stop fault injection, if requested */
196 if (stop_inj) {
197 v_output("Stopping fault injection session.\n");
198 if (ioctl(fd, SDIOCSTOP, NULL) == -1) {
199 perror("ioctl SDIOCSTOP");
200 exit(1);
201 }
202 }
203
204 return (0);
205 }