switch (oldstate) {
case SHOST_CREATED:
case SHOST_RUNNING:
+ case SHOST_CANCEL_RECOVERY:
break;
default:
goto illegal;
case SHOST_DEL:
switch (oldstate) {
case SHOST_CANCEL:
+ case SHOST_DEL_RECOVERY:
break;
default:
goto illegal;
}
break;
+ case SHOST_CANCEL_RECOVERY:
+ switch (oldstate) {
+ case SHOST_CANCEL:
+ case SHOST_RECOVERY:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ case SHOST_DEL_RECOVERY:
+ switch (oldstate) {
+ case SHOST_CANCEL_RECOVERY:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
}
shost->shost_state = state;
return 0;
**/
void scsi_remove_host(struct Scsi_Host *shost)
{
+ unsigned long flags;
down(&shost->scan_mutex);
- scsi_host_set_state(shost, SHOST_CANCEL);
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (scsi_host_set_state(shost, SHOST_CANCEL))
+ if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) {
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ up(&shost->scan_mutex);
+ return;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
up(&shost->scan_mutex);
scsi_forget_host(shost);
scsi_proc_host_rm(shost);
- scsi_host_set_state(shost, SHOST_DEL);
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (scsi_host_set_state(shost, SHOST_DEL))
+ BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY));
+ spin_unlock_irqrestore(shost->host_lock, flags);
transport_unregister_device(&shost->shost_gendev);
class_device_unregister(&shost->shost_classdev);
list_for_each_safe(lh, lh_sf, &active_list) {
scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
list_del_init(lh);
- if (recovery) {
- scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD);
- } else {
+ if (recovery &&
+ !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD)) {
scmd->result = (DID_ABORT << 16);
scsi_finish_command(scmd);
}
{
struct Scsi_Host *shost = scmd->device->host;
unsigned long flags;
+ int ret = 0;
if (shost->eh_wait == NULL)
return 0;
spin_lock_irqsave(shost->host_lock, flags);
+ if (scsi_host_set_state(shost, SHOST_RECOVERY))
+ if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY))
+ goto out_unlock;
+ ret = 1;
scmd->eh_eflags |= eh_flag;
list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
- scsi_host_set_state(shost, SHOST_RECOVERY);
shost->host_failed++;
scsi_eh_wakeup(shost);
+ out_unlock:
spin_unlock_irqrestore(shost->host_lock, flags);
- return 1;
+ return ret;
}
/**
}
if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
- panic("Error handler thread not present at %p %p %s %d",
- scmd, scmd->device->host, __FILE__, __LINE__);
+ scmd->result |= DID_TIME_OUT << 16;
+ __scsi_done(scmd);
}
}
{
int online;
- wait_event(sdev->host->host_wait, (sdev->host->shost_state !=
- SHOST_RECOVERY));
+ wait_event(sdev->host->host_wait, !scsi_host_in_recovery(sdev->host));
online = scsi_device_online(sdev);
static void scsi_restart_operations(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
+ unsigned long flags;
/*
* If the door was locked, we need to insert a door lock request
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n",
__FUNCTION__));
- scsi_host_set_state(shost, SHOST_RUNNING);
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (scsi_host_set_state(shost, SHOST_RUNNING))
+ if (scsi_host_set_state(shost, SHOST_CANCEL))
+ BUG_ON(scsi_host_set_state(shost, SHOST_DEL));
+ spin_unlock_irqrestore(shost->host_lock, flags);
wake_up(&shost->host_wait);
* error processing, as long as the device was opened
* non-blocking */
if (filp && filp->f_flags & O_NONBLOCK) {
- if (sdev->host->shost_state == SHOST_RECOVERY)
+ if (scsi_host_in_recovery(sdev->host))
return -ENODEV;
} else if (!scsi_block_when_processing_errors(sdev))
return -ENODEV;
spin_lock_irqsave(shost->host_lock, flags);
shost->host_busy--;
- if (unlikely((shost->shost_state == SHOST_RECOVERY) &&
+ if (unlikely(scsi_host_in_recovery(shost) &&
shost->host_failed))
scsi_eh_wakeup(shost);
spin_unlock(shost->host_lock);
struct Scsi_Host *shost,
struct scsi_device *sdev)
{
- if (shost->shost_state == SHOST_RECOVERY)
+ if (scsi_host_in_recovery(shost))
return 0;
if (shost->host_busy == 0 && shost->host_blocked) {
/*
{ SHOST_CANCEL, "cancel" },
{ SHOST_DEL, "deleted" },
{ SHOST_RECOVERY, "recovery" },
+ { SHOST_CANCEL_RECOVERY, "cancel/recovery" },
+ { SHOST_DEL_RECOVERY, "deleted/recovery", },
};
const char *scsi_host_state_name(enum scsi_host_state state)
{
if (sdp->detached)
return -ENODEV;
if (filp->f_flags & O_NONBLOCK) {
- if (sdp->device->host->shost_state == SHOST_RECOVERY)
+ if (scsi_host_in_recovery(sdp->device->host))
return -EBUSY;
} else if (!scsi_block_when_processing_errors(sdp->device))
return -EBUSY;
SHOST_CANCEL,
SHOST_DEL,
SHOST_RECOVERY,
+ SHOST_CANCEL_RECOVERY,
+ SHOST_DEL_RECOVERY,
};
struct Scsi_Host {
return container_of(dev, struct Scsi_Host, shost_gendev);
}
+static inline int scsi_host_in_recovery(struct Scsi_Host *shost)
+{
+ return shost->shost_state == SHOST_RECOVERY ||
+ shost->shost_state == SHOST_CANCEL_RECOVERY ||
+ shost->shost_state == SHOST_DEL_RECOVERY;
+}
+
extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *);
extern void scsi_flush_work(struct Scsi_Host *);