[SCSI] zfcp: Block FC transport rports early on errors
authorChristof Schmitt <christof.schmitt@de.ibm.com>
Mon, 2 Mar 2009 12:09:08 +0000 (13:09 +0100)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Thu, 12 Mar 2009 17:58:21 +0000 (12:58 -0500)
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>
drivers/s390/scsi/zfcp_aux.c
drivers/s390/scsi/zfcp_def.h
drivers/s390/scsi/zfcp_erp.c
drivers/s390/scsi/zfcp_ext.h
drivers/s390/scsi/zfcp_fc.c
drivers/s390/scsi/zfcp_fsf.c
drivers/s390/scsi/zfcp_scsi.c

index 69a31187e54df871f0f4e5aff530f1fa8921d2e2..b2be6593b56342412029df8d6b7a45006efcf7bc 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Module interface and handling of zfcp data structures.
  *
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
  */
 
 /*
@@ -606,10 +606,12 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
        INIT_LIST_HEAD(&port->unit_list_head);
        INIT_WORK(&port->gid_pn_work, zfcp_erp_port_strategy_open_lookup);
        INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
+       INIT_WORK(&port->rport_work, zfcp_scsi_rport_work);
 
        port->adapter = adapter;
        port->d_id = d_id;
        port->wwpn = wwpn;
+       port->rport_task = RPORT_NONE;
 
        /* mark port unusable as long as sysfs registration is not complete */
        atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
index 22e418db451866b01dbd6a31c9e35b7529919fa3..a0318630f04723c1756bd2ac6a007d53bf8d4c14 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Global definitions for the zfcp device driver.
  *
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
  */
 
 #ifndef ZFCP_DEF_H
@@ -512,6 +512,8 @@ struct zfcp_port {
        u32                    supported_classes;
        struct work_struct     gid_pn_work;
        struct work_struct     test_link_work;
+       struct work_struct     rport_work;
+       enum { RPORT_NONE, RPORT_ADD, RPORT_DEL }  rport_task;
 };
 
 struct zfcp_unit {
index 65addf6a91ecaddfc08910212cc9d3478d0b34e6..dee1cc3ce21b2614467ad577941b54027e652958 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Error Recovery Procedures (ERP).
  *
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
  */
 
 #define KMSG_COMPONENT "zfcp"
@@ -240,6 +240,7 @@ static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
                                    int clear_mask, char *id, void *ref)
 {
        zfcp_erp_adapter_block(adapter, clear_mask);
+       zfcp_scsi_schedule_rports_block(adapter);
 
        /* ensure propagation of failed status to new devices */
        if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
@@ -322,6 +323,7 @@ static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port,
                                         int clear, char *id, void *ref)
 {
        zfcp_erp_port_block(port, clear);
+       zfcp_scsi_schedule_rport_block(port);
 
        if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
                return;
@@ -353,6 +355,7 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id,
                                 void *ref)
 {
        zfcp_erp_port_block(port, clear);
+       zfcp_scsi_schedule_rport_block(port);
 
        if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
                /* ensure propagation of failed status to new devices */
@@ -1211,37 +1214,6 @@ static void zfcp_erp_schedule_work(struct zfcp_unit *unit)
        queue_work(zfcp_data.work_queue, &p->work);
 }
 
