Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / s390 / scsi / zfcp_erp.c
diff --git a/kernel/drivers/s390/scsi/zfcp_erp.c b/kernel/drivers/s390/scsi/zfcp_erp.c
new file mode 100644 (file)
index 0000000..acde3f5
--- /dev/null
@@ -0,0 +1,1566 @@
+/*
+ * zfcp device driver
+ *
+ * Error Recovery Procedures (ERP).
+ *
+ * Copyright IBM Corp. 2002, 2010
+ */
+
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kthread.h>
+#include "zfcp_ext.h"
+#include "zfcp_reqlist.h"
+
+#define ZFCP_MAX_ERPS                   3
+
+enum zfcp_erp_act_flags {
+       ZFCP_STATUS_ERP_TIMEDOUT        = 0x10000000,
+       ZFCP_STATUS_ERP_CLOSE_ONLY      = 0x01000000,
+       ZFCP_STATUS_ERP_DISMISSING      = 0x00100000,
+       ZFCP_STATUS_ERP_DISMISSED       = 0x00200000,
+       ZFCP_STATUS_ERP_LOWMEM          = 0x00400000,
+       ZFCP_STATUS_ERP_NO_REF          = 0x00800000,
+};
+
+enum zfcp_erp_steps {
+       ZFCP_ERP_STEP_UNINITIALIZED     = 0x0000,
+       ZFCP_ERP_STEP_FSF_XCONFIG       = 0x0001,
+       ZFCP_ERP_STEP_PHYS_PORT_CLOSING = 0x0010,
+       ZFCP_ERP_STEP_PORT_CLOSING      = 0x0100,
+       ZFCP_ERP_STEP_PORT_OPENING      = 0x0800,
+       ZFCP_ERP_STEP_LUN_CLOSING       = 0x1000,
+       ZFCP_ERP_STEP_LUN_OPENING       = 0x2000,
+};
+
+enum zfcp_erp_act_type {
+       ZFCP_ERP_ACTION_REOPEN_LUN         = 1,
+       ZFCP_ERP_ACTION_REOPEN_PORT        = 2,
+       ZFCP_ERP_ACTION_REOPEN_PORT_FORCED = 3,
+       ZFCP_ERP_ACTION_REOPEN_ADAPTER     = 4,
+};
+
+enum zfcp_erp_act_state {
+       ZFCP_ERP_ACTION_RUNNING = 1,
+       ZFCP_ERP_ACTION_READY   = 2,
+};
+
+enum zfcp_erp_act_result {
+       ZFCP_ERP_SUCCEEDED = 0,
+       ZFCP_ERP_FAILED    = 1,
+       ZFCP_ERP_CONTINUES = 2,
+       ZFCP_ERP_EXIT      = 3,
+       ZFCP_ERP_DISMISSED = 4,
+       ZFCP_ERP_NOMEM     = 5,
+};
+
+static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int mask)
+{
+       zfcp_erp_clear_adapter_status(adapter,
+                                      ZFCP_STATUS_COMMON_UNBLOCKED | mask);
+}
+
+static int zfcp_erp_action_exists(struct zfcp_erp_action *act)
+{
+       struct zfcp_erp_action *curr_act;
+
+       list_for_each_entry(curr_act, &act->adapter->erp_running_head, list)
+               if (act == curr_act)
+                       return ZFCP_ERP_ACTION_RUNNING;
+       return 0;
+}
+
+static void zfcp_erp_action_ready(struct zfcp_erp_action *act)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+
+       list_move(&act->list, &act->adapter->erp_ready_head);
+       zfcp_dbf_rec_run("erardy1", act);
+       wake_up(&adapter->erp_ready_wq);
+       zfcp_dbf_rec_run("erardy2", act);
+}
+
+static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act)
+{
+       act->status |= ZFCP_STATUS_ERP_DISMISSED;
+       if (zfcp_erp_action_exists(act) == ZFCP_ERP_ACTION_RUNNING)
+               zfcp_erp_action_ready(act);
+}
+
+static void zfcp_erp_action_dismiss_lun(struct scsi_device *sdev)
+{
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+       if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+               zfcp_erp_action_dismiss(&zfcp_sdev->erp_action);
+}
+
+static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+{
+       struct scsi_device *sdev;
+
+       if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+               zfcp_erp_action_dismiss(&port->erp_action);
+       else {
+               spin_lock(port->adapter->scsi_host->host_lock);
+               __shost_for_each_device(sdev, port->adapter->scsi_host)
+                       if (sdev_to_zfcp(sdev)->port == port)
+                               zfcp_erp_action_dismiss_lun(sdev);
+               spin_unlock(port->adapter->scsi_host->host_lock);
+       }
+}
+
+static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+{
+       struct zfcp_port *port;
+
+       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+               zfcp_erp_action_dismiss(&adapter->erp_action);
+       else {
+               read_lock(&adapter->port_list_lock);
+               list_for_each_entry(port, &adapter->port_list, list)
+                   zfcp_erp_action_dismiss_port(port);
+               read_unlock(&adapter->port_list_lock);
+       }
+}
+
+static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter,
+                                struct zfcp_port *port,
+                                struct scsi_device *sdev)
+{
+       int need = want;
+       int l_status, p_status, a_status;
+       struct zfcp_scsi_dev *zfcp_sdev;
+
+       switch (want) {
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               zfcp_sdev = sdev_to_zfcp(sdev);
+               l_status = atomic_read(&zfcp_sdev->status);
+               if (l_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+                       return 0;
+               p_status = atomic_read(&port->status);
+               if (!(p_status & ZFCP_STATUS_COMMON_RUNNING) ||
+                     p_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+                       return 0;
+               if (!(p_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+                       need = ZFCP_ERP_ACTION_REOPEN_PORT;
+               /* fall through */
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               p_status = atomic_read(&port->status);
+               if (!(p_status & ZFCP_STATUS_COMMON_OPEN))
+                       need = ZFCP_ERP_ACTION_REOPEN_PORT;
+               /* fall through */
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               p_status = atomic_read(&port->status);
+               if (p_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+                       return 0;
+               a_status = atomic_read(&adapter->status);
+               if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) ||
+                     a_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+                       return 0;
+               if (p_status & ZFCP_STATUS_COMMON_NOESC)
+                       return need;
+               if (!(a_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+                       need = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
+               /* fall through */
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               a_status = atomic_read(&adapter->status);
+               if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+                       return 0;
+               if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) &&
+                   !(a_status & ZFCP_STATUS_COMMON_OPEN))
+                       return 0; /* shutdown requested for closed adapter */
+       }
+
+       return need;
+}
+
+static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
+                                                 struct zfcp_adapter *adapter,
+                                                 struct zfcp_port *port,
+                                                 struct scsi_device *sdev)
+{
+       struct zfcp_erp_action *erp_action;
+       struct zfcp_scsi_dev *zfcp_sdev;
+
+       switch (need) {
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               zfcp_sdev = sdev_to_zfcp(sdev);
+               if (!(act_status & ZFCP_STATUS_ERP_NO_REF))
+                       if (scsi_device_get(sdev))
+                               return NULL;
+               atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+                               &zfcp_sdev->status);
+               erp_action = &zfcp_sdev->erp_action;
+               memset(erp_action, 0, sizeof(struct zfcp_erp_action));
+               erp_action->port = port;
+               erp_action->sdev = sdev;
+               if (!(atomic_read(&zfcp_sdev->status) &
+                     ZFCP_STATUS_COMMON_RUNNING))
+                       act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               if (!get_device(&port->dev))
+                       return NULL;
+               zfcp_erp_action_dismiss_port(port);
+               atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
+               erp_action = &port->erp_action;
+               memset(erp_action, 0, sizeof(struct zfcp_erp_action));
+               erp_action->port = port;
+               if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING))
+                       act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               kref_get(&adapter->ref);
+               zfcp_erp_action_dismiss_adapter(adapter);
+               atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
+               erp_action = &adapter->erp_action;
+               memset(erp_action, 0, sizeof(struct zfcp_erp_action));
+               if (!(atomic_read(&adapter->status) &
+                     ZFCP_STATUS_COMMON_RUNNING))
+                       act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
+               break;
+
+       default:
+               return NULL;
+       }
+
+       erp_action->adapter = adapter;
+       erp_action->action = need;
+       erp_action->status = act_status;
+
+       return erp_action;
+}
+
+static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
+                                  struct zfcp_port *port,
+                                  struct scsi_device *sdev,
+                                  char *id, u32 act_status)
+{
+       int retval = 1, need;
+       struct zfcp_erp_action *act;
+
+       if (!adapter->erp_thread)
+               return -EIO;
+
+       need = zfcp_erp_required_act(want, adapter, port, sdev);
+       if (!need)
+               goto out;
+
+       act = zfcp_erp_setup_act(need, act_status, adapter, port, sdev);
+       if (!act)
+               goto out;
+       atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
+       ++adapter->erp_total_count;
+       list_add_tail(&act->list, &adapter->erp_ready_head);
+       wake_up(&adapter->erp_ready_wq);
+       retval = 0;
+ out:
+       zfcp_dbf_rec_trig(id, adapter, port, sdev, want, need);
+       return retval;
+}
+
+static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
+                                   int clear_mask, char *id)
+{
+       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) {
+               zfcp_erp_set_adapter_status(adapter,
+                                           ZFCP_STATUS_COMMON_ERP_FAILED);
+               return -EIO;
+       }
+       return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
+                                      adapter, NULL, NULL, id, 0);
+}
+
+/**
+ * zfcp_erp_adapter_reopen - Reopen adapter.
+ * @adapter: Adapter to reopen.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ */
+void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, char *id)
+{
+       unsigned long flags;
+
+       zfcp_erp_adapter_block(adapter, clear);
+       zfcp_scsi_schedule_rports_block(adapter);
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+               zfcp_erp_set_adapter_status(adapter,
+                                           ZFCP_STATUS_COMMON_ERP_FAILED);
+       else
+               zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter,
+                                       NULL, NULL, id, 0);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
+/**
+ * zfcp_erp_adapter_shutdown - Shutdown adapter.
+ * @adapter: Adapter to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ */
+void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear,
+                              char *id)
+{
+       int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+       zfcp_erp_adapter_reopen(adapter, clear | flags, id);
+}
+
+/**
+ * zfcp_erp_port_shutdown - Shutdown port
+ * @port: Port to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ */
+void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, char *id)
+{
+       int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+       zfcp_erp_port_reopen(port, clear | flags, id);
+}
+
+static void zfcp_erp_port_block(struct zfcp_port *port, int clear)
+{
+       zfcp_erp_clear_port_status(port,
+                                   ZFCP_STATUS_COMMON_UNBLOCKED | clear);
+}
+
+static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear,
+                                        char *id)
+{
+       zfcp_erp_port_block(port, clear);
+       zfcp_scsi_schedule_rport_block(port);
+
+       if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+               return;
+
+       zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
+                               port->adapter, port, NULL, id, 0);
+}
+
+/**
+ * zfcp_erp_port_forced_reopen - Forced close of port and open again
+ * @port: Port to force close and to reopen.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ */
+void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id)
+{
+       unsigned long flags;
+       struct zfcp_adapter *adapter = port->adapter;
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       _zfcp_erp_port_forced_reopen(port, clear, id);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
+static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id)
+{
+       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 */
+               zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED);
+               return -EIO;
+       }
+
+       return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
+                                      port->adapter, port, NULL, id, 0);
+}
+
+/**
+ * zfcp_erp_port_reopen - trigger remote port recovery
+ * @port: port to recover
+ * @clear_mask: flags in port status to be cleared
+ * @id: Id for debug trace event.
+ *
+ * Returns 0 if recovery has been triggered, < 0 if not.
+ */
+int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id)
+{
+       int retval;
+       unsigned long flags;
+       struct zfcp_adapter *adapter = port->adapter;
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       retval = _zfcp_erp_port_reopen(port, clear, id);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+       return retval;
+}
+
+static void zfcp_erp_lun_block(struct scsi_device *sdev, int clear_mask)
+{
+       zfcp_erp_clear_lun_status(sdev,
+                                 ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask);
+}
+
+static void _zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id,
+                                u32 act_status)
+{
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+       struct zfcp_adapter *adapter = zfcp_sdev->port->adapter;
+
+       zfcp_erp_lun_block(sdev, clear);
+
+       if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+               return;
+
+       zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_LUN, adapter,
+                               zfcp_sdev->port, sdev, id, act_status);
+}
+
+/**
+ * zfcp_erp_lun_reopen - initiate reopen of a LUN
+ * @sdev: SCSI device / LUN to be reopened
+ * @clear_mask: specifies flags in LUN status to be cleared
+ * @id: Id for debug trace event.
+ *
+ * Return: 0 on success, < 0 on error
+ */
+void zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id)
+{
+       unsigned long flags;
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+       struct zfcp_port *port = zfcp_sdev->port;
+       struct zfcp_adapter *adapter = port->adapter;
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       _zfcp_erp_lun_reopen(sdev, clear, id, 0);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
+/**
+ * zfcp_erp_lun_shutdown - Shutdown LUN
+ * @sdev: SCSI device / LUN to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ */
+void zfcp_erp_lun_shutdown(struct scsi_device *sdev, int clear, char *id)
+{
+       int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+       zfcp_erp_lun_reopen(sdev, clear | flags, id);
+}
+
+/**
+ * zfcp_erp_lun_shutdown_wait - Shutdown LUN and wait for erp completion
+ * @sdev: SCSI device / LUN to shut down.
+ * @id: Id for debug trace event.
+ *
+ * Do not acquire a reference for the LUN when creating the ERP
+ * action. It is safe, because this function waits for the ERP to
+ * complete first. This allows to shutdown the LUN, even when the SCSI
+ * device is in the state SDEV_DEL when scsi_device_get will fail.
+ */
+void zfcp_erp_lun_shutdown_wait(struct scsi_device *sdev, char *id)
+{
+       unsigned long flags;
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+       struct zfcp_port *port = zfcp_sdev->port;
+       struct zfcp_adapter *adapter = port->adapter;
+       int clear = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       _zfcp_erp_lun_reopen(sdev, clear, id, ZFCP_STATUS_ERP_NO_REF);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+       zfcp_erp_wait(adapter);
+}
+
+static int status_change_set(unsigned long mask, atomic_t *status)
+{
+       return (atomic_read(status) ^ mask) & mask;
+}
+
+static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
+{
+       if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
+               zfcp_dbf_rec_run("eraubl1", &adapter->erp_action);
+       atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
+}
+
+static void zfcp_erp_port_unblock(struct zfcp_port *port)
+{
+       if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
+               zfcp_dbf_rec_run("erpubl1", &port->erp_action);
+       atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
+}
+
+static void zfcp_erp_lun_unblock(struct scsi_device *sdev)
+{
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+       if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status))
+               zfcp_dbf_rec_run("erlubl1", &sdev_to_zfcp(sdev)->erp_action);
+       atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status);
+}
+
+static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
+{
+       list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
+       zfcp_dbf_rec_run("erator1", erp_action);
+}
+
+static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+       struct zfcp_fsf_req *req;
+
+       if (!act->fsf_req_id)
+               return;
+
+       spin_lock(&adapter->req_list->lock);
+       req = _zfcp_reqlist_find(adapter->req_list, act->fsf_req_id);
+       if (req && req->erp_action == act) {
+               if (act->status & (ZFCP_STATUS_ERP_DISMISSED |
+                                  ZFCP_STATUS_ERP_TIMEDOUT)) {
+                       req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+                       zfcp_dbf_rec_run("erscf_1", act);
+                       req->erp_action = NULL;
+               }
+               if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
+                       zfcp_dbf_rec_run("erscf_2", act);
+               if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED)
+                       act->fsf_req_id = 0;
+       } else
+               act->fsf_req_id = 0;
+       spin_unlock(&adapter->req_list->lock);
+}
+
+/**
+ * zfcp_erp_notify - Trigger ERP action.
+ * @erp_action: ERP action to continue.
+ * @set_mask: ERP action status flags to set.
+ */
+void zfcp_erp_notify(struct zfcp_erp_action *erp_action, unsigned long set_mask)
+{
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       unsigned long flags;
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
+               erp_action->status |= set_mask;
+               zfcp_erp_action_ready(erp_action);
+       }
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
+/**
+ * zfcp_erp_timeout_handler - Trigger ERP action from timed out ERP request
+ * @data: ERP action (from timer data)
+ */
+void zfcp_erp_timeout_handler(unsigned long data)
+{
+       struct zfcp_erp_action *act = (struct zfcp_erp_action *) data;
+       zfcp_erp_notify(act, ZFCP_STATUS_ERP_TIMEDOUT);
+}
+
+static void zfcp_erp_memwait_handler(unsigned long data)
+{
+       zfcp_erp_notify((struct zfcp_erp_action *)data, 0);
+}
+
+static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
+{
+       init_timer(&erp_action->timer);
+       erp_action->timer.function = zfcp_erp_memwait_handler;
+       erp_action->timer.data = (unsigned long) erp_action;
+       erp_action->timer.expires = jiffies + HZ;
+       add_timer(&erp_action->timer);
+}
+
+static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
+                                     int clear, char *id)
+{
+       struct zfcp_port *port;
+
+       read_lock(&adapter->port_list_lock);
+       list_for_each_entry(port, &adapter->port_list, list)
+               _zfcp_erp_port_reopen(port, clear, id);
+       read_unlock(&adapter->port_list_lock);
+}
+
+static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear,
+                                    char *id)
+{
+       struct scsi_device *sdev;
+
+       spin_lock(port->adapter->scsi_host->host_lock);
+       __shost_for_each_device(sdev, port->adapter->scsi_host)
+               if (sdev_to_zfcp(sdev)->port == port)
+                       _zfcp_erp_lun_reopen(sdev, clear, id, 0);
+       spin_unlock(port->adapter->scsi_host->host_lock);
+}
+
+static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act)
+{
+       switch (act->action) {
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               _zfcp_erp_adapter_reopen(act->adapter, 0, "ersff_1");
+               break;
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               _zfcp_erp_port_forced_reopen(act->port, 0, "ersff_2");
+               break;
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               _zfcp_erp_port_reopen(act->port, 0, "ersff_3");
+               break;
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               _zfcp_erp_lun_reopen(act->sdev, 0, "ersff_4", 0);
+               break;
+       }
+}
+
+static void zfcp_erp_strategy_followup_success(struct zfcp_erp_action *act)
+{
+       switch (act->action) {
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               _zfcp_erp_port_reopen_all(act->adapter, 0, "ersfs_1");
+               break;
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               _zfcp_erp_port_reopen(act->port, 0, "ersfs_2");
+               break;
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               _zfcp_erp_lun_reopen_all(act->port, 0, "ersfs_3");
+               break;
+       }
+}
+
+static void zfcp_erp_wakeup(struct zfcp_adapter *adapter)
+{
+       unsigned long flags;
+
+       read_lock_irqsave(&adapter->erp_lock, flags);
+       if (list_empty(&adapter->erp_ready_head) &&
+           list_empty(&adapter->erp_running_head)) {
+                       atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
+                                         &adapter->status);
+                       wake_up(&adapter->erp_done_wqh);
+       }
+       read_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
+static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter)
+{
+       struct zfcp_port *port;
+       port = zfcp_port_enqueue(adapter, adapter->peer_wwpn, 0,
+                                adapter->peer_d_id);
+       if (IS_ERR(port)) /* error or port already attached */
+               return;
+       _zfcp_erp_port_reopen(port, 0, "ereptp1");
+}
+
+static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
+{
+       int retries;
+       int sleep = 1;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
+
+       for (retries = 7; retries; retries--) {
+               atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+                                 &adapter->status);
+               write_lock_irq(&adapter->erp_lock);
+               zfcp_erp_action_to_running(erp_action);
+               write_unlock_irq(&adapter->erp_lock);
+               if (zfcp_fsf_exchange_config_data(erp_action)) {
+                       atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+                                         &adapter->status);
+                       return ZFCP_ERP_FAILED;
+               }
+
+               wait_event(adapter->erp_ready_wq,
+                          !list_empty(&adapter->erp_ready_head));
+               if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT)
+                       break;
+
+               if (!(atomic_read(&adapter->status) &
+                     ZFCP_STATUS_ADAPTER_HOST_CON_INIT))
+                       break;
+
+               ssleep(sleep);
+               sleep *= 2;
+       }
+
+       atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+                         &adapter->status);
+
+       if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK))
+               return ZFCP_ERP_FAILED;
+
+       if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
+               zfcp_erp_enqueue_ptp_port(adapter);
+
+       return ZFCP_ERP_SUCCEEDED;
+}
+
+static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *act)
+{
+       int ret;
+       struct zfcp_adapter *adapter = act->adapter;
+
+       write_lock_irq(&adapter->erp_lock);
+       zfcp_erp_action_to_running(act);
+       write_unlock_irq(&adapter->erp_lock);
+
+       ret = zfcp_fsf_exchange_port_data(act);
+       if (ret == -EOPNOTSUPP)
+               return ZFCP_ERP_SUCCEEDED;
+       if (ret)
+               return ZFCP_ERP_FAILED;
+
+       zfcp_dbf_rec_run("erasox1", act);
+       wait_event(adapter->erp_ready_wq,
+                  !list_empty(&adapter->erp_ready_head));
+       zfcp_dbf_rec_run("erasox2", act);
+       if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
+               return ZFCP_ERP_FAILED;
+
+       return ZFCP_ERP_SUCCEEDED;
+}
+
+static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *act)
+{
+       if (zfcp_erp_adapter_strat_fsf_xconf(act) == ZFCP_ERP_FAILED)
+               return ZFCP_ERP_FAILED;
+
+       if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED)
+               return ZFCP_ERP_FAILED;
+
+       if (mempool_resize(act->adapter->pool.sr_data,
+                          act->adapter->stat_read_buf_num))
+               return ZFCP_ERP_FAILED;
+
+       if (mempool_resize(act->adapter->pool.status_read_req,
+                          act->adapter->stat_read_buf_num))
+               return ZFCP_ERP_FAILED;
+
+       atomic_set(&act->adapter->stat_miss, act->adapter->stat_read_buf_num);
+       if (zfcp_status_read_refill(act->adapter))
+               return ZFCP_ERP_FAILED;
+
+       return ZFCP_ERP_SUCCEEDED;
+}
+
+static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+
+       /* close queues to ensure that buffers are not accessed by adapter */
+       zfcp_qdio_close(adapter->qdio);
+       zfcp_fsf_req_dismiss_all(adapter);
+       adapter->fsf_req_seq_no = 0;
+       zfcp_fc_wka_ports_force_offline(adapter->gs);
+       /* all ports and LUNs are closed */
+       zfcp_erp_clear_adapter_status(adapter, ZFCP_STATUS_COMMON_OPEN);
+
+       atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+                         ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
+}
+
+static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *act)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+
+       if (zfcp_qdio_open(adapter->qdio)) {
+               atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+                                 ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+                                 &adapter->status);
+               return ZFCP_ERP_FAILED;
+       }
+
+       if (zfcp_erp_adapter_strategy_open_fsf(act)) {
+               zfcp_erp_adapter_strategy_close(act);
+               return ZFCP_ERP_FAILED;
+       }
+
+       atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &adapter->status);
+
+       return ZFCP_ERP_SUCCEEDED;
+}
+
+static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *act)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+
+       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN) {
+               zfcp_erp_adapter_strategy_close(act);
+               if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+                       return ZFCP_ERP_EXIT;
+       }
+
+       if (zfcp_erp_adapter_strategy_open(act)) {
+               ssleep(8);
+               return ZFCP_ERP_FAILED;
+       }
+
+       return ZFCP_ERP_SUCCEEDED;
+}
+
+static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act)
+{
+       int retval;
+
+       retval = zfcp_fsf_close_physical_port(act);
+       if (retval == -ENOMEM)
+               return ZFCP_ERP_NOMEM;
+       act->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
+       if (retval)
+               return ZFCP_ERP_FAILED;
+
+       return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
+{
+       struct zfcp_port *port = erp_action->port;
+       int status = atomic_read(&port->status);
+
+       switch (erp_action->step) {
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+               if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) &&
+                   (status & ZFCP_STATUS_COMMON_OPEN))
+                       return zfcp_erp_port_forced_strategy_close(erp_action);
+               else
+                       return ZFCP_ERP_FAILED;
+
+       case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+               if (!(status & ZFCP_STATUS_PORT_PHYS_OPEN))
+                       return ZFCP_ERP_SUCCEEDED;
+       }
+       return ZFCP_ERP_FAILED;
+}
+
+static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+
+       retval = zfcp_fsf_close_port(erp_action);
+       if (retval == -ENOMEM)
+               return ZFCP_ERP_NOMEM;
+       erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
+       if (retval)
+               return ZFCP_ERP_FAILED;
+       return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+
+       retval = zfcp_fsf_open_port(erp_action);
+       if (retval == -ENOMEM)
+               return ZFCP_ERP_NOMEM;
+       erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
+       if (retval)
+               return ZFCP_ERP_FAILED;
+       return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+       struct zfcp_port *port = act->port;
+
+       if (port->wwpn != adapter->peer_wwpn) {
+               zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED);
+               return ZFCP_ERP_FAILED;
+       }
+       port->d_id = adapter->peer_d_id;
+       return zfcp_erp_port_strategy_open_port(act);
+}
+
+static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+       struct zfcp_port *port = act->port;
+       int p_status = atomic_read(&port->status);
+
+       switch (act->step) {
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+       case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+       case ZFCP_ERP_STEP_PORT_CLOSING:
+               if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
+                       return zfcp_erp_open_ptp_port(act);
+               if (!port->d_id) {
+                       zfcp_fc_trigger_did_lookup(port);
+                       return ZFCP_ERP_EXIT;
+               }
+               return zfcp_erp_port_strategy_open_port(act);
+
+       case ZFCP_ERP_STEP_PORT_OPENING:
+               /* D_ID might have changed during open */
+               if (p_status & ZFCP_STATUS_COMMON_OPEN) {
+                       if (!port->d_id) {
+                               zfcp_fc_trigger_did_lookup(port);
+                               return ZFCP_ERP_EXIT;
+                       }
+                       return ZFCP_ERP_SUCCEEDED;
+               }
+               if (port->d_id && !(p_status & ZFCP_STATUS_COMMON_NOESC)) {
+                       port->d_id = 0;
+                       return ZFCP_ERP_FAILED;
+               }
+               /* fall through otherwise */
+       }
+       return ZFCP_ERP_FAILED;
+}
+
+static int zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action)
+{
+       struct zfcp_port *port = erp_action->port;
+       int p_status = atomic_read(&port->status);
+
+       if ((p_status & ZFCP_STATUS_COMMON_NOESC) &&
+           !(p_status & ZFCP_STATUS_COMMON_OPEN))
+               goto close_init_done;
+
+       switch (erp_action->step) {
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+               if (p_status & ZFCP_STATUS_COMMON_OPEN)
+                       return zfcp_erp_port_strategy_close(erp_action);
+               break;
+
+       case ZFCP_ERP_STEP_PORT_CLOSING:
+               if (p_status & ZFCP_STATUS_COMMON_OPEN)
+                       return ZFCP_ERP_FAILED;
+               break;
+       }
+
+close_init_done:
+       if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+               return ZFCP_ERP_EXIT;
+
+       return zfcp_erp_port_strategy_open_common(erp_action);
+}
+
+static void zfcp_erp_lun_strategy_clearstati(struct scsi_device *sdev)
+{
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+       atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED,
+                         &zfcp_sdev->status);
+}
+
+static int zfcp_erp_lun_strategy_close(struct zfcp_erp_action *erp_action)
+{
+       int retval = zfcp_fsf_close_lun(erp_action);
+       if (retval == -ENOMEM)
+               return ZFCP_ERP_NOMEM;
+       erp_action->step = ZFCP_ERP_STEP_LUN_CLOSING;
+       if (retval)
+               return ZFCP_ERP_FAILED;
+       return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_lun_strategy_open(struct zfcp_erp_action *erp_action)
+{
+       int retval = zfcp_fsf_open_lun(erp_action);
+       if (retval == -ENOMEM)
+               return ZFCP_ERP_NOMEM;
+       erp_action->step = ZFCP_ERP_STEP_LUN_OPENING;
+       if (retval)
+               return  ZFCP_ERP_FAILED;
+       return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_lun_strategy(struct zfcp_erp_action *erp_action)
+{
+       struct scsi_device *sdev = erp_action->sdev;
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+       switch (erp_action->step) {
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+               zfcp_erp_lun_strategy_clearstati(sdev);
+               if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN)
+                       return zfcp_erp_lun_strategy_close(erp_action);
+               /* already closed, fall through */
+       case ZFCP_ERP_STEP_LUN_CLOSING:
+               if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN)
+                       return ZFCP_ERP_FAILED;
+               if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+                       return ZFCP_ERP_EXIT;
+               return zfcp_erp_lun_strategy_open(erp_action);
+
+       case ZFCP_ERP_STEP_LUN_OPENING:
+               if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN)
+                       return ZFCP_ERP_SUCCEEDED;
+       }
+       return ZFCP_ERP_FAILED;
+}
+
+static int zfcp_erp_strategy_check_lun(struct scsi_device *sdev, int result)
+{
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+       switch (result) {
+       case ZFCP_ERP_SUCCEEDED :
+               atomic_set(&zfcp_sdev->erp_counter, 0);
+               zfcp_erp_lun_unblock(sdev);
+               break;
+       case ZFCP_ERP_FAILED :
+               atomic_inc(&zfcp_sdev->erp_counter);
+               if (atomic_read(&zfcp_sdev->erp_counter) > ZFCP_MAX_ERPS) {
+                       dev_err(&zfcp_sdev->port->adapter->ccw_device->dev,
+                               "ERP failed for LUN 0x%016Lx on "
+                               "port 0x%016Lx\n",
+                               (unsigned long long)zfcp_scsi_dev_lun(sdev),
+                               (unsigned long long)zfcp_sdev->port->wwpn);
+                       zfcp_erp_set_lun_status(sdev,
+                                               ZFCP_STATUS_COMMON_ERP_FAILED);
+               }
+               break;
+       }
+
+       if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+               zfcp_erp_lun_block(sdev, 0);
+               result = ZFCP_ERP_EXIT;
+       }
+       return result;
+}
+
+static int zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
+{
+       switch (result) {
+       case ZFCP_ERP_SUCCEEDED :
+               atomic_set(&port->erp_counter, 0);
+               zfcp_erp_port_unblock(port);
+               break;
+
+       case ZFCP_ERP_FAILED :
+               if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC) {
+                       zfcp_erp_port_block(port, 0);
+                       result = ZFCP_ERP_EXIT;
+               }
+               atomic_inc(&port->erp_counter);
+               if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) {
+                       dev_err(&port->adapter->ccw_device->dev,
+                               "ERP failed for remote port 0x%016Lx\n",
+                               (unsigned long long)port->wwpn);
+                       zfcp_erp_set_port_status(port,
+                                        ZFCP_STATUS_COMMON_ERP_FAILED);
+               }
+               break;
+       }
+
+       if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+               zfcp_erp_port_block(port, 0);
+               result = ZFCP_ERP_EXIT;
+       }
+       return result;
+}
+
+static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter,
+                                          int result)
+{
+       switch (result) {
+       case ZFCP_ERP_SUCCEEDED :
+               atomic_set(&adapter->erp_counter, 0);
+               zfcp_erp_adapter_unblock(adapter);
+               break;
+
+       case ZFCP_ERP_FAILED :
+               atomic_inc(&adapter->erp_counter);
+               if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) {
+                       dev_err(&adapter->ccw_device->dev,
+                               "ERP cannot recover an error "
+                               "on the FCP device\n");
+                       zfcp_erp_set_adapter_status(adapter,
+                                           ZFCP_STATUS_COMMON_ERP_FAILED);
+               }
+               break;
+       }
+
+       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+               zfcp_erp_adapter_block(adapter, 0);
+               result = ZFCP_ERP_EXIT;
+       }
+       return result;
+}
+
+static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *erp_action,
+                                         int result)
+{
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_port *port = erp_action->port;
+       struct scsi_device *sdev = erp_action->sdev;
+
+       switch (erp_action->action) {
+
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               result = zfcp_erp_strategy_check_lun(sdev, result);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               result = zfcp_erp_strategy_check_port(port, result);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               result = zfcp_erp_strategy_check_adapter(adapter, result);
+               break;
+       }
+       return result;
+}
+
+static int zfcp_erp_strat_change_det(atomic_t *target_status, u32 erp_status)
+{
+       int status = atomic_read(target_status);
+
+       if ((status & ZFCP_STATUS_COMMON_RUNNING) &&
+           (erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY))
+               return 1; /* take it online */
+
+       if (!(status & ZFCP_STATUS_COMMON_RUNNING) &&
+           !(erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY))
+               return 1; /* take it offline */
+
+       return 0;
+}
+
+static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret)
+{
+       int action = act->action;
+       struct zfcp_adapter *adapter = act->adapter;
+       struct zfcp_port *port = act->port;
+       struct scsi_device *sdev = act->sdev;
+       struct zfcp_scsi_dev *zfcp_sdev;
+       u32 erp_status = act->status;
+
+       switch (action) {
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) {
+                       _zfcp_erp_adapter_reopen(adapter,
+                                                ZFCP_STATUS_COMMON_ERP_FAILED,
+                                                "ersscg1");
+                       return ZFCP_ERP_EXIT;
+               }
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               if (zfcp_erp_strat_change_det(&port->status, erp_status)) {
+                       _zfcp_erp_port_reopen(port,
+                                             ZFCP_STATUS_COMMON_ERP_FAILED,
+                                             "ersscg2");
+                       return ZFCP_ERP_EXIT;
+               }
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               zfcp_sdev = sdev_to_zfcp(sdev);
+               if (zfcp_erp_strat_change_det(&zfcp_sdev->status, erp_status)) {
+                       _zfcp_erp_lun_reopen(sdev,
+                                            ZFCP_STATUS_COMMON_ERP_FAILED,
+                                            "ersscg3", 0);
+                       return ZFCP_ERP_EXIT;
+               }
+               break;
+       }
+       return ret;
+}
+
+static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
+{
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_scsi_dev *zfcp_sdev;
+
+       adapter->erp_total_count--;
+       if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
+               adapter->erp_low_mem_count--;
+               erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
+       }
+
+       list_del(&erp_action->list);
+       zfcp_dbf_rec_run("eractd1", erp_action);
+
+       switch (erp_action->action) {
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               zfcp_sdev = sdev_to_zfcp(erp_action->sdev);
+               atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+                                 &zfcp_sdev->status);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+                                 &erp_action->port->status);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+                                 &erp_action->adapter->status);
+               break;
+       }
+}
+
+static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
+{
+       struct zfcp_adapter *adapter = act->adapter;
+       struct zfcp_port *port = act->port;
+       struct scsi_device *sdev = act->sdev;
+
+       switch (act->action) {
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               if (!(act->status & ZFCP_STATUS_ERP_NO_REF))
+                       scsi_device_put(sdev);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               if (result == ZFCP_ERP_SUCCEEDED)
+                       zfcp_scsi_schedule_rport_register(port);
+               /* fall through */
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               put_device(&port->dev);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               if (result == ZFCP_ERP_SUCCEEDED) {
+                       register_service_level(&adapter->service_level);
+                       zfcp_fc_conditional_port_scan(adapter);
+                       queue_work(adapter->work_queue, &adapter->ns_up_work);
+               } else
+                       unregister_service_level(&adapter->service_level);
+
+               kref_put(&adapter->ref, zfcp_adapter_release);
+               break;
+       }
+}
+
+static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
+{
+       switch (erp_action->action) {
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               return zfcp_erp_adapter_strategy(erp_action);
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               return zfcp_erp_port_forced_strategy(erp_action);
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               return zfcp_erp_port_strategy(erp_action);
+       case ZFCP_ERP_ACTION_REOPEN_LUN:
+               return zfcp_erp_lun_strategy(erp_action);
+       }
+       return ZFCP_ERP_FAILED;
+}
+
+static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       unsigned long flags;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       kref_get(&adapter->ref);
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       zfcp_erp_strategy_check_fsfreq(erp_action);
+
+       if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
+               zfcp_erp_action_dequeue(erp_action);
+               retval = ZFCP_ERP_DISMISSED;
+               goto unlock;
+       }
+
+       if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
+               retval = ZFCP_ERP_FAILED;
+               goto check_target;
+       }
+
+       zfcp_erp_action_to_running(erp_action);
+
+       /* no lock to allow for blocking operations */
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+       retval = zfcp_erp_strategy_do_action(erp_action);
+       write_lock_irqsave(&adapter->erp_lock, flags);
+
+       if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED)
+               retval = ZFCP_ERP_CONTINUES;
+
+       switch (retval) {
+       case ZFCP_ERP_NOMEM:
+               if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) {
+                       ++adapter->erp_low_mem_count;
+                       erp_action->status |= ZFCP_STATUS_ERP_LOWMEM;
+               }
+               if (adapter->erp_total_count == adapter->erp_low_mem_count)
+                       _zfcp_erp_adapter_reopen(adapter, 0, "erstgy1");
+               else {
+                       zfcp_erp_strategy_memwait(erp_action);
+                       retval = ZFCP_ERP_CONTINUES;
+               }
+               goto unlock;
+
+       case ZFCP_ERP_CONTINUES:
+               if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
+                       --adapter->erp_low_mem_count;
+                       erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
+               }
+               goto unlock;
+       }
+
+check_target:
+       retval = zfcp_erp_strategy_check_target(erp_action, retval);
+       zfcp_erp_action_dequeue(erp_action);
+       retval = zfcp_erp_strategy_statechange(erp_action, retval);
+       if (retval == ZFCP_ERP_EXIT)
+               goto unlock;
+       if (retval == ZFCP_ERP_SUCCEEDED)
+               zfcp_erp_strategy_followup_success(erp_action);
+       if (retval == ZFCP_ERP_FAILED)
+               zfcp_erp_strategy_followup_failed(erp_action);
+
+ unlock:
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+       if (retval != ZFCP_ERP_CONTINUES)
+               zfcp_erp_action_cleanup(erp_action, retval);
+
+       kref_put(&adapter->ref, zfcp_adapter_release);
+       return retval;
+}
+
+static int zfcp_erp_thread(void *data)
+{
+       struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+       struct list_head *next;
+       struct zfcp_erp_action *act;
+       unsigned long flags;
+
+       for (;;) {
+               wait_event_interruptible(adapter->erp_ready_wq,
+                          !list_empty(&adapter->erp_ready_head) ||
+                          kthread_should_stop());
+
+               if (kthread_should_stop())
+                       break;
+
+               write_lock_irqsave(&adapter->erp_lock, flags);
+               next = adapter->erp_ready_head.next;
+               write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+               if (next != &adapter->erp_ready_head) {
+                       act = list_entry(next, struct zfcp_erp_action, list);
+
+                       /* there is more to come after dismission, no notify */
+                       if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED)
+                               zfcp_erp_wakeup(adapter);
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * zfcp_erp_thread_setup - Start ERP thread for adapter
+ * @adapter: Adapter to start the ERP thread for
+ *
+ * Returns 0 on success or error code from kernel_thread()
+ */
+int zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
+{
+       struct task_struct *thread;
+
+       thread = kthread_run(zfcp_erp_thread, adapter, "zfcperp%s",
+                            dev_name(&adapter->ccw_device->dev));
+       if (IS_ERR(thread)) {
+               dev_err(&adapter->ccw_device->dev,
+                       "Creating an ERP thread for the FCP device failed.\n");
+               return PTR_ERR(thread);
+       }
+
+       adapter->erp_thread = thread;
+       return 0;
+}
+
+/**
+ * zfcp_erp_thread_kill - Stop ERP thread.
+ * @adapter: Adapter where the ERP thread should be stopped.
+ *
+ * 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.
+ */
+void zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
+{
+       kthread_stop(adapter->erp_thread);
+       adapter->erp_thread = NULL;
+       WARN_ON(!list_empty(&adapter->erp_ready_head));
+       WARN_ON(!list_empty(&adapter->erp_running_head));
+}
+
+/**
+ * zfcp_erp_wait - wait for completion of error recovery on an adapter
+ * @adapter: adapter for which to wait for completion of its error recovery
+ */
+void zfcp_erp_wait(struct zfcp_adapter *adapter)
+{
+       wait_event(adapter->erp_done_wqh,
+                  !(atomic_read(&adapter->status) &
+                       ZFCP_STATUS_ADAPTER_ERP_PENDING));
+}
+
+/**
+ * zfcp_erp_set_adapter_status - set adapter status bits
+ * @adapter: adapter to change the status
+ * @mask: status bits to change
+ *
+ * Changes in common status bits are propagated to attached ports and LUNs.
+ */
+void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask)
+{
+       struct zfcp_port *port;
+       struct scsi_device *sdev;
+       unsigned long flags;
+       u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+
+       atomic_set_mask(mask, &adapter->status);
+
+       if (!common_mask)
+               return;
+
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list)
+               atomic_set_mask(common_mask, &port->status);
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
+
+       spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, adapter->scsi_host)
+               atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status);
+       spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
+}
+
+/**
+ * zfcp_erp_clear_adapter_status - clear adapter status bits
+ * @adapter: adapter to change the status
+ * @mask: status bits to change
+ *
+ * Changes in common status bits are propagated to attached ports and LUNs.
+ */
+void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask)
+{
+       struct zfcp_port *port;
+       struct scsi_device *sdev;
+       unsigned long flags;
+       u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+       u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED;
+
+       atomic_clear_mask(mask, &adapter->status);
+
+       if (!common_mask)
+               return;
+
+       if (clear_counter)
+               atomic_set(&adapter->erp_counter, 0);
+
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list) {
+               atomic_clear_mask(common_mask, &port->status);
+               if (clear_counter)
+                       atomic_set(&port->erp_counter, 0);
+       }
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
+
+       spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, adapter->scsi_host) {
+               atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status);
+               if (clear_counter)
+                       atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
+       }
+       spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
+}
+
+/**
+ * zfcp_erp_set_port_status - set port status bits
+ * @port: port to change the status
+ * @mask: status bits to change
+ *
+ * Changes in common status bits are propagated to attached LUNs.
+ */
+void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask)
+{
+       struct scsi_device *sdev;
+       u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+       unsigned long flags;
+
+       atomic_set_mask(mask, &port->status);
+
+       if (!common_mask)
+               return;
+
+       spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, port->adapter->scsi_host)
+               if (sdev_to_zfcp(sdev)->port == port)
+                       atomic_set_mask(common_mask,
+                                       &sdev_to_zfcp(sdev)->status);
+       spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags);
+}
+
+/**
+ * zfcp_erp_clear_port_status - clear port status bits
+ * @port: adapter to change the status
+ * @mask: status bits to change
+ *
+ * Changes in common status bits are propagated to attached LUNs.
+ */
+void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask)
+{
+       struct scsi_device *sdev;
+       u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+       u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED;
+       unsigned long flags;
+
+       atomic_clear_mask(mask, &port->status);
+
+       if (!common_mask)
+               return;
+
+       if (clear_counter)
+               atomic_set(&port->erp_counter, 0);
+
+       spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, port->adapter->scsi_host)
+               if (sdev_to_zfcp(sdev)->port == port) {
+                       atomic_clear_mask(common_mask,
+                                         &sdev_to_zfcp(sdev)->status);
+                       if (clear_counter)
+                               atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
+               }
+       spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags);
+}
+
+/**
+ * zfcp_erp_set_lun_status - set lun status bits
+ * @sdev: SCSI device / lun to set the status bits
+ * @mask: status bits to change
+ */
+void zfcp_erp_set_lun_status(struct scsi_device *sdev, u32 mask)
+{
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+       atomic_set_mask(mask, &zfcp_sdev->status);
+}
+
+/**
+ * zfcp_erp_clear_lun_status - clear lun status bits
+ * @sdev: SCSi device / lun to clear the status bits
+ * @mask: status bits to change
+ */
+void zfcp_erp_clear_lun_status(struct scsi_device *sdev, u32 mask)
+{
+       struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+       atomic_clear_mask(mask, &zfcp_sdev->status);
+
+       if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
+               atomic_set(&zfcp_sdev->erp_counter, 0);
+}
+