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	*/
62extern	nl_catd l_catd;
63
64
65/*	External functions	*/
66extern	int	rand_r(unsigned int *);
67
68
69static int
70wait_random_time(void)
71{
72time_t		timeval;
73struct tm	*tmbuf = NULL;
74struct timeval	tval;
75unsigned int	seed;
76int		random;
77pid_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 */
119int
120cmd(int file, struct uscsi_cmd *command, int flag)
121{
122struct scsi_extended_sense	*rqbuf;
123int				status, i, retry_cnt = 0, err;
124char				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
183retry:
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