xref: /illumos-gate/usr/src/lib/storage/libg_fc/common/cmd.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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
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
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