+++ /dev/null
-From: Gerald Schaefer <geraldsc@de.ibm.com>
-Subject: zfcp: Block remote ports (rports) early
-References: bnc#482818,LTC#51961
-
-Symptom: On path failover, the SCSI error recovery can set
- SCSI devices offline.
-Problem: For path failover to work correctly, the FC rports
- in the Linux kernel have to be in the state BLOCKED.
- This prevents SCSI timeouts from triggering the SCSI
- error recovery.
-Solution: On any problem (local link, remote port or any type
- of error recovery) set the FC rports to BLOCKED early.
-
-Acked-by: John Jolly <jjolly@suse.de>
----
- drivers/s390/scsi/zfcp_aux.c | 2
- drivers/s390/scsi/zfcp_dbf.c | 1
- drivers/s390/scsi/zfcp_def.h | 2
- drivers/s390/scsi/zfcp_erp.c | 48 ++---------------
- drivers/s390/scsi/zfcp_ext.h | 4 +
- drivers/s390/scsi/zfcp_fc.c | 20 ++++++-
- drivers/s390/scsi/zfcp_fsf.c | 1
- drivers/s390/scsi/zfcp_scsi.c | 117 +++++++++++++++++++++++++++++++++++++++++-
- 8 files changed, 149 insertions(+), 46 deletions(-)
-
---- a/drivers/s390/scsi/zfcp_aux.c
-+++ b/drivers/s390/scsi/zfcp_aux.c
-@@ -612,10 +612,12 @@ struct zfcp_port *zfcp_port_enqueue(stru
- 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);
---- a/drivers/s390/scsi/zfcp_dbf.c
-+++ b/drivers/s390/scsi/zfcp_dbf.c
-@@ -644,6 +644,7 @@ static const char *zfcp_rec_dbf_ids[] =
- [149] = "port scan",
- [150] = "ptp attach",
- [151] = "port validation failed",
-+ [152] = "term rport io",
- };
-
- static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
---- a/drivers/s390/scsi/zfcp_def.h
-+++ b/drivers/s390/scsi/zfcp_def.h
-@@ -540,6 +540,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 {
---- a/drivers/s390/scsi/zfcp_erp.c
-+++ b/drivers/s390/scsi/zfcp_erp.c
-@@ -239,6 +239,7 @@ static int _zfcp_erp_adapter_reopen(stru
- int clear_mask, u8 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) {
-@@ -319,6 +320,7 @@ static void _zfcp_erp_port_forced_reopen
- int clear, u8 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;
-@@ -350,6 +352,7 @@ static int _zfcp_erp_port_reopen(struct
- 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 */
-@@ -1218,37 +1221,6 @@ static void zfcp_erp_schedule_work(struc
- 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;
-@@ -1257,8 +1229,8 @@ static void zfcp_erp_action_cleanup(stru
-
- 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) {
- atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED,
- &unit->status);
- if (!(atomic_read(&unit->status) &
-@@ -1274,18 +1246,12 @@ static void zfcp_erp_action_cleanup(stru
- zfcp_port_put(port);
- return;
- }
-- 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)
-- zfcp_erp_rports_del(adapter);
- zfcp_adapter_put(adapter);
- break;
- }
---- a/drivers/s390/scsi/zfcp_ext.h
-+++ b/drivers/s390/scsi/zfcp_ext.h
-@@ -154,6 +154,10 @@ extern int zfcp_adapter_scsi_register(st
- 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;
---- a/drivers/s390/scsi/zfcp_fc.c
-+++ b/drivers/s390/scsi/zfcp_fc.c
-@@ -370,9 +370,14 @@ static void zfcp_fc_adisc_handler(unsign
- 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, 64,
- NULL);
-+ goto out;
-+ }
-+
-+ /* port is good, unblock rport without going through erp */
-+ zfcp_scsi_schedule_rport_register(port);
-
- out:
- zfcp_port_put(port);
-@@ -417,14 +422,23 @@ void zfcp_fc_link_test_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, 65, NULL);
-+
- zfcp_port_put(port);
-- if (retval != -EBUSY)
-- zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
- }
-
- /**
---- a/drivers/s390/scsi/zfcp_fsf.c
-+++ b/drivers/s390/scsi/zfcp_fsf.c
-@@ -180,6 +180,7 @@ static void zfcp_fsf_link_down_info_eval
- return;
-
- atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
-+ zfcp_scsi_schedule_rports_block(adapter);
-
- if (!link_down)
- goto out;
---- a/drivers/s390/scsi/zfcp_scsi.c
-+++ b/drivers/s390/scsi/zfcp_scsi.c
-@@ -57,8 +57,8 @@ static int zfcp_scsi_queuecommand(struct
- {
- 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
- 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))) {
-@@ -478,6 +486,109 @@ static void zfcp_set_rport_dev_loss_tmo(
- 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, 152, 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,
-@@ -496,6 +607,8 @@ struct fc_function_template zfcp_transpo
- .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 */