-static void zfcp_erp_rport_register(struct zfcp_port *port)
-{
-       struct fc_rport_identifiers ids;
-       ids.node_name = port->wwnn;
-       ids.port_name = port->wwpn;
-       ids.port_id = port->d_id;
-       ids.roles = FC_RPORT_ROLE_FCP_TARGET;
-       port->rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
-       if (!port->rport) {
-               dev_err(&port->adapter->ccw_device->dev,
-                       "Registering port 0x%016Lx failed\n",
-                       (unsigned long long)port->wwpn);
-               return;
-       }
-
-       scsi_target_unblock(&port->rport->dev);
-       port->rport->maxframe_size = port->maxframe_size;
-       port->rport->supported_classes = port->supported_classes;
-}
-
-static void zfcp_erp_rports_del(struct zfcp_adapter *adapter)
-{
-       struct zfcp_port *port;
-       list_for_each_entry(port, &adapter->port_list_head, list) {
-               if (!port->rport)
-                       continue;
-               fc_remote_port_delete(port->rport);
-               port->rport = NULL;
-       }
-}
-
 static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
 {
        struct zfcp_adapter *adapter = act->adapter;
@@ -1250,8 +1222,8 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
 
        switch (act->action) {
        case ZFCP_ERP_ACTION_REOPEN_UNIT:
-               if ((result == ZFCP_ERP_SUCCEEDED) &&
-                   !unit->device && port->rport) {
+               flush_work(&port->rport_work);
+               if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) {
                        if (!(atomic_read(&unit->status) &
                              ZFCP_STATUS_UNIT_SCSI_WORK_PENDING))
                                zfcp_erp_schedule_work(unit);
@@ -1261,23 +1233,17 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
 
        case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
        case ZFCP_ERP_ACTION_REOPEN_PORT:
-               if ((result == ZFCP_ERP_SUCCEEDED) && !port->rport)
-                       zfcp_erp_rport_register(port);
-               if ((result != ZFCP_ERP_SUCCEEDED) && port->rport) {
-                       fc_remote_port_delete(port->rport);
-                       port->rport = NULL;
-               }
+               if (result == ZFCP_ERP_SUCCEEDED)
+                       zfcp_scsi_schedule_rport_register(port);
                zfcp_port_put(port);
                break;
 
        case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-               if (result != ZFCP_ERP_SUCCEEDED) {
-                       unregister_service_level(&adapter->service_level);
-                       zfcp_erp_rports_del(adapter);
-               } else {
+               if (result == ZFCP_ERP_SUCCEEDED) {
                        register_service_level(&adapter->service_level);
                        schedule_work(&adapter->scan_work);
-               }
+               } else
+                       unregister_service_level(&adapter->service_level);
                zfcp_adapter_put(adapter);
                break;
        }
index 569d2437e99bacd09605b038727b6046aaf6ef2c..f6399ca97bcb35c902d79d3606dedd8d94e10745 100644 (file)
@@ -3,7 +3,7 @@
  *
  * External function declarations.
  *
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
  */
 
 #ifndef ZFCP_EXT_H
@@ -154,6 +154,10 @@ extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
 extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
 extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
 extern struct fc_function_template zfcp_transport_functions;
+extern void zfcp_scsi_rport_work(struct work_struct *);
+extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);
+extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *);
+extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *);
 
 /* zfcp_sysfs.c */
 extern struct attribute_group zfcp_sysfs_unit_attrs;
index 49a7a90501b615eec51f360147fa43148f70b6a2..c22c4786855014270bc30e03f754a370465d041f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Fibre Channel related functions for the zfcp device driver.
  *
- * Copyright IBM Corporation 2008
+ * Copyright IBM Corporation 2008, 2009
  */
 
 #define KMSG_COMPONENT "zfcp"
@@ -376,10 +376,14 @@ static void zfcp_fc_adisc_handler(unsigned long data)
                port->wwnn = ls_adisc->wwnn;
 
        if ((port->wwpn != ls_adisc->wwpn) ||
-           !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN))
+           !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) {
                zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
                                     "fcadh_2", NULL);
+               goto out;
+       }
 
+       /* port is good, unblock rport without going through erp */
+       zfcp_scsi_schedule_rport_register(port);
  out:
        zfcp_port_put(port);
        kfree(adisc);
@@ -423,14 +427,23 @@ void zfcp_fc_link_test_work(struct work_struct *work)
                container_of(work, struct zfcp_port, test_link_work);
        int retval;
 
+       if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_UNBLOCKED)) {
+               zfcp_port_put(port);
+               return; /* port erp is running and will update rport status */
+       }
+
+       zfcp_port_get(port);
+       port->rport_task = RPORT_DEL;
+       zfcp_scsi_rport_work(&port->rport_work);
+
        retval = zfcp_fc_adisc(port);
        if (retval == 0)
                return;
 
        /* send of ADISC was not possible */
+       zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);
+
        zfcp_port_put(port);
-       if (retval != -EBUSY)
-               zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);
 }
 
 /**
index 71c32f3ffcb779fb5a373996195f043dff95f206..9fa8c8990a11d6d46d9dfeb74bd1e42167710099 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Implementation of FSF commands.
  *
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
  */
 
 #define KMSG_COMPONENT "zfcp"
@@ -177,6 +177,7 @@ static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, char *id,
                return;
 
        atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
+       zfcp_scsi_schedule_rports_block(adapter);
 
        if (!link_down)
                goto out;
index 2af8cfbc38905ce310ed7ce64129ec6c47853855..7141f9a675dff91e001e43ad6eee7d6ac52ceb77 100644 (file)
@@ -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 */