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 ...@@ -203,6 +203,14 @@ config SCSI_FC_ATTRS
each attached FiberChannel device to sysfs, say Y. each attached FiberChannel device to sysfs, say Y.
Otherwise, say N. 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 endmenu
menu "SCSI low-level drivers" menu "SCSI low-level drivers"
......
...@@ -28,7 +28,7 @@ obj-$(CONFIG_SCSI) += scsi_mod.o ...@@ -28,7 +28,7 @@ obj-$(CONFIG_SCSI) += scsi_mod.o
# -------------------------- # --------------------------
obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o
obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.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_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422) * 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) * Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002)
* by D. Gilbert and aeb (20020609) * 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> #include <linux/config.h>
...@@ -15,96 +16,78 @@ ...@@ -15,96 +16,78 @@
#include <scsi/scsi_device.h> #include <scsi/scsi_device.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#include <scsi/scsi_request.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[] = { #ifdef CONFIG_SCSI_CONSTANTS
/* 20-22 */ unknown, unknown, unknown, static const char * cdb_byte0_names[] = {
/* 23-28 */ unknown, "Define window parameters", "Read Capacity", /* 00-03 */ "Test Unit Ready", "Rezero Unit/Rewind", NULL, "Request Sense",
unknown, unknown, "Read (10)", /* 04-07 */ "Format Unit/Medium", "Read Block Limits", NULL,
/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase", "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", "Read updated block",
/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal", /* 2e-31 */ "Write Verify (10)", "Verify (10)", "Search High", "Search Equal",
/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", /* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position",
/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data", /* 35-37 */ "Synchronize Cache (10)", "Lock/Unlock Cache (10)",
"Read Defect Data(10)",
/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer", /* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
"Read Buffer", "Read Buffer",
/* 3d-3f */ "Update Block", "Read Long", "Write Long", /* 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",
static const char *group_2_commands[] = {
/* 40-41 */ "Change Definition", "Write Same",
/* 42-48 */ "Read sub-channel", "Read TOC", "Read header",
"Play audio (10)", "Get configuration", "Play audio msf", "Play audio (10)", "Get configuration", "Play audio msf",
"Play audio track/index", "Play audio track/index",
/* 49-4f */ "Play track relative (10)", "Get event status notification", /* 49-4f */ "Play track relative (10)", "Get event status notification",
"Pause/resume", "Log Select", "Log Sense", "Stop play/scan", "Pause/resume", "Log Select", "Log Sense", "Stop play/scan",
unknown, NULL,
/* 50-55 */ "Xdwrite", "Xpwrite, Read disk info", "Xdread, Read track info", /* 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", /* 56-5b */ "Reserve (10)", "Release (10)", "Repair track", "Read master cue",
"Mode Sense (10)", "Close track/session", "Mode Sense (10)", "Close track/session",
/* 5c-5f */ "Read buffer capacity", "Send cue sheet", "Persistent reserve in", /* 5c-5f */ "Read buffer capacity", "Send cue sheet", "Persistent reserve in",
"Persistent reserve out", "Persistent reserve out",
}; /* 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,
/* The following are 16 byte commands in group 4 */ /* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Variable length",
static const char *group_4_commands[] = {
/* 80-84 */ "Xdwrite (16)", "Rebuild (16)", "Regenerate (16)", "Extended copy", /* 80-84 */ "Xdwrite (16)", "Rebuild (16)", "Regenerate (16)", "Extended copy",
"Receive copy results", "Receive copy results",
/* 85-89 */ "Memory Export In (16)", "Access control in", "Access control out", /* 85-89 */ "Memory Export In (16)", "Access control in", "Access control out",
"Read (16)", "Memory Export Out (16)", "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)", "Write and verify (16)", "Verify (16)",
/* 90-94 */ "Pre-fetch (16)", "Synchronize cache (16)", /* 90-94 */ "Pre-fetch (16)", "Synchronize cache (16)",
"Lock/unlock cache (16)", "Write same (16)", unknown, "Lock/unlock cache (16)", "Write same (16)", NULL,
/* 95-99 */ unknown, unknown, unknown, unknown, unknown, /* 95-99 */ NULL, NULL, NULL, NULL, NULL,
/* 9a-9f */ unknown, unknown, unknown, unknown, "Service action in", /* 9a-9f */ NULL, NULL, NULL, NULL, "Service action in (16)",
"Service action out", "Service action out (16)",
}; /* a0-a5 */ "Report luns", "Blank", "Send event", "Maintenance in",
"Maintenance out", "Move medium/play audio(12)",
/* 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)",
/* a6-a9 */ "Exchange medium", "Move medium attached", "Read(12)", /* a6-a9 */ "Exchange medium", "Move medium attached", "Read(12)",
"Play track relative(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)", "Read DVD structure", "Write and verify(12)",
/* af-b1 */ "Verify(12)", "Search data high(12)", "Search data equal(12)", /* af-b1 */ "Verify(12)", "Search data high(12)", "Search data equal(12)",
/* b2-b4 */ "Search data low(12)", "Set limits(12)", /* b2-b4 */ "Search data low(12)", "Set limits(12)",
...@@ -112,57 +95,278 @@ static const char *group_5_commands[] = { ...@@ -112,57 +95,278 @@ static const char *group_5_commands[] = {
/* b5-b6 */ "Request volume element address", "Send volume tag, set streaming", /* b5-b6 */ "Request volume element address", "Send volume tag, set streaming",
/* b7-b9 */ "Read defect data(12)", "Read element status", "Read CD msf", /* b7-b9 */ "Read defect data(12)", "Read element status", "Read CD msf",
/* ba-bc */ "Redundancy group (in), Scan", /* ba-bc */ "Redundancy group (in), Scan",
"Redundancy group (out), Set cd-rom speed", "Spare (in), Play cd", "Redundancy group (out), Set cd-rom speed", "Spare in, Play cd",
/* bd-bf */ "Spare (out), Mechanism status", "Volume set (in), Read cd", /* bd-bf */ "Spare out, Mechanism status", "Volume set in, Read cd",
"Volume set (out), Send DVD structure", "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 static const struct value_name_pair serv_out12_arr[] = {
#define VENDOR_GROUP 1 {-1, "dummy entry"},
};
#define SERV_OUT12_SZ \
(int)(sizeof(serv_out12_arr) / sizeof(serv_in12_arr[0]))
static const char **commands[] = { static const struct value_name_pair serv_in16_arr[] = {
group_0_commands, group_1_commands, group_2_commands, {0x10, "Read capacity(16)"},
(const char **) RESERVED_GROUP, group_4_commands, {0x11, "Read long(16)"},
group_5_commands, (const char **) VENDOR_GROUP,
(const char **) VENDOR_GROUP
}; };
#define SERV_IN16_SZ \
(int)(sizeof(serv_in16_arr) / sizeof(serv_in16_arr[0]))
static const char reserved[] = "RESERVED"; static const struct value_name_pair serv_out16_arr[] = {
static const char vendor[] = "VENDOR SPECIFIC"; {0x11, "Write long(16)"},
{0x1f, "Notify data transfer device(16)"},
static void print_opcode(int opcode) { };
const char **table = commands[ group(opcode) ]; #define SERV_OUT16_SZ \
switch ((unsigned long) table) { (int)(sizeof(serv_out16_arr) / sizeof(serv_in16_arr[0]))
case RESERVED_GROUP:
printk("%s(0x%02x) ", reserved, opcode); static const struct value_name_pair variable_length_arr[] = {
break; {0x1, "Rebuild(32)"},
case VENDOR_GROUP: {0x2, "Regenerate(32)"},
printk("%s(0x%02x) ", vendor, opcode); {0x3, "Xdread(32)"},
break; {0x4, "Xdwrite(32)"},
default: {0x5, "Xdwrite extended(32)"},
if (table[opcode & 0x1f] != unknown) {0x6, "Xpwrite(32)"},
printk("%s ",table[opcode & 0x1f]); {0x7, "Xdwriteread(32)"},
else {0x8, "Xdwrite extended(64)"},
printk("%s(0x%02x) ", unknown, opcode); {0x9, "Read(32)"},
break; {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 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 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 (cdb0 < 0xc0) {
name = cdb_byte0_names[cdb0];
if (name)
printk("%s%s", leadin, name);
else
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) { #else /* ifndef CONFIG_SCSI_CONSTANTS */
printk("0x%02x ", opcode);
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 #endif
void __scsi_print_command (unsigned char *command) { void __scsi_print_command(unsigned char *command)
int i,s; {
print_opcode(command[0]); int k, len;
for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
printk("%02x ", command[i]); print_opcode_name(command, 0, 1);
printk("\n"); 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) { ...@@ -177,7 +381,7 @@ void __scsi_print_command (unsigned char *command) {
**/ **/
void void
scsi_print_status(unsigned char scsi_status) { scsi_print_status(unsigned char scsi_status) {
#if (CONSTANTS & CONST_STATUS) #ifdef CONFIG_SCSI_CONSTANTS
const char * ccp; const char * ccp;
switch (scsi_status) { switch (scsi_status) {
...@@ -194,13 +398,13 @@ scsi_print_status(unsigned char scsi_status) { ...@@ -194,13 +398,13 @@ scsi_print_status(unsigned char scsi_status) {
case 0x40: ccp = "Task Aborted"; break; case 0x40: ccp = "Task Aborted"; break;
default: ccp = "Unknown status"; default: ccp = "Unknown status";
} }
printk("%s", ccp); printk(KERN_INFO "%s", ccp);
#else #else
printk("0x%0x", scsi_status); printk(KERN_INFO "0x%0x", scsi_status);
#endif #endif
} }
#if (CONSTANTS & CONST_XSENSE) #ifdef CONFIG_SCSI_CONSTANTS
struct error_info { struct error_info {
unsigned short code12; /* 0x0302 looks better than 0x03,0x02 */ unsigned short code12; /* 0x0302 looks better than 0x03,0x02 */
...@@ -253,6 +457,8 @@ static struct error_info additional[] = ...@@ -253,6 +457,8 @@ static struct error_info additional[] =
{0x040C, "Logical unit not accessible, target port in unavailable " {0x040C, "Logical unit not accessible, target port in unavailable "
"state"}, "state"},
{0x0410, "Logical unit not ready, auxiliary memory not accessible"}, {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"}, {0x0500, "Logical unit does not respond to selection"},
...@@ -300,7 +506,14 @@ static struct error_info additional[] = ...@@ -300,7 +506,14 @@ static struct error_info additional[] =
{0x0D04, "Copy target device data underrun"}, {0x0D04, "Copy target device data underrun"},
{0x0D05, "Copy target device data overrun"}, {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"}, {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"}, {0x1100, "Unrecovered read error"},
{0x1101, "Read retries exhausted"}, {0x1101, "Read retries exhausted"},
...@@ -407,8 +620,10 @@ static struct error_info additional[] = ...@@ -407,8 +620,10 @@ static struct error_info additional[] =
{0x2400, "Invalid field in cdb"}, {0x2400, "Invalid field in cdb"},
{0x2401, "CDB decryption error"}, {0x2401, "CDB decryption error"},
{0x2402, "Obsolete"}, {0x2404, "Security audit value frozen"},
{0x2403, "Obsolete"}, {0x2405, "Security working key frozen"},
{0x2406, "Nonce not unique"},
{0x2407, "Nonce timestamp out of range"},
{0x2500, "Logical unit not supported"}, {0x2500, "Logical unit not supported"},
...@@ -426,6 +641,8 @@ static struct error_info additional[] = ...@@ -426,6 +641,8 @@ static struct error_info additional[] =
{0x260B, "Inline data length exceeded"}, {0x260B, "Inline data length exceeded"},
{0x260C, "Invalid operation for copy source or destination"}, {0x260C, "Invalid operation for copy source or destination"},
{0x260D, "Copy segment granularity violation"}, {0x260D, "Copy segment granularity violation"},
{0x260E, "Invalid parameter while port is enabled"},
{0x260F, "Invalid data-out buffer integrity"},
{0x2700, "Write protected"}, {0x2700, "Write protected"},
{0x2701, "Hardware write protected"}, {0x2701, "Hardware write protected"},
...@@ -455,6 +672,8 @@ static struct error_info additional[] = ...@@ -455,6 +672,8 @@ static struct error_info additional[] =
{0x2A05, "Registrations preempted"}, {0x2A05, "Registrations preempted"},
{0x2A06, "Asymmetric access state changed"}, {0x2A06, "Asymmetric access state changed"},
{0x2A07, "Implicit asymmetric access state transition failed"}, {0x2A07, "Implicit asymmetric access state transition failed"},
{0x2A08, "Priority changed"},
{0x2A09, "Capacity data has changed"},
{0x2B00, "Copy cannot execute since host cannot disconnect"}, {0x2B00, "Copy cannot execute since host cannot disconnect"},
...@@ -468,6 +687,8 @@ static struct error_info additional[] = ...@@ -468,6 +687,8 @@ static struct error_info additional[] =
{0x2C07, "Previous busy status"}, {0x2C07, "Previous busy status"},
{0x2C08, "Previous task set full status"}, {0x2C08, "Previous task set full status"},
{0x2C09, "Previous reservation conflict status"}, {0x2C09, "Previous reservation conflict status"},
{0x2C0A, "Partition or collection contains user objects"},
{0x2C0B, "Not reserved"},
{0x2D00, "Overwrite error on update in place"}, {0x2D00, "Overwrite error on update in place"},
...@@ -485,6 +706,8 @@ static struct error_info additional[] = ...@@ -485,6 +706,8 @@ static struct error_info additional[] =
{0x3007, "Cleaning failure"}, {0x3007, "Cleaning failure"},
{0x3008, "Cannot write - application code mismatch"}, {0x3008, "Cannot write - application code mismatch"},
{0x3009, "Current session not fixated for append"}, {0x3009, "Current session not fixated for append"},
{0x300A, "Cleaning request rejected"},
{0x300C, "WORM medium, overwrite attempted"},
{0x3010, "Medium not formatted"}, {0x3010, "Medium not formatted"},
{0x3100, "Medium format corrupted"}, {0x3100, "Medium format corrupted"},
...@@ -503,6 +726,7 @@ static struct error_info additional[] = ...@@ -503,6 +726,7 @@ static struct error_info additional[] =
{0x3502, "Enclosure services unavailable"}, {0x3502, "Enclosure services unavailable"},
{0x3503, "Enclosure services transfer failure"}, {0x3503, "Enclosure services transfer failure"},
{0x3504, "Enclosure services transfer refused"}, {0x3504, "Enclosure services transfer refused"},
{0x3505, "Enclosure services checksum error"},
{0x3600, "Ribbon, ink, or toner failure"}, {0x3600, "Ribbon, ink, or toner failure"},
...@@ -543,6 +767,7 @@ static struct error_info additional[] = ...@@ -543,6 +767,7 @@ static struct error_info additional[] =
{0x3B14, "Medium magazine locked"}, {0x3B14, "Medium magazine locked"},
{0x3B15, "Medium magazine unlocked"}, {0x3B15, "Medium magazine unlocked"},
{0x3B16, "Mechanical positioning or changer error"}, {0x3B16, "Mechanical positioning or changer error"},
{0x3B17, "Read past end of user object"},
{0x3D00, "Invalid bits in identify message"}, {0x3D00, "Invalid bits in identify message"},
...@@ -570,14 +795,12 @@ static struct error_info additional[] = ...@@ -570,14 +795,12 @@ static struct error_info additional[] =
{0x3F0F, "Echo buffer overwritten"}, {0x3F0F, "Echo buffer overwritten"},
{0x3F10, "Medium loadable"}, {0x3F10, "Medium loadable"},
{0x3F11, "Medium auxiliary memory accessible"}, {0x3F11, "Medium auxiliary memory accessible"},
/*
#if 0 * {0x40NN, "Ram failure"},
{0x40NN, "Ram failure"}, * {0x40NN, "Diagnostic failure on component nn"},
{0x40NN, "Diagnostic failure on component nn"}, * {0x41NN, "Data path failure"},
{0x41NN, "Data path failure"}, * {0x42NN, "Power-on or self-test failure"},
{0x42NN, "Power-on or self-test failure"}, */
#endif
{0x4300, "Message error"}, {0x4300, "Message error"},
{0x4400, "Internal target failure"}, {0x4400, "Internal target failure"},
...@@ -592,6 +815,7 @@ static struct error_info additional[] = ...@@ -592,6 +815,7 @@ static struct error_info additional[] =
{0x4703, "Information unit CRC error detected"}, {0x4703, "Information unit CRC error detected"},
{0x4704, "Asynchronous information protection error detected"}, {0x4704, "Asynchronous information protection error detected"},
{0x4705, "Protocol service CRC error"}, {0x4705, "Protocol service CRC error"},
{0x477f, "Some commands cleared by iSCSI Protocol event"},
{0x4800, "Initiator detected error message received"}, {0x4800, "Initiator detected error message received"},
...@@ -600,13 +824,17 @@ static struct error_info additional[] = ...@@ -600,13 +824,17 @@ static struct error_info additional[] =
{0x4A00, "Command phase error"}, {0x4A00, "Command phase error"},
{0x4B00, "Data 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"}, {0x4C00, "Logical unit failed self-configuration"},
/*
#if 0 * {0x4DNN, "Tagged overlapped commands (nn = queue tag)"},
{0x4DNN, "Tagged overlapped commands (nn = queue tag)"}, */
#endif
{0x4E00, "Overlapped commands attempted"}, {0x4E00, "Overlapped commands attempted"},
{0x5000, "Write append error"}, {0x5000, "Write append error"},
...@@ -631,6 +859,7 @@ static struct error_info additional[] = ...@@ -631,6 +859,7 @@ static struct error_info additional[] =
{0x5504, "Insufficient registration resources"}, {0x5504, "Insufficient registration resources"},
{0x5505, "Insufficient access control resources"}, {0x5505, "Insufficient access control resources"},
{0x5506, "Auxiliary memory out of space"}, {0x5506, "Auxiliary memory out of space"},
{0x5507, "Quota error"},
{0x5700, "Unable to recover table-of-contents"}, {0x5700, "Unable to recover table-of-contents"},
...@@ -806,11 +1035,9 @@ static struct error_info additional[] = ...@@ -806,11 +1035,9 @@ static struct error_info additional[] =
{0x6F03, "Read of scrambled sector without authentication"}, {0x6F03, "Read of scrambled sector without authentication"},
{0x6F04, "Media region code is mismatched to logical unit region"}, {0x6F04, "Media region code is mismatched to logical unit region"},
{0x6F05, "Drive region must be permanent/region reset count error"}, {0x6F05, "Drive region must be permanent/region reset count error"},
/*
#if 0 * {0x70NN, "Decompression exception short algorithm id of nn"},
{0x70NN, "Decompression exception short algorithm id of nn"}, */
#endif
{0x7100, "Decompression exception long algorithm id"}, {0x7100, "Decompression exception long algorithm id"},
{0x7200, "Session fixation error"}, {0x7200, "Session fixation error"},
...@@ -845,9 +1072,7 @@ static struct error_info2 additional2[] = ...@@ -845,9 +1072,7 @@ static struct error_info2 additional2[] =
{0x70,0x00,0xff,"Decompression exception short algorithm id of %x"}, {0x70,0x00,0xff,"Decompression exception short algorithm id of %x"},
{0, 0, 0, NULL} {0, 0, 0, NULL}
}; };
#endif
#if (CONSTANTS & CONST_SENSE)
/* description of the sense key values */ /* description of the sense key values */
static const char *snstext[] = { static const char *snstext[] = {
"No Sense", /* 0: There is no sense information */ "No Sense", /* 0: There is no sense information */
...@@ -858,11 +1083,11 @@ static const char *snstext[] = { ...@@ -858,11 +1083,11 @@ static const char *snstext[] = {
"Hardware Error", /* 4: Controller or device failure */ "Hardware Error", /* 4: Controller or device failure */
"Illegal Request", /* 5: Error in request */ "Illegal Request", /* 5: Error in request */
"Unit Attention", /* 6: Removable medium was changed, or "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 */ "Data Protect", /* 7: Access to the data is blocked */
"Blank Check", /* 8: Reached unexpected written or unwritten "Blank Check", /* 8: Reached unexpected written or unwritten
region of the medium */ region of the medium */
"Vendor Specific", /* 9: Vendor specific */ "Vendor Specific(9)",
"Copy Aborted", /* A: COPY or COMPARE was aborted */ "Copy Aborted", /* A: COPY or COMPARE was aborted */
"Aborted Command", /* B: The target aborted the command */ "Aborted Command", /* B: The target aborted the command */
"Equal", /* C: A SEARCH DATA command found data equal */ "Equal", /* C: A SEARCH DATA command found data equal */
...@@ -875,7 +1100,7 @@ static const char *snstext[] = { ...@@ -875,7 +1100,7 @@ static const char *snstext[] = {
/* Get sense key string or NULL if not available */ /* Get sense key string or NULL if not available */
const char * const char *
scsi_sense_key_string(unsigned char key) { scsi_sense_key_string(unsigned char key) {
#if (CONSTANTS & CONST_SENSE) #ifdef CONFIG_SCSI_CONSTANTS
if (key <= 0xE) if (key <= 0xE)
return snstext[key]; return snstext[key];
#endif #endif
...@@ -883,12 +1108,12 @@ scsi_sense_key_string(unsigned char key) { ...@@ -883,12 +1108,12 @@ scsi_sense_key_string(unsigned char key) {
} }
/* /*
* Get extended sense key string or NULL if not available. * Get additional sense code string or NULL if not available.
* This string may contain a %x and must be printed with ascq as arg. * This string may contain a "%x" and should be printed with ascq as arg.
*/ */
const char * const char *
scsi_extd_sense_format(unsigned char asc, unsigned char ascq) { scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
#if (CONSTANTS & CONST_XSENSE) #ifdef CONFIG_SCSI_CONSTANTS
int i; int i;
unsigned short code = ((asc << 8) | ascq); unsigned short code = ((asc << 8) | ascq);
...@@ -904,17 +1129,25 @@ scsi_extd_sense_format(unsigned char asc, unsigned char ascq) { ...@@ -904,17 +1129,25 @@ scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
return NULL; return NULL;
} }
/* Print extended sense information */ /* Print extended sense information; no leadin, no linefeed */
static void 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); const char *extd_sense_fmt = scsi_extd_sense_format(asc, ascq);
if (extd_sense_fmt) { if (extd_sense_fmt) {
printk("Additional sense: "); if (strstr(extd_sense_fmt, "%x")) {
printk(extd_sense_fmt, ascq); printk("Additional sense: ");
printk("\n"); printk(extd_sense_fmt, ascq);
} else
printk("Additional sense: %s", extd_sense_fmt);
} else { } 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) { ...@@ -922,112 +1155,115 @@ scsi_show_extd_sense(unsigned char asc, unsigned char ascq) {
static void static void
print_sense_internal(const char *devclass, print_sense_internal(const char *devclass,
const unsigned char *sense_buffer, const unsigned char *sense_buffer,
int sense_len,
struct request *req) struct request *req)
{ {
int s, sense_class, valid, code, info; int k, num, res;
const char *error = NULL; unsigned int info;
unsigned char asc, ascq; const char *error;
const char *sense_txt; const char *sense_txt;
const char *name = req->rq_disk ? req->rq_disk->disk_name : devclass; const char *name = req->rq_disk ? req->rq_disk->disk_name : devclass;
struct scsi_sense_hdr ssh;
sense_class = (sense_buffer[0] >> 4) & 0x07; res = scsi_normalize_sense(sense_buffer, sense_len, &ssh);
code = sense_buffer[0] & 0xf; if (0 == res) {
valid = sense_buffer[0] & 0x80; /* this may be SCSI-1 sense data */
num = (sense_len < 32) ? sense_len : 32;
if (sense_class == 7) { /* extended sense data */ printk(KERN_INFO "Unrecognized sense data (in hex):");
s = sense_buffer[7] + 8; for (k = 0; k < num; ++k) {
if (s > SCSI_SENSE_BUFFERSIZE) if (0 == (k % 16)) {
s = SCSI_SENSE_BUFFERSIZE; printk("\n");
printk(KERN_INFO " ");
info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) | }
(sense_buffer[5] << 8) | sense_buffer[6]); printk("%02x ", sense_buffer[k]);
if (info || valid) {
printk("Info fld=0x%x", info);
if (!valid) /* info data not according to standard */
printk(" (nonstd)");
printk(", ");
}
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("\n");
return;
}
printk("%s ", error); /* 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(ssh.sense_key);
if (sense_txt)
printk(": sense key: %s\n", sense_txt);
else
printk(": sense key=0x%x\n", ssh.sense_key);
printk(KERN_INFO " ");
scsi_show_extd_sense(ssh.asc, ssh.ascq);
printk("\n");
sense_txt = scsi_sense_key_string(sense_buffer[2]); if (ssh.response_code < 0x72) {
if (sense_txt) /* only decode extras for "fixed" format now */
printk("%s: sense key %s\n", name, sense_txt); char buff[80];
else int blen, fixed_valid;
printk("%s: sense = %2x %2x\n", name,
sense_buffer[0], sense_buffer[2]);
asc = ascq = 0; fixed_valid = sense_buffer[0] & 0x80;
if (sense_buffer[7] + 7 >= 13) { info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) |
asc = sense_buffer[12]; (sense_buffer[5] << 8) | sense_buffer[6]);
ascq = sense_buffer[13]; 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 (asc || ascq) if (sense_buffer[2] & 0x40) {
scsi_show_extd_sense(asc, ascq); /* end-of-medium condition exists */
if (res > 0)
} else { /* non-extended sense data */ res += snprintf(buff + res, blen - res, ", ");
res += snprintf(buff + res, blen - res, "EOM");
/* }
* Standard says: if (sense_buffer[2] & 0x20) {
* sense_buffer[0] & 0200 : address valid /* incorrect block length requested */
* sense_buffer[0] & 0177 : vendor-specific error code if (res > 0)
* sense_buffer[1] & 0340 : vendor-specific res += snprintf(buff + res, blen - res, ", ");
* sense_buffer[1..3] : 21-bit logical block address res += snprintf(buff + res, blen - res, "ILI");
*/ }
if (res > 0)
sense_txt = scsi_sense_key_string(sense_buffer[0]); printk(KERN_INFO "%s\n", buff);
if (sense_txt) } else if (ssh.additional_length > 0) {
printk("%s: old sense key %s\n", name, sense_txt); /* descriptor format with sense descriptors */
else num = 8 + ssh.additional_length;
printk("%s: sense = %2x %2x\n", name, num = (sense_len < num) ? sense_len : num;
sense_buffer[0], sense_buffer[2]); printk(KERN_INFO "Descriptor sense data with sense "
"descriptors (in hex):");
printk("Non-extended sense class %d code 0x%0x\n", for (k = 0; k < num; ++k) {
sense_class, code); if (0 == (k % 16)) {
s = 4; printk("\n");
} printk(KERN_INFO " ");
}
#if !(CONSTANTS & CONST_SENSE) printk("%02x ", sense_buffer[k]);
{ }
int i; printk("\n");
printk("Raw sense data:");
for (i = 0; i < s; ++i)
printk("0x%02x ", sense_buffer[i]);
printk("\n");
} }
#endif
} }
void scsi_print_sense(const char *devclass, struct scsi_cmnd *cmd) 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) 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[] = { static const char *one_byte_msgs[] = {
/* 0x00 */ "Command Complete", NULL, "Save Pointers", /* 0x00 */ "Command Complete", NULL, "Save Pointers",
/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error", /* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error",
...@@ -1036,161 +1272,164 @@ static const char *one_byte_msgs[] = { ...@@ -1036,161 +1272,164 @@ static const char *one_byte_msgs[] = {
/* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue", /* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue",
/* 0x0f */ "Initiate Recovery", "Release Recovery" /* 0x0f */ "Initiate Recovery", "Release Recovery"
}; };
#define NO_ONE_BYTE_MSGS (sizeof(one_byte_msgs) / sizeof (const char *)) #define NO_ONE_BYTE_MSGS (sizeof(one_byte_msgs) / sizeof (const char *))
static const char *two_byte_msgs[] = { static const char *two_byte_msgs[] = {
/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag" /* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag"
/* 0x23 */ "Ignore Wide Residue" /* 0x23 */ "Ignore Wide Residue"
}; };
#define NO_TWO_BYTE_MSGS (sizeof(two_byte_msgs) / sizeof (const char *)) #define NO_TWO_BYTE_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
static const char *extended_msgs[] = { static const char *extended_msgs[] = {
/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request", /* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request" /* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request"
}; };
#define NO_EXTENDED_MSGS (sizeof(two_byte_msgs) / sizeof (const char *)) #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) { int len = 0, i;
len = 3 + msg[1]; if (msg[0] == EXTENDED_MESSAGE) {
#if (CONSTANTS & CONST_MSG) len = 3 + msg[1];
if (msg[2] < NO_EXTENDED_MSGS) if (msg[2] < NO_EXTENDED_MSGS)
printk ("%s ", extended_msgs[msg[2]]); printk ("%s ", extended_msgs[msg[2]]);
else else
printk ("Extended Message, reserved code (0x%02x) ", (int) msg[2]); printk ("Extended Message, reserved code (0x%02x) ",
switch (msg[2]) { (int) msg[2]);
case EXTENDED_MODIFY_DATA_POINTER: switch (msg[2]) {
printk("pointer = %d", (int) (msg[3] << 24) | (msg[4] << 16) | case EXTENDED_MODIFY_DATA_POINTER:
(msg[5] << 8) | msg[6]); printk("pointer = %d", (int) (msg[3] << 24) |
break; (msg[4] << 16) | (msg[5] << 8) | msg[6]);
case EXTENDED_SDTR: break;
printk("period = %d ns, offset = %d", (int) msg[3] * 4, (int) case EXTENDED_SDTR:
msg[4]); printk("period = %d ns, offset = %d",
break; (int) msg[3] * 4, (int) msg[4]);
case EXTENDED_WDTR: break;
printk("width = 2^%d bytes", msg[3]); case EXTENDED_WDTR:
break; printk("width = 2^%d bytes", msg[3]);
default: break;
for (i = 2; i < len; ++i) default:
printk("%02x ", msg[i]); for (i = 2; i < len; ++i)
} printk("%02x ", msg[i]);
#else }
for (i = 0; i < len; ++i)
printk("%02x ", msg[i]);
#endif
/* Identify */ /* Identify */
} else if (msg[0] & 0x80) { } else if (msg[0] & 0x80) {
#if (CONSTANTS & CONST_MSG) printk("Identify disconnect %sallowed %s %d ",
printk("Identify disconnect %sallowed %s %d ", (msg[0] & 0x40) ? "" : "not ",
(msg[0] & 0x40) ? "" : "not ", (msg[0] & 0x20) ? "target routine" : "lun",
(msg[0] & 0x20) ? "target routine" : "lun", msg[0] & 0x7);
msg[0] & 0x7); len = 1;
#else
printk("%02x ", msg[0]);
#endif
len = 1;
/* Normal One byte */ /* Normal One byte */
} else if (msg[0] < 0x1f) { } else if (msg[0] < 0x1f) {
#if (CONSTANTS & CONST_MSG) if (msg[0] < NO_ONE_BYTE_MSGS)
if (msg[0] < NO_ONE_BYTE_MSGS) printk(one_byte_msgs[msg[0]]);
printk(one_byte_msgs[msg[0]]); else
else printk("reserved (%02x) ", msg[0]);
printk("reserved (%02x) ", msg[0]); len = 1;
#else
printk("%02x ", msg[0]);
#endif
len = 1;
/* Two byte */ /* Two byte */
} else if (msg[0] <= 0x2f) { } else if (msg[0] <= 0x2f) {
#if (CONSTANTS & CONST_MSG) if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS)
if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS) printk("%s %02x ", two_byte_msgs[msg[0] - 0x20],
printk("%s %02x ", two_byte_msgs[msg[0] - 0x20], msg[1]);
msg[1]); else
else printk("reserved two byte (%02x %02x) ",
printk("reserved two byte (%02x %02x) ", msg[0], msg[1]);
msg[0], msg[1]); len = 2;
#else } else
printk("%02x %02x", msg[0], msg[1]); printk("reserved");
#endif return len;
len = 2; }
} else
#if (CONSTANTS & CONST_MSG) #else /* ifndef CONFIG_SCSI_CONSTANTS */
printk(reserved);
#else int scsi_print_msg (const unsigned char *msg)
printk("%02x ", msg[0]); {
#endif int len = 0, i;
return len;
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]);
len = 2;
} else
printk("%02x ", msg[0]);
return len;
} }
#endif /* ! CONFIG_SCSI_CONSTANTS */
void scsi_print_command(struct scsi_cmnd *cmd) { void scsi_print_command(struct scsi_cmnd *cmd)
printk("scsi%d : destination target %d, lun %d\n", {
cmd->device->host->host_no, /* Assume appended output (i.e. not at start of line) */
cmd->device->id, printk("scsi%d : destination target %d, lun %d\n",
cmd->device->lun); cmd->device->host->host_no,
printk(" command = "); cmd->device->id,
__scsi_print_command(cmd->cmnd); cmd->device->lun);
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[]={ static const char * hostbyte_table[]={
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", "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_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) void scsi_print_hostbyte(int scsiresult)
{ static int maxcode=0; {
int i; int hb = host_byte(scsiresult);
if(!maxcode) { printk("Hostbyte=0x%02x", hb);
for(i=0;hostbyte_table[i];i++) ; if (hb < NUM_HOSTBYTE_STRS)
maxcode=i-1; printk("(%s) ", hostbyte_table[hb]);
} else
printk("Hostbyte=0x%02x",host_byte(scsiresult)); printk("is invalid ");
if(host_byte(scsiresult)>maxcode) {
printk("is invalid ");
return;
}
printk("(%s) ",hostbyte_table[host_byte(scsiresult)]);
} }
#else #else
void scsi_print_hostbyte(int scsiresult) void scsi_print_hostbyte(int scsiresult)
{ printk("Hostbyte=0x%02x ",host_byte(scsiresult)); {
printk("Hostbyte=0x%02x ", host_byte(scsiresult));
} }
#endif #endif
#if (CONSTANTS & CONST_DRIVER) #ifdef CONFIG_SCSI_CONSTANTS
static const char * driverbyte_table[]={ static const char * driverbyte_table[]={
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", "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", static const char * driversuggest_table[]={"SUGGEST_OK",
"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE", "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) void scsi_print_driverbyte(int scsiresult)
{ static int driver_max=0,suggest_max=0; {
int i,dr=driver_byte(scsiresult)&DRIVER_MASK, int dr = (driver_byte(scsiresult) & DRIVER_MASK);
su=(driver_byte(scsiresult)&SUGGEST_MASK)>>4; int su = ((driver_byte(scsiresult) & SUGGEST_MASK) >> 4);
if(!driver_max) { printk("Driverbyte=0x%02x ", driver_byte(scsiresult));
for(i=0;driverbyte_table[i];i++) ; printk("(%s,%s) ",
driver_max=i; (dr < NUM_DRIVERBYTE_STRS ? driverbyte_table[dr] : "invalid"),
for(i=0;driversuggest_table[i];i++) ; (su < NUM_SUGGEST_STRS ? driversuggest_table[su] : "invalid"));
suggest_max=i;
}
printk("Driverbyte=0x%02x",driver_byte(scsiresult));
printk("(%s,%s) ",
dr<driver_max ? driverbyte_table[dr]:"invalid",
su<suggest_max ? driversuggest_table[su]:"invalid");
} }
#else #else
void scsi_print_driverbyte(int scsiresult) void scsi_print_driverbyte(int scsiresult)
{ printk("Driverbyte=0x%02x ",driver_byte(scsiresult)); {
printk("Driverbyte=0x%02x ", driver_byte(scsiresult));
} }
#endif #endif
...@@ -79,6 +79,8 @@ void scsi_remove_host(struct Scsi_Host *shost) ...@@ -79,6 +79,8 @@ void scsi_remove_host(struct Scsi_Host *shost)
set_bit(SHOST_DEL, &shost->shost_state); set_bit(SHOST_DEL, &shost->shost_state);
if (shost->transportt->host_destroy)
shost->transportt->host_destroy(shost);
class_device_unregister(&shost->shost_classdev); class_device_unregister(&shost->shost_classdev);
if (shost->transport_classdev.class) if (shost->transport_classdev.class)
class_device_unregister(&shost->transport_classdev); class_device_unregister(&shost->transport_classdev);
...@@ -133,11 +135,14 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) ...@@ -133,11 +135,14 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
error = scsi_sysfs_add_host(shost); error = scsi_sysfs_add_host(shost);
if (error) if (error)
goto out_del_classdev; goto out_destroy_host;
scsi_proc_host_add(shost); scsi_proc_host_add(shost);
return error; return error;
out_destroy_host:
if (shost->transportt->host_destroy)
shost->transportt->host_destroy(shost);
out_del_classdev: out_del_classdev:
class_device_del(&shost->shost_classdev); class_device_del(&shost->shost_classdev);
out_del_gendev: out_del_gendev:
......
...@@ -55,8 +55,8 @@ ...@@ -55,8 +55,8 @@
#include "scsi_logging.h" #include "scsi_logging.h"
#include "scsi_debug.h" #include "scsi_debug.h"
#define SCSI_DEBUG_VERSION "1.74" #define SCSI_DEBUG_VERSION "1.75"
static const char * scsi_debug_version_date = "20040829"; static const char * scsi_debug_version_date = "20041023";
/* Additional Sense Code (ASC) used */ /* Additional Sense Code (ASC) used */
#define NO_ADDED_SENSE 0x0 #define NO_ADDED_SENSE 0x0
...@@ -82,7 +82,7 @@ static const char * scsi_debug_version_date = "20040829"; ...@@ -82,7 +82,7 @@ static const char * scsi_debug_version_date = "20040829";
#define DEF_EVERY_NTH 0 #define DEF_EVERY_NTH 0
#define DEF_NUM_PARTS 0 #define DEF_NUM_PARTS 0
#define DEF_OPTS 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_PTYPE 0
#define DEF_D_SENSE 0 #define DEF_D_SENSE 0
...@@ -95,6 +95,13 @@ static const char * scsi_debug_version_date = "20040829"; ...@@ -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 no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
* - a RECOVERED_ERROR is simulated on successful read and write * - a RECOVERED_ERROR is simulated on successful read and write
* commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set. * 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 /* 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 = { ...@@ -195,14 +202,12 @@ static Scsi_Host_Template sdebug_driver_template = {
.cmd_per_lun = 3, .cmd_per_lun = 3,
.max_sectors = 4096, .max_sectors = 4096,
.unchecked_isa_dma = 0, .unchecked_isa_dma = 0,
.use_clustering = ENABLE_CLUSTERING, .use_clustering = DISABLE_CLUSTERING,
.module = THIS_MODULE, .module = THIS_MODULE,
}; };
static unsigned char * fake_storep; /* ramdisk storage */ static unsigned char * fake_storep; /* ramdisk storage */
static unsigned char spare_buff[SDEBUG_SENSE_LEN];
static int num_aborts = 0; static int num_aborts = 0;
static int num_dev_resets = 0; static int num_dev_resets = 0;
static int num_bus_resets = 0; static int num_bus_resets = 0;
...@@ -228,21 +233,28 @@ static const int check_condition_result = ...@@ -228,21 +233,28 @@ static const int check_condition_result =
(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
/* function declarations */ /* function declarations */
static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
int bufflen, struct sdebug_dev_info * devip); struct sdebug_dev_info * devip);
static int resp_mode_sense(unsigned char * cmd, int target, static int resp_requests(struct scsi_cmnd * SCpnt,
unsigned char * buff, int bufflen, 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); struct sdebug_dev_info * devip);
static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block, static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip); int num, struct sdebug_dev_info * devip);
static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block, static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip); int num, struct sdebug_dev_info * devip);
static int resp_report_luns(unsigned char * cmd, unsigned char * buff, static int resp_report_luns(struct scsi_cmnd * SCpnt,
int bufflen, struct sdebug_dev_info * devip); 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 void timer_intr_handler(unsigned long);
static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev); static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, 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, static int check_reset(struct scsi_cmnd * SCpnt,
struct sdebug_dev_info * devip); struct sdebug_dev_info * devip);
static int schedule_resp(struct scsi_cmnd * cmnd, static int schedule_resp(struct scsi_cmnd * cmnd,
...@@ -264,49 +276,20 @@ static void sdebug_max_tgts_luns(void); ...@@ -264,49 +276,20 @@ static void sdebug_max_tgts_luns(void);
static struct device pseudo_primary; static struct device pseudo_primary;
static struct bus_type pseudo_lld_bus; 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 static
int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
{ {
unsigned char *cmd = (unsigned char *) SCpnt->cmnd; unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
int block, upper_blk, num, k; int block, upper_blk, num, k;
unsigned char *buff;
int errsts = 0; int errsts = 0;
int target = SCpnt->device->id; int target = SCpnt->device->id;
int bufflen = SCpnt->request_bufflen;
unsigned long capac;
struct sdebug_dev_info * devip = NULL; struct sdebug_dev_info * devip = NULL;
unsigned char * sbuff;
int inj_recovered = 0; int inj_recovered = 0;
if (done == NULL) if (done == NULL)
return 0; /* assume mid level reprocessing command */ 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) { if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
printk(KERN_INFO "scsi_debug: cmd "); printk(KERN_INFO "scsi_debug: cmd ");
for (k = 0, num = SCpnt->cmd_len; k < num; ++k) 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) ...@@ -328,9 +311,11 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
return schedule_resp(SCpnt, NULL, done, return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0); DID_NO_CONNECT << 16, 0);
if ((scsi_debug_every_nth > 0) && if ((scsi_debug_every_nth != 0) &&
(++scsi_debug_cmnd_count >= scsi_debug_every_nth)) { (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
scsi_debug_cmnd_count =0; scsi_debug_cmnd_count = 0;
if (scsi_debug_every_nth < -1)
scsi_debug_every_nth = -1;
if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
return 0; /* ignore command causing timeout */ return 0; /* ignore command causing timeout */
else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) 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) ...@@ -339,23 +324,14 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
switch (*cmd) { switch (*cmd) {
case INQUIRY: /* mandatory, ignore unit attention */ case INQUIRY: /* mandatory, ignore unit attention */
errsts = resp_inquiry(cmd, target, buff, bufflen, devip); errsts = resp_inquiry(SCpnt, target, devip);
break; break;
case REQUEST_SENSE: /* mandatory, ignore unit attention */ case REQUEST_SENSE: /* mandatory, ignore unit attention */
if (devip) { errsts = resp_requests(SCpnt, 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;
}
break; break;
case REZERO_UNIT: /* actually this is REWIND for SSC */ case REZERO_UNIT: /* actually this is REWIND for SSC */
case START_STOP: case START_STOP:
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case ALLOW_MEDIUM_REMOVAL: case ALLOW_MEDIUM_REMOVAL:
if ((errsts = check_reset(SCpnt, devip))) if ((errsts = check_reset(SCpnt, devip)))
...@@ -366,40 +342,24 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -366,40 +342,24 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
break; break;
case SEND_DIAGNOSTIC: /* mandatory */ case SEND_DIAGNOSTIC: /* mandatory */
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case TEST_UNIT_READY: /* mandatory */ case TEST_UNIT_READY: /* mandatory */
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case RESERVE: case RESERVE:
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case RESERVE_10: case RESERVE_10:
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case RELEASE: case RELEASE:
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case RELEASE_10: case RELEASE_10:
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
case READ_CAPACITY: case READ_CAPACITY:
errsts = check_reset(SCpnt, devip); errsts = resp_readcap(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;
}
break; break;
case READ_16: case READ_16:
case READ_12: case READ_12:
...@@ -432,12 +392,15 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -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); errsts = resp_read(SCpnt, upper_blk, block, num, devip);
if (inj_recovered && (0 == errsts)) { if (inj_recovered && (0 == errsts)) {
mk_sense_buffer(devip, RECOVERED_ERROR, mk_sense_buffer(devip, RECOVERED_ERROR,
THRESHHOLD_EXCEEDED, 0, 18); THRESHHOLD_EXCEEDED, 0);
errsts = check_condition_result; errsts = check_condition_result;
} }
break; break;
case REPORT_LUNS: /* mandatory, ignore unit attention */ 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; break;
case WRITE_16: case WRITE_16:
case WRITE_12: case WRITE_12:
...@@ -470,19 +433,16 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -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); errsts = resp_write(SCpnt, upper_blk, block, num, devip);
if (inj_recovered && (0 == errsts)) { if (inj_recovered && (0 == errsts)) {
mk_sense_buffer(devip, RECOVERED_ERROR, mk_sense_buffer(devip, RECOVERED_ERROR,
THRESHHOLD_EXCEEDED, 0, 18); THRESHHOLD_EXCEEDED, 0);
errsts = check_condition_result; errsts = check_condition_result;
} }
break; break;
case MODE_SENSE: case MODE_SENSE:
case MODE_SENSE_10: case MODE_SENSE_10:
if ((errsts = check_reset(SCpnt, devip))) errsts = resp_mode_sense(SCpnt, target, devip);
break;
errsts = resp_mode_sense(cmd, target, buff, bufflen, devip);
break; break;
case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE:
errsts = check_reset(SCpnt, devip); errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break; break;
default: default:
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
...@@ -490,7 +450,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) ...@@ -490,7 +450,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
"supported\n", *cmd); "supported\n", *cmd);
if ((errsts = check_reset(SCpnt, devip))) if ((errsts = check_reset(SCpnt, devip)))
break; /* Unit attention takes precedence */ 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; errsts = check_condition_result;
break; break;
} }
...@@ -513,18 +473,105 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip) ...@@ -513,18 +473,105 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
printk(KERN_INFO "scsi_debug: Reporting Unit " printk(KERN_INFO "scsi_debug: Reporting Unit "
"attention: power on reset\n"); "attention: power on reset\n");
devip->reset = 0; 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 check_condition_result;
} }
return 0; return 0;
} }
#define SDEBUG_LONG_INQ_SZ 96 /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
#define SDEBUG_MAX_INQ_ARR_SZ 128 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;
static const char * vendor_id = "Linux "; if (0 == scp->request_bufflen)
static const char * product_id = "scsi_debug "; return 0;
static const char * product_rev = "0004"; 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;
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, static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
const char * dev_id_str, int dev_id_str_len) 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, ...@@ -536,8 +583,8 @@ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
arr[0] = 0x2; /* ASCII */ arr[0] = 0x2; /* ASCII */
arr[1] = 0x1; arr[1] = 0x1;
arr[2] = 0x0; arr[2] = 0x0;
memcpy(&arr[4], vendor_id, 8); memcpy(&arr[4], inq_vendor_id, 8);
memcpy(&arr[12], product_id, 16); memcpy(&arr[12], inq_product_id, 16);
memcpy(&arr[28], dev_id_str, dev_id_str_len); memcpy(&arr[28], dev_id_str, dev_id_str_len);
num = 8 + 16 + dev_id_str_len; num = 8 + 16 + dev_id_str_len;
arr[3] = num; arr[3] = num;
...@@ -558,24 +605,25 @@ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num, ...@@ -558,24 +605,25 @@ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
return num + 12; 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 pq_pdt;
unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ]; unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
int min_len = bufflen > SDEBUG_MAX_INQ_ARR_SZ ? unsigned char *cmd = (unsigned char *)scp->cmnd;
SDEBUG_MAX_INQ_ARR_SZ : bufflen; int alloc_len;
if (bufflen < cmd[4]) alloc_len = (cmd[3] << 8) + cmd[4];
printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d "
"< alloc_length=%d\n", bufflen, (int)cmd[4]);
memset(buff, 0, bufflen);
memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ); memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
pq_pdt = (scsi_debug_ptype & 0x1f); pq_pdt = (scsi_debug_ptype & 0x1f);
arr[0] = pq_pdt; arr[0] = pq_pdt;
if (0x2 & cmd[1]) { /* CMDDT bit set */ if (0x2 & cmd[1]) { /* CMDDT bit set */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18); 0);
return check_condition_result; return check_condition_result;
} else if (0x1 & cmd[1]) { /* EVPD bit set */ } else if (0x1 & cmd[1]) { /* EVPD bit set */
int dev_id_num, len; int dev_id_num, len;
...@@ -600,11 +648,11 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, ...@@ -600,11 +648,11 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
} else { } else {
/* Illegal request, invalid field in cdb */ /* Illegal request, invalid field in cdb */
mk_sense_buffer(devip, ILLEGAL_REQUEST, mk_sense_buffer(devip, ILLEGAL_REQUEST,
INVALID_FIELD_IN_CDB, 0, 18); INVALID_FIELD_IN_CDB, 0);
return check_condition_result; return check_condition_result;
} }
memcpy(buff, arr, min_len); return fill_from_dev_buffer(scp, arr,
return 0; min(alloc_len, SDEBUG_MAX_INQ_ARR_SZ));
} }
/* drops through here for a standard inquiry */ /* drops through here for a standard inquiry */
arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */ 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, ...@@ -612,20 +660,67 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
arr[3] = 2; /* response_data_format==2 */ arr[3] = 2; /* response_data_format==2 */
arr[4] = SDEBUG_LONG_INQ_SZ - 5; arr[4] = SDEBUG_LONG_INQ_SZ - 5;
arr[6] = 0x1; /* claim: ADDR16 */ arr[6] = 0x1; /* claim: ADDR16 */
/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */ arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
memcpy(&arr[8], vendor_id, 8); memcpy(&arr[8], inq_vendor_id, 8);
memcpy(&arr[16], product_id, 16); memcpy(&arr[16], inq_product_id, 16);
memcpy(&arr[32], product_rev, 4); memcpy(&arr[32], inq_product_rev, 4);
/* version descriptors (2 bytes each) follow */ /* version descriptors (2 bytes each) follow */
arr[58] = 0x0; arr[59] = 0x40; /* SAM-2 */ 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) { if (scsi_debug_ptype == 0) {
arr[62] = 0x1; arr[63] = 0x80; /* SBC */ arr[62] = 0x1; arr[63] = 0x80; /* SBC */
} else if (scsi_debug_ptype == 1) { } else if (scsi_debug_ptype == 1) {
arr[62] = 0x2; arr[63] = 0x00; /* SSC */ arr[62] = 0x2; arr[63] = 0x00; /* SSC */
} }
memcpy(buff, arr, min_len); return fill_from_dev_buffer(scp, arr,
return 0; 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>> */ /* <<Following mode page info copied from ST318451LW>> */
...@@ -706,34 +801,29 @@ static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target) ...@@ -706,34 +801,29 @@ static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
#define SDEBUG_MAX_MSENSE_SZ 256 #define SDEBUG_MAX_MSENSE_SZ 256
static int resp_mode_sense(unsigned char * cmd, int target, static int resp_mode_sense(struct scsi_cmnd * scp, int target,
unsigned char * buff, int bufflen,
struct sdebug_dev_info * devip) struct sdebug_dev_info * devip)
{ {
unsigned char dbd; unsigned char dbd;
int pcontrol, pcode, subpcode; int pcontrol, pcode, subpcode;
unsigned char dev_spec; unsigned char dev_spec;
int alloc_len, msense_6, offset, len; int alloc_len, msense_6, offset, len, errsts;
unsigned char * ap; unsigned char * ap;
unsigned char arr[SDEBUG_MAX_MSENSE_SZ]; unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
int min_len = bufflen > SDEBUG_MAX_MSENSE_SZ ? unsigned char *cmd = (unsigned char *)scp->cmnd;
SDEBUG_MAX_MSENSE_SZ : bufflen;
SCSI_LOG_LLQUEUE(3, printk("Mode sense ...(%p %d)\n", buff, bufflen)); if ((errsts = check_reset(scp, devip)))
return errsts;
dbd = cmd[1] & 0x8; dbd = cmd[1] & 0x8;
pcontrol = (cmd[2] & 0xc0) >> 6; pcontrol = (cmd[2] & 0xc0) >> 6;
pcode = cmd[2] & 0x3f; pcode = cmd[2] & 0x3f;
subpcode = cmd[3]; subpcode = cmd[3];
msense_6 = (MODE_SENSE == cmd[0]); msense_6 = (MODE_SENSE == cmd[0]);
alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]); 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); memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
if (0x3 == pcontrol) { /* Saving values not supported */ if (0x3 == pcontrol) { /* Saving values not supported */
mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP, mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
0, 18); 0);
return check_condition_result; return check_condition_result;
} }
dev_spec = DEV_READONLY(target) ? 0x80 : 0x0; dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
...@@ -748,7 +838,7 @@ static int resp_mode_sense(unsigned char * cmd, int target, ...@@ -748,7 +838,7 @@ static int resp_mode_sense(unsigned char * cmd, int target,
if (0 != subpcode) { /* TODO: Control Extension page */ if (0 != subpcode) { /* TODO: Control Extension page */
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18); 0);
return check_condition_result; return check_condition_result;
} }
switch (pcode) { switch (pcode) {
...@@ -787,146 +877,104 @@ static int resp_mode_sense(unsigned char * cmd, int target, ...@@ -787,146 +877,104 @@ static int resp_mode_sense(unsigned char * cmd, int target,
break; break;
default: default:
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18); 0);
return check_condition_result; return check_condition_result;
} }
if (msense_6) if (msense_6)
arr[0] = offset - 1; arr[0] = offset - 1;
else { else {
offset -= 2; arr[0] = ((offset - 2) >> 8) & 0xff;
arr[0] = (offset >> 8) & 0xff; arr[1] = (offset - 2) & 0xff;
arr[1] = offset & 0xff;
} }
memcpy(buff, arr, min_len); return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
return 0;
} }
static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block, static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip) 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; unsigned long iflags;
int ret;
if (upper_blk || (block + num > sdebug_capacity)) { if (upper_blk || (block + num > sdebug_capacity)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE, mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
0, 18); 0);
return check_condition_result; return check_condition_result;
} }
if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
(block <= OPT_MEDIUM_ERR_ADDR) && (block <= OPT_MEDIUM_ERR_ADDR) &&
((block + num) > OPT_MEDIUM_ERR_ADDR)) { ((block + num) > OPT_MEDIUM_ERR_ADDR)) {
mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR, mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
0, 18); 0);
/* claim unrecoverable read error */ /* claim unrecoverable read error */
return check_condition_result; return check_condition_result;
} }
read_lock_irqsave(&atomic_rw, iflags); read_lock_irqsave(&atomic_rw, iflags);
sgcount = 0; ret = fill_from_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
nbytes = bufflen; num * SECT_SIZE);
/* 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);
read_unlock_irqrestore(&atomic_rw, iflags); read_unlock_irqrestore(&atomic_rw, iflags);
return 0; return ret;
} }
static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block, static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip) 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; unsigned long iflags;
int res;
if (upper_blk || (block + num > sdebug_capacity)) { if (upper_blk || (block + num > sdebug_capacity)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE, mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
0, 18); 0);
return check_condition_result; return check_condition_result;
} }
write_lock_irqsave(&atomic_rw, iflags); write_lock_irqsave(&atomic_rw, iflags);
sgcount = 0; res = fetch_to_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
nbytes = bufflen; num * SECT_SIZE);
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);
write_unlock_irqrestore(&atomic_rw, iflags); 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; return 0;
} }
static int resp_report_luns(unsigned char * cmd, unsigned char * buff, #define SDEBUG_RLUN_ARR_SZ 128
int bufflen, struct sdebug_dev_info * devip)
static int resp_report_luns(struct scsi_cmnd * scp,
struct sdebug_dev_info * devip)
{ {
unsigned int alloc_len; unsigned int alloc_len;
int lun_cnt, i, upper; int lun_cnt, i, upper;
unsigned char *cmd = (unsigned char *)scp->cmnd;
int select_report = (int)cmd[2]; int select_report = (int)cmd[2];
struct scsi_lun *one_lun; 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); alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
if ((alloc_len < 16) || (select_report > 2)) { if ((alloc_len < 16) || (select_report > 2)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
0, 18); 0);
return check_condition_result; return check_condition_result;
} }
if (bufflen > 8) { /* can produce response with up to 16k luns /* can produce response with up to 16k luns (lun 0 to lun 16383) */
(lun 0 to lun 16383) */ memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
memset(buff, 0, bufflen); lun_cnt = scsi_debug_max_luns;
lun_cnt = scsi_debug_max_luns; arr[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff;
buff[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff; arr[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff;
buff[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff; lun_cnt = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
lun_cnt = min((int)((bufflen - 8) / sizeof(struct scsi_lun)), sizeof(struct scsi_lun)), lun_cnt);
lun_cnt); one_lun = (struct scsi_lun *) &arr[8];
one_lun = (struct scsi_lun *) &buff[8]; for (i = 0; i < lun_cnt; i++) {
for (i = 0; i < lun_cnt; i++) { upper = (i >> 8) & 0x3f;
upper = (i >> 8) & 0x3f; if (upper)
if (upper) one_lun[i].scsi_lun[0] =
one_lun[i].scsi_lun[0] = (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
(upper | (SAM2_LUN_ADDRESS_METHOD << 6)); one_lun[i].scsi_lun[1] = i & 0xff;
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. */ /* When timer goes off this function is called. */
...@@ -1041,14 +1089,19 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) ...@@ -1041,14 +1089,19 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
open_devip->reset = 1; open_devip->reset = 1;
open_devip->used = 1; open_devip->used = 1;
memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN); memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
open_devip->sense_buff[0] = 0x70; 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 open_devip;
} }
return NULL; return NULL;
} }
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, 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; unsigned char * sbuff;
...@@ -1060,11 +1113,9 @@ static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, ...@@ -1060,11 +1113,9 @@ static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
sbuff[2] = asc; sbuff[2] = asc;
sbuff[3] = asq; sbuff[3] = asq;
} else { } else {
if (inbandLen > SDEBUG_SENSE_LEN)
inbandLen = SDEBUG_SENSE_LEN;
sbuff[0] = 0x70; /* fixed, current */ sbuff[0] = 0x70; /* fixed, current */
sbuff[2] = key; sbuff[2] = key;
sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0; sbuff[7] = 0xa; /* implies 18 byte sense buffer */
sbuff[12] = asc; sbuff[12] = asc;
sbuff[13] = asq; sbuff[13] = asq;
} }
...@@ -1355,7 +1406,7 @@ MODULE_PARM_DESC(num_parts, "number of partitions(def=0)"); ...@@ -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(num_tgts, "number of SCSI targets per host to simulate");
MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->..."); MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->...");
MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])"); 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]; static char sdebug_info[256];
...@@ -1391,7 +1442,7 @@ static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **sta ...@@ -1391,7 +1442,7 @@ static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **sta
if (1 != sscanf(arr, "%d", &pos)) if (1 != sscanf(arr, "%d", &pos))
return -EINVAL; return -EINVAL;
scsi_debug_opts = pos; scsi_debug_opts = pos;
if (scsi_debug_every_nth > 0) if (scsi_debug_every_nth != 0)
scsi_debug_cmnd_count = 0; scsi_debug_cmnd_count = 0;
return length; return length;
} }
...@@ -1547,7 +1598,7 @@ static ssize_t sdebug_every_nth_store(struct device_driver * ddp, ...@@ -1547,7 +1598,7 @@ static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
{ {
int nth; 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_every_nth = nth;
scsi_debug_cmnd_count = 0; scsi_debug_cmnd_count = 0;
return count; return count;
......
...@@ -268,16 +268,42 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, ...@@ -268,16 +268,42 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
* *
* Return value: * Return value:
* SUCCESS or FAILED or NEEDS_RETRY * 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) static int scsi_check_sense(struct scsi_cmnd *scmd)
{ {
if (!SCSI_SENSE_VALID(scmd)) struct scsi_sense_hdr sshdr;
return FAILED;
if (scmd->sense_buffer[2] & 0xe0) if (! scsi_command_normalize_sense(scmd, &sshdr))
return SUCCESS; return FAILED; /* no valid sense data */
switch (scmd->sense_buffer[2] & 0xf) { 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 (sshdr.sense_key) {
case NO_SENSE: case NO_SENSE:
return SUCCESS; return SUCCESS;
case RECOVERED_ERROR: case RECOVERED_ERROR:
...@@ -301,19 +327,15 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) ...@@ -301,19 +327,15 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
* if the device is in the process of becoming ready, we * if the device is in the process of becoming ready, we
* should retry. * should retry.
*/ */
if ((scmd->sense_buffer[12] == 0x04) && if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
(scmd->sense_buffer[13] == 0x01)) {
return NEEDS_RETRY; return NEEDS_RETRY;
}
/* /*
* if the device is not started, we need to wake * if the device is not started, we need to wake
* the error handler to start the motor * the error handler to start the motor
*/ */
if (scmd->device->allow_restart && if (scmd->device->allow_restart &&
(scmd->sense_buffer[12] == 0x04) && (sshdr.asc == 0x04) && (sshdr.ascq == 0x02))
(scmd->sense_buffer[13] == 0x02)) {
return FAILED; return FAILED;
}
return SUCCESS; return SUCCESS;
/* these three are not supported */ /* these three are not supported */
...@@ -1358,7 +1380,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) ...@@ -1358,7 +1380,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
return SUCCESS; return SUCCESS;
case RESERVATION_CONFLICT: 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->host->host_no, scmd->device->channel,
scmd->device->id, scmd->device->lun); scmd->device->id, scmd->device->lun);
return SUCCESS; /* causes immediate i/o error */ return SUCCESS; /* causes immediate i/o error */
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <scsi/scsi_ioctl.h> #include <scsi/scsi_ioctl.h>
#include <scsi/scsi_request.h> #include <scsi/scsi_request.h>
#include <scsi/sg.h> #include <scsi/sg.h>
#include <scsi/scsi_dbg.h>
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -94,12 +95,13 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd, ...@@ -94,12 +95,13 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
{ {
struct scsi_request *sreq; struct scsi_request *sreq;
int result; int result;
struct scsi_sense_hdr sshdr;
SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd)); SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd));
sreq = scsi_allocate_request(sdev, GFP_KERNEL); sreq = scsi_allocate_request(sdev, GFP_KERNEL);
if (!sreq) { if (!sreq) {
printk("SCSI internal ioctl failed, no memory\n"); printk(KERN_WARNING "SCSI internal ioctl failed, no memory\n");
return -ENOMEM; return -ENOMEM;
} }
...@@ -108,17 +110,21 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd, ...@@ -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)); SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", sreq->sr_result));
if (driver_byte(sreq->sr_result)) { if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
switch (sreq->sr_sense_buffer[2] & 0xf) { (scsi_request_normalize_sense(sreq, &sshdr))) {
switch (sshdr.sense_key) {
case ILLEGAL_REQUEST: case ILLEGAL_REQUEST:
if (cmd[0] == ALLOW_MEDIUM_REMOVAL) if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
sdev->lockable = 0; sdev->lockable = 0;
else 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; break;
case NOT_READY: /* This happens if there is no disc in drive */ case NOT_READY: /* This happens if there is no disc in drive */
if (sdev->removable && (cmd[0] != TEST_UNIT_READY)) { 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; break;
} }
case UNIT_ATTENTION: case UNIT_ATTENTION:
...@@ -128,16 +134,15 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd, ...@@ -128,16 +134,15 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
break; break;
} }
default: /* Fall through for non-removable media */ 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->host->host_no,
sdev->channel,
sdev->id, sdev->id,
sdev->lun, sdev->lun,
sreq->sr_result); sreq->sr_result);
printk("\tSense class %x, sense error %x, extended sense %x\n", scsi_print_req_sense(" ", sreq);
sense_class(sreq->sr_sense_buffer[0]), break;
sense_error(sreq->sr_sense_buffer[0]),
sreq->sr_sense_buffer[2] & 0xf);
} }
} }
...@@ -401,7 +406,8 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) ...@@ -401,7 +406,8 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
case SCSI_IOCTL_SYNC: case SCSI_IOCTL_SYNC:
case SCSI_IOCTL_START_UNIT: case SCSI_IOCTL_START_UNIT:
case SCSI_IOCTL_STOP_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; break;
default: default:
break; break;
......
...@@ -718,7 +718,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, ...@@ -718,7 +718,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
clear_errors = 0; clear_errors = 0;
if (scsi_command_normalize_sense(cmd, &sshdr)) { 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]; int len = 8 + cmd->sense_buffer[7];
...@@ -844,9 +844,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, ...@@ -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); cmd = scsi_end_request(cmd, 0, this_count, 1);
return; return;
case VOLUME_OVERFLOW: case VOLUME_OVERFLOW:
printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ", printk(KERN_INFO "Volume overflow <%d %d %d %d> CDB: ",
cmd->device->host->host_no, (int) cmd->device->channel, cmd->device->host->host_no,
(int) cmd->device->id, (int) cmd->device->lun); (int)cmd->device->channel,
(int)cmd->device->id, (int)cmd->device->lun);
__scsi_print_command(cmd->data_cmnd); __scsi_print_command(cmd->data_cmnd);
scsi_print_sense("", cmd); scsi_print_sense("", cmd);
cmd = scsi_end_request(cmd, 0, block_bytes, 1); 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, ...@@ -865,8 +866,8 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
return; return;
} }
if (result) { if (result) {
printk("SCSI error : <%d %d %d %d> return code = 0x%x\n", printk(KERN_INFO "SCSI error : <%d %d %d %d> return code "
cmd->device->host->host_no, "= 0x%x\n", cmd->device->host->host_no,
cmd->device->channel, cmd->device->channel,
cmd->device->id, cmd->device->id,
cmd->device->lun, result); cmd->device->lun, result);
...@@ -1604,12 +1605,15 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries) ...@@ -1604,12 +1605,15 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries)
sreq->sr_data_direction = DMA_NONE; sreq->sr_data_direction = DMA_NONE;
scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries); scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries);
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && sdev->removable) {
((sreq->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION || struct scsi_sense_hdr sshdr;
(sreq->sr_sense_buffer[2] & 0x0f) == NOT_READY) &&
sdev->removable) { if ((scsi_request_normalize_sense(sreq, &sshdr)) &&
sdev->changed = 1; ((sshdr.sense_key == UNIT_ATTENTION) ||
sreq->sr_result = 0; (sshdr.sense_key == NOT_READY))) {
sdev->changed = 1;
sreq->sr_result = 0;
}
} }
result = sreq->sr_result; result = sreq->sr_result;
scsi_release_request(sreq); scsi_release_request(sreq);
...@@ -1668,6 +1672,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) ...@@ -1668,6 +1672,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
case SDEV_CREATED: case SDEV_CREATED:
case SDEV_RUNNING: case SDEV_RUNNING:
case SDEV_QUIESCE: case SDEV_QUIESCE:
case SDEV_BLOCK:
break; break;
default: default:
goto illegal; goto illegal;
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#include <scsi/scsi_request.h> #include <scsi/scsi_request.h>
#include <scsi/scsi_transport.h> #include <scsi/scsi_transport.h>
#include <scsi/scsi_eh.h>
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -253,6 +254,11 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -253,6 +254,11 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
sdev->request_queue->queuedata = sdev; sdev->request_queue->queuedata = sdev;
scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); 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) { if (shost->hostt->slave_alloc) {
ret = shost->hostt->slave_alloc(sdev); ret = shost->hostt->slave_alloc(sdev);
if (ret) { if (ret) {
...@@ -262,15 +268,10 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -262,15 +268,10 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
*/ */
if (ret == -ENXIO) if (ret == -ENXIO)
display_failure_msg = 0; 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) if (scsi_sysfs_device_initialize(sdev) != 0)
goto out_cleanup_slave; goto out_cleanup_slave;
...@@ -290,6 +291,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -290,6 +291,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
out_cleanup_slave: out_cleanup_slave:
if (shost->hostt->slave_destroy) if (shost->hostt->slave_destroy)
shost->hostt->slave_destroy(sdev); shost->hostt->slave_destroy(sdev);
out_device_destroy:
if (shost->transportt->device_destroy)
shost->transportt->device_destroy(sdev);
out_free_queue: out_free_queue:
scsi_free_queue(sdev->request_queue); scsi_free_queue(sdev->request_queue);
out_free_dev: out_free_dev:
...@@ -322,6 +326,7 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, ...@@ -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 first_inquiry_len, try_inquiry_len, next_inquiry_len;
int response_len = 0; int response_len = 0;
int pass, count; int pass, count;
struct scsi_sense_hdr sshdr;
*bflags = 0; *bflags = 0;
...@@ -357,17 +362,20 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, ...@@ -357,17 +362,20 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
sreq->sr_result)); sreq->sr_result));
if (sreq->sr_result) { if (sreq->sr_result) {
/*
/* not-ready to ready transition or power-on - good */ * not-ready to ready transition [asc/ascq=0x28/0x0]
/* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */ * or power-on, reset [asc/ascq=0x29/0x0], continue.
/* Supposedly, but many buggy devices do so anyway. */ * INQUIRY should not yield UNIT_ATTENTION
* but many buggy devices do so anyway.
*/
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
(sreq->sr_sense_buffer[2] & 0xf) == scsi_request_normalize_sense(sreq, &sshdr)) {
UNIT_ATTENTION && if ((sshdr.sense_key == UNIT_ATTENTION) &&
(sreq->sr_sense_buffer[12] == 0x28 || ((sshdr.asc == 0x28) ||
sreq->sr_sense_buffer[12] == 0x29) && (sshdr.asc == 0x29)) &&
sreq->sr_sense_buffer[13] == 0) (sshdr.ascq == 0))
continue; continue;
}
} }
break; break;
} }
...@@ -741,6 +749,8 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, ...@@ -741,6 +749,8 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
} else { } else {
if (sdev->host->hostt->slave_destroy) if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
if (sdev->host->transportt->device_destroy)
sdev->host->transportt->device_destroy(sdev);
put_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);
} }
out: out:
...@@ -893,6 +903,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, ...@@ -893,6 +903,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
struct scsi_lun *lunp, *lun_data; struct scsi_lun *lunp, *lun_data;
struct scsi_request *sreq; struct scsi_request *sreq;
u8 *data; u8 *data;
struct scsi_sense_hdr sshdr;
/* /*
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set. * 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, ...@@ -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 " %s (try %d) result 0x%x\n", sreq->sr_result
? "failed" : "successful", retries, ? "failed" : "successful", retries,
sreq->sr_result)); sreq->sr_result));
if (sreq->sr_result == 0 || if (sreq->sr_result == 0)
sreq->sr_sense_buffer[2] != UNIT_ATTENTION)
break; break;
else if (scsi_request_normalize_sense(sreq, &sshdr)) {
if (sshdr.sense_key != UNIT_ATTENTION)
break;
}
} }
if (sreq->sr_result) { if (sreq->sr_result) {
...@@ -1299,5 +1313,7 @@ void scsi_free_host_dev(struct scsi_device *sdev) ...@@ -1299,5 +1313,7 @@ void scsi_free_host_dev(struct scsi_device *sdev)
if (sdev->host->hostt->slave_destroy) if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
if (sdev->host->transportt->device_destroy)
sdev->host->transportt->device_destroy(sdev);
put_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);
} }
...@@ -169,7 +169,10 @@ void scsi_device_dev_release(struct device *dev) ...@@ -169,7 +169,10 @@ void scsi_device_dev_release(struct device *dev)
if (delete) { if (delete) {
struct scsi_target *starget = to_scsi_target(parent); struct scsi_target *starget = to_scsi_target(parent);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
if (!starget->create) { if (!starget->create) {
if (shost->transportt->target_destroy)
shost->transportt->target_destroy(starget);
device_del(parent); device_del(parent);
if (starget->transport_classdev.class) if (starget->transport_classdev.class)
class_device_unregister(&starget->transport_classdev); class_device_unregister(&starget->transport_classdev);
...@@ -601,6 +604,8 @@ void scsi_remove_device(struct scsi_device *sdev) ...@@ -601,6 +604,8 @@ void scsi_remove_device(struct scsi_device *sdev)
scsi_device_set_state(sdev, SDEV_DEL); scsi_device_set_state(sdev, SDEV_DEL);
if (sdev->host->hostt->slave_destroy) if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
if (sdev->host->transportt->device_destroy)
sdev->host->transportt->device_destroy(sdev);
put_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);
out: out:
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
static void transport_class_release(struct class_device *class_dev); static void transport_class_release(struct class_device *class_dev);
static void host_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_NUM_ATTRS 4 /* increase this if you add attributes */
#define FC_STARGET_OTHER_ATTRS 0 /* increase this if you add "always on" #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) ...@@ -87,10 +89,18 @@ static int fc_setup_starget_transport_attrs(struct scsi_target *starget)
fc_starget_port_name(starget) = -1; fc_starget_port_name(starget) = -1;
fc_starget_port_id(starget) = -1; fc_starget_port_id(starget) = -1;
fc_starget_dev_loss_tmo(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; 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) 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) ...@@ -99,10 +109,18 @@ static int fc_setup_host_transport_attrs(struct Scsi_Host *shost)
* all transport attributes to valid values per host. * all transport attributes to valid values per host.
*/ */
fc_host_link_down_tmo(shost) = -1; 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; 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) static void transport_class_release(struct class_device *class_dev)
{ {
struct scsi_target *starget = transport_class_to_starget(class_dev); struct scsi_target *starget = transport_class_to_starget(class_dev);
...@@ -277,11 +295,13 @@ fc_attach_transport(struct fc_function_template *ft) ...@@ -277,11 +295,13 @@ fc_attach_transport(struct fc_function_template *ft)
i->t.target_attrs = &i->starget_attrs[0]; i->t.target_attrs = &i->starget_attrs[0];
i->t.target_class = &fc_transport_class; i->t.target_class = &fc_transport_class;
i->t.target_setup = &fc_setup_starget_transport_attrs; 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.target_size = sizeof(struct fc_starget_attrs);
i->t.host_attrs = &i->host_attrs[0]; i->t.host_attrs = &i->host_attrs[0];
i->t.host_class = &fc_host_class; i->t.host_class = &fc_host_class;
i->t.host_setup = &fc_setup_host_transport_attrs; 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->t.host_size = sizeof(struct fc_host_attrs);
i->f = ft; i->f = ft;
...@@ -353,7 +373,7 @@ static int fc_device_unblock(struct device *dev, void *data) ...@@ -353,7 +373,7 @@ static int fc_device_unblock(struct device *dev, void *data)
* that fail to recover in the alloted time. * that fail to recover in the alloted time.
* @data: scsi target that failed to reappear 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; struct scsi_target *starget = (struct scsi_target *)data;
...@@ -388,7 +408,7 @@ int ...@@ -388,7 +408,7 @@ int
fc_target_block(struct scsi_target *starget) fc_target_block(struct scsi_target *starget)
{ {
int timeout = fc_starget_dev_loss_tmo(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) if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
return -EINVAL; return -EINVAL;
...@@ -396,10 +416,7 @@ fc_target_block(struct scsi_target *starget) ...@@ -396,10 +416,7 @@ fc_target_block(struct scsi_target *starget)
device_for_each_child(&starget->dev, NULL, fc_device_block); device_for_each_child(&starget->dev, NULL, fc_device_block);
/* The scsi lld blocks this target for the timeout period only. */ /* The scsi lld blocks this target for the timeout period only. */
timer->data = (unsigned long)starget; schedule_delayed_work(work, timeout * HZ);
timer->expires = jiffies + timeout * HZ;
timer->function = fc_timeout_blocked_tgt;
add_timer(timer);
return 0; return 0;
} }
...@@ -424,7 +441,8 @@ fc_target_unblock(struct scsi_target *starget) ...@@ -424,7 +441,8 @@ fc_target_unblock(struct scsi_target *starget)
* failure as the state machine state change will validate the * failure as the state machine state change will validate the
* transaction. * 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); device_for_each_child(&starget->dev, NULL, fc_device_unblock);
} }
...@@ -436,7 +454,7 @@ EXPORT_SYMBOL(fc_target_unblock); ...@@ -436,7 +454,7 @@ EXPORT_SYMBOL(fc_target_unblock);
* @data: scsi host that failed to recover its devices in the alloted * @data: scsi host that failed to recover its devices in the alloted
* time. * 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_Host *shost = (struct Scsi_Host *)data;
struct scsi_device *sdev; struct scsi_device *sdev;
...@@ -475,7 +493,7 @@ fc_host_block(struct Scsi_Host *shost) ...@@ -475,7 +493,7 @@ fc_host_block(struct Scsi_Host *shost)
{ {
struct scsi_device *sdev; struct scsi_device *sdev;
int timeout = fc_host_link_down_tmo(shost); 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) if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
return -EINVAL; return -EINVAL;
...@@ -484,11 +502,7 @@ fc_host_block(struct Scsi_Host *shost) ...@@ -484,11 +502,7 @@ fc_host_block(struct Scsi_Host *shost)
scsi_internal_device_block(sdev); scsi_internal_device_block(sdev);
} }
/* The scsi lld blocks this host for the timeout period only. */ schedule_delayed_work(work, timeout * HZ);
timer->data = (unsigned long)shost;
timer->expires = jiffies + timeout * HZ;
timer->function = fc_timeout_blocked_host;
add_timer(timer);
return 0; return 0;
} }
...@@ -516,7 +530,9 @@ fc_host_unblock(struct Scsi_Host *shost) ...@@ -516,7 +530,9 @@ fc_host_unblock(struct Scsi_Host *shost)
* failure as the state machine state change will validate the * failure as the state machine state change will validate the
* transaction. * 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) { shost_for_each_device(sdev, shost) {
scsi_internal_device_unblock(sdev); 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 { ...@@ -40,6 +40,11 @@ struct scsi_transport_template {
int (*target_setup)(struct scsi_target *); int (*target_setup)(struct scsi_target *);
int (*host_setup)(struct Scsi_Host *); 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 /* The size of the specific transport attribute structure (a
* space of this size will be left at the end of the * space of this size will be left at the end of the
* scsi_* structure */ * scsi_* structure */
......
...@@ -29,7 +29,7 @@ struct fc_starget_attrs { /* aka fc_target_attrs */ ...@@ -29,7 +29,7 @@ struct fc_starget_attrs { /* aka fc_target_attrs */
uint64_t node_name; uint64_t node_name;
uint64_t port_name; uint64_t port_name;
uint32_t dev_loss_tmo; /* Remote Port loss timeout in seconds. */ 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) \ #define fc_starget_port_id(x) \
...@@ -40,18 +40,18 @@ struct fc_starget_attrs { /* aka fc_target_attrs */ ...@@ -40,18 +40,18 @@ struct fc_starget_attrs { /* aka fc_target_attrs */
(((struct fc_starget_attrs *)&(x)->starget_data)->port_name) (((struct fc_starget_attrs *)&(x)->starget_data)->port_name)
#define fc_starget_dev_loss_tmo(x) \ #define fc_starget_dev_loss_tmo(x) \
(((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_tmo) (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_tmo)
#define fc_starget_dev_loss_timer(x) \ #define fc_starget_dev_loss_work(x) \
(((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_timer) (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_work)
struct fc_host_attrs { struct fc_host_attrs {
uint32_t link_down_tmo; /* Link Down timeout in seconds. */ 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) \ #define fc_host_link_down_tmo(x) \
(((struct fc_host_attrs *)(x)->shost_data)->link_down_tmo) (((struct fc_host_attrs *)(x)->shost_data)->link_down_tmo)
#define fc_host_link_down_timer(x) \ #define fc_host_link_down_work(x) \
(((struct fc_host_attrs *)(x)->shost_data)->link_down_timer) (((struct fc_host_attrs *)(x)->shost_data)->link_down_work)
/* The functions by which the transport class and the driver communicate */ /* 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