1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*LINTLIBRARY*/
28
29 /*
30 * This module is part of the photon Command Line
31 * Interface program.
32 *
33 */
34
35 /*
36 * I18N message number ranges
37 * This file: 9500 - 9999
38 * Shared common messages: 1 - 1999
39 */
40
41 /* Includes */
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <string.h>
48 #include <sys/scsi/scsi.h>
49 #include <nl_types.h>
50 #include <sys/time.h>
51 #include <l_common.h>
52 #include <stgcom.h>
53 #include <l_error.h>
54 #include <g_state.h>
55
56
57 /* Defines */
58 #define MAXLEN 1000
59
60
61 /* Global variables */
62 extern nl_catd l_catd;
63
64
65 /* External functions */
66 extern int rand_r(unsigned int *);
67
68
69 static int
wait_random_time(void)70 wait_random_time(void)
71 {
72 time_t timeval;
73 struct tm *tmbuf = NULL;
74 struct timeval tval;
75 unsigned int seed;
76 int random;
77 pid_t pid;
78
79
80 /*
81 * Get the system time and use "system seconds"
82 * as 'seed' to generate a random number. Then,
83 * wait between 1/10 - 1/2 seconds before retry.
84 * Get the current process id and ex-or it with
85 * the seed so that the random number is always
86 * different even in case of multiple processes
87 * generate a random number at the same time.
88 */
89 if ((timeval = time(NULL)) == -1) {
90 return (errno);
91 }
92 if ((tmbuf = localtime(&timeval)) == NULL) {
93 return (L_LOCALTIME_ERROR);
94 }
95
96 pid = getpid();
97
98 /* get a random number. */
99 seed = (unsigned int) tmbuf->tm_sec;
100 seed ^= pid;
101 random = rand_r(&seed);
102
103
104 random = ((random % 500) + 100) * MILLISEC;
105 tval.tv_sec = random / MICROSEC;
106 tval.tv_usec = random % MICROSEC;
107
108 if (select(0, NULL, NULL, NULL, &tval) == -1) {
109 return (L_SELECT_ERROR);
110 }
111 return (0);
112 }
113
114
115
116 /*
117 * Execute a command and determine the result.
118 */
119 int
cmd(int file,struct uscsi_cmd * command,int flag)120 cmd(int file, struct uscsi_cmd *command, int flag)
121 {
122 struct scsi_extended_sense *rqbuf;
123 int status, i, retry_cnt = 0, err;
124 char errorMsg[MAXLEN];
125
126 /*
127 * Set function flags for driver.
128 *
129 * Set Automatic request sense enable
130 *
131 */
132 command->uscsi_flags = USCSI_RQENABLE;
133 command->uscsi_flags |= flag;
134
135 /* intialize error message array */
136 errorMsg[0] = '\0';
137
138 /* print command for debug */
139 if (getenv("_LUX_S_DEBUG") != NULL) {
140 if ((command->uscsi_cdb == NULL) ||
141 (flag & USCSI_RESET) ||
142 (flag & USCSI_RESET_ALL)) {
143 if (flag & USCSI_RESET) {
144 (void) printf(" Issuing a SCSI Reset.\n");
145 }
146 if (flag & USCSI_RESET_ALL) {
147 (void) printf(" Issuing a SCSI Reset All.\n");
148 }
149
150 } else {
151 (void) printf(" Issuing the following "
152 "SCSI command: %s\n",
153 g_scsi_find_command_name(command->uscsi_cdb[0]));
154 (void) printf(" fd=0x%x cdb=", file);
155 for (i = 0; i < (int)command->uscsi_cdblen; i++) {
156 (void) printf("%x ", *(command->uscsi_cdb + i));
157 }
158 (void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
159 " flags=0x%x\n",
160 command->uscsi_cdblen,
161 command->uscsi_bufaddr,
162 command->uscsi_buflen, command->uscsi_flags);
163
164 if ((command->uscsi_buflen > 0) &&
165 ((flag & USCSI_READ) == 0)) {
166 (void) g_dump(" Buffer data: ",
167 (uchar_t *)command->uscsi_bufaddr,
168 MIN(command->uscsi_buflen, 512), HEX_ASCII);
169 }
170 }
171 fflush(stdout);
172 }
173
174
175 /*
176 * Default command timeout in case command left it 0
177 */
178 if (command->uscsi_timeout == 0) {
179 command->uscsi_timeout = 60;
180 }
181 /* Issue command - finally */
182
183 retry:
184 status = ioctl(file, USCSICMD, command);
185 if (status == 0 && command->uscsi_status == 0) {
186 if (getenv("_LUX_S_DEBUG") != NULL) {
187 if ((command->uscsi_buflen > 0) &&
188 (flag & USCSI_READ)) {
189 (void) g_dump("\tData read:",
190 (uchar_t *)command->uscsi_bufaddr,
191 MIN(command->uscsi_buflen, 512), HEX_ASCII);
192 }
193 }
194 return (status);
195 }
196 if ((status != 0) && (command->uscsi_status == 0)) {
197 if ((getenv("_LUX_S_DEBUG") != NULL) ||
198 (getenv("_LUX_ER_DEBUG") != NULL)) {
199 (void) printf("Unexpected USCSICMD ioctl error: %s\n",
200 strerror(errno));
201 }
202 return (status);
203 }
204
205 /*
206 * Just a SCSI error, create error message
207 * Retry once for Unit Attention,
208 * Not Ready, and Aborted Command
209 */
210 if ((command->uscsi_rqbuf != NULL) &&
211 (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
212
213 rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
214
215 switch (rqbuf->es_key) {
216 case KEY_NOT_READY:
217 if (retry_cnt++ < 1) {
218 ER_DPRINTF("Note: Device Not Ready."
219 " Retrying...\n");
220
221 if ((err = wait_random_time()) == 0) {
222 goto retry;
223 } else {
224 return (err);
225 }
226 }
227 break;
228
229 case KEY_UNIT_ATTENTION:
230 if (retry_cnt++ < 1) {
231 ER_DPRINTF(" cmd():"
232 " UNIT_ATTENTION: Retrying...\n");
233
234 goto retry;
235 }
236 break;
237
238 case KEY_ABORTED_COMMAND:
239 if (retry_cnt++ < 1) {
240 ER_DPRINTF("Note: Command is aborted."
241 " Retrying...\n");
242
243 goto retry;
244 }
245 break;
246 }
247 if ((getenv("_LUX_S_DEBUG") != NULL) ||
248 (getenv("_LUX_ER_DEBUG") != NULL)) {
249 g_scsi_printerr(command,
250 (struct scsi_extended_sense *)command->uscsi_rqbuf,
251 (command->uscsi_rqlen - command->uscsi_rqresid),
252 errorMsg, strerror(errno));
253 }
254
255 } else {
256
257 /*
258 * Retry 5 times in case of BUSY, and only
259 * once for Reservation-conflict, Command
260 * Termination and Queue Full. Wait for
261 * random amount of time (between 1/10 - 1/2 secs.)
262 * between each retry. This random wait is to avoid
263 * the multiple threads being executed at the same time
264 * and also the constraint in Photon IB, where the
265 * command queue has a depth of one command.
266 */
267 switch ((uchar_t)command->uscsi_status & STATUS_MASK) {
268 case STATUS_BUSY:
269 if (retry_cnt++ < 5) {
270 if ((err = wait_random_time()) == 0) {
271 R_DPRINTF(" cmd(): No. of retries %d."
272 " STATUS_BUSY: Retrying...\n",
273 retry_cnt);
274 goto retry;
275
276 } else {
277 return (err);
278 }
279 }
280 break;
281
282 case STATUS_RESERVATION_CONFLICT:
283 if (retry_cnt++ < 1) {
284 if ((err = wait_random_time()) == 0) {
285 R_DPRINTF(" cmd():"
286 " RESERVATION_CONFLICT:"
287 " Retrying...\n");
288 goto retry;
289
290 } else {
291 return (err);
292 }
293 }
294 break;
295
296 case STATUS_TERMINATED:
297 if (retry_cnt++ < 1) {
298 R_DPRINTF("Note: Command Terminated."
299 " Retrying...\n");
300
301 if ((err = wait_random_time()) == 0) {
302 goto retry;
303 } else {
304 return (err);
305 }
306 }
307 break;
308
309 case STATUS_QFULL:
310 if (retry_cnt++ < 1) {
311 R_DPRINTF("Note: Command Queue is full."
312 " Retrying...\n");
313
314 if ((err = wait_random_time()) == 0) {
315 goto retry;
316 } else {
317 return (err);
318 }
319 }
320 break;
321 }
322
323 }
324 if (((getenv("_LUX_S_DEBUG") != NULL) ||
325 (getenv("_LUX_ER_DEBUG") != NULL)) &&
326 (errorMsg[0] != '\0')) {
327 (void) fprintf(stdout, " %s\n", errorMsg);
328 }
329 return (L_SCSI_ERROR | command->uscsi_status);
330 }
331