aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/scsi/zfcp_erp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/scsi/zfcp_erp.c')
-rw-r--r--drivers/s390/scsi/zfcp_erp.c3585
1 files changed, 3585 insertions, 0 deletions
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
new file mode 100644
index 00000000000..cfc0d8c588d
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -0,0 +1,3585 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_erp.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP
+
+#define ZFCP_ERP_REVISION "$Revision: 1.86 $"
+
+#include "zfcp_ext.h"
+
+static int zfcp_erp_adisc(struct zfcp_adapter *, fc_id_t);
+static void zfcp_erp_adisc_handler(unsigned long);
+
+static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int);
+static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int);
+static int zfcp_erp_port_reopen_internal(struct zfcp_port *, int);
+static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *, int);
+
+static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *, int);
+static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *, int);
+
+static void zfcp_erp_adapter_block(struct zfcp_adapter *, int);
+static void zfcp_erp_adapter_unblock(struct zfcp_adapter *);
+static void zfcp_erp_port_block(struct zfcp_port *, int);
+static void zfcp_erp_port_unblock(struct zfcp_port *);
+static void zfcp_erp_unit_block(struct zfcp_unit *, int);
+static void zfcp_erp_unit_unblock(struct zfcp_unit *);
+
+static int zfcp_erp_thread(void *);
+
+static int zfcp_erp_strategy(struct zfcp_erp_action *);
+
+static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *);
+static int zfcp_erp_strategy_memwait(struct zfcp_erp_action *);
+static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *, int);
+static int zfcp_erp_strategy_check_unit(struct zfcp_unit *, int);
+static int zfcp_erp_strategy_check_port(struct zfcp_port *, int);
+static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *, int);
+static int zfcp_erp_strategy_statechange(int, u32, struct zfcp_adapter *,
+ struct zfcp_port *,
+ struct zfcp_unit *, int);
+static inline int zfcp_erp_strategy_statechange_detected(atomic_t *, u32);
+static int zfcp_erp_strategy_followup_actions(int, struct zfcp_adapter *,
+ struct zfcp_port *,
+ struct zfcp_unit *, int);
+static int zfcp_erp_strategy_check_queues(struct zfcp_adapter *);
+static int zfcp_erp_strategy_check_action(struct zfcp_erp_action *, int);
+
+static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *, int);
+static int zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_close_qdio(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_close_fsf(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_fsf_statusread(
+ struct zfcp_erp_action *);
+
+static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *);
+
+static int zfcp_erp_port_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_clearstati(struct zfcp_port *);
+static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_nameserver_wakeup(
+ struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *);
+
+static int zfcp_erp_unit_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *);
+static int zfcp_erp_unit_strategy_close(struct zfcp_erp_action *);
+static int zfcp_erp_unit_strategy_open(struct zfcp_erp_action *);
+
+static int zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *);
+static int zfcp_erp_action_dismiss_port(struct zfcp_port *);
+static int zfcp_erp_action_dismiss_unit(struct zfcp_unit *);
+static int zfcp_erp_action_dismiss(struct zfcp_erp_action *);
+
+static int zfcp_erp_action_enqueue(int, struct zfcp_adapter *,
+ struct zfcp_port *, struct zfcp_unit *);
+static int zfcp_erp_action_dequeue(struct zfcp_erp_action *);
+static void zfcp_erp_action_cleanup(int, struct zfcp_adapter *,
+ struct zfcp_port *, struct zfcp_unit *,
+ int);
+
+static void zfcp_erp_action_ready(struct zfcp_erp_action *);
+static int zfcp_erp_action_exists(struct zfcp_erp_action *);
+
+static inline void zfcp_erp_action_to_ready(struct zfcp_erp_action *);
+static inline void zfcp_erp_action_to_running(struct zfcp_erp_action *);
+
+static void zfcp_erp_memwait_handler(unsigned long);
+static void zfcp_erp_timeout_handler(unsigned long);
+static inline void zfcp_erp_timeout_init(struct zfcp_erp_action *);
+
+/**
+ * zfcp_fsf_request_timeout_handler - called if a request timed out
+ * @data: pointer to adapter for handler function
+ *
+ * This function needs to be called if requests (ELS, Generic Service,
+ * or SCSI commands) exceed a certain time limit. The assumption is
+ * that after the time limit the adapter get stuck. So we trigger a reopen of
+ * the adapter. This should not be used for error recovery, SCSI abort
+ * commands and SCSI requests from SCSI mid-layer.
+ */
+void
+zfcp_fsf_request_timeout_handler(unsigned long data)
+{
+ struct zfcp_adapter *adapter;
+
+ adapter = (struct zfcp_adapter *) data;
+
+ zfcp_erp_adapter_reopen(adapter, 0);
+}
+
+/*
+ * function: zfcp_fsf_scsi_er_timeout_handler
+ *
+ * purpose: This function needs to be called whenever a SCSI error recovery
+ * action (abort/reset) does not return.
+ * Re-opening the adapter means that the command can be returned
+ * by zfcp (it is guarranteed that it does not return via the
+ * adapter anymore). The buffer can then be used again.
+ *
+ * returns: sod all
+ */
+void
+zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
+{
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+
+ ZFCP_LOG_NORMAL("warning: SCSI error recovery timed out. "
+ "Restarting all operations on the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 1, "eh_lmem_tout");
+ zfcp_erp_adapter_reopen(adapter, 0);
+
+ return;
+}
+
+/*
+ * function:
+ *
+ * purpose: called if an adapter failed,
+ * initiates adapter recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+int
+zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter, int clear_mask)
+{
+ int retval;
+
+ debug_text_event(adapter->erp_dbf, 5, "a_ro");
+ ZFCP_LOG_DEBUG("reopen adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+
+ zfcp_erp_adapter_block(adapter, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
+ ZFCP_LOG_DEBUG("skipped reopen of failed adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 5, "a_ro_f");
+ /* ensure propagation of failed status to new devices */
+ zfcp_erp_adapter_failed(adapter);
+ retval = -EIO;
+ goto out;
+ }
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
+ adapter, NULL, NULL);
+
+ out:
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: Wrappper for zfcp_erp_adapter_reopen_internal
+ * used to ensure the correct locking
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+int
+zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear_mask)
+{
+ int retval;
+
+ retval = zfcp_erp_adapter_reopen(adapter,
+ ZFCP_STATUS_COMMON_RUNNING |
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ clear_mask);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_erp_port_shutdown(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+
+ retval = zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_RUNNING |
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ clear_mask);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask)
+{
+ int retval;
+
+ retval = zfcp_erp_unit_reopen(unit,
+ ZFCP_STATUS_COMMON_RUNNING |
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ clear_mask);
+
+ return retval;
+}
+
+
+/**
+ * zfcp_erp_adisc - send ADISC ELS command
+ * @adapter: adapter structure
+ * @d_id: d_id of port where ADISC is sent to
+ */
+int
+zfcp_erp_adisc(struct zfcp_adapter *adapter, fc_id_t d_id)
+{
+ struct zfcp_send_els *send_els;
+ struct zfcp_ls_adisc *adisc;
+ void *address = NULL;
+ int retval = 0;
+ struct timer_list *timer;
+
+ send_els = kmalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC);
+ if (send_els == NULL)
+ goto nomem;
+ memset(send_els, 0, sizeof(*send_els));
+
+ send_els->req = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
+ if (send_els->req == NULL)
+ goto nomem;
+ memset(send_els->req, 0, sizeof(*send_els->req));
+
+ send_els->resp = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
+ if (send_els->resp == NULL)
+ goto nomem;
+ memset(send_els->resp, 0, sizeof(*send_els->resp));
+
+ address = (void *) get_zeroed_page(GFP_ATOMIC);
+ if (address == NULL)
+ goto nomem;
+
+ zfcp_address_to_sg(address, send_els->req);
+ address += PAGE_SIZE >> 1;
+ zfcp_address_to_sg(address, send_els->resp);
+ send_els->req_count = send_els->resp_count = 1;
+
+ send_els->adapter = adapter;
+ send_els->d_id = d_id;
+ send_els->handler = zfcp_erp_adisc_handler;
+ send_els->handler_data = (unsigned long) send_els;
+
+ adisc = zfcp_sg_to_address(send_els->req);
+ send_els->ls_code = adisc->code = ZFCP_LS_ADISC;
+
+ send_els->req->length = sizeof(struct zfcp_ls_adisc);
+ send_els->resp->length = sizeof(struct zfcp_ls_adisc_acc);
+
+ /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
+ without FC-AL-2 capability, so we don't set it */
+ adisc->wwpn = adapter->wwpn;
+ adisc->wwnn = adapter->wwnn;
+ adisc->nport_id = adapter->s_id;
+ ZFCP_LOG_INFO("ADISC request from s_id 0x%08x to d_id 0x%08x "
+ "(wwpn=0x%016Lx, wwnn=0x%016Lx, "
+ "hard_nport_id=0x%08x, nport_id=0x%08x)\n",
+ adapter->s_id, d_id, (wwn_t) adisc->wwpn,
+ (wwn_t) adisc->wwnn, adisc->hard_nport_id,
+ adisc->nport_id);
+
+ timer = kmalloc(sizeof(struct timer_list), GFP_ATOMIC);
+ if (!timer)
+ goto nomem;
+
+ init_timer(timer);
+ timer->function = zfcp_fsf_request_timeout_handler;
+ timer->data = (unsigned long) adapter;
+ timer->expires = ZFCP_FSF_REQUEST_TIMEOUT;
+ send_els->timer = timer;
+
+ retval = zfcp_fsf_send_els(send_els);
+ if (retval != 0) {
+ ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port "
+ "0x%08x on adapter %s\n", d_id,
+ zfcp_get_busid_by_adapter(adapter));
+ del_timer(send_els->timer);
+ goto freemem;
+ }
+
+ goto out;
+
+ nomem:
+ retval = -ENOMEM;
+ freemem:
+ if (address != NULL)
+ __free_pages(send_els->req->page, 0);
+ if (send_els != NULL) {
+ kfree(send_els->timer);
+ kfree(send_els->req);
+ kfree(send_els->resp);
+ kfree(send_els);
+ }
+ out:
+ return retval;
+}
+
+
+/**
+ * zfcp_erp_adisc_handler - handler for ADISC ELS command
+ * @data: pointer to struct zfcp_send_els
+ *
+ * If ADISC failed (LS_RJT or timed out) forced reopen of the port is triggered.
+ */
+void
+zfcp_erp_adisc_handler(unsigned long data)
+{
+ struct zfcp_send_els *send_els;
+ struct zfcp_port *port;
+ struct zfcp_adapter *adapter;
+ fc_id_t d_id;
+ struct zfcp_ls_adisc_acc *adisc;
+
+ send_els = (struct zfcp_send_els *) data;
+
+ del_timer(send_els->timer);
+
+ adapter = send_els->adapter;
+ d_id = send_els->d_id;
+
+ read_lock(&zfcp_data.config_lock);
+ port = zfcp_get_port_by_did(send_els->adapter, send_els->d_id);
+ read_unlock(&zfcp_data.config_lock);
+
+ BUG_ON(port == NULL);
+
+ /* request rejected or timed out */
+ if (send_els->status != 0) {
+ ZFCP_LOG_NORMAL("ELS request rejected/timed out, "
+ "force physical port reopen "
+ "(adapter %s, port d_id=0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ debug_text_event(adapter->erp_dbf, 3, "forcreop");
+ if (zfcp_erp_port_forced_reopen(port, 0))
+ ZFCP_LOG_NORMAL("failed reopen of port "
+ "(adapter %s, wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_port(port),
+ port->wwpn);
+ goto out;
+ }
+
+ adisc = zfcp_sg_to_address(send_els->resp);
+
+ ZFCP_LOG_INFO("ADISC response from d_id 0x%08x to s_id "
+ "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, "
+ "hard_nport_id=0x%08x, nport_id=0x%08x)\n",
+ d_id, adapter->s_id, (wwn_t) adisc->wwpn,
+ (wwn_t) adisc->wwnn, adisc->hard_nport_id,
+ adisc->nport_id);
+
+ /* set wwnn for port */
+ if (port->wwnn == 0)
+ port->wwnn = adisc->wwnn;
+
+ if (port->wwpn != adisc->wwpn) {
+ ZFCP_LOG_NORMAL("d_id assignment changed, reopening "
+ "port (adapter %s, wwpn=0x%016Lx, "
+ "adisc_resp_wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_port(port),
+ port->wwpn, (wwn_t) adisc->wwpn);
+ if (zfcp_erp_port_reopen(port, 0))
+ ZFCP_LOG_NORMAL("failed reopen of port "
+ "(adapter %s, wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_port(port),
+ port->wwpn);
+ }
+
+ out:
+ zfcp_port_put(port);
+ __free_pages(send_els->req->page, 0);
+ kfree(send_els->timer);
+ kfree(send_els->req);
+ kfree(send_els->resp);
+ kfree(send_els);
+}
+
+
+/**
+ * zfcp_test_link - lightweight link test procedure
+ * @port: port to be tested
+ *
+ * Test status of a link to a remote port using the ELS command ADISC.
+ */
+int
+zfcp_test_link(struct zfcp_port *port)
+{
+ int retval;
+
+ zfcp_port_get(port);
+ retval = zfcp_erp_adisc(port->adapter, port->d_id);
+ if (retval != 0) {
+ zfcp_port_put(port);
+ ZFCP_LOG_NORMAL("reopen needed for port 0x%016Lx "
+ "on adapter %s\n ", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ retval = zfcp_erp_port_forced_reopen(port, 0);
+ if (retval != 0) {
+ ZFCP_LOG_NORMAL("reopen of remote port 0x%016Lx "
+ "on adapter %s failed\n", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ retval = -EPERM;
+ }
+ }
+
+ return retval;
+}
+
+
+/*
+ * function:
+ *
+ * purpose: called if a port failed to be opened normally
+ * initiates Forced Reopen recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+static int
+zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 5, "pf_ro");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+
+ ZFCP_LOG_DEBUG("forced reopen of port 0x%016Lx on adapter %s\n",
+ port->wwpn, zfcp_get_busid_by_port(port));
+
+ zfcp_erp_port_block(port, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
+ ZFCP_LOG_DEBUG("skipped forced reopen of failed port 0x%016Lx "
+ "on adapter %s\n", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ debug_text_event(adapter->erp_dbf, 5, "pf_ro_f");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+ retval = -EIO;
+ goto out;
+ }
+
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
+ port->adapter, port, NULL);
+
+ out:
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: Wrappper for zfcp_erp_port_forced_reopen_internal
+ * used to ensure the correct locking
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+int
+zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+ struct zfcp_adapter *adapter;
+
+ adapter = port->adapter;
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_port_forced_reopen_internal(port, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: called if a port is to be opened
+ * initiates Reopen recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+static int
+zfcp_erp_port_reopen_internal(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 5, "p_ro");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+
+ ZFCP_LOG_DEBUG("reopen of port 0x%016Lx on adapter %s\n",
+ port->wwpn, zfcp_get_busid_by_port(port));
+
+ zfcp_erp_port_block(port, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
+ ZFCP_LOG_DEBUG("skipped reopen of failed port 0x%016Lx "
+ "on adapter %s\n", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ debug_text_event(adapter->erp_dbf, 5, "p_ro_f");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+ /* ensure propagation of failed status to new devices */
+ zfcp_erp_port_failed(port);
+ retval = -EIO;
+ goto out;
+ }
+
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
+ port->adapter, port, NULL);
+
+ out:
+ return retval;
+}
+
+/**
+ * zfcp_erp_port_reopen - initiate reopen of a remote port
+ * @port: port to be reopened
+ * @clear_mask: specifies flags in port status to be cleared
+ * Return: 0 on success, < 0 on error
+ *
+ * This is a wrappper function for zfcp_erp_port_reopen_internal. It ensures
+ * correct locking. An error recovery task is initiated to do the reopen.
+ * To wait for the completion of the reopen zfcp_erp_wait should be used.
+ */
+int
+zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+ struct zfcp_adapter *adapter = port->adapter;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_port_reopen_internal(port, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: called if a unit is to be opened
+ * initiates Reopen recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+static int
+zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 5, "u_ro");
+ debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
+ ZFCP_LOG_DEBUG("reopen of unit 0x%016Lx on port 0x%016Lx "
+ "on adapter %s\n", unit->fcp_lun,
+ unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+
+ zfcp_erp_unit_block(unit, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
+ ZFCP_LOG_DEBUG("skipped reopen of failed unit 0x%016Lx "
+ "on port 0x%016Lx on adapter %s\n",
+ unit->fcp_lun, unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit));
+ debug_text_event(adapter->erp_dbf, 5, "u_ro_f");
+ debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
+ sizeof (fcp_lun_t));
+ retval = -EIO;
+ goto out;
+ }
+
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
+ unit->port->adapter, unit->port, unit);
+ out:
+ return retval;
+}
+
+/**
+ * zfcp_erp_unit_reopen - initiate reopen of a unit
+ * @unit: unit to be reopened
+ * @clear_mask: specifies flags in unit status to be cleared
+ * Return: 0 on success, < 0 on error
+ *
+ * This is a wrappper for zfcp_erp_unit_reopen_internal. It ensures correct
+ * locking. An error recovery task is initiated to do the reopen.
+ * To wait for the completion of the reopen zfcp_erp_wait should be used.
+ */
+int
+zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+ struct zfcp_adapter *adapter;
+ struct zfcp_port *port;
+
+ port = unit->port;
+ adapter = port->adapter;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_unit_reopen_internal(unit, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: disable I/O,
+ * return any open requests and clean them up,
+ * aim: no pending and incoming I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
+{
+ debug_text_event(adapter->erp_dbf, 6, "a_bl");
+ zfcp_erp_modify_adapter_status(adapter,
+ ZFCP_STATUS_COMMON_UNBLOCKED |
+ clear_mask, ZFCP_CLEAR);
+}
+
+/*
+ * function:
+ *
+ * purpose: enable I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
+{
+ debug_text_event(adapter->erp_dbf, 6, "a_ubl");
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
+}
+
+/*
+ * function:
+ *
+ * purpose: disable I/O,
+ * return any open requests and clean them up,
+ * aim: no pending and incoming I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_port_block(struct zfcp_port *port, int clear_mask)
+{
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "p_bl");
+ debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+ zfcp_erp_modify_port_status(port,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
+ ZFCP_CLEAR);
+}
+
+/*
+ * function:
+ *
+ * purpose: enable I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_port_unblock(struct zfcp_port *port)
+{
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "p_ubl");
+ debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
+}
+
+/*
+ * function:
+ *
+ * purpose: disable I/O,
+ * return any open requests and clean them up,
+ * aim: no pending and incoming I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "u_bl");
+ debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
+ zfcp_erp_modify_unit_status(unit,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
+ ZFCP_CLEAR);
+}
+
+/*
+ * function:
+ *
+ * purpose: enable I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_unit_unblock(struct zfcp_unit *unit)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "u_ubl");
+ debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+static void
+zfcp_erp_action_ready(struct zfcp_erp_action *erp_action)
+{
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 4, "a_ar");
+ debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof (int));
+
+ zfcp_erp_action_to_ready(erp_action);
+ up(&adapter->erp_ready_sem);
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns: <0 erp_action not found in any list
+ * ZFCP_ERP_ACTION_READY erp_action is in ready list
+ * ZFCP_ERP_ACTION_RUNNING erp_action is in running list
+ *
+ * locks: erp_lock must be held
+ */
+static int
+zfcp_erp_action_exists(struct zfcp_erp_action *erp_action)
+{
+ int retval = -EINVAL;
+ struct list_head *entry;
+ struct zfcp_erp_action *entry_erp_action;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ /* search in running list */
+ list_for_each(entry, &adapter->erp_running_head) {
+ entry_erp_action =
+ list_entry(entry, struct zfcp_erp_action, list);
+ if (entry_erp_action == erp_action) {
+ retval = ZFCP_ERP_ACTION_RUNNING;
+ goto out;
+ }
+ }
+ /* search in ready list */
+ list_for_each(entry, &adapter->erp_ready_head) {
+ entry_erp_action =
+ list_entry(entry, struct zfcp_erp_action, list);
+ if (entry_erp_action == erp_action) {
+ retval = ZFCP_ERP_ACTION_READY;
+ goto out;
+ }
+ }
+
+ out:
+ return retval;
+}
+
+/*
+ * purpose: checks current status of action (timed out, dismissed, ...)
+ * and does appropriate preparations (dismiss fsf request, ...)
+ *
+ * locks: called under erp_lock (disabled interrupts)
+ *
+ * returns: 0
+ */
+static int
+zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action)
+{
+ int retval = 0;
+ struct zfcp_fsf_req *fsf_req;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ if (erp_action->fsf_req) {
+ /* take lock to ensure that request is not being deleted meanwhile */
+ write_lock(&adapter->fsf_req_list_lock);
+ /* check whether fsf req does still exist */
+ list_for_each_entry(fsf_req, &adapter->fsf_req_list_head, list)
+ if (fsf_req == erp_action->fsf_req)
+ break;
+ if (fsf_req == erp_action->fsf_req) {
+ /* fsf_req still exists */
+ debug_text_event(adapter->erp_dbf, 3, "a_ca_req");
+ debug_event(adapter->erp_dbf, 3, &fsf_req,
+ sizeof (unsigned long));
+ /* dismiss fsf_req of timed out or dismissed erp_action */
+ if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
+ ZFCP_STATUS_ERP_TIMEDOUT)) {
+ debug_text_event(adapter->erp_dbf, 3,
+ "a_ca_disreq");
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+ }
+ if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
+ ZFCP_LOG_NORMAL("error: erp step timed out "
+ "(action=%d, fsf_req=%p)\n ",
+ erp_action->action,
+ erp_action->fsf_req);
+ }
+ /*
+ * If fsf_req is neither dismissed nor completed
+ * then keep it running asynchronously and don't mess
+ * with the association of erp_action and fsf_req.
+ */
+ if (fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
+ ZFCP_STATUS_FSFREQ_DISMISSED)) {
+ /* forget about association between fsf_req
+ and erp_action */
+ fsf_req->erp_action = NULL;
+ erp_action->fsf_req = NULL;
+ }
+ } else {
+ debug_text_event(adapter->erp_dbf, 3, "a_ca_gonereq");
+ /*
+ * even if this fsf_req has gone, forget about
+ * association between erp_action and fsf_req
+ */
+ erp_action->fsf_req = NULL;
+ }
+ write_unlock(&adapter->fsf_req_list_lock);
+ } else
+ debug_text_event(adapter->erp_dbf, 3, "a_ca_noreq");
+
+ return retval;
+}
+
+/*
+ * purpose: generic handler for asynchronous events related to erp_action events
+ * (normal completion, time-out, dismissing, retry after
+ * low memory condition)
+ *
+ * note: deletion of timer is not required (e.g. in case of a time-out),
+ * but a second try does no harm,
+ * we leave it in here to allow for greater simplification
+ *
+ * returns: 0 - there was an action to handle
+ * !0 - otherwise
+ */
+static int
+zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action,
+ unsigned long set_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
+ debug_text_event(adapter->erp_dbf, 2, "a_asyh_ex");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action,
+ sizeof (int));
+ if (!(set_mask & ZFCP_STATUS_ERP_TIMEDOUT))
+ del_timer(&erp_action->timer);
+ erp_action->status |= set_mask;
+ zfcp_erp_action_ready(erp_action);
+ retval = 0;
+ } else {
+ /* action is ready or gone - nothing to do */
+ debug_text_event(adapter->erp_dbf, 3, "a_asyh_gone");
+ debug_event(adapter->erp_dbf, 3, &erp_action->action,
+ sizeof (int));
+ retval = 1;
+ }
+
+ return retval;
+}
+
+/*
+ * purpose: generic handler for asynchronous events related to erp_action
+ * events (normal completion, time-out, dismissing, retry after
+ * low memory condition)
+ *
+ * note: deletion of timer is not required (e.g. in case of a time-out),
+ * but a second try does no harm,
+ * we leave it in here to allow for greater simplification
+ *
+ * returns: 0 - there was an action to handle
+ * !0 - otherwise
+ */
+int
+zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
+ unsigned long set_mask)
+{
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ unsigned long flags;
+ int retval;
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ retval = zfcp_erp_async_handler_nolock(erp_action, set_mask);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+ return retval;
+}
+
+/*
+ * purpose: is called for erp_action which was slept waiting for
+ * memory becoming avaliable,
+ * will trigger that this action will be continued
+ */
+static void
+zfcp_erp_memwait_handler(unsigned long data)
+{
+ struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 2, "a_mwh");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
+
+ zfcp_erp_async_handler(erp_action, 0);
+}
+
+/*
+ * purpose: is called if an asynchronous erp step timed out,
+ * action gets an appropriate flag and will be processed
+ * accordingly
+ */
+static void
+zfcp_erp_timeout_handler(unsigned long data)
+{
+ struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 2, "a_th");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
+
+ zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
+}
+
+/*
+ * purpose: is called for an erp_action which needs to be ended
+ * though not being done,
+ * this is usually required if an higher is generated,
+ * action gets an appropriate flag and will be processed
+ * accordingly
+ *
+ * locks: erp_lock held (thus we need to call another handler variant)
+ */
+static int
+zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
+{
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 2, "a_adis");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
+
+ zfcp_erp_async_handler_nolock(erp_action, ZFCP_STATUS_ERP_DISMISSED);
+
+ return 0;
+}
+
+int
+zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
+{
+ int retval = 0;
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+
+ rwlock_init(&adapter->erp_lock);
+ INIT_LIST_HEAD(&adapter->erp_ready_head);
+ INIT_LIST_HEAD(&adapter->erp_running_head);
+ sema_init(&adapter->erp_ready_sem, 0);
+
+ retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
+ if (retval < 0) {
+ ZFCP_LOG_NORMAL("error: creation of erp thread failed for "
+ "adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 5, "a_thset_fail");
+ } else {
+ wait_event(adapter->erp_thread_wqh,
+ atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
+ &adapter->status));
+ debug_text_event(adapter->erp_dbf, 5, "a_thset_ok");
+ }
+
+ return (retval < 0);
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * context: process (i.e. proc-fs or rmmod/insmod)
+ *
+ * note: The caller of this routine ensures that the specified
+ * adapter has been shut down and that this operation
+ * has been completed. Thus, there are no pending erp_actions
+ * which would need to be handled here.
+ */
+int
+zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
+{
+ int retval = 0;
+
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
+ up(&adapter->erp_ready_sem);
+
+ wait_event(adapter->erp_thread_wqh,
+ !atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
+ &adapter->status));
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
+ &adapter->status);
+
+ debug_text_event(adapter->erp_dbf, 5, "a_thki_ok");
+
+ return retval;
+}
+
+/*
+ * purpose: is run as a kernel thread,
+ * goes through list of error recovery actions of associated adapter
+ * and delegates single action to execution
+ *
+ * returns: 0
+ */