2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A
2N/A/*LINTLIBRARY*/
2N/A
2N/A/*
2N/A * This module is part of the photon Command Line
2N/A * Interface program.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * I18N message number ranges
2N/A * This file: 9500 - 9999
2N/A * Shared common messages: 1 - 1999
2N/A */
2N/A
2N/A/* Includes */
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <sys/types.h>
2N/A#include <unistd.h>
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A#include <sys/scsi/scsi.h>
2N/A#include <nl_types.h>
2N/A#include <sys/time.h>
2N/A#include <l_common.h>
2N/A#include <stgcom.h>
2N/A#include <l_error.h>
2N/A#include <g_state.h>
2N/A
2N/A
2N/A/* Defines */
2N/A#define MAXLEN 1000
2N/A
2N/A
2N/A/* Global variables */
2N/Aextern nl_catd l_catd;
2N/A
2N/A
2N/A/* External functions */
2N/Aextern int rand_r(unsigned int *);
2N/A
2N/A
2N/Astatic int
2N/Await_random_time(void)
2N/A{
2N/Atime_t timeval;
2N/Astruct tm *tmbuf = NULL;
2N/Astruct timeval tval;
2N/Aunsigned int seed;
2N/Aint random;
2N/Apid_t pid;
2N/A
2N/A
2N/A /*
2N/A * Get the system time and use "system seconds"
2N/A * as 'seed' to generate a random number. Then,
2N/A * wait between 1/10 - 1/2 seconds before retry.
2N/A * Get the current process id and ex-or it with
2N/A * the seed so that the random number is always
2N/A * different even in case of multiple processes
2N/A * generate a random number at the same time.
2N/A */
2N/A if ((timeval = time(NULL)) == -1) {
2N/A return (errno);
2N/A }
2N/A if ((tmbuf = localtime(&timeval)) == NULL) {
2N/A return (L_LOCALTIME_ERROR);
2N/A }
2N/A
2N/A pid = getpid();
2N/A
2N/A /* get a random number. */
2N/A seed = (unsigned int) tmbuf->tm_sec;
2N/A seed ^= pid;
2N/A random = rand_r(&seed);
2N/A
2N/A
2N/A random = ((random % 500) + 100) * MILLISEC;
2N/A tval.tv_sec = random / MICROSEC;
2N/A tval.tv_usec = random % MICROSEC;
2N/A
2N/A if (select(0, NULL, NULL, NULL, &tval) == -1) {
2N/A return (L_SELECT_ERROR);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * Execute a command and determine the result.
2N/A */
2N/Aint
2N/Acmd(int file, struct uscsi_cmd *command, int flag)
2N/A{
2N/Astruct scsi_extended_sense *rqbuf;
2N/Aint status, i, retry_cnt = 0, err;
2N/Achar errorMsg[MAXLEN];
2N/A
2N/A /*
2N/A * Set function flags for driver.
2N/A *
2N/A * Set Automatic request sense enable
2N/A *
2N/A */
2N/A command->uscsi_flags = USCSI_RQENABLE;
2N/A command->uscsi_flags |= flag;
2N/A
2N/A /* intialize error message array */
2N/A errorMsg[0] = '\0';
2N/A
2N/A /* print command for debug */
2N/A if (getenv("_LUX_S_DEBUG") != NULL) {
2N/A if ((command->uscsi_cdb == NULL) ||
2N/A (flag & USCSI_RESET) ||
2N/A (flag & USCSI_RESET_ALL)) {
2N/A if (flag & USCSI_RESET) {
2N/A (void) printf(" Issuing a SCSI Reset.\n");
2N/A }
2N/A if (flag & USCSI_RESET_ALL) {
2N/A (void) printf(" Issuing a SCSI Reset All.\n");
2N/A }
2N/A
2N/A } else {
2N/A (void) printf(" Issuing the following "
2N/A "SCSI command: %s\n",
2N/A g_scsi_find_command_name(command->uscsi_cdb[0]));
2N/A (void) printf(" fd=0x%x cdb=", file);
2N/A for (i = 0; i < (int)command->uscsi_cdblen; i++) {
2N/A (void) printf("%x ", *(command->uscsi_cdb + i));
2N/A }
2N/A (void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
2N/A " flags=0x%x\n",
2N/A command->uscsi_cdblen,
2N/A command->uscsi_bufaddr,
2N/A command->uscsi_buflen, command->uscsi_flags);
2N/A
2N/A if ((command->uscsi_buflen > 0) &&
2N/A ((flag & USCSI_READ) == 0)) {
2N/A (void) g_dump(" Buffer data: ",
2N/A (uchar_t *)command->uscsi_bufaddr,
2N/A MIN(command->uscsi_buflen, 512), HEX_ASCII);
2N/A }
2N/A }
2N/A fflush(stdout);
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Default command timeout in case command left it 0
2N/A */
2N/A if (command->uscsi_timeout == 0) {
2N/A command->uscsi_timeout = 60;
2N/A }
2N/A /* Issue command - finally */
2N/A
2N/Aretry:
2N/A status = ioctl(file, USCSICMD, command);
2N/A if (status == 0 && command->uscsi_status == 0) {
2N/A if (getenv("_LUX_S_DEBUG") != NULL) {
2N/A if ((command->uscsi_buflen > 0) &&
2N/A (flag & USCSI_READ)) {
2N/A (void) g_dump("\tData read:",
2N/A (uchar_t *)command->uscsi_bufaddr,
2N/A MIN(command->uscsi_buflen, 512), HEX_ASCII);
2N/A }
2N/A }
2N/A return (status);
2N/A }
2N/A if ((status != 0) && (command->uscsi_status == 0)) {
2N/A if ((getenv("_LUX_S_DEBUG") != NULL) ||
2N/A (getenv("_LUX_ER_DEBUG") != NULL)) {
2N/A (void) printf("Unexpected USCSICMD ioctl error: %s\n",
2N/A strerror(errno));
2N/A }
2N/A return (status);
2N/A }
2N/A
2N/A /*
2N/A * Just a SCSI error, create error message
2N/A * Retry once for Unit Attention,
2N/A * Not Ready, and Aborted Command
2N/A */
2N/A if ((command->uscsi_rqbuf != NULL) &&
2N/A (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
2N/A
2N/A rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
2N/A
2N/A switch (rqbuf->es_key) {
2N/A case KEY_NOT_READY:
2N/A if (retry_cnt++ < 1) {
2N/A ER_DPRINTF("Note: Device Not Ready."
2N/A " Retrying...\n");
2N/A
2N/A if ((err = wait_random_time()) == 0) {
2N/A goto retry;
2N/A } else {
2N/A return (err);
2N/A }
2N/A }
2N/A break;
2N/A
2N/A case KEY_UNIT_ATTENTION:
2N/A if (retry_cnt++ < 1) {
2N/A ER_DPRINTF(" cmd():"
2N/A " UNIT_ATTENTION: Retrying...\n");
2N/A
2N/A goto retry;
2N/A }
2N/A break;
2N/A
2N/A case KEY_ABORTED_COMMAND:
2N/A if (retry_cnt++ < 1) {
2N/A ER_DPRINTF("Note: Command is aborted."
2N/A " Retrying...\n");
2N/A
2N/A goto retry;
2N/A }
2N/A break;
2N/A }
2N/A if ((getenv("_LUX_S_DEBUG") != NULL) ||
2N/A (getenv("_LUX_ER_DEBUG") != NULL)) {
2N/A g_scsi_printerr(command,
2N/A (struct scsi_extended_sense *)command->uscsi_rqbuf,
2N/A (command->uscsi_rqlen - command->uscsi_rqresid),
2N/A errorMsg, strerror(errno));
2N/A }
2N/A
2N/A } else {
2N/A
2N/A /*
2N/A * Retry 5 times in case of BUSY, and only
2N/A * once for Reservation-conflict, Command
2N/A * Termination and Queue Full. Wait for
2N/A * random amount of time (between 1/10 - 1/2 secs.)
2N/A * between each retry. This random wait is to avoid
2N/A * the multiple threads being executed at the same time
2N/A * and also the constraint in Photon IB, where the
2N/A * command queue has a depth of one command.
2N/A */
2N/A switch ((uchar_t)command->uscsi_status & STATUS_MASK) {
2N/A case STATUS_BUSY:
2N/A if (retry_cnt++ < 5) {
2N/A if ((err = wait_random_time()) == 0) {
2N/A R_DPRINTF(" cmd(): No. of retries %d."
2N/A " STATUS_BUSY: Retrying...\n",
2N/A retry_cnt);
2N/A goto retry;
2N/A
2N/A } else {
2N/A return (err);
2N/A }
2N/A }
2N/A break;
2N/A
2N/A case STATUS_RESERVATION_CONFLICT:
2N/A if (retry_cnt++ < 1) {
2N/A if ((err = wait_random_time()) == 0) {
2N/A R_DPRINTF(" cmd():"
2N/A " RESERVATION_CONFLICT:"
2N/A " Retrying...\n");
2N/A goto retry;
2N/A
2N/A } else {
2N/A return (err);
2N/A }
2N/A }
2N/A break;
2N/A
2N/A case STATUS_TERMINATED:
2N/A if (retry_cnt++ < 1) {
2N/A R_DPRINTF("Note: Command Terminated."
2N/A " Retrying...\n");
2N/A
2N/A if ((err = wait_random_time()) == 0) {
2N/A goto retry;
2N/A } else {
2N/A return (err);
2N/A }
2N/A }
2N/A break;
2N/A
2N/A case STATUS_QFULL:
2N/A if (retry_cnt++ < 1) {
2N/A R_DPRINTF("Note: Command Queue is full."
2N/A " Retrying...\n");
2N/A
2N/A if ((err = wait_random_time()) == 0) {
2N/A goto retry;
2N/A } else {
2N/A return (err);
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A
2N/A }
2N/A if (((getenv("_LUX_S_DEBUG") != NULL) ||
2N/A (getenv("_LUX_ER_DEBUG") != NULL)) &&
2N/A (errorMsg[0] != '\0')) {
2N/A (void) fprintf(stdout, " %s\n", errorMsg);
2N/A }
2N/A return (L_SCSI_ERROR | command->uscsi_status);
2N/A}