preprocessor cleanup: __sparc
[unleashed/tickless.git] / usr / src / lib / storage / libg_fc / common / cmd.c
blob53602345a4fc64c34b71a1b1f2c860126b9317ed
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 /*LINTLIBRARY*/
30 * This module is part of the photon Command Line
31 * Interface program.
36 * I18N message number ranges
37 * This file: 9500 - 9999
38 * Shared common messages: 1 - 1999
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>
57 /* Defines */
58 #define MAXLEN 1000
61 /* Global variables */
62 extern nl_catd l_catd;
65 /* External functions */
66 extern int rand_r(unsigned int *);
69 static int
70 wait_random_time(void)
72 time_t timeval;
73 struct tm *tmbuf = NULL;
74 struct timeval tval;
75 unsigned int seed;
76 int random;
77 pid_t pid;
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.
89 if ((timeval = time(NULL)) == -1) {
90 return (errno);
92 if ((tmbuf = localtime(&timeval)) == NULL) {
93 return (L_LOCALTIME_ERROR);
96 pid = getpid();
98 /* get a random number. */
99 seed = (unsigned int) tmbuf->tm_sec;
100 seed ^= pid;
101 random = rand_r(&seed);
104 random = ((random % 500) + 100) * MILLISEC;
105 tval.tv_sec = random / MICROSEC;
106 tval.tv_usec = random % MICROSEC;
108 if (select(0, NULL, NULL, NULL, &tval) == -1) {
109 return (L_SELECT_ERROR);
111 return (0);
117 * Execute a command and determine the result.
120 cmd(int file, struct uscsi_cmd *command, int flag)
122 struct scsi_extended_sense *rqbuf;
123 int status, i, retry_cnt = 0, err;
124 char errorMsg[MAXLEN];
127 * Set function flags for driver.
129 * Set Automatic request sense enable
132 command->uscsi_flags = USCSI_RQENABLE;
133 command->uscsi_flags |= flag;
135 /* intialize error message array */
136 errorMsg[0] = '\0';
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");
146 if (flag & USCSI_RESET_ALL) {
147 (void) printf(" Issuing a SCSI Reset All.\n");
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));
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);
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);
171 fflush(stdout);
176 * Default command timeout in case command left it 0
178 if (command->uscsi_timeout == 0) {
179 command->uscsi_timeout = 60;
181 /* Issue command - finally */
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);
194 return (status);
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));
202 return (status);
206 * Just a SCSI error, create error message
207 * Retry once for Unit Attention,
208 * Not Ready, and Aborted Command
210 if ((command->uscsi_rqbuf != NULL) &&
211 (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
213 rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
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");
221 if ((err = wait_random_time()) == 0) {
222 goto retry;
223 } else {
224 return (err);
227 break;
229 case KEY_UNIT_ATTENTION:
230 if (retry_cnt++ < 1) {
231 ER_DPRINTF(" cmd():"
232 " UNIT_ATTENTION: Retrying...\n");
234 goto retry;
236 break;
238 case KEY_ABORTED_COMMAND:
239 if (retry_cnt++ < 1) {
240 ER_DPRINTF("Note: Command is aborted."
241 " Retrying...\n");
243 goto retry;
245 break;
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));
255 } else {
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.
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;
276 } else {
277 return (err);
280 break;
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;
290 } else {
291 return (err);
294 break;
296 case STATUS_TERMINATED:
297 if (retry_cnt++ < 1) {
298 R_DPRINTF("Note: Command Terminated."
299 " Retrying...\n");
301 if ((err = wait_random_time()) == 0) {
302 goto retry;
303 } else {
304 return (err);
307 break;
309 case STATUS_QFULL:
310 if (retry_cnt++ < 1) {
311 R_DPRINTF("Note: Command Queue is full."
312 " Retrying...\n");
314 if ((err = wait_random_time()) == 0) {
315 goto retry;
316 } else {
317 return (err);
320 break;
324 if (((getenv("_LUX_S_DEBUG") != NULL) ||
325 (getenv("_LUX_ER_DEBUG") != NULL)) &&
326 (errorMsg[0] != '\0')) {
327 (void) fprintf(stdout, " %s\n", errorMsg);
329 return (L_SCSI_ERROR | command->uscsi_status);