[SCSI] Allow FC LLD to fast-fail scsi eh by introducing new eh return

If the scsi eh is running and then a FC LLD calls
fc_remote_port_delete, the SCSI commands sent from the eh will fail.
To prevent this, a FC LLD can call fc_block_scsi_eh from the eh
callback, blocking the eh thread until the dev_loss_tmo fires or the
remote port is available again.

If (e.g. for a multipathing setup) the dev_loss_tmo is set to a very
large value, thus preventing the scsi device removal , the scsi eh can
block for a long time. For multipathing, the fast_io_fail_tmo is then
set to a low value to detect path problems sooner.

This patch introduces a new return code FAST_IO_FAIL. The function
fc_block_scsi_eh now returns FAST_IO_FAIL when the fast_io_fail_tmo
fires. This indicates that the LLD terminated all pending I/O requests
and there are no more pending SCSI commands for the scsi eh to wait
for. This return code can be passed back to the scsi eh to stop the
escalation and finish the recovery process for this device.

Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
Christof Schmitt 2010-03-24 16:50:30 +01:00 committed by James Bottomley
parent 7794a5af67
commit 2f2eb58762
4 changed files with 27 additions and 11 deletions

View File

@ -957,9 +957,10 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
"0x%p\n", current->comm, "0x%p\n", current->comm,
scmd)); scmd));
rtn = scsi_try_to_abort_cmd(scmd); rtn = scsi_try_to_abort_cmd(scmd);
if (rtn == SUCCESS) { if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD; scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD;
if (!scsi_device_online(scmd->device) || if (!scsi_device_online(scmd->device) ||
rtn == FAST_IO_FAIL ||
!scsi_eh_tur(scmd)) { !scsi_eh_tur(scmd)) {
scsi_eh_finish_cmd(scmd, done_q); scsi_eh_finish_cmd(scmd, done_q);
} }
@ -1086,8 +1087,9 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
" 0x%p\n", current->comm, " 0x%p\n", current->comm,
sdev)); sdev));
rtn = scsi_try_bus_device_reset(bdr_scmd); rtn = scsi_try_bus_device_reset(bdr_scmd);
if (rtn == SUCCESS) { if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
if (!scsi_device_online(sdev) || if (!scsi_device_online(sdev) ||
rtn == FAST_IO_FAIL ||
!scsi_eh_tur(bdr_scmd)) { !scsi_eh_tur(bdr_scmd)) {
list_for_each_entry_safe(scmd, next, list_for_each_entry_safe(scmd, next,
work_q, eh_entry) { work_q, eh_entry) {
@ -1150,10 +1152,11 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
"to target %d\n", "to target %d\n",
current->comm, id)); current->comm, id));
rtn = scsi_try_target_reset(tgtr_scmd); rtn = scsi_try_target_reset(tgtr_scmd);
if (rtn == SUCCESS) { if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
list_for_each_entry_safe(scmd, next, work_q, eh_entry) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
if (id == scmd_id(scmd)) if (id == scmd_id(scmd))
if (!scsi_device_online(scmd->device) || if (!scsi_device_online(scmd->device) ||
rtn == FAST_IO_FAIL ||
!scsi_eh_tur(tgtr_scmd)) !scsi_eh_tur(tgtr_scmd))
scsi_eh_finish_cmd(scmd, scsi_eh_finish_cmd(scmd,
done_q); done_q);
@ -1209,10 +1212,11 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost,
" %d\n", current->comm, " %d\n", current->comm,
channel)); channel));
rtn = scsi_try_bus_reset(chan_scmd); rtn = scsi_try_bus_reset(chan_scmd);
if (rtn == SUCCESS) { if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
list_for_each_entry_safe(scmd, next, work_q, eh_entry) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
if (channel == scmd_channel(scmd)) if (channel == scmd_channel(scmd))
if (!scsi_device_online(scmd->device) || if (!scsi_device_online(scmd->device) ||
rtn == FAST_IO_FAIL ||
!scsi_eh_tur(scmd)) !scsi_eh_tur(scmd))
scsi_eh_finish_cmd(scmd, scsi_eh_finish_cmd(scmd,
done_q); done_q);
@ -1246,9 +1250,10 @@ static int scsi_eh_host_reset(struct list_head *work_q,
, current->comm)); , current->comm));
rtn = scsi_try_host_reset(scmd); rtn = scsi_try_host_reset(scmd);
if (rtn == SUCCESS) { if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
list_for_each_entry_safe(scmd, next, work_q, eh_entry) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
if (!scsi_device_online(scmd->device) || if (!scsi_device_online(scmd->device) ||
rtn == FAST_IO_FAIL ||
(!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) || (!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) ||
!scsi_eh_tur(scmd)) !scsi_eh_tur(scmd))
scsi_eh_finish_cmd(scmd, done_q); scsi_eh_finish_cmd(scmd, done_q);

View File

@ -3197,23 +3197,33 @@ fc_scsi_scan_rport(struct work_struct *work)
* *
* This routine can be called from a FC LLD scsi_eh callback. It * This routine can be called from a FC LLD scsi_eh callback. It
* blocks the scsi_eh thread until the fc_rport leaves the * blocks the scsi_eh thread until the fc_rport leaves the
* FC_PORTSTATE_BLOCKED. This is necessary to avoid the scsi_eh * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is
* failing recovery actions for blocked rports which would lead to * necessary to avoid the scsi_eh failing recovery actions for blocked
* offlined SCSI devices. * rports which would lead to offlined SCSI devices.
*
* Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED.
* FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be
* passed back to scsi_eh.
*/ */
void fc_block_scsi_eh(struct scsi_cmnd *cmnd) int fc_block_scsi_eh(struct scsi_cmnd *cmnd)
{ {
struct Scsi_Host *shost = cmnd->device->host; struct Scsi_Host *shost = cmnd->device->host;
struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
unsigned long flags; unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
while (rport->port_state == FC_PORTSTATE_BLOCKED) { while (rport->port_state == FC_PORTSTATE_BLOCKED &&
!(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)) {
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
msleep(1000); msleep(1000);
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
} }
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)
return FAST_IO_FAIL;
return 0;
} }
EXPORT_SYMBOL(fc_block_scsi_eh); EXPORT_SYMBOL(fc_block_scsi_eh);

View File

@ -423,6 +423,7 @@ static inline int scsi_is_wlun(unsigned int lun)
#define ADD_TO_MLQUEUE 0x2006 #define ADD_TO_MLQUEUE 0x2006
#define TIMEOUT_ERROR 0x2007 #define TIMEOUT_ERROR 0x2007
#define SCSI_RETURN_NOT_HANDLED 0x2008 #define SCSI_RETURN_NOT_HANDLED 0x2008
#define FAST_IO_FAIL 0x2009
/* /*
* Midlevel queue return values. * Midlevel queue return values.

View File

@ -807,6 +807,6 @@ void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel, struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel,
struct fc_vport_identifiers *); struct fc_vport_identifiers *);
int fc_vport_terminate(struct fc_vport *vport); int fc_vport_terminate(struct fc_vport *vport);
void fc_block_scsi_eh(struct scsi_cmnd *cmnd); int fc_block_scsi_eh(struct scsi_cmnd *cmnd);
#endif /* SCSI_TRANSPORT_FC_H */ #endif /* SCSI_TRANSPORT_FC_H */