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