[PATCH v2][RFC] scsi_transport_fc: Implement I_T nexus reset

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



'Bus reset' is not really applicable to FibreChannel, as
the concept of a bus doesn't really apply. All FC driver
simulate a 'bus reset' by sending a target reset to each
attached remote port, causing error handling to spill
over to unaffected devices.
In addition, 'Target reset' has been removed from SAM
since SAM-3.

Instead, SAM-5 proposes an REMOVE I_T NEXUS TMF,
which just removes the I_T nexus, thereby avoiding
any spill-over to unaffected ports.

This patch implements fc_eh_it_nexus_loss_handler(),
which attempts to reset the I_T nexus to the remote
port.

For I_T nexus reset we first check if the port
is already blocked, then call a new LLDD-provided
'eh_it_nexus_loss' callback to allow the LLDD
to cleanup any outstanding resources or abort I/O.
If the callback succeeds the dev_loss_tmo
mechanism is called with '-1' fast fail timeout,
which causes fast_io_fail_tmo to be skipped.
Otherwise the dev_loss_tmo mechanism is called
with a '0' fast fail timeout, causing any
outstanding I/O to be aborted immediately.
The port is then set to 'blocked' to indicate that
no further I/O should be issued to this port.
Finally the standard dev_loss_tmo mechanism will
eventually clear up any outstanding resources.

fc_eh_it_nexus_loss_handler() is invoked as the
eh_target_reset_handler() callback and the
eh_bus_reset_handler() is removed.

Signed-off-by: Hannes Reinecke <hare@xxxxxxx>
Cc: Mike Christie <michaelc@xxxxxxxxxxx>
Cc: James Smart <james.smart@xxxxxxxxxx>
Cc: Andrew Vasquez <andrew.vasquez@xxxxxxxxxx>
Cc: Chad Dupuis <chad.dupuis@xxxxxxxxxx>
Cc: Krishna C Gudipati <kgudipat@xxxxxxxxxxx>
Cc: James Bottomley <jbottomley@xxxxxxxxxxxxx>
---
 drivers/scsi/bfa/bfad_im.c       |    6 ++-
 drivers/scsi/lpfc/lpfc_scsi.c    |    8 ++--
 drivers/scsi/qla2xxx/qla_os.c    |    4 +-
 drivers/scsi/scsi_transport_fc.c |   63 +++++++++++++++++++++++++++++++++++---
 include/scsi/scsi_transport_fc.h |    2 +
 5 files changed, 70 insertions(+), 13 deletions(-)

diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index 8f92732..fd1fc4a 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -793,7 +793,8 @@ struct scsi_host_template bfad_im_scsi_host_template = {
 	.queuecommand = bfad_im_queuecommand,
 	.eh_abort_handler = bfad_im_abort_handler,
 	.eh_device_reset_handler = bfad_im_reset_lun_handler,
-	.eh_bus_reset_handler = bfad_im_reset_bus_handler,
+	.eh_target_reset_handler = fc_eh_it_nexus_loss_handler,
+	.eh_bus_reset_handler = NULL,
 
 	.slave_alloc = bfad_im_slave_alloc,
 	.slave_configure = bfad_im_slave_configure,
@@ -815,7 +816,8 @@ struct scsi_host_template bfad_im_vport_template = {
 	.queuecommand = bfad_im_queuecommand,
 	.eh_abort_handler = bfad_im_abort_handler,
 	.eh_device_reset_handler = bfad_im_reset_lun_handler,
-	.eh_bus_reset_handler = bfad_im_reset_bus_handler,
+	.eh_target_reset_handler = fc_eh_it_nexus_loss_handler,
+	.eh_bus_reset_handler = NULL,
 
 	.slave_alloc = bfad_im_slave_alloc,
 	.slave_configure = bfad_im_slave_configure,
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 60e5a17..c4e2788 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -5135,8 +5135,8 @@ struct scsi_host_template lpfc_template = {
 	.queuecommand		= lpfc_queuecommand,
 	.eh_abort_handler	= lpfc_abort_handler,
 	.eh_device_reset_handler = lpfc_device_reset_handler,
-	.eh_target_reset_handler = lpfc_target_reset_handler,
-	.eh_bus_reset_handler	= lpfc_bus_reset_handler,
+	.eh_target_reset_handler = fc_eh_it_nexus_loss_handler,
+	.eh_bus_reset_handler	= NULL,
 	.eh_host_reset_handler  = lpfc_host_reset_handler,
 	.slave_alloc		= lpfc_slave_alloc,
 	.slave_configure	= lpfc_slave_configure,
@@ -5159,8 +5159,8 @@ struct scsi_host_template lpfc_vport_template = {
 	.queuecommand		= lpfc_queuecommand,
 	.eh_abort_handler	= lpfc_abort_handler,
 	.eh_device_reset_handler = lpfc_device_reset_handler,
-	.eh_target_reset_handler = lpfc_target_reset_handler,
-	.eh_bus_reset_handler	= lpfc_bus_reset_handler,
+	.eh_target_reset_handler = fc_eh_it_nexus_loss_handler,
+	.eh_bus_reset_handler	= NULL,
 	.slave_alloc		= lpfc_slave_alloc,
 	.slave_configure	= lpfc_slave_configure,
 	.slave_destroy		= lpfc_slave_destroy,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 3a1661c..c59e681 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -245,8 +245,8 @@ struct scsi_host_template qla2xxx_driver_template = {
 
 	.eh_abort_handler	= qla2xxx_eh_abort,
 	.eh_device_reset_handler = qla2xxx_eh_device_reset,
-	.eh_target_reset_handler = qla2xxx_eh_target_reset,
-	.eh_bus_reset_handler	= qla2xxx_eh_bus_reset,
+	.eh_target_reset_handler = fc_eh_it_nexus_loss_handler,
+	.eh_bus_reset_handler	= NULL,
 	.eh_host_reset_handler	= qla2xxx_eh_host_reset,
 
 	.slave_configure	= qla2xxx_slave_configure,
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index e894ca7..da647d3 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -2971,7 +2971,7 @@ EXPORT_SYMBOL(fc_remote_port_add);
  *	This routine assumes no locks are held on entry.
  */
 void
-fc_remote_port_delete(struct fc_rport  *rport)
+__fc_remote_port_delete(struct fc_rport *rport, int fast_io_fail_tmo)
 {
 	struct Scsi_Host *shost = rport_to_shost(rport);
 	unsigned long timeout = rport->dev_loss_tmo;
@@ -3018,14 +3018,19 @@ fc_remote_port_delete(struct fc_rport  *rport)
 	scsi_target_block(&rport->dev);
 
 	/* see if we need to kill io faster than waiting for device loss */
-	if ((rport->fast_io_fail_tmo != -1) &&
-	    (rport->fast_io_fail_tmo < timeout))
+	if ((fast_io_fail_tmo != -1) && (fast_io_fail_tmo < timeout))
 		fc_queue_devloss_work(shost, &rport->fail_io_work,
-					rport->fast_io_fail_tmo * HZ);
+					fast_io_fail_tmo * HZ);
 
 	/* cap the length the devices can be blocked until they are deleted */
 	fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ);
 }
+
+void
+fc_remote_port_delete(struct fc_rport  *rport)
+{
+	__fc_remote_port_delete(rport, rport->fast_io_fail_tmo);
+}
 EXPORT_SYMBOL(fc_remote_port_delete);
 
 /**
@@ -3266,8 +3271,8 @@ fc_timeout_fail_rport_io(struct work_struct *work)
 	if (rport->port_state != FC_PORTSTATE_BLOCKED)
 		return;
 
-	rport->flags |= FC_RPORT_FAST_FAIL_TIMEDOUT;
 	fc_terminate_rport_io(rport);
+	rport->flags |= FC_RPORT_FAST_FAIL_TIMEDOUT;
 }
 
 /**
@@ -3332,6 +3337,54 @@ int fc_block_scsi_eh(struct scsi_cmnd *cmnd)
 EXPORT_SYMBOL(fc_block_scsi_eh);
 
 /**
+ * fc_eh_it_nexus_loss_handler - Invoke REMOVE I_T NEXUS TMF
+ * @cmnd: SCSI command that scsi_eh is trying to recover
+ *
+ * This routine can be called from a FC LLD scsi_eh callback. It
+ * attempts to perform an REMOVE I_T NEXUS transport management
+ * function by failing all outstanding commands and invoke
+ * dev_loss_tmo() on the affected port.
+ *
+ * Returns: SUCCESS if all commands on the remote port have been
+ *	    terminated or the port is in PORTSTATE_ONLINE again
+ *	    FAST_IO_FAIL if the fast_io_fail_tmo fired and there
+ *	    is still I/O in flight
+ *	    FAILED otherwise.
+ */
+int
+fc_eh_it_nexus_loss_handler(struct scsi_cmnd *cmnd)
+{
+	struct fc_internal *i = to_fc_internal(cmnd->device->host->transportt);
+	struct scsi_target *starget = scsi_target(cmnd->device);
+	struct fc_rport *rport = starget_to_rport(starget);
+	int ret;
+
+	ret = fc_block_scsi_eh(cmnd);
+	if (i->f->eh_it_nexus_loss)
+		ret = i->f->eh_it_nexus_loss(cmnd);
+
+	/* FAST_IO_FAIL indicates the port is already blocked */
+	if (ret == FAST_IO_FAIL)
+		return ret;
+	if (ret == SUCCESS)
+		/* All outstanding I/O has been aborted */
+		__fc_remote_port_delete(rport, -1);
+	else {
+		/* Failed to abort outstanding I/O, trigger FAST_IO_FAIL */
+		__fc_remote_port_delete(rport, 0);
+		ret = fc_block_scsi_eh(cmnd);
+	}
+	if (ret != FAST_IO_FAIL) {
+		if (rport->port_state == FC_PORTSTATE_ONLINE)
+			ret = SUCCESS;
+		else
+			ret = FAILED;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(fc_eh_it_nexus_loss_handler);
+
+/**
  * fc_vport_setup - allocates and creates a FC virtual port.
  * @shost:	scsi host the virtual port is connected to.
  * @channel:	Channel on shost port connected to.
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h
index b797e8f..17e2968 100644
--- a/include/scsi/scsi_transport_fc.h
+++ b/include/scsi/scsi_transport_fc.h
@@ -684,6 +684,7 @@ struct fc_function_template {
 
 	void    (*dev_loss_tmo_callbk)(struct fc_rport *);
 	void	(*terminate_rport_io)(struct fc_rport *);
+	int	(*eh_it_nexus_loss)(struct scsi_cmnd *);
 
 	void	(*set_vport_symbolic_name)(struct fc_vport *);
 	int  	(*vport_create)(struct fc_vport *, bool);
@@ -851,5 +852,6 @@ struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel,
 		struct fc_vport_identifiers *);
 int fc_vport_terminate(struct fc_vport *vport);
 int fc_block_scsi_eh(struct scsi_cmnd *cmnd);
+int fc_eh_it_nexus_loss_handler(struct scsi_cmnd *cmnd);
 
 #endif /* SCSI_TRANSPORT_FC_H */
-- 
1.7.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux