diff -ur orig/drivers/scsi/scsi_error.c linux-rpc/drivers/scsi/scsi_error.c --- orig/drivers/scsi/scsi_error.c Mon Aug 5 13:31:24 2002 +++ linux-rpc/drivers/scsi/scsi_error.c Wed Sep 25 13:52:26 2002 @@ -35,6 +35,8 @@ #include "hosts.h" #include "constants.h" +#include /* grr */ + /* * We must always allow SHUTDOWN_SIGS. Even if we are not a module, * the host drivers that we are using may be loaded as modules, and @@ -1227,6 +1229,82 @@ /* + * Function: scsi_eh_lock_done + * + * Purpose: free the command and request structures associated + * with the error handlers door lock request + * + * Arguments: SCpnt - the SCSI command block for the door lock request. + * + * Returns: Nothing + * + * Notes: We completed the asynchronous door lock request, and + * it has either locked the door or failed. We must free + * the command structures associated with this request. + */ +static void scsi_eh_lock_done(struct scsi_cmnd *SCpnt) +{ + struct scsi_request *SRpnt = SCpnt->sc_request; + + SCpnt->sc_request = NULL; + SRpnt->sr_command = NULL; + + scsi_release_command(SCpnt); + scsi_release_request(SRpnt); +} + + +/* + * Function: scsi_eh_lock_door + * + * Purpose: Prevent medium removal for the specified device + * + * Arguments: dev - SCSI device to prevent medium removal + * + * Locking: We must be called from process context; + * scsi_allocate_request() may sleep. + * + * Returns: Nothing + * + * Notes: We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request + * on the head of the devices request queue, and continue. + * + * Bugs: scsi_allocate_request() may sleep waiting for existing + * requests to be processed. However, since we haven't + * kicked off any request processing for this host, this + * may deadlock. + * + * If scsi_allocate_request() fails for what ever reason, + * we completely forget to lock the door. + */ +STATIC void scsi_eh_lock_door(struct scsi_device *dev) +{ + struct scsi_request *SRpnt = scsi_allocate_request(dev); + + if (SRpnt == NULL) { + /* what now? */ + return; + } + + SRpnt->sr_cmnd[0] = ALLOW_MEDIUM_REMOVAL; + SRpnt->sr_cmnd[1] = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0; + SRpnt->sr_cmnd[2] = 0; + SRpnt->sr_cmnd[3] = 0; + SRpnt->sr_cmnd[4] = SCSI_REMOVAL_PREVENT; + SRpnt->sr_cmnd[5] = 0; + SRpnt->sr_data_direction = SCSI_DATA_NONE; + SRpnt->sr_bufflen = 0; + SRpnt->sr_buffer = NULL; + SRpnt->sr_allowed = 5; + SRpnt->sr_done = scsi_eh_lock_done; + SRpnt->sr_timeout_per_command = 10 * HZ; + SRpnt->sr_cmd_len = COMMAND_SIZE(SRpnt->sr_cmnd[0]); + + scsi_insert_special_req(SRpnt, 1); +} + + +/* * Function: scsi_restart_operations * * Purpose: Restart IO operations to the specified host. @@ -1248,6 +1326,15 @@ ASSERT_LOCK(&io_request_lock, 0); /* + * If the door was locked, we need to insert a door lock request + * onto the head of the SCSI request queue for the device. There + * is no point trying to lock the door of an off-line device. + */ + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) + if (SDpnt->online && SDpnt->locked) + scsi_eh_lock_door(SDpnt); + + /* * Next free up anything directly waiting upon the host. This will be * requests for character device operations, and also for ioctls to queued * block devices. diff -ur orig/drivers/scsi/scsi_lib.c linux-rpc/drivers/scsi/scsi_lib.c --- orig/drivers/scsi/scsi_lib.c Mon Aug 5 13:31:24 2002 +++ linux-rpc/drivers/scsi/scsi_lib.c Tue Sep 24 17:27:00 2002 @@ -925,8 +925,17 @@ * space. Technically the error handling thread should be * doing this crap, but the error handler isn't used by * most hosts. + * + * (rmk) + * Trying to lock the door can cause deadlocks. We therefore + * only use this for old hosts; our door locking is now done + * by the error handler in scsi_restart_operations for new + * eh hosts. + * + * Note that we don't clear was_reset here; this is used by + * st.c, and either one or other has to die. */ - if (SDpnt->was_reset) { + if (SHpnt->hostt->use_new_eh_code == 0 && SDpnt->was_reset) { /* * We need to relock the door, but we might * be in an interrupt handler. Only do this @@ -937,7 +946,7 @@ * this work. */ SDpnt->was_reset = 0; - if (SDpnt->removable && !in_interrupt()) { + if (SDpnt->removable && SDpnt->locked && !in_interrupt()) { spin_unlock_irq(&io_request_lock); scsi_ioctl(SDpnt, SCSI_IOCTL_DOORLOCK, 0); spin_lock_irq(&io_request_lock);