summaryrefslogtreecommitdiffstats
path: root/drivers/s390/scsi/zfcp_scsi.c
diff options
context:
space:
mode:
authorChristof Schmitt <christof.schmitt@de.ibm.com>2009-03-02 13:09:08 +0100
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-03-12 12:58:21 -0500
commita2fa0aede07c9488239dcac1eae58233181c355a (patch)
tree406836319208a5f8597010b0f25f599eae922e66 /drivers/s390/scsi/zfcp_scsi.c
parent24095490681d130979c18685dc0b5a308057e225 (diff)
[SCSI] zfcp: Block FC transport rports early on errors
Use the I/O blocking mechanism in the FC transport class to allow faster failovers for multipathing: - Call fc_remote_port_delete early to set the rport to BLOCKED. - Check the rport status in queuecommand with fc_remote_portchkready to no longer accept new I/O for this port and fail the I/O with the appropriate scsi_cmnd result. - Implement the terminate_rport_io handler to abort all pending I/O requests - Return SCSI commands with DID_TRANSPORT_DISRUPTED while erp is running. - When updating the remote port status, check for late changes and update the remote ports status accordingly. Acked-by: Swen Schillig <swen@vnet.ibm.com> Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/s390/scsi/zfcp_scsi.c')
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c119
1 files changed, 116 insertions, 3 deletions
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 2af8cfbc389..7141f9a675d 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -57,8 +57,8 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
{
struct zfcp_unit *unit;
struct zfcp_adapter *adapter;
- int status;
- int ret;
+ int status, scsi_result, ret;
+ struct fc_rport *rport = starget_to_rport(scsi_target(scpnt->device));
/* reset the status for this request */
scpnt->result = 0;
@@ -80,6 +80,14 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
return 0;
}
+ scsi_result = fc_remote_port_chkready(rport);
+ if (unlikely(scsi_result)) {
+ scpnt->result = scsi_result;
+ zfcp_scsi_dbf_event_result("fail", 4, adapter, scpnt, NULL);
+ scpnt->scsi_done(scpnt);
+ return 0;
+ }
+
status = atomic_read(&unit->status);
if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) ||
!(status & ZFCP_STATUS_COMMON_RUNNING))) {
@@ -473,6 +481,109 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
rport->dev_loss_tmo = timeout;
}
+/**
+ * zfcp_scsi_dev_loss_tmo_callbk - Free any reference to rport
+ * @rport: The rport that is about to be deleted.
+ */
+static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport)
+{
+ struct zfcp_port *port = rport->dd_data;
+
+ write_lock_irq(&zfcp_data.config_lock);
+ port->rport = NULL;
+ write_unlock_irq(&zfcp_data.config_lock);
+}
+
+/**
+ * zfcp_scsi_terminate_rport_io - Terminate all I/O on a rport
+ * @rport: The FC rport where to teminate I/O
+ *
+ * Abort all pending SCSI commands for a port by closing the
+ * port. Using a reopen for avoids a conflict with a shutdown
+ * overwriting a reopen.
+ */
+static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
+{
+ struct zfcp_port *port = rport->dd_data;
+
+ zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL);
+}
+
+static void zfcp_scsi_rport_register(struct zfcp_port *port)
+{
+ struct fc_rport_identifiers ids;
+ struct fc_rport *rport;
+
+ ids.node_name = port->wwnn;
+ ids.port_name = port->wwpn;
+ ids.port_id = port->d_id;
+ ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+
+ rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
+ if (!rport) {
+ dev_err(&port->adapter->ccw_device->dev,
+ "Registering port 0x%016Lx failed\n",
+ (unsigned long long)port->wwpn);
+ return;
+ }
+
+ rport->dd_data = port;
+ rport->maxframe_size = port->maxframe_size;
+ rport->supported_classes = port->supported_classes;
+ port->rport = rport;
+}
+
+static void zfcp_scsi_rport_block(struct zfcp_port *port)
+{
+ if (port->rport)
+ fc_remote_port_delete(port->rport);
+}
+
+void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
+{
+ zfcp_port_get(port);
+ port->rport_task = RPORT_ADD;
+
+ if (!queue_work(zfcp_data.work_queue, &port->rport_work))
+ zfcp_port_put(port);
+}
+
+void zfcp_scsi_schedule_rport_block(struct zfcp_port *port)
+{
+ zfcp_port_get(port);
+ port->rport_task = RPORT_DEL;
+
+ if (!queue_work(zfcp_data.work_queue, &port->rport_work))
+ zfcp_port_put(port);
+}
+
+void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter)
+{
+ struct zfcp_port *port;
+
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ zfcp_scsi_schedule_rport_block(port);
+}
+
+void zfcp_scsi_rport_work(struct work_struct *work)
+{
+ struct zfcp_port *port = container_of(work, struct zfcp_port,
+ rport_work);
+
+ while (port->rport_task) {
+ if (port->rport_task == RPORT_ADD) {
+ port->rport_task = RPORT_NONE;
+ zfcp_scsi_rport_register(port);
+ } else {
+ port->rport_task = RPORT_NONE;
+ zfcp_scsi_rport_block(port);
+ }
+ }
+
+ zfcp_port_put(port);
+}
+
+
struct fc_function_template zfcp_transport_functions = {
.show_starget_port_id = 1,
.show_starget_port_name = 1,
@@ -491,6 +602,8 @@ struct fc_function_template zfcp_transport_functions = {
.reset_fc_host_stats = zfcp_reset_fc_host_stats,
.set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo,
.get_host_port_state = zfcp_get_host_port_state,
+ .dev_loss_tmo_callbk = zfcp_scsi_dev_loss_tmo_callbk,
+ .terminate_rport_io = zfcp_scsi_terminate_rport_io,
.show_host_port_state = 1,
/* no functions registered for following dynamic attributes but
directly set by LLDD */