/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <sys/byteorder.h>
#include "common.h"
#include "errorcodes.h"
extern int rand_r(unsigned int *);
static int cleanup_dotdot_path(char *path);
static int wait_random_time(void);
static char *scsi_find_command_name(int cmd);
char msg_string[], char *err_string);
char msg_string[]);
static int
wait_random_time(void)
{
unsigned int seed;
int random;
/*
* Get the system time and use "system seconds"
* as 'seed' to generate a random number. Then,
* wait between 1/10 - 1/2 seconds before retry.
* Get the current process id and ex-or it with
* the seed so that the random number is always
* different even in case of multiple processes
* generate a random number at the same time.
*/
return (errno);
}
return (-1); /* L_LOCALTIME_ERROR */
}
/* get a random number. */
return (-1); /* L_SELECT_ERROR */
}
return (0);
}
/*
* Special string dump for error message
*/
static void
{
int i;
int n;
char *p;
char s[256];
for (p = s; *p; p++) {
*p = ' ';
}
p = hdr;
while (nbytes > 0) {
p = s;
for (i = 0; i < n; i++) {
}
for (i = BYTES_PER_LINE-n; i > 0; i--) {
" ");
}
" ");
for (i = 0; i < n; i++) {
}
}
nbytes -= n;
src += n;
}
}
/*
* Return a pointer to a string telling us the name of the command.
*/
static char *
{
/*
* Names of commands. Must have SCMD_UNKNOWN at end of list.
*/
struct scsi_command_name {
int command;
char *name;
register struct scsi_command_name *c;
break;
return (c->name);
}
/*
* Function to create error message containing
* scsi request sense information
*/
static void
{
int blkno;
case KEY_NO_SENSE:
break;
case KEY_RECOVERABLE_ERROR:
break;
case KEY_NOT_READY:
(void) sprintf(msg_string,
MSGSTR(10503,
"Device Not ready. Error: Random Retry Failed: %s\n."),
break;
case KEY_MEDIUM_ERROR:
break;
case KEY_HARDWARE_ERROR:
break;
case KEY_ILLEGAL_REQUEST:
break;
case KEY_UNIT_ATTENTION:
(void) sprintf(msg_string,
MSGSTR(10504,
"Unit attention."
"Error: Random Retry Failed.\n"));
break;
case KEY_WRITE_PROTECT:
break;
case KEY_BLANK_CHECK:
break;
case KEY_VENDOR_UNIQUE:
break;
case KEY_COPY_ABORTED:
break;
case KEY_ABORTED_COMMAND:
(void) sprintf(msg_string,
MSGSTR(10505,
"Aborted command. Error: Random Retry Failed.\n"));
break;
case KEY_EQUAL:
break;
case KEY_VOLUME_OVERFLOW:
break;
case KEY_MISCOMPARE:
break;
case KEY_RESERVED:
"Reserved value found"));
break;
default:
break;
}
}
"ASC Qualifier: 0x%x\n"),
/*
* rq->es_add_info[ADD_SENSE_CODE],
* rq->es_add_info[ADD_SENSE_QUAL_CODE]);
*/
}
}
}
/*
* Execute a command and determine the result.
*/
static int
{
/*
* Set function flags for driver.
*
* Set Automatic request sense enable
*
*/
/* intialize error message array */
errorMsg[0] = '\0';
/* print command for debug */
(flag & USCSI_RESET) ||
(flag & USCSI_RESET_ALL)) {
if (flag & USCSI_RESET) {
(void) printf(" Issuing a SCSI Reset.\n");
}
if (flag & USCSI_RESET_ALL) {
(void) printf(" Issuing a SCSI Reset All.\n");
}
} else {
(void) printf(" Issuing the following "
"SCSI command: %s\n",
for (i = 0; i < (int)command->uscsi_cdblen; i++) {
}
(void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
" flags=0x%x\n",
if ((command->uscsi_buflen > 0) &&
((flag & USCSI_READ) == 0)) {
(void) dump_hex_data(" Buffer data: ",
}
}
}
/*
* Default command timeout in case command left it 0
*/
if (command->uscsi_timeout == 0) {
}
/* Issue command - finally */
if ((command->uscsi_buflen > 0) &&
(flag & USCSI_READ)) {
(void) dump_hex_data("\tData read:",
}
}
return (status);
}
(void) printf("Unexpected USCSICMD ioctl error: %s\n",
}
return (status);
}
/*
* Just a SCSI error, create error message
* Retry once for Unit Attention,
* Not Ready, and Aborted Command
*/
case KEY_NOT_READY:
if (retry_cnt++ < 1) {
ER_DPRINTF("Note: Device Not Ready."
" Retrying...\n");
if ((err = wait_random_time()) == 0) {
goto retry;
} else {
return (err);
}
}
break;
case KEY_UNIT_ATTENTION:
if (retry_cnt++ < 1) {
ER_DPRINTF(" cmd():"
" UNIT_ATTENTION: Retrying...\n");
goto retry;
}
break;
case KEY_ABORTED_COMMAND:
if (retry_cnt++ < 1) {
ER_DPRINTF("Note: Command is aborted."
" Retrying...\n");
goto retry;
}
break;
}
}
} else {
/*
* Retry 5 times in case of BUSY, and only
* once for Reservation-conflict, Command
* Termination and Queue Full. Wait for
* random amount of time (between 1/10 - 1/2 secs.)
* between each retry. This random wait is to avoid
* the multiple threads being executed at the same time
* and also the constraint in Photon IB, where the
* command queue has a depth of one command.
*/
case STATUS_BUSY:
if (retry_cnt++ < 5) {
if ((err = wait_random_time()) == 0) {
R_DPRINTF(" cmd(): No. of retries %d."
" STATUS_BUSY: Retrying...\n",
goto retry;
} else {
return (err);
}
}
break;
if (retry_cnt++ < 1) {
if ((err = wait_random_time()) == 0) {
R_DPRINTF(" cmd():"
" RESERVATION_CONFLICT:"
" Retrying...\n");
goto retry;
} else {
return (err);
}
}
break;
case STATUS_TERMINATED:
if (retry_cnt++ < 1) {
R_DPRINTF("Note: Command Terminated."
" Retrying...\n");
if ((err = wait_random_time()) == 0) {
goto retry;
} else {
return (err);
}
}
break;
case STATUS_QFULL:
if (retry_cnt++ < 1) {
R_DPRINTF("Note: Command Queue is full."
" Retrying...\n");
if ((err = wait_random_time()) == 0) {
goto retry;
} else {
return (err);
}
}
break;
}
}
(errorMsg[0] != '\0')) {
}
}
/*
* MODE SENSE USCSI command
*
*
* pc = page control field
* page_code = Pages to return
*/
int
int buf_len,
{
/* 10 byte Mode Select cmd */
int status;
static int uscsi_count;
return (-1); /* L_INVALID_ARG */
}
/* Just for me - a sanity check */
(buf_len > MAX_MODE_SENSE_LEN)) {
return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
}
/* Bytes actually transfered */
if (status == 0) {
S_DPRINTF(" Number of bytes read on "
"Mode Sense 0x%x\n", uscsi_count);
}
}
return (status);
}
int
{
return (1);
ucmd.uscsi_buflen = 0;
return (status);
}
int
{
return (1);
ucmd.uscsi_buflen = 0;
return (status);
}
/*
* Print out fabric dev dtype
*/
void
{
(dtype_prop & DTYPE_MASK),
(dtype_prop & DTYPE_MASK));
} else {
/* Check to see if this is the HBA */
" 0x%-2x (Unknown Type)\n"),
(dtype_prop & DTYPE_MASK));
} else {
/* MATCH */
" 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
(dtype_prop & DTYPE_MASK));
}
}
}
void
{
char **p;
/*
* Intialize scsi_inquiry_labels_2 with i18n strings
*/
scsi_inquiry_labels_2[10] =
scsi_inquiry_labels_2[11] =
scsi_inquiry_labels_2[15] =
scsi_inquiry_labels_2[16] =
scsi_inquiry_labels_2[17] =
scsi_inquiry_labels_2[20] =
/*
* Intialize scsi_inquiry_labels_3 with i18n strings
*/
scsi_inquiry_labels_3[11] =
scsi_inquiry_labels_3[12] =
scsi_inquiry_labels_3[13] =
scsi_inquiry_labels_3[16] =
scsi_inquiry_labels_3[17] =
scsi_inquiry_labels_3[20] =
/*
* Intialize scsi_inquiry_labels_3 with i18n strings
*/
" (Device might or might not comply to an ANSI version)");
" (This code is reserved for historical uses)");
" (Device complies to ANSI X3.131-1994 (SCSI-2))");
" (Device complies to ANSI INCITS 301-1997 (SPC))");
" (Device complies to ANSI INCITS 351-2001 (SPC-2))");
" (Device complies to ANSI INCITS 408-2005 (SPC-3))");
/* print inquiry information */
/*
* arg_path is the path sent to luxadm by the user. if arg_path
* is a /devices path, then we do not need to print out physical
* path info
*/
}
scsi_3 = 0;
} else {
scsi_3 = 1;
}
p += 1;
} else {
/* */
}
p += 1;
} else {
}
p += 1;
} else {
}
p += 2;
} else {
/*
* If Pluto then print
* firmware rev & serial #.
*/
sizeof (inq.inq_firmware_rev), 0);
p++;
} else {
/* if we miss both the above if's */
p += 2;
}
}
} else {
}
} else {
}
if (scsi_3) {
} else {
}
}
} else
} else {
p++;
}
if (scsi_3) {
} else {
}
}
if (inq.inq_trmiop) {
} else {
p++;
}
if (scsi_3) {
if (inq.inq_dual_p) {
"%sa\n"), *p++);
} else {
"%sb\n"), *p++);
}
} else {
p++;
}
}
if (scsi_3) {
}
p++;
}
}
p++;
if (!scsi_3) {
}
p++;
}
p++;
}
p++;
}
}
p++;
if (scsi_3) {
} else {
}
}
}
p++;
if (!scsi_3) {
}
p++;
}
/*
* Now print the vendor-specific data.
*/
"Number of Ports, Targets: %d,%d\n"),
v_parm += 20;
length -= 20;
v_parm += 16;
length -= 16;
}
/*
* Do hex Dump of rest of the data.
*/
if (length > 0) {
MSGSTR(2190,
" VENDOR-SPECIFIC PARAMETERS\n"));
MSGSTR(2191,
"Byte# Hex Value "
" ASCII\n"));
(void) sprintf(byte_number,
}
/*
* Skip reserved bytes 56-95.
*/
if (length > 0) {
}
}
dump_hex_data("\nComplete Inquiry: ",
}
}
/*
* Internal routine to clean up ../'s in paths.
* returns 0 if no "../" are left.
*
* Wouldn't it be nice if there was a standard system library
* routine to do this...?
*/
static int
{
char *dotdot;
char *previous_slash;
/* Find the first "/../" in the string */
return (0);
}
/*
* If the [0] character is '/' and "../" immediatly
* follows it, then we can strip the ../
*
*
*/
return (1);
}
/*
* Now look for the LAST "/" before the "/../"
* as this is the parent dir we can get rid of.
* We do this by temporarily truncating the string
* at the '/' just before "../" using the dotdot pointer.
*/
*dotdot = '\0';
if (previous_slash == NULL) {
/*
* hmm, somethings wrong. path looks something
* like "foo/../bar/" so we can't really deal with it.
*/
return (0);
}
/*
* Now truncate the path just after the previous '/'
* and slam everything after the "../" back on
*/
return (1); /* We may have more "../"s */
}
/*
* Follow symbolic links from the logical device name to
* the /devfs physical device name. To be complete, we
* handle the case of multiple links. This function
* either returns NULL (no links, or some other error),
* or the physical device name, alloc'ed on the heap.
*
* NOTE: If the path is relative, it will be forced into
* an absolute path by pre-pending the pwd to it.
*/
char *
{
int cnt;
/* return NULL if path is NULL */
return (NULL);
}
for (;;) {
/*
* First make sure the path is absolute. If not, make it.
* If it's already an absolute path, we have no need
* to determine the cwd, so the program should still
* function within security-by-obscurity directories.
*/
if (source[0] != '/') {
return (NULL);
}
/*
*/
} else { /* no "./" so just take everything */
}
}
/*
* Clean up any "../"s that are in the path
*/
while (cleanup_dotdot_path(source))
;
/*
* source is now an absolute path to the link we're
* concerned with
*/
if (flag == NOT_IGNORE_DANGLING_LINK) {
/*
* In order not to ingore dangling links, check
* the lstat. If lstat succeeds, return the path
* from readlink.
* Note: osDevName input with /devices path from
* a dangling /dev link doesn't pass lstat so
* NULL is returned.
*/
if (!is_lstat_failed &&
/*
* lstat succeeded previously and source
* contains "/devices" then it is
* dangling node.
*/
}
return (phys_path);
} else if (is_lstat_failed) {
/* check lstat result. */
return (NULL);
} else {
/* and continue */
}
} else {
/*
* With algorithm that resolves a link
* and then issues readlink(), should
* not be reached here.
*/
return (NULL);
}
} else {
/*
* when stat succeeds it is not
* a dangling node so it is not
* a special case.
*/
return (NULL);
}
}
} else if (flag == STANDARD_DEVNAME_HANDLING) {
/*
* See if there's a real file out there. If not,
* we have a dangling link and we ignore it.
*/
return (NULL);
}
return (NULL);
}
} else {
/* invalid flag */
return (NULL);
}
/*
* If the file is not a link, we're done one
* way or the other. If there were links,
* return the full pathname of the resulting
* file.
*
* Note: All of our temp's are on the stack,
* so we have to copy the final result to the heap.
*/
}
return (phys_path);
}
if (cnt < 0) {
return (NULL);
}
/*
* scratch is on the heap, and for some reason readlink
* doesn't always terminate things properly so we have
* to make certain we're properly terminated
*/
/*
* Now check to see if the link is relative. If so,
* then we have to append it to the directory
* which the source was in. (This is non trivial)
*/
if (scratch[0] != '/') {
O_DPRINTF("Internal error... corrupt path.\n");
return (NULL);
}
/* Now strip off just the directory path */
/* and append the new link */
/*
* Note: At this point, source should have "../"s
* but we'll clean it up in the next pass through
* the loop.
*/
} else {
/* It's an absolute link so no worries */
}
}
/* Never reach here */
}
/*
* Input - Space for client_path, phci_path and paddr fields of ioc structure
* need to be allocated by the caller of this routine.
*/
int
{
int retval;
int fd;
int initial_path_count;
int current_path_count;
int i;
char *delimiter;
int malloc_error = 0;
int prop_buf_size;
int pathlist_retry_count = 0;
return (L_INVALID_PATH);
}
return (L_INVALID_PATH);
}
} else {
return (L_MALLOC_FAILED);
}
}
/* move beyond "/devices" prefix */
/* remove :c,raw suffix */
/* if we didn't find the ':' fine, else truncate */
}
/*
* We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
* at least twice. The first time will get the path count
* and the size of the ioctl propoerty buffer. The second
* time will get the path_info for each path.
*
* It's possible that additional paths are added while this
* code is running. If the path count increases between the
* 2 ioctl's above, then we'll retry (and assume all is well).
*/
/* free physical path */
/* 0 buf_size asks driver to return actual size needed */
/* open the ioctl file descriptor */
return (L_OPEN_PATH_FAIL);
}
if (retval != 0) {
return (L_SCSI_VHCI_ERROR);
}
while (pathlist_retry_count <= RETRY_PATHLIST) {
/* Make driver put actual # paths in variable */
/*
* Allocate space for array of path_info structures.
* Allocate enough space for # paths from get_pathcount
*/
return (L_MALLOC_FAILED);
}
/*
* Allocate space for path properties returned by driver
*/
malloc_error = 0;
for (i = 0; i < initial_path_count; i++) {
malloc_error = 1;
break;
}
malloc_error = 1;
break;
}
}
if (malloc_error == 1) {
for (i = 0; i < initial_path_count; i++) {
}
return (L_MALLOC_FAILED);
}
if (retval != 0) {
for (i = 0; i < initial_path_count; i++) {
}
return (L_SCSI_VHCI_ERROR);
}
if (initial_path_count < current_path_count) {
/* then a new path was added */
} else {
break;
}
}
/* we are done with ioctl's, lose the fd */
/*
* Compare the length num elements from the ioctl response
* and the caller's request - use smaller value.
*
* pathlist_p->path_count now has count returned from ioctl.
* ioc.buf_elem has the value the caller provided.
*/
if (initial_path_count < current_path_count) {
/* More paths exist than we allocated space for */
} else {
}
return (0);
}
int
{
/* open controller */
return (-1); /* L_OPEN_PATH_FAIL */
/*
* Read the first part of the page to get the page size
*/
size = 20;
return (L_MALLOC_FAILED);
}
/* read page */
0, MODEPAGE_ALLPAGES)) {
return (status);
}
/* Now get the size for all pages */
sizeof (mode_header_ptr->length);
return (L_MALLOC_FAILED);
}
/* read all pages */
0, MODEPAGE_ALLPAGES)) {
return (status);
}
return (0);
}
/*
* Dump a structure in hexadecimal.
*/
void
{
int i;
int n;
char *p;
char s[256];
for (p = s; *p; p++) {
*p = ' ';
}
p = hdr;
while (nbytes > 0) {
p = s;
for (i = 0; i < n; i++) {
}
for (i = BYTES_PER_LINE-n; i > 0; i--) {
}
for (i = 0; i < n; i++) {
}
}
nbytes -= n;
src += n;
}
}