Commit ac4646f7 authored by James Bottomley's avatar James Bottomley

Merge ssh://linux-scsi@linux-scsi.bkbits.net/scsi-misc-2.6

into mulgrave.(none):/home/jejb/BK/scsi-misc-2.6
parents 0a57a616 c53ba412
......@@ -203,6 +203,14 @@ config SCSI_FC_ATTRS
each attached FiberChannel device to sysfs, say Y.
Otherwise, say N.
config SCSI_ISCSI_ATTRS
tristate "iSCSI Transport Attributes"
depends on SCSI
help
If you wish to export transport-specific information about
each attached iSCSI device to sysfs, say Y.
Otherwise, say N.
endmenu
menu "SCSI low-level drivers"
......
......@@ -28,7 +28,7 @@ obj-$(CONFIG_SCSI) += scsi_mod.o
# --------------------------
obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o
obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o
obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
......
......@@ -4,6 +4,7 @@
* Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422)
* Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002)
* by D. Gilbert and aeb (20020609)
* Additions for SPC-3 T10/1416-D Rev 21 22 Sept 2004, D. Gilbert 20041025
*/
#include <linux/config.h>
......@@ -15,96 +16,78 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_request.h>
#include <scsi/scsi_eh.h>
#define CONST_COMMAND 0x01
#define CONST_STATUS 0x02
#define CONST_SENSE 0x04
#define CONST_XSENSE 0x08
#define CONST_CMND 0x10
#define CONST_MSG 0x20
#define CONST_HOST 0x40
#define CONST_DRIVER 0x80
static const char unknown[] = "UNKNOWN";
/* Commands with service actions that change the command name */
#define MAINTENANCE_IN 0xa3
#define MAINTENANCE_OUT 0xa4
#define SERVICE_ACTION_IN_12 0xab
#define SERVICE_ACTION_OUT_12 0xa9
#define SERVICE_ACTION_IN_16 0x9e
#define SERVICE_ACTION_OUT_16 0x9f
#define VARIABLE_LENGTH_CMD 0x7f
#ifdef CONFIG_SCSI_CONSTANTS
#ifdef CONSTANTS
#undef CONSTANTS
#endif
#define CONSTANTS (CONST_COMMAND | CONST_STATUS | CONST_SENSE | CONST_XSENSE \
| CONST_CMND | CONST_MSG | CONST_HOST | CONST_DRIVER)
#else
#define CONSTANTS 0
#endif
#if (CONSTANTS & CONST_COMMAND)
static const char * group_0_commands[] = {
/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
/* 13-16 */ "Verify", "Recover Buffered Data", "Mode Select", "Reserve",
/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
};
static const char *group_1_commands[] = {
/* 20-22 */ unknown, unknown, unknown,
/* 23-28 */ unknown, "Define window parameters", "Read Capacity",
unknown, unknown, "Read (10)",
/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase",
#ifdef CONFIG_SCSI_CONSTANTS
static const char * cdb_byte0_names[] = {
/* 00-03 */ "Test Unit Ready", "Rezero Unit/Rewind", NULL, "Request Sense",
/* 04-07 */ "Format Unit/Medium", "Read Block Limits", NULL,
"Reasssign Blocks",
/* 08-0d */ "Read (6)", NULL, "Write (6)", "Seek (6)", NULL, NULL,
/* 0e-12 */ NULL, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
/* 13-16 */ "Verify (6)", "Recover Buffered Data", "Mode Select (6)",
"Reserve (6)",
/* 17-1a */ "Release (6)", "Copy", "Erase", "Mode Sense (6)",
/* 1b-1d */ "Start/Stop Unit", "Receive Diagnostic", "Send Diagnostic",
/* 1e-1f */ "Prevent/Allow Medium Removal", NULL,
/* 20-22 */ NULL, NULL, NULL,
/* 23-28 */ "Read Format Capacities", "Set Window",
"Read Capacity (10)", NULL, NULL, "Read (10)",
/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase (10)",
"Read updated block",
/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
/* 2e-31 */ "Write Verify (10)", "Verify (10)", "Search High", "Search Equal",
/* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position",
/* 35-37 */ "Synchronize Cache (10)", "Lock/Unlock Cache (10)",
"Read Defect Data(10)",
/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
"Read Buffer",
/* 3d-3f */ "Update Block", "Read Long", "Write Long",
};
static const char *group_2_commands[] = {
/* 40-41 */ "Change Definition", "Write Same",
/* 42-48 */ "Read sub-channel", "Read TOC", "Read header",
/* 3d-3f */ "Update Block", "Read Long (10)", "Write Long (10)",
/* 40-41 */ "Change Definition", "Write Same (10)",
/* 42-48 */ "Read sub-channel", "Read TOC/PMA/ATIP", "Read density support",
"Play audio (10)", "Get configuration", "Play audio msf",
"Play audio track/index",
/* 49-4f */ "Play track relative (10)", "Get event status notification",
"Pause/resume", "Log Select", "Log Sense", "Stop play/scan",
unknown,
NULL,
/* 50-55 */ "Xdwrite", "Xpwrite, Read disk info", "Xdread, Read track info",
"Reserve track", "Send OPC onfo", "Mode Select (10)",
"Reserve track", "Send OPC info", "Mode Select (10)",
/* 56-5b */ "Reserve (10)", "Release (10)", "Repair track", "Read master cue",
"Mode Sense (10)", "Close track/session",
/* 5c-5f */ "Read buffer capacity", "Send cue sheet", "Persistent reserve in",
"Persistent reserve out",
};
/* The following are 16 byte commands in group 4 */
static const char *group_4_commands[] = {
/* 60-67 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 68-6f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 70-77 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Variable length",
/* 80-84 */ "Xdwrite (16)", "Rebuild (16)", "Regenerate (16)", "Extended copy",
"Receive copy results",
/* 85-89 */ "Memory Export In (16)", "Access control in", "Access control out",
"Read (16)", "Memory Export Out (16)",
/* 8a-8f */ "Write (16)", unknown, "Read attributes", "Write attributes",
/* 8a-8f */ "Write (16)", NULL, "Read attributes", "Write attributes",
"Write and verify (16)", "Verify (16)",
/* 90-94 */ "Pre-fetch (16)", "Synchronize cache (16)",
"Lock/unlock cache (16)", "Write same (16)", unknown,
/* 95-99 */ unknown, unknown, unknown, unknown, unknown,
/* 9a-9f */ unknown, unknown, unknown, unknown, "Service action in",
"Service action out",
};
/* The following are 12 byte commands in group 5 */
static const char *group_5_commands[] = {
/* a0-a5 */ "Report luns", "Blank", "Send event", "Maintenance (in)",
"Maintenance (out)", "Move medium/play audio(12)",
"Lock/unlock cache (16)", "Write same (16)", NULL,
/* 95-99 */ NULL, NULL, NULL, NULL, NULL,
/* 9a-9f */ NULL, NULL, NULL, NULL, "Service action in (16)",
"Service action out (16)",
/* a0-a5 */ "Report luns", "Blank", "Send event", "Maintenance in",
"Maintenance out", "Move medium/play audio(12)",
/* a6-a9 */ "Exchange medium", "Move medium attached", "Read(12)",
"Play track relative(12)",
/* aa-ae */ "Write(12)", unknown, "Erase(12), Get Performance",
/* aa-ae */ "Write(12)", NULL, "Erase(12), Get Performance",
"Read DVD structure", "Write and verify(12)",
/* af-b1 */ "Verify(12)", "Search data high(12)", "Search data equal(12)",
/* b2-b4 */ "Search data low(12)", "Set limits(12)",
......@@ -112,56 +95,277 @@ static const char *group_5_commands[] = {
/* b5-b6 */ "Request volume element address", "Send volume tag, set streaming",
/* b7-b9 */ "Read defect data(12)", "Read element status", "Read CD msf",
/* ba-bc */ "Redundancy group (in), Scan",
"Redundancy group (out), Set cd-rom speed", "Spare (in), Play cd",
/* bd-bf */ "Spare (out), Mechanism status", "Volume set (in), Read cd",
"Volume set (out), Send DVD structure",
"Redundancy group (out), Set cd-rom speed", "Spare in, Play cd",
/* bd-bf */ "Spare out, Mechanism status", "Volume set in, Read cd",
"Volume set out, Send DVD structure",
};
struct value_name_pair {
int value;
const char * name;
};
static const struct value_name_pair maint_in_arr[] = {
{0x5, "Report device identifier"},
{0xa, "Report target port groups"},
{0xb, "Report aliases"},
{0xc, "Report supported operation codes"},
{0xd, "Report supported task management functions"},
{0xe, "Report priority"},
};
#define MAINT_IN_SZ \
(int)(sizeof(maint_in_arr) / sizeof(maint_in_arr[0]))
static const struct value_name_pair maint_out_arr[] = {
{0x6, "Set device identifier"},
{0xa, "Set target port groups"},
{0xb, "Change aliases"},
{0xe, "Set priority"},
};
#define MAINT_OUT_SZ \
(int)(sizeof(maint_out_arr) / sizeof(maint_out_arr[0]))
#define group(opcode) (((opcode) >> 5) & 7)
static const struct value_name_pair serv_in12_arr[] = {
{0x1, "Read media serial number"},
};
#define SERV_IN12_SZ \
(int)(sizeof(serv_in12_arr) / sizeof(serv_in12_arr[0]))
#define RESERVED_GROUP 0
#define VENDOR_GROUP 1
static const struct value_name_pair serv_out12_arr[] = {
{-1, "dummy entry"},
};
#define SERV_OUT12_SZ \
(int)(sizeof(serv_out12_arr) / sizeof(serv_in12_arr[0]))
static const char **commands[] = {
group_0_commands, group_1_commands, group_2_commands,
(const char **) RESERVED_GROUP, group_4_commands,
group_5_commands, (const char **) VENDOR_GROUP,
(const char **) VENDOR_GROUP
static const struct value_name_pair serv_in16_arr[] = {
{0x10, "Read capacity(16)"},
{0x11, "Read long(16)"},
};
#define SERV_IN16_SZ \
(int)(sizeof(serv_in16_arr) / sizeof(serv_in16_arr[0]))
static const char reserved[] = "RESERVED";
static const char vendor[] = "VENDOR SPECIFIC";
static const struct value_name_pair serv_out16_arr[] = {
{0x11, "Write long(16)"},
{0x1f, "Notify data transfer device(16)"},
};
#define SERV_OUT16_SZ \
(int)(sizeof(serv_out16_arr) / sizeof(serv_in16_arr[0]))
static const struct value_name_pair variable_length_arr[] = {
{0x1, "Rebuild(32)"},
{0x2, "Regenerate(32)"},
{0x3, "Xdread(32)"},
{0x4, "Xdwrite(32)"},
{0x5, "Xdwrite extended(32)"},
{0x6, "Xpwrite(32)"},
{0x7, "Xdwriteread(32)"},
{0x8, "Xdwrite extended(64)"},
{0x9, "Read(32)"},
{0xa, "Verify(32)"},
{0xb, "Write(32)"},
{0xc, "Write an verify(32)"},
{0xd, "Write same(32)"},
{0x8801, "Format OSD"},
{0x8802, "Create (osd)"},
{0x8803, "List (osd)"},
{0x8805, "Read (osd)"},
{0x8806, "Write (osd)"},
{0x8807, "Append (osd)"},
{0x8808, "Flush (osd)"},
{0x880a, "Remove (osd)"},
{0x880b, "Create partition (osd)"},
{0x880c, "Remove partition (osd)"},
{0x880e, "Get attributes (osd)"},
{0x880f, "Set attributes (osd)"},
{0x8812, "Create and write (osd)"},
{0x8815, "Create collection (osd)"},
{0x8816, "Remove collection (osd)"},
{0x8817, "List collection (osd)"},
{0x8818, "Set key (osd)"},
{0x8819, "Set master key (osd)"},
{0x881a, "Flush collection (osd)"},
{0x881b, "Flush partition (osd)"},
{0x881c, "Flush OSD"},
{0x8f7e, "Perform SCSI command (osd)"},
{0x8f7f, "Perform task management function (osd)"},
};
#define VARIABLE_LENGTH_SZ \
(int)(sizeof(variable_length_arr) / sizeof(variable_length_arr[0]))
static void print_opcode(int opcode) {
const char **table = commands[ group(opcode) ];
switch ((unsigned long) table) {
case RESERVED_GROUP:
printk("%s(0x%02x) ", reserved, opcode);
static const char * get_sa_name(const struct value_name_pair * arr,
int arr_sz, int service_action)
{
int k;
for (k = 0; k < arr_sz; ++k, ++arr) {
if (service_action == arr->value)
break;
}
return (k < arr_sz) ? arr->name : NULL;
}
/* attempt to guess cdb length if cdb_len==0 . No trailing linefeed. */
static void print_opcode_name(unsigned char * cdbp, int cdb_len,
int start_of_line)
{
int sa, len, cdb0;
const char * name;
const char * leadin = start_of_line ? KERN_INFO : "";
cdb0 = cdbp[0];
switch(cdb0) {
case VARIABLE_LENGTH_CMD:
len = cdbp[7] + 8;
if (len < 10) {
printk("%sshort variable length command, "
"len=%d ext_len=%d", leadin, len, cdb_len);
break;
}
sa = (cdbp[8] << 8) + cdbp[9];
name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
if (name) {
printk("%s%s", leadin, name);
if ((cdb_len > 0) && (len != cdb_len))
printk(", in_cdb_len=%d, ext_len=%d",
len, cdb_len);
} else {
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
if ((cdb_len > 0) && (len != cdb_len))
printk(", in_cdb_len=%d, ext_len=%d",
len, cdb_len);
}
break;
case MAINTENANCE_IN:
sa = cdbp[1] & 0x1f;
name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
if (name)
printk("%s%s", leadin, name);
else
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
break;
case MAINTENANCE_OUT:
sa = cdbp[1] & 0x1f;
name = get_sa_name(maint_out_arr, MAINT_OUT_SZ, sa);
if (name)
printk("%s%s", leadin, name);
else
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
break;
case VENDOR_GROUP:
printk("%s(0x%02x) ", vendor, opcode);
case SERVICE_ACTION_IN_12:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_in12_arr, SERV_IN12_SZ, sa);
if (name)
printk("%s%s", leadin, name);
else
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
break;
case SERVICE_ACTION_OUT_12:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_out12_arr, SERV_OUT12_SZ, sa);
if (name)
printk("%s%s", leadin, name);
else
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
break;
case SERVICE_ACTION_IN_16:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_in16_arr, SERV_IN16_SZ, sa);
if (name)
printk("%s%s", leadin, name);
else
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
break;
case SERVICE_ACTION_OUT_16:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_out16_arr, SERV_OUT16_SZ, sa);
if (name)
printk("%s%s", leadin, name);
else
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
break;
default:
if (table[opcode & 0x1f] != unknown)
printk("%s ",table[opcode & 0x1f]);
if (cdb0 < 0xc0) {
name = cdb_byte0_names[cdb0];
if (name)
printk("%s%s", leadin, name);
else
printk("%s(0x%02x) ", unknown, opcode);
printk("%scdb[0]=0x%x (reserved)",
leadin, cdb0);
} else
printk("%scdb[0]=0x%x (vendor)", leadin, cdb0);
break;
}
}
#else /* CONST & CONST_COMMAND */
static void print_opcode(int opcode) {
printk("0x%02x ", opcode);
#else /* ifndef CONFIG_SCSI_CONSTANTS */
static void print_opcode_name(unsigned char * cdbp, int cdb_len,
int start_of_line)
{
int sa, len, cdb0;
const char * leadin = start_of_line ? KERN_INFO : "";
cdb0 = cdbp[0];
switch(cdb0) {
case VARIABLE_LENGTH_CMD:
len = cdbp[7] + 8;
if (len < 10) {
printk("%sshort opcode=0x%x command, len=%d "
"ext_len=%d", leadin, cdb0, len, cdb_len);
break;
}
sa = (cdbp[8] << 8) + cdbp[9];
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
if (len != cdb_len)
printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len);
break;
case MAINTENANCE_IN:
case MAINTENANCE_OUT:
case SERVICE_ACTION_IN_12:
case SERVICE_ACTION_OUT_12:
case SERVICE_ACTION_IN_16:
case SERVICE_ACTION_OUT_16:
sa = cdbp[1] & 0x1f;
printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
break;
default:
if (cdb0 < 0xc0)
printk("%scdb[0]=0x%x", leadin, cdb0);
else
printk("%scdb[0]=0x%x (vendor)", leadin, cdb0);
break;
}
}
#endif
void __scsi_print_command (unsigned char *command) {
int i,s;
print_opcode(command[0]);
for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
printk("%02x ", command[i]);
void __scsi_print_command(unsigned char *command)
{
int k, len;
print_opcode_name(command, 0, 1);
if (VARIABLE_LENGTH_CMD == command[0])
len = command[7] + 8;
else
len = COMMAND_SIZE(command[0]);
/* print out all bytes in cdb */
for (k = 0; k < len; ++k)
printk(" %02x", command[k]);
printk("\n");
}
/* This function (perhaps with the addition of peripheral device type)
* is more approriate than __scsi_print_command(). Perhaps that static
* can be dropped later if it replaces the __scsi_print_command version.
*/
static void scsi_print_cdb(unsigned char *cdb, int cdb_len, int start_of_line)
{
int k;
print_opcode_name(cdb, cdb_len, start_of_line);
/* print out all bytes in cdb */
printk(":");
for (k = 0; k < cdb_len; ++k)
printk(" %02x", cdb[k]);
printk("\n");
}
......@@ -177,7 +381,7 @@ void __scsi_print_command (unsigned char *command) {
**/
void
scsi_print_status(unsigned char scsi_status) {
#if (CONSTANTS & CONST_STATUS)
#ifdef CONFIG_SCSI_CONSTANTS
const char * ccp;
switch (scsi_status) {
......@@ -194,13 +398,13 @@ scsi_print_status(unsigned char scsi_status) {
case 0x40: ccp = "Task Aborted"; break;
default: ccp = "Unknown status";
}
printk("%s", ccp);
printk(KERN_INFO "%s", ccp);
#else
printk("0x%0x", scsi_status);
printk(KERN_INFO "0x%0x", scsi_status);
#endif
}
#if (CONSTANTS & CONST_XSENSE)
#ifdef CONFIG_SCSI_CONSTANTS
struct error_info {
unsigned short code12; /* 0x0302 looks better than 0x03,0x02 */
......@@ -253,6 +457,8 @@ static struct error_info additional[] =
{0x040C, "Logical unit not accessible, target port in unavailable "
"state"},
{0x0410, "Logical unit not ready, auxiliary memory not accessible"},
{0x0411, "Logical unit not ready, notify (enable spinup) required"},
{0x0412, "Logical unit not ready, offline"},
{0x0500, "Logical unit does not respond to selection"},
......@@ -300,7 +506,14 @@ static struct error_info additional[] =
{0x0D04, "Copy target device data underrun"},
{0x0D05, "Copy target device data overrun"},
{0x0E00, "Invalid information unit"},
{0x0E01, "Information unit too short"},
{0x0E02, "Information unit too long"},
{0x1000, "Id CRC or ECC error"},
{0x1001, "Data block guard check failed"},
{0x1002, "Data block application tag check failed"},
{0x1003, "Data block reference tag check failed"},
{0x1100, "Unrecovered read error"},
{0x1101, "Read retries exhausted"},
......@@ -407,8 +620,10 @@ static struct error_info additional[] =
{0x2400, "Invalid field in cdb"},
{0x2401, "CDB decryption error"},
{0x2402, "Obsolete"},
{0x2403, "Obsolete"},
{0x2404, "Security audit value frozen"},
{0x2405, "Security working key frozen"},
{0x2406, "Nonce not unique"},
{0x2407, "Nonce timestamp out of range"},
{0x2500, "Logical unit not supported"},
......@@ -426,6 +641,8 @@ static struct error_info additional[] =
{0x260B, "Inline data length exceeded"},
{0x260C, "Invalid operation for copy source or destination"},
{0x260D, "Copy segment granularity violation"},
{0x260E, "Invalid parameter while port is enabled"},
{0x260F, "Invalid data-out buffer integrity"},
{0x2700, "Write protected"},
{0x2701, "Hardware write protected"},
......@@ -455,6 +672,8 @@ static struct error_info additional[] =
{0x2A05, "Registrations preempted"},
{0x2A06, "Asymmetric access state changed"},
{0x2A07, "Implicit asymmetric access state transition failed"},
{0x2A08, "Priority changed"},
{0x2A09, "Capacity data has changed"},
{0x2B00, "Copy cannot execute since host cannot disconnect"},
......@@ -468,6 +687,8 @@ static struct error_info additional[] =
{0x2C07, "Previous busy status"},
{0x2C08, "Previous task set full status"},
{0x2C09, "Previous reservation conflict status"},
{0x2C0A, "Partition or collection contains user objects"},
{0x2C0B, "Not reserved"},
{0x2D00, "Overwrite error on update in place"},
......@@ -485,6 +706,8 @@ static struct error_info additional[] =
{0x3007, "Cleaning failure"},
{0x3008, "Cannot write - application code mismatch"},
{0x3009, "Current session not fixated for append"},
{0x300A, "Cleaning request rejected"},
{0x300C, "WORM medium, overwrite attempted"},
{0x3010, "Medium not formatted"},
{0x3100, "Medium format corrupted"},
......@@ -503,6 +726,7 @@ static struct error_info additional[] =
{0x3502, "Enclosure services unavailable"},
{0x3503, "Enclosure services transfer failure"},
{0x3504, "Enclosure services transfer refused"},
{0x3505, "Enclosure services checksum error"},
{0x3600, "Ribbon, ink, or toner failure"},
......@@ -543,6 +767,7 @@ static struct error_info additional[] =
{0x3B14, "Medium magazine locked"},
{0x3B15, "Medium magazine unlocked"},
{0x3B16, "Mechanical positioning or changer error"},
{0x3B17, "Read past end of user object"},
{0x3D00, "Invalid bits in identify message"},
......@@ -570,14 +795,12 @@ static struct error_info additional[] =
{0x3F0F, "Echo buffer overwritten"},
{0x3F10, "Medium loadable"},
{0x3F11, "Medium auxiliary memory accessible"},
#if 0
{0x40NN, "Ram failure"},
{0x40NN, "Diagnostic failure on component nn"},
{0x41NN, "Data path failure"},
{0x42NN, "Power-on or self-test failure"},
#endif
/*
* {0x40NN, "Ram failure"},
* {0x40NN, "Diagnostic failure on component nn"},
* {0x41NN, "Data path failure"},
* {0x42NN, "Power-on or self-test failure"},
*/
{0x4300, "Message error"},
{0x4400, "Internal target failure"},
......@@ -592,6 +815,7 @@ static struct error_info additional[] =
{0x4703, "Information unit CRC error detected"},
{0x4704, "Asynchronous information protection error detected"},
{0x4705, "Protocol service CRC error"},
{0x477f, "Some commands cleared by iSCSI Protocol event"},
{0x4800, "Initiator detected error message received"},
......@@ -600,13 +824,17 @@ static struct error_info additional[] =
{0x4A00, "Command phase error"},
{0x4B00, "Data phase error"},
{0x4B01, "Invalid target port transfer tag received"},
{0x4B02, "Too much write data"},
{0x4B03, "Ack/nak timeout"},
{0x4B04, "Nak received"},
{0x4B05, "Data offset error"},
{0x4B06, "Initiator response timeout"},
{0x4C00, "Logical unit failed self-configuration"},
#if 0
{0x4DNN, "Tagged overlapped commands (nn = queue tag)"},
#endif
/*
* {0x4DNN, "Tagged overlapped commands (nn = queue tag)"},
*/
{0x4E00, "Overlapped commands attempted"},
{0x5000, "Write append error"},
......@@ -631,6 +859,7 @@ static struct error_info additional[] =
{0x5504, "Insufficient registration resources"},
{0x5505, "Insufficient access control resources"},
{0x5506, "Auxiliary memory out of space"},
{0x5507, "Quota error"},
{0x5700, "Unable to recover table-of-contents"},
......@@ -806,11 +1035,9 @@ static struct error_info additional[] =
{0x6F03, "Read of scrambled sector without authentication"},
{0x6F04, "Media region code is mismatched to logical unit region"},
{0x6F05, "Drive region must be permanent/region reset count error"},
#if 0
{0x70NN, "Decompression exception short algorithm id of nn"},
#endif
/*
* {0x70NN, "Decompression exception short algorithm id of nn"},
*/
{0x7100, "Decompression exception long algorithm id"},
{0x7200, "Session fixation error"},
......@@ -845,9 +1072,7 @@ static struct error_info2 additional2[] =
{0x70,0x00,0xff,"Decompression exception short algorithm id of %x"},
{0, 0, 0, NULL}
};
#endif
#if (CONSTANTS & CONST_SENSE)
/* description of the sense key values */
static const char *snstext[] = {
"No Sense", /* 0: There is no sense information */
......@@ -858,11 +1083,11 @@ static const char *snstext[] = {
"Hardware Error", /* 4: Controller or device failure */
"Illegal Request", /* 5: Error in request */
"Unit Attention", /* 6: Removable medium was changed, or
the target has been reset */
the target has been reset, or ... */
"Data Protect", /* 7: Access to the data is blocked */
"Blank Check", /* 8: Reached unexpected written or unwritten
region of the medium */
"Vendor Specific", /* 9: Vendor specific */
"Vendor Specific(9)",
"Copy Aborted", /* A: COPY or COMPARE was aborted */
"Aborted Command", /* B: The target aborted the command */
"Equal", /* C: A SEARCH DATA command found data equal */
......@@ -875,7 +1100,7 @@ static const char *snstext[] = {
/* Get sense key string or NULL if not available */
const char *
scsi_sense_key_string(unsigned char key) {
#if (CONSTANTS & CONST_SENSE)
#ifdef CONFIG_SCSI_CONSTANTS
if (key <= 0xE)
return snstext[key];
#endif
......@@ -883,12 +1108,12 @@ scsi_sense_key_string(unsigned char key) {
}
/*
* Get extended sense key string or NULL if not available.
* This string may contain a %x and must be printed with ascq as arg.
* Get additional sense code string or NULL if not available.
* This string may contain a "%x" and should be printed with ascq as arg.
*/
const char *
scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
#if (CONSTANTS & CONST_XSENSE)
#ifdef CONFIG_SCSI_CONSTANTS
int i;
unsigned short code = ((asc << 8) | ascq);
......@@ -904,17 +1129,25 @@ scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
return NULL;
}
/* Print extended sense information */
/* Print extended sense information; no leadin, no linefeed */
static void
scsi_show_extd_sense(unsigned char asc, unsigned char ascq) {
scsi_show_extd_sense(unsigned char asc, unsigned char ascq)
{
const char *extd_sense_fmt = scsi_extd_sense_format(asc, ascq);
if (extd_sense_fmt) {
if (strstr(extd_sense_fmt, "%x")) {
printk("Additional sense: ");
printk(extd_sense_fmt, ascq);
printk("\n");
} else
printk("Additional sense: %s", extd_sense_fmt);
} else {
printk("ASC=%2x ASCQ=%2x\n", asc, ascq);
if (asc >= 0x80)
printk("<<vendor>> ASC=0x%x ASCQ=0x%x", asc, ascq);
if (ascq >= 0x80)
printk("ASC=0x%x <<vendor>> ASCQ=0x%x", asc, ascq);
else
printk("ASC=0x%x ASCQ=0x%x", asc, ascq);
}
}
......@@ -922,112 +1155,115 @@ scsi_show_extd_sense(unsigned char asc, unsigned char ascq) {
static void
print_sense_internal(const char *devclass,
const unsigned char *sense_buffer,
int sense_len,
struct request *req)
{
int s, sense_class, valid, code, info;
const char *error = NULL;
unsigned char asc, ascq;
int k, num, res;
unsigned int info;
const char *error;
const char *sense_txt;
const char *name = req->rq_disk ? req->rq_disk->disk_name : devclass;
sense_class = (sense_buffer[0] >> 4) & 0x07;
code = sense_buffer[0] & 0xf;
valid = sense_buffer[0] & 0x80;
if (sense_class == 7) { /* extended sense data */
s = sense_buffer[7] + 8;
if (s > SCSI_SENSE_BUFFERSIZE)
s = SCSI_SENSE_BUFFERSIZE;
info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) |
(sense_buffer[5] << 8) | sense_buffer[6]);
if (info || valid) {
printk("Info fld=0x%x", info);
if (!valid) /* info data not according to standard */
printk(" (nonstd)");
printk(", ");
struct scsi_sense_hdr ssh;
res = scsi_normalize_sense(sense_buffer, sense_len, &ssh);
if (0 == res) {
/* this may be SCSI-1 sense data */
num = (sense_len < 32) ? sense_len : 32;
printk(KERN_INFO "Unrecognized sense data (in hex):");
for (k = 0; k < num; ++k) {
if (0 == (k % 16)) {
printk("\n");
printk(KERN_INFO " ");
}
if (sense_buffer[2] & 0x80)
printk( "FMK "); /* current command has read a filemark */
if (sense_buffer[2] & 0x40)
printk( "EOM "); /* end-of-medium condition exists */
if (sense_buffer[2] & 0x20)
printk( "ILI "); /* incorrect block length requested */
switch (code) {
case 0x0:
error = "Current"; /* error concerns current command */
break;
case 0x1:
error = "Deferred"; /* error concerns some earlier command */
/* e.g., an earlier write to disk cache succeeded, but
now the disk discovers that it cannot write the data */
break;
default:
error = "Invalid";
printk("%02x ", sense_buffer[k]);
}
printk("%s ", error);
sense_txt = scsi_sense_key_string(sense_buffer[2]);
if (sense_txt)
printk("%s: sense key %s\n", name, sense_txt);
else
printk("%s: sense = %2x %2x\n", name,
sense_buffer[0], sense_buffer[2]);
asc = ascq = 0;
if (sense_buffer[7] + 7 >= 13) {
asc = sense_buffer[12];
ascq = sense_buffer[13];
printk("\n");
return;
}
if (asc || ascq)
scsi_show_extd_sense(asc, ascq);
} else { /* non-extended sense data */
/*
* Standard says:
* sense_buffer[0] & 0200 : address valid
* sense_buffer[0] & 0177 : vendor-specific error code
* sense_buffer[1] & 0340 : vendor-specific
* sense_buffer[1..3] : 21-bit logical block address
/* An example of deferred is when an earlier write to disk cache
* succeeded, but now the disk discovers that it cannot write the
* data to the magnetic media.
*/
error = scsi_sense_is_deferred(&ssh) ?
"<<DEFERRED>>" : "Current";
printk(KERN_INFO "%s: %s", name, error);
if (ssh.response_code >= 0x72)
printk(" [descriptor]");
sense_txt = scsi_sense_key_string(sense_buffer[0]);
sense_txt = scsi_sense_key_string(ssh.sense_key);
if (sense_txt)
printk("%s: old sense key %s\n", name, sense_txt);
printk(": sense key: %s\n", sense_txt);
else
printk("%s: sense = %2x %2x\n", name,
sense_buffer[0], sense_buffer[2]);
printk(": sense key=0x%x\n", ssh.sense_key);
printk(KERN_INFO " ");
scsi_show_extd_sense(ssh.asc, ssh.ascq);
printk("\n");
printk("Non-extended sense class %d code 0x%0x\n",
sense_class, code);
s = 4;
}
if (ssh.response_code < 0x72) {
/* only decode extras for "fixed" format now */
char buff[80];
int blen, fixed_valid;
#if !(CONSTANTS & CONST_SENSE)
{
int i;
printk("Raw sense data:");
for (i = 0; i < s; ++i)
printk("0x%02x ", sense_buffer[i]);
fixed_valid = sense_buffer[0] & 0x80;
info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) |
(sense_buffer[5] << 8) | sense_buffer[6]);
res = 0;
memset(buff, 0, sizeof(buff));
blen = sizeof(buff) - 1;
if (fixed_valid)
res += snprintf(buff + res, blen - res,
"Info fld=0x%x", info);
if (sense_buffer[2] & 0x80) {
/* current command has read a filemark */
if (res > 0)
res += snprintf(buff + res, blen - res, ", ");
res += snprintf(buff + res, blen - res, "FMK");
}
if (sense_buffer[2] & 0x40) {
/* end-of-medium condition exists */
if (res > 0)
res += snprintf(buff + res, blen - res, ", ");
res += snprintf(buff + res, blen - res, "EOM");
}
if (sense_buffer[2] & 0x20) {
/* incorrect block length requested */
if (res > 0)
res += snprintf(buff + res, blen - res, ", ");
res += snprintf(buff + res, blen - res, "ILI");
}
if (res > 0)
printk(KERN_INFO "%s\n", buff);
} else if (ssh.additional_length > 0) {
/* descriptor format with sense descriptors */
num = 8 + ssh.additional_length;
num = (sense_len < num) ? sense_len : num;
printk(KERN_INFO "Descriptor sense data with sense "
"descriptors (in hex):");
for (k = 0; k < num; ++k) {
if (0 == (k % 16)) {
printk("\n");
printk(KERN_INFO " ");
}
printk("%02x ", sense_buffer[k]);
}
printk("\n");
}
#endif
}
void scsi_print_sense(const char *devclass, struct scsi_cmnd *cmd)
{
print_sense_internal(devclass, cmd->sense_buffer, cmd->request);
print_sense_internal(devclass, cmd->sense_buffer,
SCSI_SENSE_BUFFERSIZE, cmd->request);
}
void scsi_print_req_sense(const char *devclass, struct scsi_request *sreq)
{
print_sense_internal(devclass, sreq->sr_sense_buffer, sreq->sr_request);
print_sense_internal(devclass, sreq->sr_sense_buffer,
SCSI_SENSE_BUFFERSIZE, sreq->sr_request);
}
#if (CONSTANTS & CONST_MSG)
#ifdef CONFIG_SCSI_CONSTANTS
static const char *one_byte_msgs[] = {
/* 0x00 */ "Command Complete", NULL, "Save Pointers",
/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error",
......@@ -1036,41 +1272,39 @@ static const char *one_byte_msgs[] = {
/* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue",
/* 0x0f */ "Initiate Recovery", "Release Recovery"
};
#define NO_ONE_BYTE_MSGS (sizeof(one_byte_msgs) / sizeof (const char *))
static const char *two_byte_msgs[] = {
/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag"
/* 0x23 */ "Ignore Wide Residue"
};
#define NO_TWO_BYTE_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
static const char *extended_msgs[] = {
/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request"
};
#define NO_EXTENDED_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
#endif /* (CONSTANTS & CONST_MSG) */
int scsi_print_msg (const unsigned char *msg) {
int scsi_print_msg (const unsigned char *msg)
{
int len = 0, i;
if (msg[0] == EXTENDED_MESSAGE) {
len = 3 + msg[1];
#if (CONSTANTS & CONST_MSG)
if (msg[2] < NO_EXTENDED_MSGS)
printk ("%s ", extended_msgs[msg[2]]);
else
printk ("Extended Message, reserved code (0x%02x) ", (int) msg[2]);
printk ("Extended Message, reserved code (0x%02x) ",
(int) msg[2]);
switch (msg[2]) {
case EXTENDED_MODIFY_DATA_POINTER:
printk("pointer = %d", (int) (msg[3] << 24) | (msg[4] << 16) |
(msg[5] << 8) | msg[6]);
printk("pointer = %d", (int) (msg[3] << 24) |
(msg[4] << 16) | (msg[5] << 8) | msg[6]);
break;
case EXTENDED_SDTR:
printk("period = %d ns, offset = %d", (int) msg[3] * 4, (int)
msg[4]);
printk("period = %d ns, offset = %d",
(int) msg[3] * 4, (int) msg[4]);
break;
case EXTENDED_WDTR:
printk("width = 2^%d bytes", msg[3]);
......@@ -1079,118 +1313,123 @@ int scsi_print_msg (const unsigned char *msg) {
for (i = 2; i < len; ++i)
printk("%02x ", msg[i]);
}
#else
for (i = 0; i < len; ++i)
printk("%02x ", msg[i]);
#endif
/* Identify */
} else if (msg[0] & 0x80) {
#if (CONSTANTS & CONST_MSG)
printk("Identify disconnect %sallowed %s %d ",
(msg[0] & 0x40) ? "" : "not ",
(msg[0] & 0x20) ? "target routine" : "lun",
msg[0] & 0x7);
#else
printk("%02x ", msg[0]);
#endif
len = 1;
/* Normal One byte */
} else if (msg[0] < 0x1f) {
#if (CONSTANTS & CONST_MSG)
if (msg[0] < NO_ONE_BYTE_MSGS)
printk(one_byte_msgs[msg[0]]);
else
printk("reserved (%02x) ", msg[0]);
#else
printk("%02x ", msg[0]);
#endif
len = 1;
/* Two byte */
} else if (msg[0] <= 0x2f) {
#if (CONSTANTS & CONST_MSG)
if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS)
printk("%s %02x ", two_byte_msgs[msg[0] - 0x20],
msg[1]);
else
printk("reserved two byte (%02x %02x) ",
msg[0], msg[1]);
#else
len = 2;
} else
printk("reserved");
return len;
}
#else /* ifndef CONFIG_SCSI_CONSTANTS */
int scsi_print_msg (const unsigned char *msg)
{
int len = 0, i;
if (msg[0] == EXTENDED_MESSAGE) {
len = 3 + msg[1];
for (i = 0; i < len; ++i)
printk("%02x ", msg[i]);
/* Identify */
} else if (msg[0] & 0x80) {
printk("%02x ", msg[0]);
len = 1;
/* Normal One byte */
} else if (msg[0] < 0x1f) {
printk("%02x ", msg[0]);
len = 1;
/* Two byte */
} else if (msg[0] <= 0x2f) {
printk("%02x %02x", msg[0], msg[1]);
#endif
len = 2;
} else
#if (CONSTANTS & CONST_MSG)
printk(reserved);
#else
printk("%02x ", msg[0]);
#endif
return len;
}
#endif /* ! CONFIG_SCSI_CONSTANTS */
void scsi_print_command(struct scsi_cmnd *cmd) {
void scsi_print_command(struct scsi_cmnd *cmd)
{
/* Assume appended output (i.e. not at start of line) */
printk("scsi%d : destination target %d, lun %d\n",
cmd->device->host->host_no,
cmd->device->id,
cmd->device->lun);
printk(" command = ");
__scsi_print_command(cmd->cmnd);
printk(KERN_INFO " command: ");
scsi_print_cdb(cmd->cmnd, cmd->cmd_len, 0);
}
#if (CONSTANTS & CONST_HOST)
#ifdef CONFIG_SCSI_CONSTANTS
static const char * hostbyte_table[]={
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET",
"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR",
"DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY", NULL};
"DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY"};
#define NUM_HOSTBYTE_STRS (sizeof(hostbyte_table) / sizeof(const char *))
void scsi_print_hostbyte(int scsiresult)
{ static int maxcode=0;
int i;
{
int hb = host_byte(scsiresult);
if(!maxcode) {
for(i=0;hostbyte_table[i];i++) ;
maxcode=i-1;
}
printk("Hostbyte=0x%02x",host_byte(scsiresult));
if(host_byte(scsiresult)>maxcode) {
printk("Hostbyte=0x%02x", hb);
if (hb < NUM_HOSTBYTE_STRS)
printk("(%s) ", hostbyte_table[hb]);
else
printk("is invalid ");
return;
}
printk("(%s) ",hostbyte_table[host_byte(scsiresult)]);
}
#else
void scsi_print_hostbyte(int scsiresult)
{ printk("Hostbyte=0x%02x ",host_byte(scsiresult));
{
printk("Hostbyte=0x%02x ", host_byte(scsiresult));
}
#endif
#if (CONSTANTS & CONST_DRIVER)
#ifdef CONFIG_SCSI_CONSTANTS
static const char * driverbyte_table[]={
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR",
"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",NULL };
"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE"};
#define NUM_DRIVERBYTE_STRS (sizeof(driverbyte_table) / sizeof(const char *))
static const char * driversuggest_table[]={"SUGGEST_OK",
"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE",
unknown,unknown,unknown, "SUGGEST_SENSE",NULL};
"SUGGEST_5", "SUGGEST_6", "SUGGEST_7", "SUGGEST_SENSE"};
#define NUM_SUGGEST_STRS (sizeof(driversuggest_table) / sizeof(const char *))
void scsi_print_driverbyte(int scsiresult)
{ static int driver_max=0,suggest_max=0;
int i,dr=driver_byte(scsiresult)&DRIVER_MASK,
su=(driver_byte(scsiresult)&SUGGEST_MASK)>>4;
if(!driver_max) {
for(i=0;driverbyte_table[i];i++) ;
driver_max=i;
for(i=0;driversuggest_table[i];i++) ;
suggest_max=i;
}
printk("Driverbyte=0x%02x",driver_byte(scsiresult));
{
int dr = (driver_byte(scsiresult) & DRIVER_MASK);
int su = ((driver_byte(scsiresult) & SUGGEST_MASK) >> 4);
printk("Driverbyte=0x%02x ", driver_byte(scsiresult));
printk("(%s,%s) ",
dr<driver_max ? driverbyte_table[dr]:"invalid",
su<suggest_max ? driversuggest_table[su]:"invalid");
(dr < NUM_DRIVERBYTE_STRS ? driverbyte_table[dr] : "invalid"),
(su < NUM_SUGGEST_STRS ? driversuggest_table[su] : "invalid"));
}
#else
void scsi_print_driverbyte(int scsiresult)
{ printk("Driverbyte=0x%02x ",driver_byte(scsiresult));
{
printk("Driverbyte=0x%02x ", driver_byte(scsiresult));
}
#endif
......@@ -79,6 +79,8 @@ void scsi_remove_host(struct Scsi_Host *shost)
set_bit(SHOST_DEL, &shost->shost_state);
if (shost->transportt->host_destroy)
shost->transportt->host_destroy(shost);
class_device_unregister(&shost->shost_classdev);
if (shost->transport_classdev.class)
class_device_unregister(&shost->transport_classdev);
......@@ -133,11 +135,14 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
error = scsi_sysfs_add_host(shost);
if (error)
goto out_del_classdev;
goto out_destroy_host;
scsi_proc_host_add(shost);
return error;
out_destroy_host:
if (shost->transportt->host_destroy)
shost->transportt->host_destroy(shost);
out_del_classdev:
class_device_del(&shost->shost_classdev);
out_del_gendev:
......
......@@ -55,8 +55,8 @@
#include "scsi_logging.h"
#include "scsi_debug.h"
#define SCSI_DEBUG_VERSION "1.74"
static const char * scsi_debug_version_date = "20040829";
#define SCSI_DEBUG_VERSION "1.75"
static const char * scsi_debug_version_date = "20041023";
/* Additional Sense Code (ASC) used */
#define NO_ADDED_SENSE 0x0
......@@ -82,7 +82,7 @@ static const char * scsi_debug_version_date = "20040829";
#define DEF_EVERY_NTH 0
#define DEF_NUM_PARTS 0
#define DEF_OPTS 0
#define DEF_SCSI_LEVEL 4 /* SPC-2 */
#define DEF_SCSI_LEVEL 5 /* INQUIRY, byte2 [5->SPC-3] */
#define DEF_PTYPE 0
#define DEF_D_SENSE 0
......@@ -95,6 +95,13 @@ static const char * scsi_debug_version_date = "20040829";
* - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
* - a RECOVERED_ERROR is simulated on successful read and write
* commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
*
* When "every_nth" < 0 then after "- every_nth" commands:
* - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
* - a RECOVERED_ERROR is simulated on successful read and write
* commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
* This will continue until some other action occurs (e.g. the user
* writing a new value (other than -1 or 1) to every_nth via sysfs).
*/
/* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
......@@ -195,14 +202,12 @@ static Scsi_Host_Template sdebug_driver_template = {
.cmd_per_lun = 3,
.max_sectors = 4096,
.unchecked_isa_dma = 0,
.use_clustering = ENABLE_CLUSTERING,
.use_clustering = DISABLE_CLUSTERING,
.module = THIS_MODULE,
};
static unsigned char * fake_storep; /* ramdisk storage */
static unsigned char spare_buff[SDEBUG_SENSE_LEN];
static int num_aborts = 0;
static int num_dev_resets = 0;
static int num_bus_resets = 0;
......@@ -228,21 +233,28 @@ static const int check_condition_result =
(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
/* function declarations */
static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip);
static int resp_mode_sense(unsigned char * cmd, int target,
unsigned char * buff, int bufflen,
static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
struct sdebug_dev_info * devip);
static int resp_requests(struct scsi_cmnd * SCpnt,
struct sdebug_dev_info * devip);
static int resp_readcap(struct scsi_cmnd * SCpnt,
struct sdebug_dev_info * devip);
static int resp_mode_sense(struct scsi_cmnd * SCpnt, int target,
struct sdebug_dev_info * devip);
static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip);
static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip);
static int resp_report_luns(unsigned char * cmd, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip);
static int resp_report_luns(struct scsi_cmnd * SCpnt,
struct sdebug_dev_info * devip);
static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
int arr_len);
static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
int max_arr_len);
static void timer_intr_handler(unsigned long);
static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
int asc, int asq, int inbandLen);
int asc, int asq);
static int check_reset(struct scsi_cmnd * SCpnt,
struct sdebug_dev_info * devip);
static int schedule_resp(struct scsi_cmnd * cmnd,
......@@ -264,49 +276,20 @@ static void sdebug_max_tgts_luns(void);
static struct device pseudo_primary;
static struct bus_type pseudo_lld_bus;
static unsigned char * scatg2virt(const struct scatterlist * sclp)
{
if (NULL == sclp)
return NULL;
else if (sclp->page)
return (unsigned char *)page_address(sclp->page) +
sclp->offset;
else
return NULL;
}
static
int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
{
unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
int block, upper_blk, num, k;
unsigned char *buff;
int errsts = 0;
int target = SCpnt->device->id;
int bufflen = SCpnt->request_bufflen;
unsigned long capac;
struct sdebug_dev_info * devip = NULL;
unsigned char * sbuff;
int inj_recovered = 0;
if (done == NULL)
return 0; /* assume mid level reprocessing command */
if (SCpnt->use_sg) { /* just use first element */
struct scatterlist *sgpnt = (struct scatterlist *)
SCpnt->request_buffer;
buff = scatg2virt(&sgpnt[0]);
bufflen = sgpnt[0].length;
/* READ and WRITE process scatterlist themselves */
}
else
buff = (unsigned char *) SCpnt->request_buffer;
if (NULL == buff) {
buff = spare_buff; /* assume cmd moves no data */
bufflen = SDEBUG_SENSE_LEN;
}
if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
printk(KERN_INFO "scsi_debug: cmd ");
for (k = 0, num = SCpnt->cmd_len; k < num; ++k)
......@@ -328,9 +311,11 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0);
if ((scsi_debug_every_nth > 0) &&
(++scsi_debug_cmnd_count >= scsi_debug_every_nth)) {
scsi_debug_cmnd_count =0;
if ((scsi_debug_every_nth != 0) &&
(++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
scsi_debug_cmnd_count = 0;
if (scsi_debug_every_nth < -1)
scsi_debug_every_nth = -1;
if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
return 0; /* ignore command causing timeout */
else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
......@@ -339,23 +324,14 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
switch (*cmd) {
case INQUIRY: /* mandatory, ignore unit attention */
errsts = resp_inquiry(cmd, target, buff, bufflen, devip);
errsts = resp_inquiry(SCpnt, target, devip);
break;
case REQUEST_SENSE: /* mandatory, ignore unit attention */
if (devip) {
sbuff = devip->sense_buff;
memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ?
bufflen : SDEBUG_SENSE_LEN);
mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0, 7);
} else {
memset(buff, 0, bufflen);
buff[0] = 0x70;
}
errsts = resp_requests(SCpnt, devip);
break;
case REZERO_UNIT: /* actually this is REWIND for SSC */
case START_STOP:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case ALLOW_MEDIUM_REMOVAL:
if ((errsts = check_reset(SCpnt, devip)))
......@@ -366,40 +342,24 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
break;
case SEND_DIAGNOSTIC: /* mandatory */
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case TEST_UNIT_READY: /* mandatory */
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RESERVE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RESERVE_10:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RELEASE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RELEASE_10:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case READ_CAPACITY:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
if (bufflen > 7) {
capac = (unsigned long)sdebug_capacity - 1;
buff[0] = (capac >> 24);
buff[1] = (capac >> 16) & 0xff;
buff[2] = (capac >> 8) & 0xff;
buff[3] = capac & 0xff;
buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
buff[7] = SECT_SIZE_PER(target) & 0xff;
}
errsts = resp_readcap(SCpnt, devip);
break;
case READ_16:
case READ_12:
......@@ -432,12 +392,15 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
errsts = resp_read(SCpnt, upper_blk, block, num, devip);
if (inj_recovered && (0 == errsts)) {
mk_sense_buffer(devip, RECOVERED_ERROR,
THRESHHOLD_EXCEEDED, 0, 18);
THRESHHOLD_EXCEEDED, 0);
errsts = check_condition_result;
}
break;
case REPORT_LUNS: /* mandatory, ignore unit attention */
errsts = resp_report_luns(cmd, buff, bufflen, devip);
errsts = resp_report_luns(SCpnt, devip);
break;
case VERIFY: /* 10 byte SBC-2 command */
errsts = check_reset(SCpnt, devip);
break;
case WRITE_16:
case WRITE_12:
......@@ -470,19 +433,16 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
errsts = resp_write(SCpnt, upper_blk, block, num, devip);
if (inj_recovered && (0 == errsts)) {
mk_sense_buffer(devip, RECOVERED_ERROR,
THRESHHOLD_EXCEEDED, 0, 18);
THRESHHOLD_EXCEEDED, 0);
errsts = check_condition_result;
}
break;
case MODE_SENSE:
case MODE_SENSE_10:
if ((errsts = check_reset(SCpnt, devip)))
break;
errsts = resp_mode_sense(cmd, target, buff, bufflen, devip);
errsts = resp_mode_sense(SCpnt, target, devip);
break;
case SYNCHRONIZE_CACHE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
default:
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
......@@ -490,7 +450,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
"supported\n", *cmd);
if ((errsts = check_reset(SCpnt, devip)))
break; /* Unit attention takes precedence */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0, 18);
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
errsts = check_condition_result;
break;
}
......@@ -513,18 +473,105 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
printk(KERN_INFO "scsi_debug: Reporting Unit "
"attention: power on reset\n");
devip->reset = 0;
mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0, 18);
mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
return check_condition_result;
}
return 0;
}
#define SDEBUG_LONG_INQ_SZ 96
#define SDEBUG_MAX_INQ_ARR_SZ 128
/* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
int arr_len)
{
int k, req_len, act_len, len, active;
void * kaddr;
void * kaddr_off;
struct scatterlist * sgpnt;
if (0 == scp->request_bufflen)
return 0;
if (NULL == scp->request_buffer)
return (DID_ERROR << 16);
if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
(scp->sc_data_direction == DMA_FROM_DEVICE)))
return (DID_ERROR << 16);
if (0 == scp->use_sg) {
req_len = scp->request_bufflen;
act_len = (req_len < arr_len) ? req_len : arr_len;
memcpy(scp->request_buffer, arr, act_len);
scp->resid = req_len - act_len;
return 0;
}
sgpnt = (struct scatterlist *)scp->request_buffer;
active = 1;
for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sgpnt) {
if (active) {
kaddr = (unsigned char *)
kmap_atomic(sgpnt->page, KM_USER0);
if (NULL == kaddr)
return (DID_ERROR << 16);
kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
len = sgpnt->length;
if ((req_len + len) > arr_len) {
active = 0;
len = arr_len - req_len;
}
memcpy(kaddr_off, arr + req_len, len);
kunmap_atomic(kaddr, KM_USER0);
act_len += len;
}
req_len += sgpnt->length;
}
scp->resid = req_len - act_len;
return 0;
}
/* Returns number of bytes fetched into 'arr' or -1 if error. */
static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
int max_arr_len)
{
int k, req_len, len, fin;
void * kaddr;
void * kaddr_off;
struct scatterlist * sgpnt;
static const char * vendor_id = "Linux ";
static const char * product_id = "scsi_debug ";
static const char * product_rev = "0004";
if (0 == scp->request_bufflen)
return 0;
if (NULL == scp->request_buffer)
return -1;
if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
(scp->sc_data_direction == DMA_TO_DEVICE)))
return -1;
if (0 == scp->use_sg) {
req_len = scp->request_bufflen;
len = (req_len < max_arr_len) ? req_len : max_arr_len;
memcpy(arr, scp->request_buffer, len);
return len;
}
sgpnt = (struct scatterlist *)scp->request_buffer;
for (k = 0, req_len = 0, fin = 0; k < scp->use_sg; ++k, ++sgpnt) {
kaddr = (unsigned char *)kmap_atomic(sgpnt->page, KM_USER0);
if (NULL == kaddr)
return -1;
kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
len = sgpnt->length;
if ((req_len + len) > max_arr_len) {
len = max_arr_len - req_len;
fin = 1;
}
memcpy(arr + req_len, kaddr_off, len);
kunmap_atomic(kaddr, KM_USER0);
if (fin)
return req_len + len;
req_len += sgpnt->length;
}
return req_len;
}
static const char * inq_vendor_id = "Linux ";
static const char * inq_product_id = "scsi_debug ";
static const char * inq_product_rev = "0004";
static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
const char * dev_id_str, int dev_id_str_len)
......@@ -536,8 +583,8 @@ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
arr[0] = 0x2; /* ASCII */
arr[1] = 0x1;
arr[2] = 0x0;
memcpy(&arr[4], vendor_id, 8);
memcpy(&arr[12], product_id, 16);
memcpy(&arr[4], inq_vendor_id, 8);
memcpy(&arr[12], inq_product_id, 16);
memcpy(&arr[28], dev_id_str, dev_id_str_len);
num = 8 + 16 + dev_id_str_len;
arr[3] = num;
......@@ -558,24 +605,25 @@ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
return num + 12;
}
static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip)
#define SDEBUG_LONG_INQ_SZ 96
#define SDEBUG_MAX_INQ_ARR_SZ 128
static int resp_inquiry(struct scsi_cmnd * scp, int target,
struct sdebug_dev_info * devip)
{
unsigned char pq_pdt;
unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
int min_len = bufflen > SDEBUG_MAX_INQ_ARR_SZ ?
SDEBUG_MAX_INQ_ARR_SZ : bufflen;
unsigned char *cmd = (unsigned char *)scp->cmnd;
int alloc_len;
if (bufflen < cmd[4])
printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d "
"< alloc_length=%d\n", bufflen, (int)cmd[4]);
memset(buff, 0, bufflen);
alloc_len = (cmd[3] << 8) + cmd[4];
memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
pq_pdt = (scsi_debug_ptype & 0x1f);
arr[0] = pq_pdt;
if (0x2 & cmd[1]) { /* CMDDT bit set */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18);
0);
return check_condition_result;
} else if (0x1 & cmd[1]) { /* EVPD bit set */
int dev_id_num, len;
......@@ -600,11 +648,11 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
} else {
/* Illegal request, invalid field in cdb */
mk_sense_buffer(devip, ILLEGAL_REQUEST,
INVALID_FIELD_IN_CDB, 0, 18);
INVALID_FIELD_IN_CDB, 0);
return check_condition_result;
}
memcpy(buff, arr, min_len);
return 0;
return fill_from_dev_buffer(scp, arr,
min(alloc_len, SDEBUG_MAX_INQ_ARR_SZ));
}
/* drops through here for a standard inquiry */
arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
......@@ -612,20 +660,67 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
arr[3] = 2; /* response_data_format==2 */
arr[4] = SDEBUG_LONG_INQ_SZ - 5;
arr[6] = 0x1; /* claim: ADDR16 */
/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
memcpy(&arr[8], vendor_id, 8);
memcpy(&arr[16], product_id, 16);
memcpy(&arr[32], product_rev, 4);
memcpy(&arr[8], inq_vendor_id, 8);
memcpy(&arr[16], inq_product_id, 16);
memcpy(&arr[32], inq_product_rev, 4);
/* version descriptors (2 bytes each) follow */
arr[58] = 0x0; arr[59] = 0x40; /* SAM-2 */
arr[60] = 0x2; arr[61] = 0x60; /* SPC-2 */
arr[60] = 0x3; arr[61] = 0x0; /* SPC-3 */
if (scsi_debug_ptype == 0) {
arr[62] = 0x1; arr[63] = 0x80; /* SBC */
} else if (scsi_debug_ptype == 1) {
arr[62] = 0x2; arr[63] = 0x00; /* SSC */
}
memcpy(buff, arr, min_len);
return 0;
return fill_from_dev_buffer(scp, arr,
min(alloc_len, SDEBUG_LONG_INQ_SZ));
}
static int resp_requests(struct scsi_cmnd * scp,
struct sdebug_dev_info * devip)
{
unsigned char * sbuff;
unsigned char *cmd = (unsigned char *)scp->cmnd;
unsigned char arr[SDEBUG_SENSE_LEN];
int len = 18;
memset(arr, 0, SDEBUG_SENSE_LEN);
if (devip->reset == 1)
mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0);
sbuff = devip->sense_buff;
if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
/* DESC bit set and sense_buff in fixed format */
arr[0] = 0x72;
arr[1] = sbuff[2]; /* sense key */
arr[2] = sbuff[12]; /* asc */
arr[3] = sbuff[13]; /* ascq */
len = 8;
} else
memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0);
return fill_from_dev_buffer(scp, arr, len);
}
#define SDEBUG_READCAP_ARR_SZ 8
static int resp_readcap(struct scsi_cmnd * scp,
struct sdebug_dev_info * devip)
{
unsigned char arr[SDEBUG_READCAP_ARR_SZ];
unsigned long capac;
int errsts;
if ((errsts = check_reset(scp, devip)))
return errsts;
memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
capac = (unsigned long)sdebug_capacity - 1;
arr[0] = (capac >> 24);
arr[1] = (capac >> 16) & 0xff;
arr[2] = (capac >> 8) & 0xff;
arr[3] = capac & 0xff;
arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
arr[7] = SECT_SIZE_PER(target) & 0xff;
return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
}
/* <<Following mode page info copied from ST318451LW>> */
......@@ -706,34 +801,29 @@ static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
#define SDEBUG_MAX_MSENSE_SZ 256
static int resp_mode_sense(unsigned char * cmd, int target,
unsigned char * buff, int bufflen,
static int resp_mode_sense(struct scsi_cmnd * scp, int target,
struct sdebug_dev_info * devip)
{
unsigned char dbd;
int pcontrol, pcode, subpcode;
unsigned char dev_spec;
int alloc_len, msense_6, offset, len;
int alloc_len, msense_6, offset, len, errsts;
unsigned char * ap;
unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
int min_len = bufflen > SDEBUG_MAX_MSENSE_SZ ?
SDEBUG_MAX_MSENSE_SZ : bufflen;
unsigned char *cmd = (unsigned char *)scp->cmnd;
SCSI_LOG_LLQUEUE(3, printk("Mode sense ...(%p %d)\n", buff, bufflen));
if ((errsts = check_reset(scp, devip)))
return errsts;
dbd = cmd[1] & 0x8;
pcontrol = (cmd[2] & 0xc0) >> 6;
pcode = cmd[2] & 0x3f;
subpcode = cmd[3];
msense_6 = (MODE_SENSE == cmd[0]);
alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
if (bufflen < alloc_len)
printk(KERN_INFO "scsi_debug: mode_sense: bufflen=%d "
"< alloc_length=%d\n", bufflen, alloc_len);
memset(buff, 0, bufflen);
memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
if (0x3 == pcontrol) { /* Saving values not supported */
mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
0, 18);
0);
return check_condition_result;
}
dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
......@@ -748,7 +838,7 @@ static int resp_mode_sense(unsigned char * cmd, int target,
if (0 != subpcode) { /* TODO: Control Extension page */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18);
0);
return check_condition_result;
}
switch (pcode) {
......@@ -787,137 +877,95 @@ static int resp_mode_sense(unsigned char * cmd, int target,
break;
default:
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18);
0);
return check_condition_result;
}
if (msense_6)
arr[0] = offset - 1;
else {
offset -= 2;
arr[0] = (offset >> 8) & 0xff;
arr[1] = offset & 0xff;
arr[0] = ((offset - 2) >> 8) & 0xff;
arr[1] = (offset - 2) & 0xff;
}
memcpy(buff, arr, min_len);
return 0;
return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
}
static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip)
{
unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
int nbytes, sgcount;
struct scatterlist *sgpnt = NULL;
int bufflen = SCpnt->request_bufflen;
unsigned long iflags;
int ret;
if (upper_blk || (block + num > sdebug_capacity)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
0, 18);
0);
return check_condition_result;
}
if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
(block <= OPT_MEDIUM_ERR_ADDR) &&
((block + num) > OPT_MEDIUM_ERR_ADDR)) {
mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
0, 18);
0);
/* claim unrecoverable read error */
return check_condition_result;
}
read_lock_irqsave(&atomic_rw, iflags);
sgcount = 0;
nbytes = bufflen;
/* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n",
block, bufflen); */
if (SCpnt->use_sg) {
sgcount = 0;
sgpnt = (struct scatterlist *) buff;
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
do {
memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen);
nbytes -= bufflen;
if (SCpnt->use_sg) {
block += bufflen >> POW2_SECT_SIZE;
sgcount++;
if (nbytes) {
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
} else if (nbytes > 0)
printk(KERN_WARNING "scsi_debug:resp_read: unexpected "
"nbytes=%d\n", nbytes);
} while (nbytes);
ret = fill_from_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
num * SECT_SIZE);
read_unlock_irqrestore(&atomic_rw, iflags);
return 0;
return ret;
}
static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip)
{
unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
int nbytes, sgcount;
struct scatterlist *sgpnt = NULL;
int bufflen = SCpnt->request_bufflen;
unsigned long iflags;
int res;
if (upper_blk || (block + num > sdebug_capacity)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
0, 18);
0);
return check_condition_result;
}
write_lock_irqsave(&atomic_rw, iflags);
sgcount = 0;
nbytes = bufflen;
if (SCpnt->use_sg) {
sgcount = 0;
sgpnt = (struct scatterlist *) buff;
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
do {
memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen);
nbytes -= bufflen;
if (SCpnt->use_sg) {
block += bufflen >> POW2_SECT_SIZE;
sgcount++;
if (nbytes) {
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
} else if (nbytes > 0)
printk(KERN_WARNING "scsi_debug:resp_write: "
"unexpected nbytes=%d\n", nbytes);
} while (nbytes);
res = fetch_to_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
num * SECT_SIZE);
write_unlock_irqrestore(&atomic_rw, iflags);
if (-1 == res)
return (DID_ERROR << 16);
else if ((res < (num * SECT_SIZE)) &&
(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
printk(KERN_INFO "scsi_debug: write: cdb indicated=%d, "
" IO sent=%d bytes\n", num * SECT_SIZE, res);
return 0;
}
static int resp_report_luns(unsigned char * cmd, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip)
#define SDEBUG_RLUN_ARR_SZ 128
static int resp_report_luns(struct scsi_cmnd * scp,
struct sdebug_dev_info * devip)
{
unsigned int alloc_len;
int lun_cnt, i, upper;
unsigned char *cmd = (unsigned char *)scp->cmnd;
int select_report = (int)cmd[2];
struct scsi_lun *one_lun;
unsigned char arr[SDEBUG_RLUN_ARR_SZ];
alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
if ((alloc_len < 16) || (select_report > 2)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18);
0);
return check_condition_result;
}
if (bufflen > 8) { /* can produce response with up to 16k luns
(lun 0 to lun 16383) */
memset(buff, 0, bufflen);
/* can produce response with up to 16k luns (lun 0 to lun 16383) */
memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
lun_cnt = scsi_debug_max_luns;
buff[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff;
buff[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff;
lun_cnt = min((int)((bufflen - 8) / sizeof(struct scsi_lun)),
lun_cnt);
one_lun = (struct scsi_lun *) &buff[8];
arr[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff;
arr[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff;
lun_cnt = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
sizeof(struct scsi_lun)), lun_cnt);
one_lun = (struct scsi_lun *) &arr[8];
for (i = 0; i < lun_cnt; i++) {
upper = (i >> 8) & 0x3f;
if (upper)
......@@ -925,8 +973,8 @@ static int resp_report_luns(unsigned char * cmd, unsigned char * buff,
(upper | (SAM2_LUN_ADDRESS_METHOD << 6));
one_lun[i].scsi_lun[1] = i & 0xff;
}
}
return 0;
return fill_from_dev_buffer(scp, arr,
min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
}
/* When timer goes off this function is called. */
......@@ -1041,14 +1089,19 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
open_devip->reset = 1;
open_devip->used = 1;
memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
if (scsi_debug_dsense)
open_devip->sense_buff[0] = 0x72;
else {
open_devip->sense_buff[0] = 0x70;
open_devip->sense_buff[7] = 0xa;
}
return open_devip;
}
return NULL;
}
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
int asc, int asq, int inbandLen)
int asc, int asq)
{
unsigned char * sbuff;
......@@ -1060,11 +1113,9 @@ static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
sbuff[2] = asc;
sbuff[3] = asq;
} else {
if (inbandLen > SDEBUG_SENSE_LEN)
inbandLen = SDEBUG_SENSE_LEN;
sbuff[0] = 0x70; /* fixed, current */
sbuff[2] = key;
sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
sbuff[7] = 0xa; /* implies 18 byte sense buffer */
sbuff[12] = asc;
sbuff[13] = asq;
}
......@@ -1355,7 +1406,7 @@ MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
MODULE_PARM_DESC(num_tgts, "number of SCSI targets per host to simulate");
MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->...");
MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=4[SPC-2])");
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
static char sdebug_info[256];
......@@ -1391,7 +1442,7 @@ static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **sta
if (1 != sscanf(arr, "%d", &pos))
return -EINVAL;
scsi_debug_opts = pos;
if (scsi_debug_every_nth > 0)
if (scsi_debug_every_nth != 0)
scsi_debug_cmnd_count = 0;
return length;
}
......@@ -1547,7 +1598,7 @@ static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
{
int nth;
if ((count > 0) && (1 == sscanf(buf, "%d", &nth)) && (nth >= 0)) {
if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
scsi_debug_every_nth = nth;
scsi_debug_cmnd_count = 0;
return count;
......
......@@ -268,16 +268,42 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
*
* Return value:
* SUCCESS or FAILED or NEEDS_RETRY
*
* Notes:
* When a deferred error is detected the current command has
* not been executed and needs retrying.
**/
static int scsi_check_sense(struct scsi_cmnd *scmd)
{
if (!SCSI_SENSE_VALID(scmd))
return FAILED;
struct scsi_sense_hdr sshdr;
if (! scsi_command_normalize_sense(scmd, &sshdr))
return FAILED; /* no valid sense data */
if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY;
/*
* Previous logic looked for FILEMARK, EOM or ILI which are
* mainly associated with tapes and returned SUCCESS.
*/
if (sshdr.response_code == 0x70) {
/* fixed format */
if (scmd->sense_buffer[2] & 0xe0)
return SUCCESS;
} else {
/*
* descriptor format: look for "stream commands sense data
* descriptor" (see SSC-3). Assume single sense data
* descriptor. Ignore ILI from SBC-2 READ LONG and WRITE LONG.
*/
if ((sshdr.additional_length > 3) &&
(scmd->sense_buffer[8] == 0x4) &&
(scmd->sense_buffer[11] & 0xe0))
return SUCCESS;
}
switch (scmd->sense_buffer[2] & 0xf) {
switch (sshdr.sense_key) {
case NO_SENSE:
return SUCCESS;
case RECOVERED_ERROR:
......@@ -301,19 +327,15 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
* if the device is in the process of becoming ready, we
* should retry.
*/
if ((scmd->sense_buffer[12] == 0x04) &&
(scmd->sense_buffer[13] == 0x01)) {
if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
return NEEDS_RETRY;
}
/*
* if the device is not started, we need to wake
* the error handler to start the motor
*/
if (scmd->device->allow_restart &&
(scmd->sense_buffer[12] == 0x04) &&
(scmd->sense_buffer[13] == 0x02)) {
(sshdr.asc == 0x04) && (sshdr.ascq == 0x02))
return FAILED;
}
return SUCCESS;
/* these three are not supported */
......@@ -1358,7 +1380,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
return SUCCESS;
case RESERVATION_CONFLICT:
printk("scsi%d (%d,%d,%d) : reservation conflict\n",
printk(KERN_INFO "scsi: reservation conflict: host"
" %d channel %d id %d lun %d\n",
scmd->device->host->host_no, scmd->device->channel,
scmd->device->id, scmd->device->lun);
return SUCCESS; /* causes immediate i/o error */
......
......@@ -21,6 +21,7 @@
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi_request.h>
#include <scsi/sg.h>
#include <scsi/scsi_dbg.h>
#include "scsi_logging.h"
......@@ -94,12 +95,13 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
{
struct scsi_request *sreq;
int result;
struct scsi_sense_hdr sshdr;
SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd));
sreq = scsi_allocate_request(sdev, GFP_KERNEL);
if (!sreq) {
printk("SCSI internal ioctl failed, no memory\n");
printk(KERN_WARNING "SCSI internal ioctl failed, no memory\n");
return -ENOMEM;
}
......@@ -108,17 +110,21 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", sreq->sr_result));
if (driver_byte(sreq->sr_result)) {
switch (sreq->sr_sense_buffer[2] & 0xf) {
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
(scsi_request_normalize_sense(sreq, &sshdr))) {
switch (sshdr.sense_key) {
case ILLEGAL_REQUEST:
if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
sdev->lockable = 0;
else
printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n");
printk(KERN_INFO "ioctl_internal_command: "
"ILLEGAL REQUEST asc=0x%x ascq=0x%x\n",
sshdr.asc, sshdr.ascq);
break;
case NOT_READY: /* This happens if there is no disc in drive */
if (sdev->removable && (cmd[0] != TEST_UNIT_READY)) {
printk(KERN_INFO "Device not ready. Make sure there is a disc in the drive.\n");
printk(KERN_INFO "Device not ready. Make sure"
" there is a disc in the drive.\n");
break;
}
case UNIT_ATTENTION:
......@@ -128,16 +134,15 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
break;
}
default: /* Fall through for non-removable media */
printk("SCSI error: host %d id %d lun %d return code = %x\n",
printk(KERN_INFO "ioctl_internal_command: <%d %d %d "
"%d> return code = %x\n",
sdev->host->host_no,
sdev->channel,
sdev->id,
sdev->lun,
sreq->sr_result);
printk("\tSense class %x, sense error %x, extended sense %x\n",
sense_class(sreq->sr_sense_buffer[0]),
sense_error(sreq->sr_sense_buffer[0]),
sreq->sr_sense_buffer[2] & 0xf);
scsi_print_req_sense(" ", sreq);
break;
}
}
......@@ -401,7 +406,8 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
case SCSI_IOCTL_SYNC:
case SCSI_IOCTL_START_UNIT:
case SCSI_IOCTL_STOP_UNIT:
printk(KERN_WARNING "program %s is using a deprecated SCSI ioctl, please convert it to SG_IO\n", current->comm);
printk(KERN_WARNING "program %s is using a deprecated SCSI "
"ioctl, please convert it to SG_IO\n", current->comm);
break;
default:
break;
......
......@@ -718,7 +718,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
clear_errors = 0;
if (scsi_command_normalize_sense(cmd, &sshdr)) {
/*
* SG_IO wants to know about deferred errors
* SG_IO wants current and deferred errors
*/
int len = 8 + cmd->sense_buffer[7];
......@@ -844,9 +844,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
cmd = scsi_end_request(cmd, 0, this_count, 1);
return;
case VOLUME_OVERFLOW:
printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ",
cmd->device->host->host_no, (int) cmd->device->channel,
(int) cmd->device->id, (int) cmd->device->lun);
printk(KERN_INFO "Volume overflow <%d %d %d %d> CDB: ",
cmd->device->host->host_no,
(int)cmd->device->channel,
(int)cmd->device->id, (int)cmd->device->lun);
__scsi_print_command(cmd->data_cmnd);
scsi_print_sense("", cmd);
cmd = scsi_end_request(cmd, 0, block_bytes, 1);
......@@ -865,8 +866,8 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
return;
}
if (result) {
printk("SCSI error : <%d %d %d %d> return code = 0x%x\n",
cmd->device->host->host_no,
printk(KERN_INFO "SCSI error : <%d %d %d %d> return code "
"= 0x%x\n", cmd->device->host->host_no,
cmd->device->channel,
cmd->device->id,
cmd->device->lun, result);
......@@ -1604,13 +1605,16 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries)
sreq->sr_data_direction = DMA_NONE;
scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries);
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
((sreq->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION ||
(sreq->sr_sense_buffer[2] & 0x0f) == NOT_READY) &&
sdev->removable) {
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && sdev->removable) {
struct scsi_sense_hdr sshdr;
if ((scsi_request_normalize_sense(sreq, &sshdr)) &&
((sshdr.sense_key == UNIT_ATTENTION) ||
(sshdr.sense_key == NOT_READY))) {
sdev->changed = 1;
sreq->sr_result = 0;
}
}
result = sreq->sr_result;
scsi_release_request(sreq);
return result;
......@@ -1668,6 +1672,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
case SDEV_CREATED:
case SDEV_RUNNING:
case SDEV_QUIESCE:
case SDEV_BLOCK:
break;
default:
goto illegal;
......
......@@ -39,6 +39,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_request.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_eh.h>
#include "scsi_priv.h"
#include "scsi_logging.h"
......@@ -253,6 +254,11 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
sdev->request_queue->queuedata = sdev;
scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
if (shost->transportt->device_setup) {
if (shost->transportt->device_setup(sdev))
goto out_free_queue;
}
if (shost->hostt->slave_alloc) {
ret = shost->hostt->slave_alloc(sdev);
if (ret) {
......@@ -262,15 +268,10 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
*/
if (ret == -ENXIO)
display_failure_msg = 0;
goto out_free_queue;
goto out_device_destroy;
}
}
if (shost->transportt->device_setup) {
if (shost->transportt->device_setup(sdev))
goto out_cleanup_slave;
}
if (scsi_sysfs_device_initialize(sdev) != 0)
goto out_cleanup_slave;
......@@ -290,6 +291,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
out_cleanup_slave:
if (shost->hostt->slave_destroy)
shost->hostt->slave_destroy(sdev);
out_device_destroy:
if (shost->transportt->device_destroy)
shost->transportt->device_destroy(sdev);
out_free_queue:
scsi_free_queue(sdev->request_queue);
out_free_dev:
......@@ -322,6 +326,7 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
int first_inquiry_len, try_inquiry_len, next_inquiry_len;
int response_len = 0;
int pass, count;
struct scsi_sense_hdr sshdr;
*bflags = 0;
......@@ -357,18 +362,21 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
sreq->sr_result));
if (sreq->sr_result) {
/* not-ready to ready transition or power-on - good */
/* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */
/* Supposedly, but many buggy devices do so anyway. */
/*
* not-ready to ready transition [asc/ascq=0x28/0x0]
* or power-on, reset [asc/ascq=0x29/0x0], continue.
* INQUIRY should not yield UNIT_ATTENTION
* but many buggy devices do so anyway.
*/
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
(sreq->sr_sense_buffer[2] & 0xf) ==
UNIT_ATTENTION &&
(sreq->sr_sense_buffer[12] == 0x28 ||
sreq->sr_sense_buffer[12] == 0x29) &&
sreq->sr_sense_buffer[13] == 0)
scsi_request_normalize_sense(sreq, &sshdr)) {
if ((sshdr.sense_key == UNIT_ATTENTION) &&
((sshdr.asc == 0x28) ||
(sshdr.asc == 0x29)) &&
(sshdr.ascq == 0))
continue;
}
}
break;
}
......@@ -741,6 +749,8 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
} else {
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
if (sdev->host->transportt->device_destroy)
sdev->host->transportt->device_destroy(sdev);
put_device(&sdev->sdev_gendev);
}
out:
......@@ -893,6 +903,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
struct scsi_lun *lunp, *lun_data;
struct scsi_request *sreq;
u8 *data;
struct scsi_sense_hdr sshdr;
/*
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
......@@ -970,9 +981,12 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
" %s (try %d) result 0x%x\n", sreq->sr_result
? "failed" : "successful", retries,
sreq->sr_result));
if (sreq->sr_result == 0 ||
sreq->sr_sense_buffer[2] != UNIT_ATTENTION)
if (sreq->sr_result == 0)
break;
else if (scsi_request_normalize_sense(sreq, &sshdr)) {
if (sshdr.sense_key != UNIT_ATTENTION)
break;
}
}
if (sreq->sr_result) {
......@@ -1299,5 +1313,7 @@ void scsi_free_host_dev(struct scsi_device *sdev)
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
if (sdev->host->transportt->device_destroy)
sdev->host->transportt->device_destroy(sdev);
put_device(&sdev->sdev_gendev);
}
......@@ -169,7 +169,10 @@ void scsi_device_dev_release(struct device *dev)
if (delete) {
struct scsi_target *starget = to_scsi_target(parent);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
if (!starget->create) {
if (shost->transportt->target_destroy)
shost->transportt->target_destroy(starget);
device_del(parent);
if (starget->transport_classdev.class)
class_device_unregister(&starget->transport_classdev);
......@@ -601,6 +604,8 @@ void scsi_remove_device(struct scsi_device *sdev)
scsi_device_set_state(sdev, SDEV_DEL);
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
if (sdev->host->transportt->device_destroy)
sdev->host->transportt->device_destroy(sdev);
put_device(&sdev->sdev_gendev);
out:
......
......@@ -29,6 +29,8 @@
static void transport_class_release(struct class_device *class_dev);
static void host_class_release(struct class_device *class_dev);
static void fc_timeout_blocked_host(void *data);
static void fc_timeout_blocked_tgt(void *data);
#define FC_STARGET_NUM_ATTRS 4 /* increase this if you add attributes */
#define FC_STARGET_OTHER_ATTRS 0 /* increase this if you add "always on"
......@@ -87,10 +89,18 @@ static int fc_setup_starget_transport_attrs(struct scsi_target *starget)
fc_starget_port_name(starget) = -1;
fc_starget_port_id(starget) = -1;
fc_starget_dev_loss_tmo(starget) = -1;
init_timer(&fc_starget_dev_loss_timer(starget));
INIT_WORK(&fc_starget_dev_loss_work(starget),
fc_timeout_blocked_tgt, starget);
return 0;
}
static void fc_destroy_starget(struct scsi_target *starget)
{
/* Stop the target timer */
if (cancel_delayed_work(&fc_starget_dev_loss_work(starget)))
flush_scheduled_work();
}
static int fc_setup_host_transport_attrs(struct Scsi_Host *shost)
{
/*
......@@ -99,10 +109,18 @@ static int fc_setup_host_transport_attrs(struct Scsi_Host *shost)
* all transport attributes to valid values per host.
*/
fc_host_link_down_tmo(shost) = -1;
init_timer(&fc_host_link_down_timer(shost));
INIT_WORK(&fc_host_link_down_work(shost),
fc_timeout_blocked_host, shost);
return 0;
}
static void fc_destroy_host(struct Scsi_Host *shost)
{
/* Stop the host timer */
if (cancel_delayed_work(&fc_host_link_down_work(shost)))
flush_scheduled_work();
}
static void transport_class_release(struct class_device *class_dev)
{
struct scsi_target *starget = transport_class_to_starget(class_dev);
......@@ -277,11 +295,13 @@ fc_attach_transport(struct fc_function_template *ft)
i->t.target_attrs = &i->starget_attrs[0];
i->t.target_class = &fc_transport_class;
i->t.target_setup = &fc_setup_starget_transport_attrs;
i->t.target_destroy = &fc_destroy_starget;
i->t.target_size = sizeof(struct fc_starget_attrs);
i->t.host_attrs = &i->host_attrs[0];
i->t.host_class = &fc_host_class;
i->t.host_setup = &fc_setup_host_transport_attrs;
i->t.host_destroy = &fc_destroy_host;
i->t.host_size = sizeof(struct fc_host_attrs);
i->f = ft;
......@@ -353,7 +373,7 @@ static int fc_device_unblock(struct device *dev, void *data)
* that fail to recover in the alloted time.
* @data: scsi target that failed to reappear in the alloted time.
**/
static void fc_timeout_blocked_tgt(unsigned long data)
static void fc_timeout_blocked_tgt(void *data)
{
struct scsi_target *starget = (struct scsi_target *)data;
......@@ -388,7 +408,7 @@ int
fc_target_block(struct scsi_target *starget)
{
int timeout = fc_starget_dev_loss_tmo(starget);
struct timer_list *timer = &fc_starget_dev_loss_timer(starget);
struct work_struct *work = &fc_starget_dev_loss_work(starget);
if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
return -EINVAL;
......@@ -396,10 +416,7 @@ fc_target_block(struct scsi_target *starget)
device_for_each_child(&starget->dev, NULL, fc_device_block);
/* The scsi lld blocks this target for the timeout period only. */
timer->data = (unsigned long)starget;
timer->expires = jiffies + timeout * HZ;
timer->function = fc_timeout_blocked_tgt;
add_timer(timer);
schedule_delayed_work(work, timeout * HZ);
return 0;
}
......@@ -424,7 +441,8 @@ fc_target_unblock(struct scsi_target *starget)
* failure as the state machine state change will validate the
* transaction.
*/
del_timer_sync(&fc_starget_dev_loss_timer(starget));
if (cancel_delayed_work(&fc_starget_dev_loss_work(starget)))
flush_scheduled_work();
device_for_each_child(&starget->dev, NULL, fc_device_unblock);
}
......@@ -436,7 +454,7 @@ EXPORT_SYMBOL(fc_target_unblock);
* @data: scsi host that failed to recover its devices in the alloted
* time.
**/
static void fc_timeout_blocked_host(unsigned long data)
static void fc_timeout_blocked_host(void *data)
{
struct Scsi_Host *shost = (struct Scsi_Host *)data;
struct scsi_device *sdev;
......@@ -475,7 +493,7 @@ fc_host_block(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
int timeout = fc_host_link_down_tmo(shost);
struct timer_list *timer = &fc_host_link_down_timer(shost);
struct work_struct *work = &fc_host_link_down_work(shost);
if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
return -EINVAL;
......@@ -484,11 +502,7 @@ fc_host_block(struct Scsi_Host *shost)
scsi_internal_device_block(sdev);
}
/* The scsi lld blocks this host for the timeout period only. */
timer->data = (unsigned long)shost;
timer->expires = jiffies + timeout * HZ;
timer->function = fc_timeout_blocked_host;
add_timer(timer);
schedule_delayed_work(work, timeout * HZ);
return 0;
}
......@@ -516,7 +530,9 @@ fc_host_unblock(struct Scsi_Host *shost)
* failure as the state machine state change will validate the
* transaction.
*/
del_timer_sync(&fc_host_link_down_timer(shost));
if (cancel_delayed_work(&fc_host_link_down_work(shost)))
flush_scheduled_work();
shost_for_each_device(sdev, shost) {
scsi_internal_device_unblock(sdev);
}
......
/*
* iSCSI transport class definitions
*
* Copyright (C) IBM Corporation, 2004
* Copyright (C) Mike Christie, 2004
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_iscsi.h>
#define ISCSI_SESSION_ATTRS 20
#define ISCSI_HOST_ATTRS 2
struct iscsi_internal {
struct scsi_transport_template t;
struct iscsi_function_template *fnt;
/*
* We do not have any private or other attrs.
*/
struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
};
#define to_iscsi_internal(tmpl) container_of(tmpl, struct iscsi_internal, t)
static void iscsi_transport_class_release(struct class_device *class_dev)
{
struct scsi_target *starget = transport_class_to_starget(class_dev);
put_device(&starget->dev);
}
struct class iscsi_transport_class = {
.name = "iscsi_transport_class",
.release = iscsi_transport_class_release,
};
static void iscsi_host_class_release(struct class_device *class_dev)
{
struct Scsi_Host *shost = transport_class_to_shost(class_dev);
put_device(&shost->shost_gendev);
}
struct class iscsi_host_class = {
.name = "iscsi_host",
.release = iscsi_host_class_release,
};
/*
* iSCSI target and session attrs
*/
#define iscsi_session_show_fn(field, format) \
\
static ssize_t \
show_session_##field(struct class_device *cdev, char *buf) \
{ \
struct scsi_target *starget = transport_class_to_starget(cdev); \
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
\
if (i->fnt->get_##field) \
i->fnt->get_##field(starget); \
return snprintf(buf, 20, format"\n", iscsi_##field(starget)); \
}
#define iscsi_session_rd_attr(field, format) \
iscsi_session_show_fn(field, format) \
static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_##field, NULL);
iscsi_session_rd_attr(tpgt, "%hu");
iscsi_session_rd_attr(tsih, "%2x");
iscsi_session_rd_attr(max_recv_data_segment_len, "%u");
iscsi_session_rd_attr(max_burst_len, "%u");
iscsi_session_rd_attr(first_burst_len, "%u");
iscsi_session_rd_attr(def_time2wait, "%hu");
iscsi_session_rd_attr(def_time2retain, "%hu");
iscsi_session_rd_attr(max_outstanding_r2t, "%hu");
iscsi_session_rd_attr(erl, "%d");
#define iscsi_session_show_bool_fn(field) \
\
static ssize_t \
show_session_bool_##field(struct class_device *cdev, char *buf) \
{ \
struct scsi_target *starget = transport_class_to_starget(cdev); \
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
\
if (i->fnt->get_##field) \
i->fnt->get_##field(starget); \
\
if (iscsi_##field(starget)) \
return sprintf(buf, "Yes\n"); \
return sprintf(buf, "No\n"); \
}
#define iscsi_session_rd_bool_attr(field) \
iscsi_session_show_bool_fn(field) \
static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_bool_##field, NULL);
iscsi_session_rd_bool_attr(initial_r2t);
iscsi_session_rd_bool_attr(immediate_data);
iscsi_session_rd_bool_attr(data_pdu_in_order);
iscsi_session_rd_bool_attr(data_sequence_in_order);
#define iscsi_session_show_digest_fn(field) \
\
static ssize_t \
show_##field(struct class_device *cdev, char *buf) \
{ \
struct scsi_target *starget = transport_class_to_starget(cdev); \
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
\
if (i->fnt->get_##field) \
i->fnt->get_##field(starget); \
\
if (iscsi_##field(starget)) \
return sprintf(buf, "CRC32C\n"); \
return sprintf(buf, "None\n"); \
}
#define iscsi_session_rd_digest_attr(field) \
iscsi_session_show_digest_fn(field) \
static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
iscsi_session_rd_digest_attr(header_digest);
iscsi_session_rd_digest_attr(data_digest);
static ssize_t
show_port(struct class_device *cdev, char *buf)
{
struct scsi_target *starget = transport_class_to_starget(cdev);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
if (i->fnt->get_port)
i->fnt->get_port(starget);
return snprintf(buf, 20, "%hu\n", ntohs(iscsi_port(starget)));
}
static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL);
static ssize_t
show_ip_address(struct class_device *cdev, char *buf)
{
struct scsi_target *starget = transport_class_to_starget(cdev);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
if (i->fnt->get_ip_address)
i->fnt->get_ip_address(starget);
if (iscsi_addr_type(starget) == AF_INET)
return sprintf(buf, "%u.%u.%u.%u\n",
NIPQUAD(iscsi_sin_addr(starget)));
else if(iscsi_addr_type(starget) == AF_INET6)
return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(iscsi_sin6_addr(starget)));
return -EINVAL;
}
static CLASS_DEVICE_ATTR(ip_address, S_IRUGO, show_ip_address, NULL);
static ssize_t
show_isid(struct class_device *cdev, char *buf)
{
struct scsi_target *starget = transport_class_to_starget(cdev);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
if (i->fnt->get_isid)
i->fnt->get_isid(starget);
return sprintf(buf, "%02x%02x%02x%02x%02x%02x\n",
iscsi_isid(starget)[0], iscsi_isid(starget)[1],
iscsi_isid(starget)[2], iscsi_isid(starget)[3],
iscsi_isid(starget)[4], iscsi_isid(starget)[5]);
}
static CLASS_DEVICE_ATTR(isid, S_IRUGO, show_isid, NULL);
/*
* This is used for iSCSI names. Normally, we follow
* the transport class convention of having the lld
* set the field, but in these cases the value is
* too large.
*/
#define iscsi_session_show_str_fn(field) \
\
static ssize_t \
show_session_str_##field(struct class_device *cdev, char *buf) \
{ \
ssize_t ret = 0; \
struct scsi_target *starget = transport_class_to_starget(cdev); \
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
\
if (i->fnt->get_##field) \
ret = i->fnt->get_##field(starget, buf, PAGE_SIZE); \
return ret; \
}
#define iscsi_session_rd_str_attr(field) \
iscsi_session_show_str_fn(field) \
static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_str_##field, NULL);
iscsi_session_rd_str_attr(target_name);
iscsi_session_rd_str_attr(target_alias);
/*
* iSCSI host attrs
*/
/*
* Again, this is used for iSCSI names. Normally, we follow
* the transport class convention of having the lld set
* the field, but in these cases the value is too large.
*/
#define iscsi_host_show_str_fn(field) \
\
static ssize_t \
show_host_str_##field(struct class_device *cdev, char *buf) \
{ \
int ret = 0; \
struct Scsi_Host *shost = transport_class_to_shost(cdev); \
struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
\
if (i->fnt->get_##field) \
ret = i->fnt->get_##field(shost, buf, PAGE_SIZE); \
return ret; \
}
#define iscsi_host_rd_str_attr(field) \
iscsi_host_show_str_fn(field) \
static CLASS_DEVICE_ATTR(field, S_IRUGO, show_host_str_##field, NULL);
iscsi_host_rd_str_attr(initiator_name);
iscsi_host_rd_str_attr(initiator_alias);
#define SETUP_SESSION_RD_ATTR(field) \
if (i->fnt->show_##field) { \
i->session_attrs[count] = &class_device_attr_##field; \
count++; \
}
#define SETUP_HOST_RD_ATTR(field) \
if (i->fnt->show_##field) { \
i->host_attrs[count] = &class_device_attr_##field; \
count++; \
}
struct scsi_transport_template *
iscsi_attach_transport(struct iscsi_function_template *fnt)
{
struct iscsi_internal *i = kmalloc(sizeof(struct iscsi_internal),
GFP_KERNEL);
int count = 0;
if (unlikely(!i))
return NULL;
memset(i, 0, sizeof(struct iscsi_internal));
i->fnt = fnt;
i->t.target_attrs = &i->session_attrs[0];
i->t.target_class = &iscsi_transport_class;
i->t.target_setup = NULL;
i->t.target_size = sizeof(struct iscsi_class_session);
SETUP_SESSION_RD_ATTR(tsih);
SETUP_SESSION_RD_ATTR(isid);
SETUP_SESSION_RD_ATTR(header_digest);
SETUP_SESSION_RD_ATTR(data_digest);
SETUP_SESSION_RD_ATTR(target_name);
SETUP_SESSION_RD_ATTR(target_alias);
SETUP_SESSION_RD_ATTR(port);
SETUP_SESSION_RD_ATTR(tpgt);
SETUP_SESSION_RD_ATTR(ip_address);
SETUP_SESSION_RD_ATTR(initial_r2t);
SETUP_SESSION_RD_ATTR(immediate_data);
SETUP_SESSION_RD_ATTR(max_recv_data_segment_len);
SETUP_SESSION_RD_ATTR(max_burst_len);
SETUP_SESSION_RD_ATTR(first_burst_len);
SETUP_SESSION_RD_ATTR(def_time2wait);
SETUP_SESSION_RD_ATTR(def_time2retain);
SETUP_SESSION_RD_ATTR(max_outstanding_r2t);
SETUP_SESSION_RD_ATTR(data_pdu_in_order);
SETUP_SESSION_RD_ATTR(data_sequence_in_order);
SETUP_SESSION_RD_ATTR(erl);
BUG_ON(count > ISCSI_SESSION_ATTRS);
i->session_attrs[count] = NULL;
i->t.host_attrs = &i->host_attrs[0];
i->t.host_class = &iscsi_host_class;
i->t.host_setup = NULL;
i->t.host_size = 0;
count = 0;
SETUP_HOST_RD_ATTR(initiator_name);
SETUP_HOST_RD_ATTR(initiator_alias);
BUG_ON(count > ISCSI_HOST_ATTRS);
i->host_attrs[count] = NULL;
return &i->t;
}
EXPORT_SYMBOL(iscsi_attach_transport);
void iscsi_release_transport(struct scsi_transport_template *t)
{
struct iscsi_internal *i = to_iscsi_internal(t);
kfree(i);
}
EXPORT_SYMBOL(iscsi_release_transport);
static __init int iscsi_transport_init(void)
{
int err = class_register(&iscsi_transport_class);
if (err)
return err;
return class_register(&iscsi_host_class);
}
static void __exit iscsi_transport_exit(void)
{
class_unregister(&iscsi_host_class);
class_unregister(&iscsi_transport_class);
}
module_init(iscsi_transport_init);
module_exit(iscsi_transport_exit);
MODULE_AUTHOR("Mike Christie");
MODULE_DESCRIPTION("iSCSI Transport Attributes");
MODULE_LICENSE("GPL");
......@@ -40,6 +40,11 @@ struct scsi_transport_template {
int (*target_setup)(struct scsi_target *);
int (*host_setup)(struct Scsi_Host *);
/* Destructor functions */
void (*device_destroy)(struct scsi_device *);
void (*target_destroy)(struct scsi_target *);
void (*host_destroy)(struct Scsi_Host *);
/* The size of the specific transport attribute structure (a
* space of this size will be left at the end of the
* scsi_* structure */
......
......@@ -29,7 +29,7 @@ struct fc_starget_attrs { /* aka fc_target_attrs */
uint64_t node_name;
uint64_t port_name;
uint32_t dev_loss_tmo; /* Remote Port loss timeout in seconds. */
struct timer_list dev_loss_timer;
struct work_struct dev_loss_work;
};
#define fc_starget_port_id(x) \
......@@ -40,18 +40,18 @@ struct fc_starget_attrs { /* aka fc_target_attrs */
(((struct fc_starget_attrs *)&(x)->starget_data)->port_name)
#define fc_starget_dev_loss_tmo(x) \
(((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_tmo)
#define fc_starget_dev_loss_timer(x) \
(((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_timer)
#define fc_starget_dev_loss_work(x) \
(((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_work)
struct fc_host_attrs {
uint32_t link_down_tmo; /* Link Down timeout in seconds. */
struct timer_list link_down_timer;
struct work_struct link_down_work;
};
#define fc_host_link_down_tmo(x) \
(((struct fc_host_attrs *)(x)->shost_data)->link_down_tmo)
#define fc_host_link_down_timer(x) \
(((struct fc_host_attrs *)(x)->shost_data)->link_down_timer)
#define fc_host_link_down_work(x) \
(((struct fc_host_attrs *)(x)->shost_data)->link_down_work)
/* The functions by which the transport class and the driver communicate */
......
/*
* iSCSI transport class definitions
*
* Copyright (C) IBM Corporation, 2004
* Copyright (C) Mike Christie, 2004
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef SCSI_TRANSPORT_ISCSI_H
#define SCSI_TRANSPORT_ISCSI_H
#include <linux/config.h>
#include <linux/in6.h>
#include <linux/in.h>
struct scsi_transport_template;
struct iscsi_class_session {
uint8_t isid[6];
uint16_t tsih;
int header_digest; /* 1 CRC32, 0 None */
int data_digest; /* 1 CRC32, 0 None */
uint16_t tpgt;
union {
struct in6_addr sin6_addr;
struct in_addr sin_addr;
} u;
sa_family_t addr_type; /* must be AF_INET or AF_INET6 */
uint16_t port; /* must be in network byte order */
int initial_r2t; /* 1 Yes, 0 No */
int immediate_data; /* 1 Yes, 0 No */
uint32_t max_recv_data_segment_len;
uint32_t max_burst_len;
uint32_t first_burst_len;
uint16_t def_time2wait;
uint16_t def_time2retain;
uint16_t max_outstanding_r2t;
int data_pdu_in_order; /* 1 Yes, 0 No */
int data_sequence_in_order; /* 1 Yes, 0 No */
int erl;
};
/*
* accessor macros
*/
#define iscsi_isid(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->isid)
#define iscsi_tsih(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->tsih)
#define iscsi_header_digest(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->header_digest)
#define iscsi_data_digest(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->data_digest)
#define iscsi_port(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->port)
#define iscsi_addr_type(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->addr_type)
#define iscsi_sin_addr(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->u.sin_addr)
#define iscsi_sin6_addr(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->u.sin6_addr)
#define iscsi_tpgt(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->tpgt)
#define iscsi_initial_r2t(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->initial_r2t)
#define iscsi_immediate_data(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->immediate_data)
#define iscsi_max_recv_data_segment_len(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->max_recv_data_segment_len)
#define iscsi_max_burst_len(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->max_burst_len)
#define iscsi_first_burst_len(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->first_burst_len)
#define iscsi_def_time2wait(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->def_time2wait)
#define iscsi_def_time2retain(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->def_time2retain)
#define iscsi_max_outstanding_r2t(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->max_outstanding_r2t)
#define iscsi_data_pdu_in_order(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->data_pdu_in_order)
#define iscsi_data_sequence_in_order(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->data_sequence_in_order)
#define iscsi_erl(x) \
(((struct iscsi_class_session *)&(x)->starget_data)->erl)
/*
* The functions by which the transport class and the driver communicate
*/
struct iscsi_function_template {
/*
* target attrs
*/
void (*get_isid)(struct scsi_target *);
void (*get_tsih)(struct scsi_target *);
void (*get_header_digest)(struct scsi_target *);
void (*get_data_digest)(struct scsi_target *);
void (*get_port)(struct scsi_target *);
void (*get_tpgt)(struct scsi_target *);
/*
* In get_ip_address the lld must set the address and
* the address type
*/
void (*get_ip_address)(struct scsi_target *);
/*
* The lld should snprintf the name or alias to the buffer
*/
ssize_t (*get_target_name)(struct scsi_target *, char *, ssize_t);
ssize_t (*get_target_alias)(struct scsi_target *, char *, ssize_t);
void (*get_initial_r2t)(struct scsi_target *);
void (*get_immediate_data)(struct scsi_target *);
void (*get_max_recv_data_segment_len)(struct scsi_target *);
void (*get_max_burst_len)(struct scsi_target *);
void (*get_first_burst_len)(struct scsi_target *);
void (*get_def_time2wait)(struct scsi_target *);
void (*get_def_time2retain)(struct scsi_target *);
void (*get_max_outstanding_r2t)(struct scsi_target *);
void (*get_data_pdu_in_order)(struct scsi_target *);
void (*get_data_sequence_in_order)(struct scsi_target *);
void (*get_erl)(struct scsi_target *);
/*
* host atts
*/
/*
* The lld should snprintf the name or alias to the buffer
*/
ssize_t (*get_initiator_alias)(struct Scsi_Host *, char *, ssize_t);
ssize_t (*get_initiator_name)(struct Scsi_Host *, char *, ssize_t);
/*
* The driver sets these to tell the transport class it
* wants the attributes displayed in sysfs. If the show_ flag
* is not set, the attribute will be private to the transport
* class. We could probably just test if a get_ fn was set
* since we only use the values for sysfs but this is how
* fc does it too.
*/
unsigned long show_isid:1;
unsigned long show_tsih:1;
unsigned long show_header_digest:1;
unsigned long show_data_digest:1;
unsigned long show_port:1;
unsigned long show_tpgt:1;
unsigned long show_ip_address:1;
unsigned long show_target_name:1;
unsigned long show_target_alias:1;
unsigned long show_initial_r2t:1;
unsigned long show_immediate_data:1;
unsigned long show_max_recv_data_segment_len:1;
unsigned long show_max_burst_len:1;
unsigned long show_first_burst_len:1;
unsigned long show_def_time2wait:1;
unsigned long show_def_time2retain:1;
unsigned long show_max_outstanding_r2t:1;
unsigned long show_data_pdu_in_order:1;
unsigned long show_data_sequence_in_order:1;
unsigned long show_erl:1;
unsigned long show_initiator_name:1;
unsigned long show_initiator_alias:1;
};
struct scsi_transport_template *iscsi_attach_transport(struct iscsi_function_template *);
void iscsi_release_transport(struct scsi_transport_template *);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment