[EMAIL PROTECTED] said:
> While linux certainly would go down this path. NO SCSI HOST/BUS/
> DEVICE SHOULD !_EVER_! BE RESET DUE TO A RESERVATION CONFLICT. The
> fact that linux will see a reservation conflict as a reset canidate
> error is just #^#%% wrong. Obviously someone either failed to read an
> entire section of the SCSI-2 spec or is just too lazy to care about
> proper operations.
Seems appropriate to this discussion. The attached patch makes the linux
kernel (2.2.16) correctly respect reservations and also provides the
completion of the reset interface to allow them to be broken again using the
sg driver.
I tested it last night in a dual initiator environment. One of my colleagues
will be testing it today on a larger SAN ring. I've only just ported it up to
2.2.16 so there may still be lurking bugs...
James Bottomley
Index: linux/drivers/scsi/scsi.c
diff -u linux/drivers/scsi/scsi.c:1.1.1.7 linux/drivers/scsi/scsi.c:1.1.1.7.2.1
--- linux/drivers/scsi/scsi.c:1.1.1.7 Tue Jun 13 17:24:04 2000
+++ linux/drivers/scsi/scsi.c Tue Jun 13 18:01:08 2000
@@ -716,7 +716,7 @@
SCSI_LOG_SCAN_BUS(3,print_hostbyte(SCpnt->result));
SCSI_LOG_SCAN_BUS(3,printk("\n"));
- if (SCpnt->result) {
+ if (SCpnt->result && status_byte(SCpnt->result) != RESERVATION_CONFLICT) {
if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
(status_byte (SCpnt->result) & CHECK_CONDITION)) &&
((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
Index: linux/drivers/scsi/scsi.h
diff -u linux/drivers/scsi/scsi.h:1.1.1.4 linux/drivers/scsi/scsi.h:1.1.1.4.6.1
--- linux/drivers/scsi/scsi.h:1.1.1.4 Wed Jan 5 12:24:50 2000
+++ linux/drivers/scsi/scsi.h Tue Jun 13 17:59:02 2000
@@ -732,6 +732,14 @@
remove_wait_queue(QUEUE, &wait);\
current->state = TASK_RUNNING; \
}; }
+/* old style reset request from external source (private to sg.c and
+ * scsi_error.c, supplied by scsi_obsolete.c)
+ * */
+#define SCSI_TRY_RESET_DEVICE 1
+#define SCSI_TRY_RESET_BUS 2
+#define SCSI_TRY_RESET_HOST 3
+extern int scsi_old_reset(Scsi_Cmnd *, unsigned int);
+extern int scsi_reset_provider(Scsi_Device *, int);
#endif
Index: linux/drivers/scsi/scsi_error.c
diff -u linux/drivers/scsi/scsi_error.c:1.1.1.3
linux/drivers/scsi/scsi_error.c:1.1.1.3.12.2
--- linux/drivers/scsi/scsi_error.c:1.1.1.3 Thu Aug 26 08:18:40 1999
+++ linux/drivers/scsi/scsi_error.c Tue Jun 13 18:01:08 2000
@@ -34,6 +34,7 @@
#include "scsi.h"
#include "hosts.h"
#include "constants.h"
+#include <scsi/sg.h>
#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
@@ -981,9 +982,14 @@
case DID_SOFT_ERROR:
return NEEDS_RETRY;
+ case DID_ERROR:
+ if(msg_byte(SCpnt->result) == COMMAND_COMPLETE
+ && status_byte(SCpnt->result) == RESERVATION_CONFLICT)
+ /* execute reservation conflict processing code lower down */
+ break;
+ /* fall through */
case DID_BUS_BUSY:
case DID_PARITY:
- case DID_ERROR:
goto maybe_retry;
case DID_TIME_OUT:
/*
@@ -1059,8 +1065,12 @@
*/
return SUCCESS;
case BUSY:
- case RESERVATION_CONFLICT:
goto maybe_retry;
+ case RESERVATION_CONFLICT:
+ printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
+ SCpnt->host->host_no, SCpnt->channel,
+ SCpnt->device->id, SCpnt->device->lun);
+ return SUCCESS; /* causes immediate I/O error */
default:
return FAILED;
}
@@ -2041,6 +2051,62 @@
*/
if( host->eh_notify != NULL )
up(host->eh_notify);
+}
+
+/*
+ * Function: scsi_reset_provider
+ *
+ * Purpose: Send requested reset to a bus or device at any phase.
+ *
+ * Arguments: device - device to send reset to
+ * flag - reset type (see scsi.h)
+ *
+ * Returns: SUCCESS/FAILURE.
+ *
+ * Notes: This is used by the SCSI Generic driver to provide
+ * Bus/Device reset capability.
+ */
+int
+scsi_reset_provider(Scsi_Device *dev, int flag)
+{
+ int rtn;
+ /* get a dummy command to issue the reset to */
+ Scsi_Cmnd *SCpnt = scsi_allocate_device(NULL, dev, 1);
+ switch(flag) {
+ case SCSI_TRY_RESET_DEVICE:
+ rtn = scsi_try_bus_device_reset(SCpnt, 0);
+ if(rtn == SUCCESS)
+ break;
+ /* fall through */
+ case SCSI_TRY_RESET_BUS:
+ rtn = scsi_try_bus_reset(SCpnt);
+ if(rtn == SUCCESS)
+ break;
+ /* fall through */
+ case SCSI_TRY_RESET_HOST:
+ rtn = scsi_try_host_reset(SCpnt);
+ break;
+ default:
+ rtn = FAILED;
+ goto error_out;
+ }
+ if(rtn == FAILED) {
+ /* if we get here, the new code all failed, so try the old
+ * reset code */
+ unsigned int old_flags = SCSI_RESET_SYNCHRONOUS;
+ switch(flag) {
+ case SCSI_TRY_RESET_BUS:
+ old_flags |= SCSI_RESET_SUGGEST_BUS_RESET;
+ break;
+ case SCSI_TRY_RESET_HOST:
+ old_flags |= SCSI_RESET_SUGGEST_HOST_RESET;
+ break;
+ }
+ rtn = (scsi_old_reset(SCpnt, old_flags) == 0) ? SUCCESS : FAILED;
+ }
+ error_out:
+ scsi_release_command(SCpnt);
+ return rtn;
}
/*
Index: linux/drivers/scsi/scsi_obsolete.c
diff -u linux/drivers/scsi/scsi_obsolete.c:1.1.1.1
linux/drivers/scsi/scsi_obsolete.c:1.1.1.1.48.2
--- linux/drivers/scsi/scsi_obsolete.c:1.1.1.1 Thu Mar 18 10:33:14 1999
+++ linux/drivers/scsi/scsi_obsolete.c Tue Jun 13 18:01:09 2000
@@ -507,11 +507,14 @@
break;
case RESERVATION_CONFLICT:
- printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
- " reset.\n", SCpnt->host->host_no, SCpnt->channel);
- scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
- status = REDO;
- break;
+ /* Most HAs will return an error for this, so usually
+ * reservation conflicts will be processed under DID_ERROR
+ * code */
+ printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
+ SCpnt->host->host_no, SCpnt->channel,
+ SCpnt->device->id, SCpnt->device->lun);
+ status = CMD_FINISHED; /* returns I/O error */
+ break;
default:
printk ("Internal error %s %d \n"
"status byte = %d \n", __FILE__,
@@ -564,6 +567,14 @@
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
case DID_ERROR:
+ if(msg_byte(result) == COMMAND_COMPLETE
+ && status_byte(result) == RESERVATION_CONFLICT) {
+ printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
+ SCpnt->host->host_no, SCpnt->channel,
+ SCpnt->device->id, SCpnt->device->lun);
+ status = CMD_FINISHED; /* returns I/O error */
+ break;
+ }
status = MAYREDO;
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
@@ -1119,6 +1130,16 @@
return rtn;
}
+
+/* This function exports SCSI Bus, Device or Host reset capability
+ * and is for use with the SCSI generic driver.
+ */
+int scsi_old_reset(Scsi_Cmnd *SCpnt, unsigned int flag)
+{
+ int retval = scsi_reset(SCpnt, flag);
+ return retval;
+}
+
/*
Index: linux/drivers/scsi/scsi_syms.c
diff -u linux/drivers/scsi/scsi_syms.c:1.1.1.2
linux/drivers/scsi/scsi_syms.c:1.1.1.2.12.1
--- linux/drivers/scsi/scsi_syms.c:1.1.1.2 Thu Aug 26 08:18:33 1999
+++ linux/drivers/scsi/scsi_syms.c Tue Jun 13 17:59:02 2000
@@ -80,5 +80,10 @@
EXPORT_SYMBOL(scsi_devicelist);
EXPORT_SYMBOL(scsi_device_types);
+/*
+ * This symbol is for the sg device only
+ */
+EXPORT_SYMBOL(scsi_reset_provider);
+
#endif /* CONFIG_MODULES */