Linux Cluster using shared scsi
I've copied linux SCSI and quoted the entire message below so they can follow.
Your assertion that this works in 2.2.16 is incorrect, the patch to fix the linux reservation conflict handler has never been added to the official tree. I suspect you actually don't have vanilla 2.2.16 but instead have a redhat or other distribution patched version. Most distributions include the Steeleye SCSI clustering patches which correct reservation handling.
I've attached the complete patch, which fixes both the old and the new error handlers in the 2.2 kernel it applies against 2.2.18.
James Bottomley
Quoted 64 lines
Problem :
install two Linux-system with a shared scsi-bus and storage on that shared bus.
suppose :
system one : SCSI ID 7
system two : SCSI ID 6
shared disk : SCSI ID 4By default, you can mount the disk on both system. This is normal behavior, but
may impose data corruption.
To prevent this, you can SCSI-reserve a disk on one system. If the other system
would try to access this device, the system should return an i/o error due to the reservation.
This is a common technique used in
- Traditional Tru64 Unix ase clustering
- Tr64 Unix V5 Clustering to accomplish i/o barriers
- Windows-NT Clusters
- Steel-eye clustering
The reservation can be done using a standard tool like scuscu -f
/dev/sdb
scu > reserve deviceOn Linux, this works fine under Kernel version 2.2.16. Below is the code that accomplish this
/usr/src/linux/drivers/scsi/scsi_obsolete.cin routinescsi_old_donecase 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; default:As of kernel version 2.2.18, this code has changed, If a scsi reserve error
occurs, the device driver does a scsi reset. This way the scsi reservation is
gone, and the device can be accessed./usr/src/linux/drivers/scsi/scsi_obsolete.cin routinescsi_old_done
caseRESERVATION_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;Fix : delete the scsi reset in the kernel code
caseRESERVATION_CONFLICT:
/* Deleted Chris Roets
printk("scsi%d, channel %d : RESERVATION CONFLICT
performing"" reset.\n", SCpnt->host->host_no, SCpnt->channel); scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); status = REDO; next four lines added */ 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;
Quoted 4 lines
and rebuild the kernel.
This should get the customer being able to continue
Questions :
Quoted 4 lines
- why is this scsi reset done/added as of kernel version 2.2.18
- as we are talking about an obsolete routine, how is this accomplished in the new code and how is it activated.
Index: linux/2.2/drivers/scsi/scsi.c
2.2/drivers/scsi/scsi.c renamed from 2.2/drivers/scsi/scsi.c:1.1.1.9 +88 -1
@@ -198,7 +198,13 @@*/ extern void scsi_old_done (Scsi_Cmnd *SCpnt);extern void scsi_old_times_out (Scsi_Cmnd * SCpnt); +extern int scsi_old_reset (Scsi_Cmnd *SCpnt,unsigned int flag); +/* +* private interface into the new error handling code +*/ +extern int scsi_new_reset (Scsi_Cmnd *SCpnt,unsigned int flag); +#if CONFIG_PROC_FS extern int (* dispatch_scsi_info_ptr)(int ino,char *buffer,char **start,off_t offset,int length,int inout);
@@ -724,7 +730,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 ) {
@@ -2180,6 +2186,87 @@printk ("\n" ); } +/* Dummy done routine. We don't want the bogus command used for the +* bus/device reset to find its way into the mid-layer so we intercept +* it here */ +static void +scsi_reset_provider_done_command (Scsi_Cmnd *SCpnt) { +/* Empty function. Some low level drivers will call scsi_done +* (and end up here), others won't bother */ +} + +/* +* 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; +unsigned long flags; +Scsi_Cmnd SC ; +Scsi_Cmnd *SCpnt = &SC ; + +/* initialise the command */ +memset (&SCpnt->eh_timeout,0 ,sizeof (SCpnt->eh_timeout)); + SCpnt->host = dev->host; + SCpnt->device = dev; + SCpnt->target = dev->id; + SCpnt->lun = dev->lun; + SCpnt->channel = dev->channel; + SCpnt->request.rq_status =RQ_SCSI_BUSY ; + SCpnt->request.sem =NULL ; + SCpnt->host_wait = FALSE; + SCpnt->device_wait = FALSE; + SCpnt->use_sg =0 ; + SCpnt->old_use_sg =0 ; + SCpnt->old_cmd_len =0 ; + SCpnt->underflow =0 ; + SCpnt->transfersize =0 ; + SCpnt->serial_number =0 ; + SCpnt->serial_number_at_timeout =0 ; + SCpnt->host_scribble =NULL ; + SCpnt->next =NULL ; + SCpnt->state =SCSI_STATE_INITIALIZING ; + SCpnt->owner =SCSI_OWNER_MIDLEVEL ; + + +memset (&SCpnt->cmnd,'\0' ,sizeof (SCpnt->cmnd)); + + SCpnt->scsi_done = scsi_reset_provider_done_command; + SCpnt->done =NULL ; + SCpnt->reset_chain =NULL ; + + SCpnt->internal_timeout =NORMAL_TIMEOUT ; + SCpnt->abort_reason =DID_ABORT ; + +/* sometimes the command can get back into the timer chain, so use +* the pid as an identifier */ + SCpnt->pid =0 ; + +if (dev->host->hostt->use_new_eh_code) { + rtn =scsi_new_reset (SCpnt, flag); + }else { +unsigned long flags; + +spin_lock_irqsave (&io_request_lock, flags); + rtn =scsi_old_reset (SCpnt, flag); +spin_unlock_irqrestore (&io_request_lock, flags); + } + +scsi_delete_timer (SCpnt); + +return rtn; +}#ifdef CONFIG_PROC_FS int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
Index: linux/2.2/drivers/scsi/scsi.h
2.2/drivers/scsi/scsi.h renamed from 2.2/drivers/scsi/scsi.h:1.1.1.4 +8 -0
@@ -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_reset_provider (Scsi_Device *,int );#endif
Index: linux/2.2/drivers/scsi/scsi_error.c
2.2/drivers/scsi/scsi_error.c renamed from 2.2/drivers/scsi/scsi_error.c:1.1.1.4 +49 -2
72 lines not on this page · raw message
Index: linux/2.2/drivers/scsi/scsi_obsolete.c
2.2/drivers/scsi/scsi_obsolete.c renamed from 2.2/drivers/scsi/scsi_obsolete.c:1.1.1.2 +40 -5
63 lines not on this page · raw message
Index: linux/2.2/drivers/scsi/scsi_syms.c
2.2/drivers/scsi/scsi_syms.c renamed from 2.2/drivers/scsi/scsi_syms.c:1.1.1.2 +5 -0
@@ -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 */