]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] s390 (7/7): zfcp host adapter.
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 6 Oct 2003 00:48:14 +0000 (17:48 -0700)
committerLinus Torvalds <torvalds@home.osdl.org>
Mon, 6 Oct 2003 00:48:14 +0000 (17:48 -0700)
The zfcp scsi host adapater.

17 files changed:
arch/s390/defconfig
drivers/s390/Makefile
drivers/s390/scsi/Makefile [new file with mode: 0644]
drivers/s390/scsi/zfcp_aux.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_ccw.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_def.h [new file with mode: 0644]
drivers/s390/scsi/zfcp_erp.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_ext.h [new file with mode: 0644]
drivers/s390/scsi/zfcp_fsf.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_fsf.h [new file with mode: 0644]
drivers/s390/scsi/zfcp_qdio.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_scsi.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_sysfs_adapter.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_sysfs_driver.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_sysfs_port.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_sysfs_unit.c [new file with mode: 0644]
drivers/scsi/Kconfig

index ab8dd4cad77a8aea97728bfad518928105304e15..a1e4e3d3e38c28227244bb5ba6222eb0fb2aca09 100644 (file)
@@ -85,7 +85,35 @@ CONFIG_PFAULT=y
 #
 # SCSI device support
 #
-# CONFIG_SCSI is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_REPORT_LUNS is not set
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_ZFCP=y
 CONFIG_CCW=y
 
 #
@@ -383,7 +411,11 @@ CONFIG_PARTITION_ADVANCED=y
 # CONFIG_ATARI_PARTITION is not set
 CONFIG_IBM_PARTITION=y
 # CONFIG_MAC_PARTITION is not set
-# CONFIG_MSDOS_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
 # CONFIG_LDM_PARTITION is not set
 # CONFIG_NEC98_PARTITION is not set
 # CONFIG_SGI_PARTITION is not set
index f191416b2d29bf40d67e5be6c7fe4a337eba48ab..b50465ef93a46a14807d119d02160eef36def428 100644 (file)
@@ -3,6 +3,6 @@
 #
 
 obj-y += s390mach.o sysinfo.o
-obj-y += cio/ block/ char/ net/
+obj-y += cio/ block/ char/ net/ scsi/
 
 drivers-y += drivers/s390/built-in.o
diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile
new file mode 100644 (file)
index 0000000..fc14530
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the S/390 specific device drivers
+#
+
+zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \
+            zfcp_fsf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \
+            zfcp_sysfs_unit.o zfcp_sysfs_driver.o
+
+obj-$(CONFIG_ZFCP) += zfcp.o
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
new file mode 100644 (file)
index 0000000..808e6c1
--- /dev/null
@@ -0,0 +1,1651 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_aux.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * Copyright 2002 IBM Corporation
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ *            Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ *            Aron Zeh <arzeh@de.ibm.com>
+ *            Wolfgang Taphorn <taphorn@de.ibm.com>
+ *            Stefan Bader <stefan.bader@de.ibm.com>
+ *            Heiko Carstens <heiko.carstens@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.
+ */
+
+/* this drivers version (do not edit !!! generated and updated by cvs) */
+#define ZFCP_AUX_REVISION "$Revision: 1.65 $"
+
+/********************** INCLUDES *********************************************/
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+
+#include "zfcp_ext.h"
+
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ebcdic.h>
+#include <asm/cpcmd.h>         /* Debugging only */
+#include <asm/processor.h>     /* Debugging only */
+
+/* accumulated log level (module parameter) */
+static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
+/*********************** FUNCTION PROTOTYPES *********************************/
+
+/* written against the module interface */
+static int __init  zfcp_module_init(void);
+static void __exit zfcp_module_exit(void);
+
+int zfcp_reboot_handler(struct notifier_block *, unsigned long, void *);
+
+/* FCP related */
+static void zfcp_nameserver_request_handler(struct zfcp_fsf_req *);
+
+/* miscellaneous */
+#ifdef ZFCP_STAT_REQSIZES
+static int zfcp_statistics_init_all(void);
+static int zfcp_statistics_clear_all(void);
+static int zfcp_statistics_clear(struct list_head *);
+static int zfcp_statistics_new(struct list_head *, u32);
+#endif
+
+/*********************** KERNEL/MODULE PARAMETERS  ***************************/
+
+/* declare driver module init/cleanup functions */
+module_init(zfcp_module_init);
+module_exit(zfcp_module_exit);
+
+MODULE_AUTHOR("Heiko Carstens <heiko.carstens@de.ibm.com>, "
+             "Martin Peschke <mpeschke@de.ibm.com>, "
+             "Raimund Schroeder <raimund.schroeder@de.ibm.com>, "
+             "Wolfgang Taphorn <taphorn@de.ibm.com>, "
+             "Aron Zeh <arzeh@de.ibm.com>, "
+             "IBM Deutschland Entwicklung GmbH");
+/* what this driver module is about */
+MODULE_DESCRIPTION
+    ("FCP (SCSI over Fibre Channel) HBA driver for IBM eServer zSeries");
+MODULE_LICENSE("GPL");
+/* log level may be provided as a module parameter */
+module_param(loglevel, uint, 0);
+/* short explaination of the previous module parameter */
+MODULE_PARM_DESC(loglevel,
+                "log levels, 8 nibbles: "
+                "(unassigned) ERP QDIO DIO Config FSF SCSI Other, "
+                "levels: 0=none 1=normal 2=devel 3=trace");
+
+#ifdef ZFCP_PRINT_FLAGS
+u32 flags_dump = 0;
+module_param(flags_dump, uint, 0);
+#endif
+
+/****************************************************************/
+/************** Functions without logging ***********************/
+/****************************************************************/
+
+void
+_zfcp_hex_dump(char *addr, int count)
+{
+       int i;
+       for (i = 0; i < count; i++) {
+               printk("%02x", addr[i]);
+               if ((i % 4) == 3)
+                       printk(" ");
+               if ((i % 32) == 31)
+                       printk("\n");
+       }
+       if ((i % 32) != 31)
+               printk("\n");
+}
+
+/****************************************************************/
+/************** Uncategorised Functions *************************/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA                  ZFCP_LOG_AREA_OTHER
+#define ZFCP_LOG_AREA_PREFIX           ZFCP_LOG_AREA_PREFIX_OTHER
+
+#ifdef ZFCP_STAT_REQSIZES
+
+static int
+zfcp_statistics_clear(struct list_head *head)
+{
+       int retval = 0;
+       unsigned long flags;
+       struct zfcp_statistics *stat, *tmp;
+
+       write_lock_irqsave(&zfcp_data.stat_lock, flags);
+       list_for_each_entry_safe(stat, tmp, head, list) {
+               list_del(&stat->list);
+               kfree(stat);
+       }
+       write_unlock_irqrestore(&zfcp_data.stat_lock, flags);
+
+       return retval;
+}
+
+/* Add new statistics entry */
+static int
+zfcp_statistics_new(struct list_head *head, u32 num)
+{
+       int retval = 0;
+       struct zfcp_statistics *stat;
+
+       stat = kmalloc(sizeof (struct zfcp_statistics), GFP_ATOMIC);
+       if (stat) {
+               memset(stat, 0, sizeof (struct zfcp_statistics));
+               stat->num = num;
+               stat->occurrence = 1;
+               list_add_tail(&stat->list, head);
+       } else
+               zfcp_data.stat_errors++;
+
+       return retval;
+}
+
+int
+zfcp_statistics_inc(struct list_head *head, u32 num)
+{
+       int retval = 0;
+       unsigned long flags;
+       struct zfcp_statistics *stat;
+
+       write_lock_irqsave(&zfcp_data.stat_lock, flags);
+       list_for_each_entry(stat, head, list) {
+               if (stat->num == num) {
+                       stat->occurrence++;
+                       goto unlock;
+               }
+       }
+       /* occurrence must be initialized to 1 */
+       zfcp_statistics_new(head, num);
+ unlock:
+       write_unlock_irqrestore(&zfcp_data.stat_lock, flags);
+       return retval;
+}
+
+static int
+zfcp_statistics_init_all(void)
+{
+       int retval = 0;
+
+       rwlock_init(&zfcp_data.stat_lock);
+       INIT_LIST_HEAD(&zfcp_data.read_req_head);
+       INIT_LIST_HEAD(&zfcp_data.write_req_head);
+       INIT_LIST_HEAD(&zfcp_data.read_sg_head);
+       INIT_LIST_HEAD(&zfcp_data.write_sg_head);
+       INIT_LIST_HEAD(&zfcp_data.read_sguse_head);
+       INIT_LIST_HEAD(&zfcp_data.write_sguse_head);
+       return retval;
+}
+
+static int
+zfcp_statistics_clear_all(void)
+{
+       int retval = 0;
+
+       zfcp_statistics_clear(&zfcp_data.read_req_head);
+       zfcp_statistics_clear(&zfcp_data.write_req_head);
+       zfcp_statistics_clear(&zfcp_data.read_sg_head);
+       zfcp_statistics_clear(&zfcp_data.write_sg_head);
+       zfcp_statistics_clear(&zfcp_data.read_sguse_head);
+       zfcp_statistics_clear(&zfcp_data.write_sguse_head);
+       return retval;
+}
+
+#endif /* ZFCP_STAT_REQSIZES */
+
+static inline int
+zfcp_fsf_req_is_scsi_cmnd(struct zfcp_fsf_req *fsf_req)
+{
+       return ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) &&
+               !(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT));
+}
+
+void
+zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req,
+                      void *add_data, int add_length)
+{
+#ifdef ZFCP_DEBUG_COMMANDS
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+       Scsi_Cmnd *scsi_cmnd;
+       int level = 3;
+       int i;
+       unsigned long flags;
+
+       write_lock_irqsave(&adapter->cmd_dbf_lock, flags);
+       if (zfcp_fsf_req_is_scsi_cmnd(fsf_req)) {
+               scsi_cmnd = fsf_req->data.send_fcp_command_task.scsi_cmnd;
+               debug_text_event(adapter->cmd_dbf, level, "fsferror");
+               debug_text_event(adapter->cmd_dbf, level, text);
+               debug_event(adapter->cmd_dbf, level, &fsf_req,
+                           sizeof (unsigned long));
+               debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no,
+                           sizeof (u32));
+               debug_event(adapter->cmd_dbf, level, &scsi_cmnd,
+                           sizeof (unsigned long));
+               for (i = 0; i < add_length; i += ZFCP_CMD_DBF_LENGTH)
+                       debug_event(adapter->cmd_dbf,
+                                   level,
+                                   (char *) add_data + i,
+                                   min(ZFCP_CMD_DBF_LENGTH, add_length - i));
+       }
+       write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
+#endif
+}
+
+void
+zfcp_cmd_dbf_event_scsi(const char *text, Scsi_Cmnd * scsi_cmnd)
+{
+#ifdef ZFCP_DEBUG_COMMANDS
+       struct zfcp_adapter *adapter;
+       union zfcp_req_data *req_data;
+       struct zfcp_fsf_req *fsf_req;
+       int level = ((host_byte(scsi_cmnd->result) != 0) ? 1 : 5);
+       unsigned long flags;
+
+       adapter = (struct zfcp_adapter *) scsi_cmnd->device->host->hostdata[0];
+       req_data = (union zfcp_req_data *) scsi_cmnd->host_scribble;
+       fsf_req = (req_data ? req_data->send_fcp_command_task.fsf_req : NULL);
+       write_lock_irqsave(&adapter->cmd_dbf_lock, flags);
+       debug_text_event(adapter->cmd_dbf, level, "hostbyte");
+       debug_text_event(adapter->cmd_dbf, level, text);
+       debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof (u32));
+       debug_event(adapter->cmd_dbf, level, &scsi_cmnd,
+                   sizeof (unsigned long));
+       if (fsf_req) {
+               debug_event(adapter->cmd_dbf, level, &fsf_req,
+                           sizeof (unsigned long));
+               debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no,
+                           sizeof (u32));
+       } else {
+               debug_text_event(adapter->cmd_dbf, level, "");
+               debug_text_event(adapter->cmd_dbf, level, "");
+       }
+       write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
+#endif
+}
+
+void
+zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text,
+                     struct fsf_status_read_buffer *status_buffer, int length)
+{
+#ifdef ZFCP_DEBUG_INCOMING_ELS
+       int level = 1;
+       int i;
+
+       debug_text_event(adapter->in_els_dbf, level, text);
+       debug_event(adapter->in_els_dbf, level, &status_buffer->d_id, 8);
+       for (i = 0; i < length; i += ZFCP_IN_ELS_DBF_LENGTH)
+               debug_event(adapter->in_els_dbf,
+                           level,
+                           (char *) status_buffer->payload + i,
+                           min(ZFCP_IN_ELS_DBF_LENGTH, length - i));
+#endif
+}
+
+static int __init
+zfcp_module_init(void)
+{
+
+       int retval = 0;
+
+       atomic_set(&zfcp_data.loglevel, loglevel);
+
+       ZFCP_LOG_DEBUG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+
+       ZFCP_LOG_TRACE("Start Address of module: 0x%lx\n",
+                      (unsigned long) &zfcp_module_init);
+
+       /* initialize adapter list */
+       INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
+
+       /* initialize adapters to be removed list head */
+       INIT_LIST_HEAD(&zfcp_data.adapter_remove_lh);
+
+#ifdef ZFCP_STAT_REQSIZES
+       zfcp_statistics_init_all();
+#endif
+
+       /* Initialise proc semaphores */
+       sema_init(&zfcp_data.config_sema, 1);
+
+       /* initialise configuration rw lock */
+       rwlock_init(&zfcp_data.config_lock);
+
+       zfcp_data.reboot_notifier.notifier_call = zfcp_reboot_handler;
+       register_reboot_notifier(&zfcp_data.reboot_notifier);
+
+       /* save address of data structure managing the driver module */
+       zfcp_data.scsi_host_template.module = THIS_MODULE;
+
+       /* setup dynamic I/O */
+       retval = zfcp_ccw_register();
+       if (retval) {
+               ZFCP_LOG_NORMAL("Registering with common I/O layer failed.\n");
+               goto out_ccw_register;
+       }
+       goto out;
+
+ out_ccw_register:
+#ifdef ZFCP_STAT_REQSIZES
+       zfcp_statistics_clear_all();
+#endif
+
+ out:
+       return retval;
+}
+
+static void __exit
+zfcp_module_exit(void)
+{
+       unregister_reboot_notifier(&zfcp_data.reboot_notifier);
+       zfcp_ccw_unregister();
+#ifdef ZFCP_STAT_REQSIZES
+       zfcp_statistics_clear_all();
+#endif
+       ZFCP_LOG_DEBUG("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+/*
+ * This function is called automatically by the kernel whenever a reboot or a 
+ * shut-down is initiated and zfcp is still loaded
+ *
+ * locks:       zfcp_data.config_sema is taken prior to shutting down the module
+ *              and removing all structures
+ * returns:     NOTIFY_DONE in all cases
+ */
+int
+zfcp_reboot_handler(struct notifier_block *notifier, unsigned long code,
+                   void *ptr)
+{
+       int retval = NOTIFY_DONE;
+
+       /* block access to config (for rest of lifetime of this Linux) */
+       down(&zfcp_data.config_sema);
+       zfcp_erp_adapter_shutdown_all();
+
+       return retval;
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
+
+/****************************************************************/
+/****** Functions for configuration/set-up of structures ********/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA                  ZFCP_LOG_AREA_CONFIG
+#define ZFCP_LOG_AREA_PREFIX           ZFCP_LOG_AREA_PREFIX_CONFIG
+
+#ifndef MODULE
+/* zfcp_loglevel boot_parameter */
+static int __init
+zfcp_loglevel_setup(char *str)
+{
+       loglevel = simple_strtoul(str, NULL, 0);
+       ZFCP_LOG_TRACE("loglevel is 0x%x\n", loglevel);
+       return 1;               /* why just 1? */
+}
+
+__setup("zfcp_loglevel=", zfcp_loglevel_setup);
+#endif                         /* not MODULE */
+
+/**
+ * zfcp_get_unit_by_lun - find unit in unit list of port by fcp lun
+ * @port: pointer to port to search for unit
+ * @fcp_lun: lun to search for
+ * Traverses list of all units of a port and returns pointer to a unit
+ * if lun of a unit matches.
+ */
+
+struct zfcp_unit *
+zfcp_get_unit_by_lun(struct zfcp_port *port, fcp_lun_t fcp_lun)
+{
+       struct zfcp_unit *unit;
+       int found = 0;
+
+       list_for_each_entry(unit, &port->unit_list_head, list) {
+               if ((unit->fcp_lun == fcp_lun) &&
+                   !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status))
+               {
+                       found = 1;
+                       break;
+               }
+       }
+       return found ? unit : NULL;
+}
+
+/**
+ * zfcp_get_port_by_wwpn - find unit in unit list of port by fcp lun
+ * @adapter: pointer to adapter to search for port
+ * @wwpn: wwpn to search for
+ * Traverses list of all ports of an adapter and returns a pointer to a port
+ * if wwpn of a port matches.
+ */
+
+struct zfcp_port *
+zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn)
+{
+       struct zfcp_port *port;
+       int found = 0;
+
+       list_for_each_entry(port, &adapter->port_list_head, list) {
+               if ((port->wwpn == wwpn) &&
+                   !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
+               {
+                       found = 1;
+                       break;
+               }
+       }
+       return found ? port : NULL;
+}
+
+/*
+ * Enqueues a logical unit at the end of the unit list associated with the 
+ * specified port. Also sets up some unit internal structures.
+ *
+ * returns:    pointer to unit with a usecount of 1 if a new unit was
+ *              successfully enqueued
+ *              NULL otherwise
+ * locks:      config_sema must be held to serialise changes to the unit list
+ */
+struct zfcp_unit *
+zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
+{
+       struct zfcp_unit *unit;
+
+       /*
+        * check that there is no unit with this FCP_LUN already in list
+        * and enqueue it.
+        * Note: Unlike for the adapter and the port, this is an error
+        */
+       read_lock_irq(&zfcp_data.config_lock);
+       unit = zfcp_get_unit_by_lun(port, fcp_lun);
+       read_unlock_irq(&zfcp_data.config_lock);
+       if (unit)
+               return NULL;
+
+       unit = kmalloc(sizeof (struct zfcp_unit), GFP_KERNEL);
+       if (!unit)
+               return NULL;
+       memset(unit, 0, sizeof (struct zfcp_unit));
+
+       /* initialise reference count stuff */
+       atomic_set(&unit->refcount, 0);
+       init_waitqueue_head(&unit->remove_wq);
+
+       unit->port = port;
+       /*
+        * FIXME: reuse of scsi_luns!
+        */
+       unit->scsi_lun = port->max_scsi_lun + 1;
+       unit->fcp_lun = fcp_lun;
+       unit->common_magic = ZFCP_MAGIC;
+       unit->specific_magic = ZFCP_MAGIC_UNIT;
+
+       /* setup for sysfs registration */
+       snprintf(unit->sysfs_device.bus_id, BUS_ID_SIZE, "0x%016llx", fcp_lun);
+       unit->sysfs_device.parent = &port->sysfs_device;
+       unit->sysfs_device.release = zfcp_sysfs_unit_release;
+       dev_set_drvdata(&unit->sysfs_device, unit);
+
+       /* mark unit unusable as long as sysfs registration is not complete */
+       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+
+       if (device_register(&unit->sysfs_device)) {
+               kfree(unit);
+               return NULL;
+       }
+
+       if (zfcp_sysfs_unit_create_files(&unit->sysfs_device)) {
+               /*
+                * failed to create all sysfs attributes, therefore the unit
+                * must be put on the unit_remove listhead of the port where
+                * the release function expects it.
+                */
+               write_lock_irq(&zfcp_data.config_lock);
+               list_add_tail(&unit->list, &port->unit_remove_lh);
+               write_unlock_irq(&zfcp_data.config_lock);
+               device_unregister(&unit->sysfs_device);
+               return NULL;
+       }
+
+       /*
+        * update max SCSI LUN of logical units attached to parent remote port
+        */
+       port->max_scsi_lun++;
+
+       /*
+        * update max SCSI LUN of logical units attached to parent adapter
+        */
+       if (port->adapter->max_scsi_lun < port->max_scsi_lun)
+               port->adapter->max_scsi_lun = port->max_scsi_lun;
+
+       /*
+        * update max SCSI LUN of logical units attached to host (SCSI stack)
+        */
+       if (port->adapter->scsi_host &&
+           (port->adapter->scsi_host->max_lun < port->max_scsi_lun))
+               port->adapter->scsi_host->max_lun = port->max_scsi_lun + 1;
+
+       zfcp_unit_get(unit);
+
+       /* unit is new and needs to be added to list */
+       write_lock_irq(&zfcp_data.config_lock);
+       atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+       atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
+       list_add_tail(&unit->list, &port->unit_list_head);
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       port->units++;
+
+       return unit;
+}
+
+/* locks:  config_sema must be held */
+void
+zfcp_unit_dequeue(struct zfcp_unit *unit)
+{
+       /* remove specified unit data structure from list */
+       write_lock_irq(&zfcp_data.config_lock);
+       list_del(&unit->list);
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       unit->port->units--;
+       zfcp_port_put(unit->port);
+
+       kfree(unit);
+
+       return;
+}
+
+static void *
+zfcp_mempool_alloc(int gfp_mask, void *size)
+{
+       return kmalloc((size_t) size, gfp_mask);
+}
+
+static void
+zfcp_mempool_free(void *element, void *size)
+{
+       kfree(element);
+}
+
+/*
+ * Allocates a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI
+ * commands.
+ * It also genrates fcp-nameserver request/response buffer and unsolicited 
+ * status read fsf_req buffers.
+ *
+ * locks:       must only be called with zfcp_data.config_sema taken
+ */
+static int
+zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+{
+       adapter->pool.erp_fsf = mempool_create(
+               1,
+               zfcp_mempool_alloc,
+               zfcp_mempool_free,
+               (void *) ZFCP_QTCB_AND_REQ_SIZE);
+       if (!adapter->pool.erp_fsf) {
+               ZFCP_LOG_INFO
+                   ("error: FCP command buffer pool allocation failed\n");
+               return -ENOMEM;
+       }
+
+       adapter->pool.nameserver = mempool_create(
+               1,
+               zfcp_mempool_alloc,
+               zfcp_mempool_free,
+               (void *) (2 *  sizeof (struct fc_ct_iu)));
+       if (!adapter->pool.nameserver) {
+               ZFCP_LOG_INFO
+                   ("error: Nameserver buffer pool allocation failed\n");
+               return -ENOMEM;
+       }
+
+       adapter->pool.status_read_fsf = mempool_create(
+               ZFCP_STATUS_READS_RECOM,
+               zfcp_mempool_alloc,
+               zfcp_mempool_free,
+               (void *) sizeof (struct zfcp_fsf_req));
+       if (!adapter->pool.status_read_fsf) {
+               ZFCP_LOG_INFO
+                   ("error: Status read request pool allocation failed\n");
+               return -ENOMEM;
+       }
+
+       adapter->pool.status_read_buf = mempool_create(
+               ZFCP_STATUS_READS_RECOM,
+               zfcp_mempool_alloc,
+               zfcp_mempool_free,
+               (void *) sizeof (struct fsf_status_read_buffer));
+       if (!adapter->pool.status_read_buf) {
+               ZFCP_LOG_INFO
+                   ("error: Status read buffer pool allocation failed\n");
+               return -ENOMEM;
+       }
+
+       adapter->pool.fcp_command_fsf = mempool_create(
+               1,
+               zfcp_mempool_alloc,
+               zfcp_mempool_free,
+               (void *)
+               ZFCP_QTCB_AND_REQ_SIZE);
+       if (!adapter->pool.fcp_command_fsf) {
+               ZFCP_LOG_INFO
+                   ("error: FCP command buffer pool allocation failed\n");
+               return -ENOMEM;
+       }
+       init_timer(&adapter->pool.fcp_command_fsf_timer);
+       adapter->pool.fcp_command_fsf_timer.function =
+           zfcp_erp_scsi_low_mem_buffer_timeout_handler;
+       adapter->pool.fcp_command_fsf_timer.data = (unsigned long) adapter;
+
+       return 0;
+}
+
+/* locks:       must only be called with zfcp_data.config_sema taken */
+static void
+zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+{
+       if (adapter->pool.status_read_fsf)
+               mempool_destroy(adapter->pool.status_read_fsf);
+       if (adapter->pool.status_read_buf)
+               mempool_destroy(adapter->pool.status_read_buf);
+       if (adapter->pool.nameserver)
+               mempool_destroy(adapter->pool.nameserver);
+       if (adapter->pool.erp_fsf)
+               mempool_destroy(adapter->pool.erp_fsf);
+       if (adapter->pool.fcp_command_fsf)
+               mempool_destroy(adapter->pool.fcp_command_fsf);
+}
+
+/*
+ * Enqueues an adapter at the end of the adapter list in the driver data.
+ * All adapter internal structures are set up.
+ * Proc-fs entries are also created.
+ *
+ * returns:    0             if a new adapter was successfully enqueued
+ *              ZFCP_KNOWN    if an adapter with this devno was already present
+ *             -ENOMEM       if alloc failed
+ * locks:      config_sema must be held to serialise changes to the adapter list
+ */
+struct zfcp_adapter *
+zfcp_adapter_enqueue(struct ccw_device *ccw_device)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter;
+       char dbf_name[20];
+
+       /*
+        * Note: It is safe to release the list_lock, as any list changes 
+        * are protected by the config_sema, which must be held to get here
+        */
+
+       /* try to allocate new adapter data structure (zeroed) */
+       adapter = kmalloc(sizeof (struct zfcp_adapter), GFP_KERNEL);
+       if (!adapter) {
+               ZFCP_LOG_INFO("error: Allocation of base adapter "
+                             "structure failed\n");
+               goto out;
+       }
+       memset(adapter, 0, sizeof (struct zfcp_adapter));
+
+       ccw_device->handler = NULL;
+
+       /* save ccw_device pointer */
+       adapter->ccw_device = ccw_device;
+
+       retval = zfcp_qdio_allocate_queues(adapter);
+       if (retval)
+               goto queues_alloc_failed;
+
+       retval = zfcp_qdio_allocate(adapter);
+       if (retval)
+               goto qdio_allocate_failed;
+
+       retval = zfcp_allocate_low_mem_buffers(adapter);
+       if (retval)
+               goto failed_low_mem_buffers;
+
+       /* set magics */
+       adapter->common_magic = ZFCP_MAGIC;
+       adapter->specific_magic = ZFCP_MAGIC_ADAPTER;
+
+       /* initialise reference count stuff */
+       atomic_set(&adapter->refcount, 0);
+       init_waitqueue_head(&adapter->remove_wq);
+
+       /* initialise list of ports */
+       INIT_LIST_HEAD(&adapter->port_list_head);
+
+       /* initialise list of ports to be removed */
+       INIT_LIST_HEAD(&adapter->port_remove_lh);
+
+       /* initialize list of fsf requests */
+       rwlock_init(&adapter->fsf_req_list_lock);
+       INIT_LIST_HEAD(&adapter->fsf_req_list_head);
+
+       /* initialize abort lock */
+       rwlock_init(&adapter->abort_lock);
+
+       /* initialise scsi faking structures */
+       rwlock_init(&adapter->fake_list_lock);
+       init_timer(&adapter->fake_scsi_timer);
+
+       /* initialise some erp stuff */
+       init_waitqueue_head(&adapter->erp_thread_wqh);
+       init_waitqueue_head(&adapter->erp_done_wqh);
+
+       /* notification when there are no outstanding SCSI commands */
+       init_waitqueue_head(&adapter->scsi_reqs_active_wq);
+
+       /* initialize lock of associated request queue */
+       rwlock_init(&adapter->request_queue.queue_lock);
+
+       /* intitialise SCSI ER timer */
+       init_timer(&adapter->scsi_er_timer);
+
+       /* set FC service class used per default */
+       adapter->fc_service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT;
+
+       sprintf(adapter->name, "%s", zfcp_get_busid_by_adapter(adapter));
+       ASCEBC(adapter->name, strlen(adapter->name));
+
+       /* mark adapter unusable as long as sysfs registration is not complete */
+       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
+
+       adapter->ccw_device = ccw_device;
+       dev_set_drvdata(&ccw_device->dev, adapter);
+
+       if (zfcp_sysfs_adapter_create_files(&ccw_device->dev))
+               goto sysfs_failed;
+
+#ifdef ZFCP_DEBUG_REQUESTS
+       /* debug feature area which records fsf request sequence numbers */
+       sprintf(dbf_name, ZFCP_REQ_DBF_NAME "0x%s",
+               zfcp_get_busid_by_adapter(adapter));
+       adapter->req_dbf = debug_register(dbf_name,
+                                         ZFCP_REQ_DBF_INDEX,
+                                         ZFCP_REQ_DBF_AREAS,
+                                         ZFCP_REQ_DBF_LENGTH);
+       if (!adapter->req_dbf) {
+               ZFCP_LOG_INFO
+                   ("error: Out of resources. Request debug feature for "
+                    "adapter %s could not be generated.\n",
+                    zfcp_get_busid_by_adapter(adapter));
+               retval = -ENOMEM;
+               goto failed_req_dbf;
+       }
+       debug_register_view(adapter->req_dbf, &debug_hex_ascii_view);
+       debug_set_level(adapter->req_dbf, ZFCP_REQ_DBF_LEVEL);
+       debug_text_event(adapter->req_dbf, 1, "zzz");
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+
+#ifdef ZFCP_DEBUG_COMMANDS
+       /* debug feature area which records SCSI command failures (hostbyte) */
+       rwlock_init(&adapter->cmd_dbf_lock);
+       sprintf(dbf_name, ZFCP_CMD_DBF_NAME "%s",
+               zfcp_get_busid_by_adapter(adapter));
+       adapter->cmd_dbf = debug_register(dbf_name,
+                                         ZFCP_CMD_DBF_INDEX,
+                                         ZFCP_CMD_DBF_AREAS,
+                                         ZFCP_CMD_DBF_LENGTH);
+       if (!adapter->cmd_dbf) {
+               ZFCP_LOG_INFO
+                   ("error: Out of resources. Command debug feature for "
+                    "adapter %s could not be generated.\n",
+                    zfcp_get_busid_by_adapter(adapter));
+               retval = -ENOMEM;
+               goto failed_cmd_dbf;
+       }
+       debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view);
+       debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL);
+#endif                         /* ZFCP_DEBUG_COMMANDS */
+
+#ifdef ZFCP_DEBUG_ABORTS
+       /* debug feature area which records SCSI command aborts */
+       sprintf(dbf_name, ZFCP_ABORT_DBF_NAME "%s",
+               zfcp_get_busid_by_adapter(adapter));
+       adapter->abort_dbf = debug_register(dbf_name,
+                                           ZFCP_ABORT_DBF_INDEX,
+                                           ZFCP_ABORT_DBF_AREAS,
+                                           ZFCP_ABORT_DBF_LENGTH);
+       if (!adapter->abort_dbf) {
+               ZFCP_LOG_INFO
+                   ("error: Out of resources. Abort debug feature for "
+                    "adapter %s could not be generated.\n",
+                    zfcp_get_busid_by_adapter(adapter));
+               retval = -ENOMEM;
+               goto failed_abort_dbf;
+       }
+       debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view);
+       debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL);
+#endif                         /* ZFCP_DEBUG_ABORTS */
+
+#ifdef ZFCP_DEBUG_INCOMING_ELS
+       /* debug feature area which records SCSI command aborts */
+       sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME "%s",
+               zfcp_get_busid_by_adapter(adapter));
+       adapter->in_els_dbf = debug_register(dbf_name,
+                                            ZFCP_IN_ELS_DBF_INDEX,
+                                            ZFCP_IN_ELS_DBF_AREAS,
+                                            ZFCP_IN_ELS_DBF_LENGTH);
+       if (!adapter->in_els_dbf) {
+               ZFCP_LOG_INFO("error: Out of resources. ELS debug feature for "
+                             "adapter %s could not be generated.\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               retval = -ENOMEM;
+               goto failed_in_els_dbf;
+       }
+       debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view);
+       debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL);
+#endif                         /* ZFCP_DEBUG_INCOMING_ELS */
+
+       sprintf(dbf_name, ZFCP_ERP_DBF_NAME "%s",
+               zfcp_get_busid_by_adapter(adapter));
+       adapter->erp_dbf = debug_register(dbf_name,
+                                         ZFCP_ERP_DBF_INDEX,
+                                         ZFCP_ERP_DBF_AREAS,
+                                         ZFCP_ERP_DBF_LENGTH);
+       if (!adapter->erp_dbf) {
+               ZFCP_LOG_INFO("error: Out of resources. ERP debug feature for "
+                             "adapter %s could not be generated.\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               retval = -ENOMEM;
+               goto failed_erp_dbf;
+       }
+       debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view);
+       debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL);
+
+       retval = zfcp_erp_thread_setup(adapter);
+       if (retval) {
+               ZFCP_LOG_INFO("error: out of resources. "
+                             "error recovery thread for the adapter %s "
+                             "could not be started\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               goto thread_failed;
+       }
+
+       /* put allocated adapter at list tail */
+       write_lock_irq(&zfcp_data.config_lock);
+       atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
+       atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status);
+       list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       zfcp_data.adapters++;
+
+       goto out;
+
+ thread_failed:
+       if (qdio_free(adapter->ccw_device) != 0)
+               ZFCP_LOG_NORMAL
+                   ("bug: could not free memory used by data transfer "
+                    "mechanism for adapter %s\n",
+                    zfcp_get_busid_by_adapter(adapter));
+
+       debug_unregister(adapter->erp_dbf);
+
+ failed_erp_dbf:
+#ifdef ZFCP_DEBUG_INCOMING_ELS
+       debug_unregister(adapter->in_els_dbf);
+ failed_in_els_dbf:
+#endif
+
+#ifdef ZFCP_DEBUG_ABORTS
+       debug_unregister(adapter->abort_dbf);
+ failed_abort_dbf:
+#endif
+
+#ifdef ZFCP_DEBUG_COMMANDS
+       debug_unregister(adapter->cmd_dbf);
+ failed_cmd_dbf:
+#endif
+
+#ifdef ZFCP_DEBUG_REQUESTS
+       debug_unregister(adapter->req_dbf);
+ failed_req_dbf:
+#endif
+       zfcp_sysfs_adapter_remove_files(&ccw_device->dev);
+ sysfs_failed:
+       dev_set_drvdata(&ccw_device->dev, NULL);
+ failed_low_mem_buffers:
+       zfcp_free_low_mem_buffers(adapter);
+       qdio_free(ccw_device);
+ qdio_allocate_failed:
+       zfcp_qdio_free_queues(adapter);
+ queues_alloc_failed:
+       kfree(adapter);
+       adapter = NULL;
+ out:
+       return adapter;
+}
+
+/*
+ * returns:    0 - struct zfcp_adapter  data structure successfully removed
+ *             !0 - struct zfcp_adapter  data structure could not be removed
+ *                     (e.g. still used)
+ * locks:      adapter list write lock is assumed to be held by caller
+ *              adapter->fsf_req_list_lock is taken and released within this 
+ *              function and must not be held on entry
+ */
+void
+zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
+{
+       int retval = 0;
+       unsigned long flags;
+
+       zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
+       dev_set_drvdata(&adapter->ccw_device->dev, NULL);
+       /* sanity check: no pending FSF requests */
+       read_lock_irqsave(&adapter->fsf_req_list_lock, flags);
+       retval = !list_empty(&adapter->fsf_req_list_head);
+       read_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
+       if (retval) {
+               ZFCP_LOG_NORMAL("bug: Adapter %s is still in use, "
+                               "%i requests are still outstanding "
+                               "(debug info 0x%lx)\n",
+                               zfcp_get_busid_by_adapter(adapter),
+                               atomic_read(&adapter->fsf_reqs_active),
+                               (unsigned long) adapter);
+               retval = -EBUSY;
+               goto out;
+       }
+
+       /* remove specified adapter data structure from list */
+       write_lock_irq(&zfcp_data.config_lock);
+       list_del(&adapter->list);
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       /* decrease number of adapters in list */
+       zfcp_data.adapters--;
+
+       ZFCP_LOG_TRACE("adapter 0x%lx removed from list, "
+                      "%i adapters still in list\n",
+                      (unsigned long) adapter, zfcp_data.adapters);
+
+       retval = zfcp_erp_thread_kill(adapter);
+
+       retval |= qdio_free(adapter->ccw_device);
+       if (retval)
+               ZFCP_LOG_NORMAL
+                   ("bug: could not free memory used by data transfer "
+                    "mechanism for adapter %s\n",
+                    zfcp_get_busid_by_adapter(adapter));
+
+       debug_unregister(adapter->erp_dbf);
+
+#ifdef ZFCP_DEBUG_REQUESTS
+       debug_unregister(adapter->req_dbf);
+#endif
+
+#ifdef ZFCP_DEBUG_COMMANDS
+       debug_unregister(adapter->cmd_dbf);
+#endif
+#ifdef ZFCP_DEBUG_ABORTS
+       debug_unregister(adapter->abort_dbf);
+#endif
+
+#ifdef ZFCP_DEBUG_INCOMING_ELS
+       debug_unregister(adapter->in_els_dbf);
+#endif
+
+       zfcp_free_low_mem_buffers(adapter);
+       /* free memory of adapter data structure and queues */
+       zfcp_qdio_free_queues(adapter);
+       ZFCP_LOG_TRACE("Freeing adapter structure.\n");
+       kfree(adapter);
+ out:
+       return;
+}
+
+/*
+ * Enqueues a remote port at the end of the port list.
+ * All port internal structures are set-up and the proc-fs entry is also 
+ * allocated. Some SCSI-stack structures are modified for the port.
+ *
+ * returns:    0            if a new port was successfully enqueued
+ *              ZFCP_KNOWN   if a port with the requested wwpn already exists
+ *              -ENOMEM      if allocation failed
+ *              -EINVAL      if at least one of the specified parameters was wrong
+ * locks:       config_sema must be held to serialise changes to the port list
+ *              within this function (must not be held on entry)
+ */
+struct zfcp_port *
+zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status)
+{
+       struct zfcp_port *port;
+       int check_scsi_id;
+       int check_wwpn;
+
+       check_scsi_id = !(status & ZFCP_STATUS_PORT_NO_SCSI_ID);
+       check_wwpn = !(status & ZFCP_STATUS_PORT_NO_WWPN);
+
+       /*
+        * check that there is no port with this WWPN already in list
+        */
+       if (check_wwpn) {
+               read_lock_irq(&zfcp_data.config_lock);
+               port = zfcp_get_port_by_wwpn(adapter, wwpn);
+               read_unlock_irq(&zfcp_data.config_lock);
+               if (port)
+                       return NULL;
+       }
+
+       port = kmalloc(sizeof (struct zfcp_port), GFP_KERNEL);
+       if (!port)
+               return NULL;
+       memset(port, 0, sizeof (struct zfcp_port));
+
+       /* initialise reference count stuff */
+       atomic_set(&port->refcount, 0);
+       init_waitqueue_head(&port->remove_wq);
+
+       INIT_LIST_HEAD(&port->unit_list_head);
+       INIT_LIST_HEAD(&port->unit_remove_lh);
+
+       port->adapter = adapter;
+
+       if (check_scsi_id)
+               port->scsi_id = adapter->max_scsi_id + 1;
+
+       if (check_wwpn)
+               port->wwpn = wwpn;
+
+       atomic_set_mask(status, &port->status);
+
+       port->common_magic = ZFCP_MAGIC;
+       port->specific_magic = ZFCP_MAGIC_PORT;
+
+       /* setup for sysfs registration */
+       if (status & ZFCP_STATUS_PORT_NAMESERVER)
+               snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, "nameserver");
+       else
+               snprintf(port->sysfs_device.bus_id,
+                        BUS_ID_SIZE, "0x%016llx", wwpn);
+       port->sysfs_device.parent = &adapter->ccw_device->dev;
+       port->sysfs_device.release = zfcp_sysfs_port_release;
+       dev_set_drvdata(&port->sysfs_device, port);
+
+       /* mark port unusable as long as sysfs registration is not complete */
+       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+
+       if (device_register(&port->sysfs_device)) {
+               kfree(port);
+               return NULL;
+       }
+
+       if (zfcp_sysfs_port_create_files(&port->sysfs_device, status)) {
+               /*
+                * failed to create all sysfs attributes, therefore the port
+                * must be put on the port_remove listhead of the adapter
+                * where the release function expects it.
+                */
+               write_lock_irq(&zfcp_data.config_lock);
+               list_add_tail(&port->list, &adapter->port_remove_lh);
+               write_unlock_irq(&zfcp_data.config_lock);
+               device_unregister(&port->sysfs_device);
+               return NULL;
+       }
+
+       if (check_scsi_id) {
+               /*
+                * update max. SCSI ID of remote ports attached to
+                * "parent" adapter if necessary
+                * (do not care about the adapters own SCSI ID)
+                */
+               adapter->max_scsi_id++;
+
+               /*
+                * update max. SCSI ID of remote ports attached to
+                * "parent" host (SCSI stack) if necessary
+                */
+               if (adapter->scsi_host &&
+                   (adapter->scsi_host->max_id < adapter->max_scsi_id + 1))
+                       adapter->scsi_host->max_id = adapter->max_scsi_id + 1;
+       }
+
+       zfcp_port_get(port);
+
+       write_lock_irq(&zfcp_data.config_lock);
+       atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+       atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
+       list_add_tail(&port->list, &adapter->port_list_head);
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       adapter->ports++;
+
+       return port;
+}
+
+/*
+ * returns:    0 - struct zfcp_port data structure successfully removed
+ *             !0 - struct zfcp_port data structure could not be removed
+ *                     (e.g. still used)
+ * locks :     port list write lock is assumed to be held by caller
+ */
+void
+zfcp_port_dequeue(struct zfcp_port *port)
+{
+       /* remove specified port from list */
+       write_lock_irq(&zfcp_data.config_lock);
+       list_del(&port->list);
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       port->adapter->ports--;
+       zfcp_adapter_put(port->adapter);
+
+       kfree(port);
+
+       return;
+}
+
+/* Enqueues a nameserver port */
+int
+zfcp_nameserver_enqueue(struct zfcp_adapter *adapter)
+{
+       struct zfcp_port *port;
+
+       /* generate port structure */
+       port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_NAMESERVER);
+       if (!port) {
+               ZFCP_LOG_INFO("error: Could not establish a connection to the "
+                             "fabric name server connected to the "
+                             "adapter %s\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               return -ENXIO;
+       }
+       /* set special D_ID */
+       port->d_id = ZFCP_DID_NAMESERVER;
+       adapter->nameserver_port = port;
+       zfcp_adapter_get(adapter);
+       zfcp_port_put(port);
+
+       return 0;
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
+
+/****************************************************************/
+/******* Fibre Channel Standard related Functions  **************/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FC
+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FC
+
+void
+zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter,
+                          struct fsf_status_read_buffer *status_buffer)
+{
+       struct fcp_rscn_head *fcp_rscn_head;
+       struct fcp_rscn_element *fcp_rscn_element;
+       struct zfcp_port *port;
+       int i;
+       int reopen_unknown = 0;
+       int no_entries;
+       unsigned long flags;
+
+       fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload;
+       fcp_rscn_element = (struct fcp_rscn_element *) status_buffer->payload;
+
+       /* see FC-FS */
+       no_entries = (fcp_rscn_head->payload_len / 4);
+
+       zfcp_in_els_dbf_event(adapter, "##rscn", status_buffer,
+                             fcp_rscn_head->payload_len);
+
+       debug_text_event(adapter->erp_dbf, 1, "unsol_els_rscn:");
+       for (i = 1; i < no_entries; i++) {
+               int known;
+               int range_mask;
+               int no_notifications;
+
+               range_mask = 0;
+               no_notifications = 0;
+               known = 0;
+               /* skip head and start with 1st element */
+               fcp_rscn_element++;
+               switch (fcp_rscn_element->addr_format) {
+               case ZFCP_PORT_ADDRESS:
+                       ZFCP_LOG_FLAGS(1, "ZFCP_PORT_ADDRESS\n");
+                       range_mask = ZFCP_PORTS_RANGE_PORT;
+                       no_notifications = 1;
+                       break;
+               case ZFCP_AREA_ADDRESS:
+                       ZFCP_LOG_FLAGS(1, "ZFCP_AREA_ADDRESS\n");
+                       /* skip head and start with 1st element */
+                       range_mask = ZFCP_PORTS_RANGE_AREA;
+                       no_notifications = ZFCP_NO_PORTS_PER_AREA;
+                       break;
+               case ZFCP_DOMAIN_ADDRESS:
+                       ZFCP_LOG_FLAGS(1, "ZFCP_DOMAIN_ADDRESS\n");
+                       range_mask = ZFCP_PORTS_RANGE_DOMAIN;
+                       no_notifications = ZFCP_NO_PORTS_PER_DOMAIN;
+                       break;
+               case ZFCP_FABRIC_ADDRESS:
+                       ZFCP_LOG_FLAGS(1, "ZFCP_FABRIC_ADDRESS\n");
+                       range_mask = ZFCP_PORTS_RANGE_FABRIC;
+                       no_notifications = ZFCP_NO_PORTS_PER_FABRIC;
+                       break;
+               default:
+                       /* cannot happen */
+                       break;
+               }
+               read_lock_irqsave(&zfcp_data.config_lock, flags);
+               list_for_each_entry(port, &adapter->port_list_head, list) {
+                       /* Do we know this port? If not skip it. */
+                       if (!atomic_test_mask
+                           (ZFCP_STATUS_PORT_DID_DID, &port->status))
+                               continue;
+                       /*
+                        * FIXME: race: d_id might being invalidated
+                        * (...DID_DID reset)
+                        */
+                       if ((port->d_id & range_mask)
+                           == (fcp_rscn_element->nport_did & range_mask)) {
+                               known++;
+                               ZFCP_LOG_TRACE("known=%d, reopen did 0x%x\n",
+                                              known,
+                                              fcp_rscn_element->nport_did);
+                               /*
+                                * Unfortunately, an RSCN does not specify the
+                                * type of change a target underwent. We assume
+                                * that it makes sense to reopen the link.
+                                * FIXME: Shall we try to find out more about
+                                * the target and link state before closing it?
+                                * How to accomplish this? (nameserver?)
+                                * Where would such code be put in?
+                                * (inside or outside erp)
+                                */
+                               ZFCP_LOG_INFO
+                                   ("Received state change notification."
+                                    "Trying to reopen the port with wwpn "
+                                    "0x%Lx.\n", port->wwpn);
+                               debug_text_event(adapter->erp_dbf, 1,
+                                                "unsol_els_rscnk:");
+                               zfcp_erp_port_reopen(port, 0);
+                       }
+               }
+               read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+               ZFCP_LOG_TRACE("known %d, no_notifications %d\n",
+                              known, no_notifications);
+               if (known < no_notifications) {
+                       ZFCP_LOG_DEBUG
+                           ("At least one unknown port changed state. "
+                            "Unknown ports need to be reopened.\n");
+                       reopen_unknown = 1;
+               }
+       }                       // for (i=1; i < no_entries; i++)
+
+       if (reopen_unknown) {
+               ZFCP_LOG_DEBUG("At least one unknown did "
+                              "underwent a state change.\n");
+               read_lock_irqsave(&zfcp_data.config_lock, flags);
+               list_for_each_entry(port, &adapter->port_list_head, list) {
+                       if (!atomic_test_mask((ZFCP_STATUS_PORT_DID_DID
+                                              | ZFCP_STATUS_PORT_NAMESERVER),
+                                             &port->status)) {
+                               ZFCP_LOG_INFO
+                                   ("Received state change notification."
+                                    "Trying to open the port with wwpn "
+                                    "0x%Lx. Hope it's there now.\n",
+                                    port->wwpn);
+                               debug_text_event(adapter->erp_dbf, 1,
+                                                "unsol_els_rscnu:");
+                               zfcp_erp_port_reopen(port,
+                                                    ZFCP_STATUS_COMMON_ERP_FAILED);
+                       }
+               }
+               read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       }
+}
+
+static void
+zfcp_fsf_incoming_els_plogi(struct zfcp_adapter *adapter,
+                           struct fsf_status_read_buffer *status_buffer)
+{
+       logi *els_logi = (logi *) status_buffer->payload;
+       struct zfcp_port *port;
+       unsigned long flags;
+
+       zfcp_in_els_dbf_event(adapter, "##plogi", status_buffer, 28);
+
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       list_for_each_entry(port, &adapter->port_list_head, list) {
+               if (port->wwpn == (*(wwn_t *) & els_logi->nport_wwn))
+                       break;
+       }
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+       if (!port || (port->wwpn != (*(wwn_t *) & els_logi->nport_wwn))) {
+               ZFCP_LOG_DEBUG("Re-open port indication received "
+                              "for the non-existing port with D_ID "
+                              "0x%3.3x, on the adapter "
+                              "%s. Ignored.\n",
+                              status_buffer->d_id,
+                              zfcp_get_busid_by_adapter(adapter));
+       } else {
+               debug_text_event(adapter->erp_dbf, 1, "unsol_els_plogi:");
+               debug_event(adapter->erp_dbf, 1, &els_logi->nport_wwn, 8);
+               zfcp_erp_port_forced_reopen(port, 0);
+       }
+}
+
+static void
+zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter,
+                          struct fsf_status_read_buffer *status_buffer)
+{
+       struct fcp_logo *els_logo = (struct fcp_logo *) status_buffer->payload;
+       struct zfcp_port *port;
+       unsigned long flags;
+
+       zfcp_in_els_dbf_event(adapter, "##logo", status_buffer, 16);
+
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       list_for_each_entry(port, &adapter->port_list_head, list) {
+               if (port->wwpn == els_logo->nport_wwpn)
+                       break;
+       }
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+       if (!port || (port->wwpn != els_logo->nport_wwpn)) {
+               ZFCP_LOG_DEBUG("Re-open port indication received "
+                              "for the non-existing port with D_ID "
+                              "0x%3.3x, on the adapter "
+                              "%s. Ignored.\n",
+                              status_buffer->d_id,
+                              zfcp_get_busid_by_adapter(adapter));
+       } else {
+               debug_text_event(adapter->erp_dbf, 1, "unsol_els_logo:");
+               debug_event(adapter->erp_dbf, 1, &els_logo->nport_wwpn, 8);
+               zfcp_erp_port_forced_reopen(port, 0);
+       }
+}
+
+static void
+zfcp_fsf_incoming_els_unknown(struct zfcp_adapter *adapter,
+                             struct fsf_status_read_buffer *status_buffer)
+{
+       zfcp_in_els_dbf_event(adapter, "##undef", status_buffer, 24);
+       ZFCP_LOG_NORMAL("warning: Unknown incoming ELS (0x%x) received "
+                       "for the adapter %s\n",
+                       *(u32 *) (status_buffer->payload),
+                       zfcp_get_busid_by_adapter(adapter));
+
+}
+
+void
+zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req)
+{
+       struct fsf_status_read_buffer *status_buffer;
+       u32 els_type;
+       struct zfcp_adapter *adapter;
+
+       status_buffer = fsf_req->data.status_read.buffer;
+       els_type = *(u32 *) (status_buffer->payload);
+       adapter = fsf_req->adapter;
+
+       if (els_type == LS_PLOGI)
+               zfcp_fsf_incoming_els_plogi(adapter, status_buffer);
+       else if (els_type == LS_LOGO)
+               zfcp_fsf_incoming_els_logo(adapter, status_buffer);
+       else if ((els_type & 0xffff0000) == LS_RSCN)
+               /* we are only concerned with the command, not the length */
+               zfcp_fsf_incoming_els_rscn(adapter, status_buffer);
+       else
+               zfcp_fsf_incoming_els_unknown(adapter, status_buffer);
+}
+
+/*
+ * function:   zfcp_release_nameserver_buffers
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static void
+zfcp_release_nameserver_buffers(struct zfcp_fsf_req *fsf_req)
+{
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+       void *buffer = fsf_req->data.send_generic.outbuf;
+
+       /* FIXME: not sure about appeal of this new flag (martin) */
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOLBUF)
+               mempool_free(buffer, adapter->pool.nameserver);
+       else
+               kfree(buffer);
+}
+
+/*
+ * function:   zfcp_get_nameserver_buffers
+ *
+ * purpose:    
+ *
+ * returns:
+ *
+ * locks:       fsf_request_list_lock is held when doing buffer pool 
+ *              operations
+ */
+static int
+zfcp_get_nameserver_buffers(struct zfcp_fsf_req *fsf_req)
+{
+       struct zfcp_send_generic *data = &fsf_req->data.send_generic;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+       int retval = 0;
+
+       data->outbuf = kmalloc(2 * sizeof (struct fc_ct_iu), GFP_KERNEL);
+       if (data->outbuf) {
+               memset(data->outbuf, 0, 2 * sizeof (struct fc_ct_iu));
+       } else {
+               ZFCP_LOG_DEBUG("Out of memory. Could not allocate at "
+                              "least one of the buffers "
+                              "required for a name-server request on the"
+                              "adapter %s directly.. trying emergency pool\n",
+                              zfcp_get_busid_by_adapter(adapter));
+               data->outbuf =
+                   mempool_alloc(adapter->pool.nameserver, GFP_KERNEL);
+               if (!data->outbuf) {
+                       ZFCP_LOG_DEBUG
+                               ("Out of memory. Could not get emergency "
+                                "buffer required for a name-server request "
+                                "on the adapter %s. All buffers are in "
+                                "use.\n",
+                                zfcp_get_busid_by_adapter(adapter));
+                       retval = -ENOMEM;
+                       goto out;
+               }
+               memset(data->outbuf, 0, 2 * sizeof (struct fc_ct_iu));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_POOLBUF;
+       }
+       data->outbuf_length = sizeof (struct fc_ct_iu);
+       data->inbuf_length = sizeof (struct fc_ct_iu);
+       data->inbuf =
+           (char *) ((unsigned long) data->outbuf + sizeof (struct fc_ct_iu));
+ out:
+       return retval;
+}
+
+/*
+ * function:   zfcp_nameserver_request
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+int
+zfcp_nameserver_request(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       struct fc_ct_iu *fc_ct_iu;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(erp_action->adapter,
+                                    FSF_QTCB_SEND_GENERIC,
+                                    &lock_flags,
+                                    ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+                                    &(erp_action->fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create a "
+                             "nameserver registration request for "
+                             "adapter %s.\n",
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto failed_req;
+       }
+       retval = zfcp_get_nameserver_buffers(erp_action->fsf_req);
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of memory. Could not allocate one of "
+                             "the buffers required for a nameserver request "
+                             "on adapter %s.\n",
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto failed_buffers;
+       }
+
+       /* setup name-server request in first page */
+       fc_ct_iu =
+           (struct fc_ct_iu *) erp_action->fsf_req->data.send_generic.outbuf;
+       fc_ct_iu->revision = ZFCP_CT_REVISION;
+       fc_ct_iu->gs_type = ZFCP_CT_DIRECTORY_SERVICE;
+       fc_ct_iu->gs_subtype = ZFCP_CT_NAME_SERVER;
+       fc_ct_iu->options = ZFCP_CT_SYNCHRONOUS;
+       fc_ct_iu->cmd_rsp_code = ZFCP_CT_GID_PN;
+       fc_ct_iu->max_res_size = ZFCP_CT_MAX_SIZE;
+       fc_ct_iu->data.wwpn = erp_action->port->wwpn;
+
+       erp_action->fsf_req->data.send_generic.handler =
+           zfcp_nameserver_request_handler;
+       erp_action->fsf_req->data.send_generic.handler_data =
+           (unsigned long) erp_action->port;
+       erp_action->fsf_req->data.send_generic.port =
+           erp_action->adapter->nameserver_port;
+       erp_action->fsf_req->erp_action = erp_action;
+
+       /* send this one */
+       retval = zfcp_fsf_send_generic(erp_action->fsf_req,
+                                      ZFCP_NAMESERVER_TIMEOUT,
+                                      &lock_flags,
+                                      &erp_action->timer);
+       if (retval) {
+               ZFCP_LOG_INFO("error: Could not send a"
+                             "nameserver request command to adapter %s\n",
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto failed_send;
+       }
+
+       goto out;
+
+ failed_send:
+       zfcp_release_nameserver_buffers(erp_action->fsf_req);
+
+ failed_buffers:
+       zfcp_fsf_req_free(erp_action->fsf_req);
+       erp_action->fsf_req = NULL;
+
+ failed_req:
+ out:
+       write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
+                               lock_flags);
+       return retval;
+}
+
+/*
+ * function:   zfcp_nameserver_request_handler
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static void
+zfcp_nameserver_request_handler(struct zfcp_fsf_req *fsf_req)
+{
+       struct fc_ct_iu *fc_ct_iu_resp =
+           (struct fc_ct_iu *) (fsf_req->data.send_generic.inbuf);
+       struct fc_ct_iu *fc_ct_iu_req =
+           (struct fc_ct_iu *) (fsf_req->data.send_generic.outbuf);
+       struct zfcp_port *port =
+           (struct zfcp_port *) fsf_req->data.send_generic.handler_data;
+
+       if (fc_ct_iu_resp->revision != ZFCP_CT_REVISION)
+               goto failed;
+       if (fc_ct_iu_resp->gs_type != ZFCP_CT_DIRECTORY_SERVICE)
+               goto failed;
+       if (fc_ct_iu_resp->gs_subtype != ZFCP_CT_NAME_SERVER)
+               goto failed;
+       if (fc_ct_iu_resp->options != ZFCP_CT_SYNCHRONOUS)
+               goto failed;
+       if (fc_ct_iu_resp->cmd_rsp_code != ZFCP_CT_ACCEPT) {
+               /* FIXME: do we need some specific erp entry points */
+               atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
+               goto failed;
+       }
+       /* paranoia */
+       if (fc_ct_iu_req->data.wwpn != port->wwpn) {
+               ZFCP_LOG_NORMAL("bug: Port WWPN returned by nameserver lookup "
+                               "does not correspond to "
+                               "the expected value on the adapter %s. "
+                               "(debug info 0x%Lx, 0x%Lx)\n",
+                               zfcp_get_busid_by_port(port),
+                               port->wwpn, fc_ct_iu_req->data.wwpn);
+               goto failed;
+       }
+
+       /* looks like a valid d_id */
+       port->d_id = ZFCP_DID_MASK & fc_ct_iu_resp->data.d_id;
+       atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
+       ZFCP_LOG_DEBUG("busid %s:  WWPN=0x%Lx ---> D_ID=0x%6.6x\n",
+                      zfcp_get_busid_by_port(port),
+                      port->wwpn, (unsigned int) port->d_id);
+       goto out;
+
+ failed:
+       ZFCP_LOG_NORMAL("warning: WWPN 0x%Lx not found by nameserver lookup "
+                       "using the adapter %s\n",
+                       port->wwpn, zfcp_get_busid_by_port(port));
+       ZFCP_LOG_DEBUG("CT IUs do not match:\n");
+       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                     (char *) fc_ct_iu_req, sizeof (struct fc_ct_iu));
+       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                     (char *) fc_ct_iu_resp, sizeof (struct fc_ct_iu));
+
+ out:
+       zfcp_release_nameserver_buffers(fsf_req);
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
new file mode 100644 (file)
index 0000000..73959d7
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_ccw.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * CCW driver related routines
+ *
+ * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
+ * Authors:
+ *      Martin Peschke <mpeschke@de.ibm.com>
+ *     Heiko Carstens <heiko.carstens@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_CCW_C_REVISION "$Revision: 1.33 $"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/ccwdev.h>
+#include "zfcp_ext.h"
+#include "zfcp_def.h"
+
+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_CONFIG
+
+static int zfcp_ccw_probe(struct ccw_device *);
+static int zfcp_ccw_remove(struct ccw_device *);
+static int zfcp_ccw_set_online(struct ccw_device *);
+static int zfcp_ccw_set_offline(struct ccw_device *);
+
+static struct ccw_device_id zfcp_ccw_device_id[] = {
+       {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
+                           ZFCP_CONTROL_UNIT_MODEL,
+                           ZFCP_DEVICE_TYPE,
+                           ZFCP_DEVICE_MODEL)},
+       {},
+};
+
+static struct ccw_driver zfcp_ccw_driver = {
+       .name        = ZFCP_NAME,
+       .ids         = zfcp_ccw_device_id,
+       .probe       = zfcp_ccw_probe,
+       .remove      = zfcp_ccw_remove,
+       .set_online  = zfcp_ccw_set_online,
+       .set_offline = zfcp_ccw_set_offline,
+};
+
+MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
+
+/**
+ * zfcp_ccw_probe - probe function of zfcp driver
+ * @ccw_device: pointer to belonging ccw device
+ *
+ * This function gets called by the common i/o layer and sets up the initial
+ * data structures for each fcp adapter, which was detected by the system.
+ * Also the sysfs files for this adapter will be created by this function.
+ * In addition the nameserver port will be added to the ports of the adapter
+ * and its sysfs representation will be created too.
+ */
+static int
+zfcp_ccw_probe(struct ccw_device *ccw_device)
+{
+       struct zfcp_adapter *adapter;
+       int retval = 0;
+
+       down(&zfcp_data.config_sema);
+       adapter = zfcp_adapter_enqueue(ccw_device);
+       if (!adapter)
+               retval = -EINVAL;
+       up(&zfcp_data.config_sema);
+       return retval;
+}
+
+/**
+ * zfcp_ccw_remove - remove function of zfcp driver
+ * @ccw_device: pointer to belonging ccw device
+ *
+ * This function gets called by the common i/o layer and removes an adapter
+ * from the system. Task of this function is to get rid of all units and
+ * ports that belong to this adapter. And addition all resources of this
+ * adapter will be freed too.
+ */
+static int
+zfcp_ccw_remove(struct ccw_device *ccw_device)
+{
+       struct zfcp_adapter *adapter;
+       struct zfcp_port *port, *p;
+       struct zfcp_unit *unit, *u;
+
+       down(&zfcp_data.config_sema);
+       adapter = dev_get_drvdata(&ccw_device->dev);
+
+       write_lock_irq(&zfcp_data.config_lock);
+       list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
+               list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
+                       list_move(&unit->list, &port->unit_remove_lh);
+                       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
+                                       &unit->status);
+               }
+               list_move(&port->list, &adapter->port_remove_lh);
+               atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+       }
+       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       list_for_each_entry_safe(port, p, &adapter->port_remove_lh, list) {
+               list_for_each_entry_safe(unit, u, &port->unit_remove_lh, list) {
+                       zfcp_unit_wait(unit);
+                       device_unregister(&unit->sysfs_device);
+               }
+               zfcp_port_wait(port);
+               device_unregister(&port->sysfs_device);
+       }
+       zfcp_adapter_wait(adapter);
+       zfcp_adapter_dequeue(adapter);
+
+       up(&zfcp_data.config_sema);
+       return 0;
+}
+
+/**
+ * zfcp_ccw_set_online - set_online function of zfcp driver
+ * @ccw_device: pointer to belonging ccw device
+ *
+ * This function gets called by the common i/o layer and sets an adapter
+ * into state online. Setting an fcp device online means that it will be
+ * registered with the SCSI stack, that the QDIO queues will be set up
+ * and that the adapter will be opened (asynchronously).
+ */
+static int
+zfcp_ccw_set_online(struct ccw_device *ccw_device)
+{
+       struct zfcp_adapter *adapter;
+       int retval;
+
+       down(&zfcp_data.config_sema);
+       adapter = dev_get_drvdata(&ccw_device->dev);
+
+       retval = zfcp_adapter_scsi_register(adapter);
+       if (retval)
+               goto out;
+       zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING,
+                                      ZFCP_SET);
+       zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval;
+}
+
+/**
+ * zfcp_ccw_set_offline - set_offline function of zfcp driver
+ * @ccw_device: pointer to belonging ccw device
+ *
+ * This function gets called by the common i/o layer and sets an adapter
+ * into state offline. Setting an fcp device offline means that it will be
+ * unregistered from the SCSI stack and that the adapter will be shut down
+ * asynchronously.
+ */
+static int
+zfcp_ccw_set_offline(struct ccw_device *ccw_device)
+{
+       struct zfcp_adapter *adapter;
+
+       down(&zfcp_data.config_sema);
+       adapter = dev_get_drvdata(&ccw_device->dev);
+       zfcp_erp_adapter_shutdown(adapter, 0);
+       zfcp_erp_wait(adapter);
+       zfcp_adapter_scsi_unregister(adapter);
+       up(&zfcp_data.config_sema);
+       return 0;
+}
+
+/**
+ * zfcp_ccw_register - ccw register function
+ *
+ * Registers the driver at the common i/o layer. This function will be called
+ * at module load time/system start.
+ */
+int __init
+zfcp_ccw_register(void)
+{
+       int retval;
+
+       retval = ccw_driver_register(&zfcp_ccw_driver);
+       if (retval)
+               goto out;
+       retval = zfcp_sysfs_driver_create_files(&zfcp_ccw_driver.driver);
+       if (retval)
+               ccw_driver_unregister(&zfcp_ccw_driver);
+ out:
+       return retval;
+}
+
+/**
+ * zfcp_ccw_unregister - ccw unregister function
+ *
+ * Unregisters the driver from common i/o layer. Function will be called at
+ * module unload/system shutdown.
+ */
+void __exit
+zfcp_ccw_unregister(void)
+{
+       zfcp_sysfs_driver_remove_files(&zfcp_ccw_driver.driver);
+       ccw_driver_unregister(&zfcp_ccw_driver);
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
new file mode 100644 (file)
index 0000000..8af5cdc
--- /dev/null
@@ -0,0 +1,961 @@
+/* 
+ * 
+ * linux/drivers/s390/scsi/zfcp_def.h
+ * 
+ * FCP adapter driver for IBM eServer zSeries 
+ * 
+ * Copyright 2002 IBM Corporation 
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com> 
+ *            Raimund Schroeder <raimund.schroeder@de.ibm.com> 
+ *            Aron Zeh <arzeh@de.ibm.com> 
+ *            Wolfgang Taphorn <taphorn@de.ibm.com> 
+ *            Stefan Bader <stefan.bader@de.ibm.com> 
+ *            Heiko Carstens <heiko.carstens@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. 
+ */ 
+
+
+#ifndef ZFCP_DEF_H
+#define ZFCP_DEF_H
+
+#ifdef __KERNEL__
+
+/* this drivers version (do not edit !!! generated and updated by cvs) */
+#define ZFCP_DEF_REVISION "$Revision: 1.41 $"
+
+/*************************** INCLUDES *****************************************/
+
+#include <linux/blkdev.h>
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "../../fc4/fc.h"
+#include "zfcp_fsf.h"                  /* FSF SW Interface */
+#include <asm/ccwdev.h>
+#include <asm/qdio.h>
+#include <asm/debug.h>
+#include <linux/reboot.h>
+
+/************************ DEBUG FLAGS *****************************************/
+
+#define        ZFCP_PRINT_FLAGS
+#define        ZFCP_DEBUG_REQUESTS     /* fsf_req tracing */
+#define ZFCP_DEBUG_COMMANDS     /* host_byte tracing */
+#define ZFCP_DEBUG_ABORTS       /* scsi_cmnd abort tracing */
+#define ZFCP_DEBUG_INCOMING_ELS /* incoming ELS tracing */
+#define        ZFCP_STAT_REQSIZES
+#define        ZFCP_STAT_QUEUES
+
+/********************* SCSI SPECIFIC DEFINES *********************************/
+
+/* 32 bit for SCSI ID and LUN as long as the SCSI stack uses this type */
+typedef u32 scsi_id_t;
+typedef u32 scsi_lun_t;
+
+#define ZFCP_FAKE_SCSI_COMPLETION_TIME         (HZ / 3)
+#define ZFCP_ERP_SCSI_LOW_MEM_TIMEOUT           (100*HZ)
+#define ZFCP_SCSI_ER_TIMEOUT                    (100*HZ)
+#define ZFCP_SCSI_HOST_FLUSH_TIMEOUT            (1*HZ)
+
+/********************* CIO/QDIO SPECIFIC DEFINES *****************************/
+
+/* Adapter Identification Parameters */
+#define ZFCP_CONTROL_UNIT_TYPE  0x1731
+#define ZFCP_CONTROL_UNIT_MODEL 0x03
+#define ZFCP_DEVICE_TYPE        0x1732
+#define ZFCP_DEVICE_MODEL       0x03
+/* allow as many chained SBALs as are supported by hardware */
+#define ZFCP_MAX_SBALS_PER_REQ         FSF_MAX_SBALS_PER_REQ
+
+/* DMQ bug workaround: don't use last SBALE */
+#define ZFCP_MAX_SBALES_PER_SBAL       (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
+
+/* index of last SBALE (with respect to DMQ bug workaround) */
+#define ZFCP_LAST_SBALE_PER_SBAL       (ZFCP_MAX_SBALES_PER_SBAL - 1)
+
+/* max. number of (data buffer) SBALEs in largest SBAL chain */
+#define ZFCP_MAX_SBALES_PER_REQ                \
+       (ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
+        /* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
+
+/* FIXME(tune): free space should be one max. SBAL chain plus what? */
+#define ZFCP_QDIO_PCI_INTERVAL         (QDIO_MAX_BUFFERS_PER_Q \
+                                         - (ZFCP_MAX_SBALS_PER_REQ + 4))
+
+#define ZFCP_SBAL_TIMEOUT               (5*HZ)
+
+#define ZFCP_TYPE2_RECOVERY_TIME        (8*HZ)
+
+/* queue polling (values in microseconds) */
+#define ZFCP_MAX_INPUT_THRESHOLD       5000    /* FIXME: tune */
+#define ZFCP_MAX_OUTPUT_THRESHOLD      1000    /* FIXME: tune */
+#define ZFCP_MIN_INPUT_THRESHOLD       1       /* ignored by QDIO layer */
+#define ZFCP_MIN_OUTPUT_THRESHOLD      1       /* ignored by QDIO layer */
+
+#define QDIO_SCSI_QFMT                 1       /* 1 for FSF */
+
+/********************* FSF SPECIFIC DEFINES *********************************/
+
+#define ZFCP_ULP_INFO_VERSION                   26
+#define ZFCP_QTCB_VERSION      FSF_QTCB_CURRENT_VERSION
+/* ATTENTION: value must not be used by hardware */
+#define FSF_QTCB_UNSOLICITED_STATUS            0x6305
+#define ZFCP_STATUS_READ_FAILED_THRESHOLD      3
+#define ZFCP_STATUS_READS_RECOM                        FSF_STATUS_READS_RECOM
+#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES      6
+#define ZFCP_EXCHANGE_CONFIG_DATA_SLEEP                50
+
+#define ZFCP_QTCB_SIZE         (sizeof(struct fsf_qtcb) + FSF_QTCB_LOG_SIZE)
+#define ZFCP_QTCB_AND_REQ_SIZE (sizeof(struct zfcp_fsf_req) + ZFCP_QTCB_SIZE)
+
+/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/
+
+typedef unsigned long long wwn_t;
+typedef unsigned int       fc_id_t;
+typedef unsigned long long fcp_lun_t;
+/* data length field may be at variable position in FCP-2 FCP_CMND IU */
+typedef unsigned int       fcp_dl_t;
+
+#define ZFCP_FC_SERVICE_CLASS_DEFAULT  FSF_CLASS_3
+
+/* timeout for name-server lookup (in seconds) */
+#define ZFCP_NAMESERVER_TIMEOUT                10
+
+/* largest SCSI command we can process */
+/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
+#define ZFCP_MAX_SCSI_CMND_LENGTH      255
+/* maximum number of commands in LUN queue (tagged queueing) */
+#define ZFCP_CMND_PER_LUN               32
+
+/* task attribute values in FCP-2 FCP_CMND IU */
+#define SIMPLE_Q       0
+#define HEAD_OF_Q      1
+#define ORDERED_Q      2
+#define ACA_Q          4
+#define UNTAGGED       5
+
+/* task management flags in FCP-2 FCP_CMND IU */
+#define CLEAR_ACA              0x40
+#define TARGET_RESET           0x20
+#define LOGICAL_UNIT_RESET     0x10
+#define CLEAR_TASK_SET         0x04
+#define ABORT_TASK_SET         0x02
+
+#define FCP_CDB_LENGTH         16
+
+#define ZFCP_DID_MASK           0x00FFFFFF
+
+/* FCP(-2) FCP_CMND IU */
+struct fcp_cmnd_iu {
+       fcp_lun_t fcp_lun;         /* FCP logical unit number */
+       u8  crn;                   /* command reference number */
+       u8  reserved0:5;           /* reserved */
+       u8  task_attribute:3;      /* task attribute */
+       u8  task_management_flags; /* task management flags */
+       u8  add_fcp_cdb_length:6;  /* additional FCP_CDB length */
+       u8  rddata:1;              /* read data */
+       u8  wddata:1;              /* write data */
+       u8  fcp_cdb[FCP_CDB_LENGTH];
+} __attribute__((packed));
+
+/* FCP(-2) FCP_RSP IU */
+struct fcp_rsp_iu {
+       u8  reserved0[10];
+       union {
+               struct {
+                       u8 reserved1:3;
+                       u8 fcp_conf_req:1;
+                       u8 fcp_resid_under:1;
+                       u8 fcp_resid_over:1;
+                       u8 fcp_sns_len_valid:1;
+                       u8 fcp_rsp_len_valid:1;
+               } bits;
+               u8 value;
+       } validity;
+       u8  scsi_status;
+       u32 fcp_resid;
+       u32 fcp_sns_len;
+       u32 fcp_rsp_len;
+} __attribute__((packed));
+
+
+#define RSP_CODE_GOOD           0
+#define RSP_CODE_LENGTH_MISMATCH 1
+#define RSP_CODE_FIELD_INVALID  2
+#define RSP_CODE_RO_MISMATCH    3
+#define RSP_CODE_TASKMAN_UNSUPP         4
+#define RSP_CODE_TASKMAN_FAILED         5
+
+/* see fc-fs */
+#define LS_FAN 0x60000000
+#define LS_RSCN 0x61040000
+
+struct fcp_rscn_head {
+        u8  command;
+        u8  page_length; /* always 0x04 */
+        u16 payload_len;
+} __attribute__((packed));
+
+struct fcp_rscn_element {
+        u8  reserved:2;
+        u8  event_qual:4;
+        u8  addr_format:2;
+        u32 nport_did:24;
+} __attribute__((packed));
+
+#define ZFCP_PORT_ADDRESS   0x0
+#define ZFCP_AREA_ADDRESS   0x1
+#define ZFCP_DOMAIN_ADDRESS 0x2
+#define ZFCP_FABRIC_ADDRESS 0x3
+
+#define ZFCP_PORTS_RANGE_PORT   0xFFFFFF
+#define ZFCP_PORTS_RANGE_AREA   0xFFFF00
+#define ZFCP_PORTS_RANGE_DOMAIN 0xFF0000
+#define ZFCP_PORTS_RANGE_FABRIC 0x000000
+
+#define ZFCP_NO_PORTS_PER_AREA    0x100
+#define ZFCP_NO_PORTS_PER_DOMAIN  0x10000
+#define ZFCP_NO_PORTS_PER_FABRIC  0x1000000
+
+struct fcp_fan {
+        u32 command;
+        u32 fport_did;
+        wwn_t fport_wwpn;
+        wwn_t fport_wwname;
+} __attribute__((packed));
+
+/* see fc-ph */
+struct fcp_logo {
+        u32 command;
+        u32 nport_did;
+        wwn_t nport_wwpn;
+} __attribute__((packed));
+
+struct fc_ct_iu {
+       u8      revision;       /* 0x01 */
+       u8      in_id[3];       /* 0x00 */
+       u8      gs_type;        /* 0xFC Directory Service */
+       u8      gs_subtype;     /* 0x02 Name Server */
+       u8      options;        /* 0x10 synchronous/single exchange */
+       u8      reserved0;
+       u16     cmd_rsp_code;   /* 0x0121 GID_PN */
+       u16     max_res_size;   /* <= (4096 - 16) / 4 */
+       u8      reserved1;
+       u8      reason_code;
+       u8      reason_code_expl;
+       u8      vendor_unique;
+       union {
+               wwn_t   wwpn;
+               fc_id_t d_id;
+       } data;
+} __attribute__ ((packed));
+
+#define ZFCP_CT_REVISION               0x01
+#define ZFCP_CT_DIRECTORY_SERVICE      0xFC
+#define ZFCP_CT_NAME_SERVER            0x02
+#define ZFCP_CT_SYNCHRONOUS            0x00
+#define ZFCP_CT_GID_PN                 0x0121
+#define ZFCP_CT_MAX_SIZE               0x1020
+#define ZFCP_CT_ACCEPT                 0x8002
+
+/***************** S390 DEBUG FEATURE SPECIFIC DEFINES ***********************/
+
+/* debug feature entries per adapter */
+#define ZFCP_ERP_DBF_INDEX     1 
+#define ZFCP_ERP_DBF_AREAS     2
+#define ZFCP_ERP_DBF_LENGTH    16
+#define ZFCP_ERP_DBF_LEVEL     3
+#define ZFCP_ERP_DBF_NAME      "zfcperp"
+
+#define ZFCP_REQ_DBF_INDEX     1
+#define ZFCP_REQ_DBF_AREAS     1
+#define ZFCP_REQ_DBF_LENGTH    8
+#define ZFCP_REQ_DBF_LEVEL     1
+#define ZFCP_REQ_DBF_NAME      "zfcpreq"
+
+#define ZFCP_CMD_DBF_INDEX     2
+#define ZFCP_CMD_DBF_AREAS     1
+#define ZFCP_CMD_DBF_LENGTH    8
+#define ZFCP_CMD_DBF_LEVEL     3
+#define ZFCP_CMD_DBF_NAME      "zfcpcmd"
+
+
+#define ZFCP_ABORT_DBF_INDEX   2
+#define ZFCP_ABORT_DBF_AREAS   1
+#define ZFCP_ABORT_DBF_LENGTH  8
+#define ZFCP_ABORT_DBF_LEVEL   6
+#define ZFCP_ABORT_DBF_NAME    "zfcpabt"
+
+#define ZFCP_IN_ELS_DBF_INDEX  2
+#define ZFCP_IN_ELS_DBF_AREAS  1
+#define ZFCP_IN_ELS_DBF_LENGTH 8
+#define ZFCP_IN_ELS_DBF_LEVEL  6
+#define ZFCP_IN_ELS_DBF_NAME   "zfcpels"
+
+#define ZFCP_ADAPTER_REQ_DBF_INDEX  4 
+#define ZFCP_ADAPTER_REQ_DBF_AREAS  1
+#define ZFCP_ADAPTER_REQ_DBF_LENGTH 8
+#define ZFCP_ADAPTER_REQ_DBF_LEVEL  6
+
+/******************** LOGGING MACROS AND DEFINES *****************************/
+
+/*
+ * Logging may be applied on certain kinds of driver operations
+ * independently. Additionally, different log-levels are supported for
+ * each of these areas.
+ */
+
+#define ZFCP_NAME               "zfcp"
+
+/* independent log areas */
+#define ZFCP_LOG_AREA_OTHER    0
+#define ZFCP_LOG_AREA_SCSI     1
+#define ZFCP_LOG_AREA_FSF      2
+#define ZFCP_LOG_AREA_CONFIG   3
+#define ZFCP_LOG_AREA_CIO      4
+#define ZFCP_LOG_AREA_QDIO     5
+#define ZFCP_LOG_AREA_ERP      6
+#define ZFCP_LOG_AREA_FC       7
+
+/* log level values*/
+#define ZFCP_LOG_LEVEL_NORMAL  0
+#define ZFCP_LOG_LEVEL_INFO    1
+#define ZFCP_LOG_LEVEL_DEBUG   2
+#define ZFCP_LOG_LEVEL_TRACE   3
+
+/* default log levels for different log areas */
+#define ZFCP_LOG_LEVEL_DEFAULT_OTHER   ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_LEVEL_DEFAULT_SCSI    ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_LEVEL_DEFAULT_FSF     ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_LEVEL_DEFAULT_CONFIG  ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_LEVEL_DEFAULT_CIO     ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_LEVEL_DEFAULT_QDIO    ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_LEVEL_DEFAULT_ERP     ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_LEVEL_DEFAULT_FC      ZFCP_LOG_LEVEL_INFO
+
+/*
+ * this allows removal of logging code by the preprocessor
+ * (the most detailed log level still to be compiled in is specified, 
+ * higher log levels are removed)
+ */
+#define ZFCP_LOG_LEVEL_LIMIT   ZFCP_LOG_LEVEL_TRACE
+
+/* positional "loglevel" nibble assignment */
+#define ZFCP_LOG_VALUE(zfcp_lognibble) \
+              ((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
+
+#define ZFCP_LOG_VALUE_OTHER   ZFCP_LOG_VALUE(ZFCP_LOG_AREA_OTHER)
+#define ZFCP_LOG_VALUE_SCSI    ZFCP_LOG_VALUE(ZFCP_LOG_AREA_SCSI)
+#define ZFCP_LOG_VALUE_FSF     ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FSF)
+#define ZFCP_LOG_VALUE_CONFIG  ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CONFIG)
+#define ZFCP_LOG_VALUE_CIO     ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CIO)
+#define ZFCP_LOG_VALUE_QDIO    ZFCP_LOG_VALUE(ZFCP_LOG_AREA_QDIO)
+#define ZFCP_LOG_VALUE_ERP     ZFCP_LOG_VALUE(ZFCP_LOG_AREA_ERP)
+#define ZFCP_LOG_VALUE_FC      ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FC)
+
+/* all log-level defaults are combined to generate initial log-level */
+#define ZFCP_LOG_LEVEL_DEFAULTS \
+       ((ZFCP_LOG_LEVEL_DEFAULT_OTHER  << (ZFCP_LOG_AREA_OTHER<<2))    | \
+        (ZFCP_LOG_LEVEL_DEFAULT_SCSI   << (ZFCP_LOG_AREA_SCSI<<2))     | \
+        (ZFCP_LOG_LEVEL_DEFAULT_FSF    << (ZFCP_LOG_AREA_FSF<<2))      | \
+        (ZFCP_LOG_LEVEL_DEFAULT_CONFIG << (ZFCP_LOG_AREA_CONFIG<<2))   | \
+        (ZFCP_LOG_LEVEL_DEFAULT_CIO    << (ZFCP_LOG_AREA_CIO<<2))      | \
+        (ZFCP_LOG_LEVEL_DEFAULT_QDIO   << (ZFCP_LOG_AREA_QDIO<<2))     | \
+        (ZFCP_LOG_LEVEL_DEFAULT_ERP    << (ZFCP_LOG_AREA_ERP<<2))      | \
+        (ZFCP_LOG_LEVEL_DEFAULT_FC     << (ZFCP_LOG_AREA_FC<<2)))
+
+/* the prefix placed at the beginning of each driver message */
+#define ZFCP_LOG_PREFIX ZFCP_NAME": "
+
+/* log area specific prefixes */
+#define ZFCP_LOG_AREA_PREFIX_OTHER     ""
+#define ZFCP_LOG_AREA_PREFIX_SCSI      "SCSI: "
+#define ZFCP_LOG_AREA_PREFIX_FSF       "FSF: "
+#define ZFCP_LOG_AREA_PREFIX_CONFIG    "config: "
+#define ZFCP_LOG_AREA_PREFIX_CIO       "common I/O: "
+#define ZFCP_LOG_AREA_PREFIX_QDIO      "QDIO: "
+#define ZFCP_LOG_AREA_PREFIX_ERP       "ERP: "
+#define ZFCP_LOG_AREA_PREFIX_FC        "FC: "
+
+/* check whether we have the right level for logging */
+#define ZFCP_LOG_CHECK(ll)     (ZFCP_LOG_VALUE(ZFCP_LOG_AREA)) >= ll
+
+/* As we have two printks it is possible for them to be seperated by another
+ * message. This holds true even for printks from within this module.
+ * In any case there should only be a small readability hit, however.
+ */
+#define _ZFCP_LOG(m...) \
+               { \
+                       printk( "%s%s: ", \
+                               ZFCP_LOG_PREFIX ZFCP_LOG_AREA_PREFIX, \
+                               __FUNCTION__); \
+                       printk(m); \
+               }
+
+#define ZFCP_LOG(ll, m...) \
+               if (ZFCP_LOG_CHECK(ll)) \
+                       _ZFCP_LOG(m)
+       
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
+#define ZFCP_LOG_NORMAL(m...)
+#else  /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_NORMAL */
+#define ZFCP_LOG_NORMAL(m...)          ZFCP_LOG(ZFCP_LOG_LEVEL_NORMAL, m)
+#endif
+
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
+#define ZFCP_LOG_INFO(m...)
+#else  /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_INFO */
+#define ZFCP_LOG_INFO(m...)            ZFCP_LOG(ZFCP_LOG_LEVEL_INFO, m)
+#endif
+
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
+#define ZFCP_LOG_DEBUG(m...)
+#else  /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_DEBUG */
+#define ZFCP_LOG_DEBUG(m...)           ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, m)
+#endif
+
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
+#define ZFCP_LOG_TRACE(m...)
+#else  /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_TRACE */
+#define ZFCP_LOG_TRACE(m...)           ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, m)
+#endif
+
+#ifdef ZFCP_PRINT_FLAGS
+extern u32 flags_dump;
+#define ZFCP_LOG_FLAGS(ll, m...) \
+               if (ll<=flags_dump) \
+                       _ZFCP_LOG(m)
+#else
+#define ZFCP_LOG_FLAGS(ll, m...)
+#endif
+
+/*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
+
+/* 
+ * Note, the leftmost status byte is common among adapter, port 
+ * and unit
+ */
+#define ZFCP_COMMON_FLAGS                       0xff000000
+#define ZFCP_SPECIFIC_FLAGS                     0x00ffffff
+
+/* common status bits */
+#define ZFCP_STATUS_COMMON_REMOVE              0x80000000
+#define ZFCP_STATUS_COMMON_RUNNING             0x40000000
+#define ZFCP_STATUS_COMMON_ERP_FAILED          0x20000000
+#define ZFCP_STATUS_COMMON_UNBLOCKED           0x10000000
+#define ZFCP_STATUS_COMMON_OPENING              0x08000000
+#define ZFCP_STATUS_COMMON_OPEN                 0x04000000
+#define ZFCP_STATUS_COMMON_CLOSING              0x02000000
+#define ZFCP_STATUS_COMMON_ERP_INUSE           0x01000000
+
+/* adapter status */
+#define ZFCP_STATUS_ADAPTER_QDIOUP             0x00000002
+#define ZFCP_STATUS_ADAPTER_REGISTERED         0x00000004
+#define ZFCP_STATUS_ADAPTER_XCONFIG_OK         0x00000008
+#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT      0x00000010
+#define ZFCP_STATUS_ADAPTER_ERP_THREAD_UP      0x00000020
+#define ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL    0x00000080
+#define ZFCP_STATUS_ADAPTER_ERP_PENDING                0x00000100
+#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED     0x00000200
+#define ZFCP_STATUS_ADAPTER_QUEUECOMMAND_BLOCK 0x00000400 
+
+#define ZFCP_STATUS_ADAPTER_SCSI_UP                    \
+               (ZFCP_STATUS_COMMON_UNBLOCKED | \
+                ZFCP_STATUS_ADAPTER_REGISTERED)
+
+
+#define ZFCP_DID_NAMESERVER                    0xFFFFFC
+
+/* remote port status */
+#define ZFCP_STATUS_PORT_PHYS_OPEN             0x00000001
+#define ZFCP_STATUS_PORT_DID_DID               0x00000002
+#define ZFCP_STATUS_PORT_PHYS_CLOSING          0x00000004
+#define ZFCP_STATUS_PORT_NO_WWPN               0x00000008
+#define ZFCP_STATUS_PORT_NO_SCSI_ID            0x00000010
+#define ZFCP_STATUS_PORT_INVALID_WWPN          0x00000020
+
+#define ZFCP_STATUS_PORT_NAMESERVER \
+               (ZFCP_STATUS_PORT_NO_WWPN | \
+                ZFCP_STATUS_PORT_NO_SCSI_ID)
+
+/* logical unit status */
+#define ZFCP_STATUS_UNIT_NOTSUPPUNITRESET      0x00000001
+
+
+/* FSF request status (this does not have a common part) */
+#define ZFCP_STATUS_FSFREQ_NOT_INIT            0x00000000
+#define ZFCP_STATUS_FSFREQ_POOL                0x00000001
+#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT     0x00000002
+#define ZFCP_STATUS_FSFREQ_COMPLETED           0x00000004
+#define ZFCP_STATUS_FSFREQ_ERROR               0x00000008
+#define ZFCP_STATUS_FSFREQ_CLEANUP             0x00000010
+#define ZFCP_STATUS_FSFREQ_ABORTING            0x00000020
+#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED      0x00000040
+#define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED       0x00000080
+#define ZFCP_STATUS_FSFREQ_ABORTED              0x00000100
+#define ZFCP_STATUS_FSFREQ_TMFUNCFAILED         0x00000200
+#define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP        0x00000400
+#define ZFCP_STATUS_FSFREQ_RETRY                0x00000800
+#define ZFCP_STATUS_FSFREQ_DISMISSED            0x00001000
+#define ZFCP_STATUS_FSFREQ_POOLBUF              0x00002000
+
+/*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/
+
+#define ZFCP_MAX_ERPS                   3
+
+#define ZFCP_ERP_FSFREQ_TIMEOUT                (100 * HZ)
+#define ZFCP_ERP_MEMWAIT_TIMEOUT       HZ
+
+#define ZFCP_STATUS_ERP_TIMEDOUT       0x10000000
+#define ZFCP_STATUS_ERP_CLOSE_ONLY     0x01000000
+#define ZFCP_STATUS_ERP_DISMISSING     0x00100000
+#define ZFCP_STATUS_ERP_DISMISSED      0x00200000
+
+#define ZFCP_ERP_STEP_UNINITIALIZED    0x00000000
+#define ZFCP_ERP_STEP_FSF_XCONFIG      0x00000001
+#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING        0x00000010
+#define ZFCP_ERP_STEP_PORT_CLOSING     0x00000100
+#define ZFCP_ERP_STEP_NAMESERVER_OPEN  0x00000200
+#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP        0x00000400
+#define ZFCP_ERP_STEP_PORT_OPENING     0x00000800
+#define ZFCP_ERP_STEP_UNIT_CLOSING     0x00001000
+#define ZFCP_ERP_STEP_UNIT_OPENING     0x00002000
+
+/* Ordered by escalation level (necessary for proper erp-code operation) */
+#define ZFCP_ERP_ACTION_REOPEN_ADAPTER         0x4
+#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED     0x3
+#define ZFCP_ERP_ACTION_REOPEN_PORT            0x2
+#define ZFCP_ERP_ACTION_REOPEN_UNIT            0x1
+
+#define ZFCP_ERP_ACTION_RUNNING                        0x1
+#define ZFCP_ERP_ACTION_READY                  0x2
+
+#define ZFCP_ERP_SUCCEEDED     0x0
+#define ZFCP_ERP_FAILED                0x1
+#define ZFCP_ERP_CONTINUES     0x2
+#define ZFCP_ERP_EXIT          0x3
+#define ZFCP_ERP_DISMISSED     0x4
+#define ZFCP_ERP_NOMEM         0x5
+
+/************************* STRUCTURE DEFINITIONS *****************************/
+
+
+struct zfcp_fsf_req;
+typedef void zfcp_send_generic_handler_t(struct zfcp_fsf_req*);
+
+struct zfcp_adapter_mempool {
+        mempool_t *status_read_fsf;
+       mempool_t *status_read_buf;
+        mempool_t *nameserver;
+        mempool_t *erp_fsf;
+        mempool_t *fcp_command_fsf;
+        struct timer_list fcp_command_fsf_timer;
+};
+
+struct  zfcp_exchange_config_data{
+};
+
+struct zfcp_open_port {
+        struct zfcp_port *port;
+};
+
+struct zfcp_close_port {
+       struct zfcp_port *port;
+};
+
+struct zfcp_open_unit {
+       struct zfcp_unit *unit;
+};
+
+struct zfcp_close_unit {
+       struct zfcp_unit *unit;
+};
+
+struct zfcp_close_physical_port {
+        struct zfcp_port *port;
+};
+
+struct zfcp_send_fcp_command_task {
+       struct zfcp_fsf_req *fsf_req;
+       struct zfcp_unit *unit;
+       Scsi_Cmnd *scsi_cmnd;
+       unsigned long start_jiffies;
+};
+
+struct zfcp_send_fcp_command_task_management {
+       struct zfcp_unit *unit;
+};
+
+struct zfcp_abort_fcp_command {
+       struct zfcp_fsf_req *fsf_req;
+       struct zfcp_unit *unit;
+};
+
+struct zfcp_send_generic {
+        struct zfcp_port *port;
+       char *outbuf;
+       char *inbuf;
+       int outbuf_length;
+       int inbuf_length;
+       zfcp_send_generic_handler_t *handler;
+       unsigned long handler_data;
+};
+
+struct zfcp_status_read {
+       struct fsf_status_read_buffer *buffer;
+};
+
+/* request specific data */
+union zfcp_req_data {
+       struct zfcp_exchange_config_data exchange_config_data;
+       struct zfcp_open_port             open_port;
+       struct zfcp_close_port            close_port;
+       struct zfcp_open_unit             open_unit;
+       struct zfcp_close_unit            close_unit;
+       struct zfcp_close_physical_port   close_physical_port;
+       struct zfcp_send_fcp_command_task send_fcp_command_task;
+        struct zfcp_send_fcp_command_task_management
+                                         send_fcp_command_task_management;
+       struct zfcp_abort_fcp_command     abort_fcp_command;
+       struct zfcp_send_generic          send_generic;
+       struct zfcp_status_read           status_read;
+};
+
+struct zfcp_qdio_queue {
+       struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
+       u8                 free_index;        /* index of next free bfr
+                                                in queue (free_count>0) */
+       atomic_t           free_count;        /* number of free buffers
+                                                in queue */
+       rwlock_t           queue_lock;        /* lock for operations on queue */
+        int                distance_from_int; /* SBALs used since PCI indication
+                                                was last set */
+};
+
+struct zfcp_erp_action {
+       struct list_head list;
+       int action;                   /* requested action code */
+       struct zfcp_adapter *adapter; /* device which should be recovered */
+       struct zfcp_port *port;
+       struct zfcp_unit *unit;
+       volatile u32 status;          /* recovery status */
+       u32 step;                     /* active step of this erp action */
+       struct zfcp_fsf_req *fsf_req; /* fsf request currently pending
+                                        for this action */
+       struct timer_list timer;
+};
+
+
+struct zfcp_adapter {
+       u32                     common_magic;      /* driver common magic */
+       u32                     specific_magic;    /* struct specific magic */
+       struct list_head        list;              /* list of adapters */
+       atomic_t                refcount;          /* reference count */
+       wait_queue_head_t       remove_wq;         /* can be used to wait for
+                                                     refcount drop to zero */
+       wwn_t                   wwnn;              /* WWNN */
+       wwn_t                   wwpn;              /* WWPN */
+       fc_id_t                 s_id;              /* N_Port ID */
+       struct ccw_device       *ccw_device;       /* S/390 ccw device */
+       u8                      fc_service_class;
+       u32                     fc_topology;       /* FC topology */
+       u32                     fc_link_speed;     /* FC interface speed */
+       u32                     hydra_version;     /* Hydra version */
+       u32                     fsf_lic_version;
+       struct Scsi_Host        *scsi_host;        /* Pointer to mid-layer */
+        Scsi_Cmnd               *first_fake_cmnd;  /* Packets in flight list */
+       rwlock_t                fake_list_lock;    /* Lock for the above */
+       struct timer_list       fake_scsi_timer;   /* Starts processing of
+                                                     faked commands */
+       unsigned char           name[9];
+       struct list_head        port_list_head;    /* remote port list */
+       struct list_head        port_remove_lh;    /* head of ports to be
+                                                     removed */
+       u32                     ports;             /* number of remote ports */
+       scsi_id_t               max_scsi_id;       /* largest SCSI ID */
+       scsi_lun_t              max_scsi_lun;      /* largest SCSI LUN */
+        struct timer_list       scsi_er_timer;     /* SCSI err recovery watch */
+       struct list_head        fsf_req_list_head; /* head of FSF req list */
+       rwlock_t                fsf_req_list_lock; /* lock for ops on list of
+                                                     FSF requests */
+        atomic_t                       fsf_reqs_active;   /* # active FSF reqs */
+        atomic_t                       scsi_reqs_active;  /* # active SCSI reqs */
+       wait_queue_head_t       scsi_reqs_active_wq; /* can be used to wait for
+                                                       fsf_reqs_active chngs */
+       struct zfcp_qdio_queue  request_queue;     /* request queue */
+       u32                     fsf_req_seq_no;    /* FSF cmnd seq number */
+       wait_queue_head_t       request_wq;        /* can be used to wait for
+                                                     more avaliable SBALs */
+       struct zfcp_qdio_queue  response_queue;    /* response queue */
+       rwlock_t                abort_lock;        /* Protects against SCSI
+                                                     stack abort/command
+                                                     completion races */
+       u16                     status_read_failed; /* # failed status reads */
+       atomic_t                status;            /* status of this adapter */
+       struct list_head        erp_ready_head;    /* error recovery for this
+                                                     adapter/devices */
+       struct list_head        erp_running_head;
+       rwlock_t                erp_lock;
+       struct semaphore        erp_ready_sem;
+       wait_queue_head_t       erp_thread_wqh;
+       wait_queue_head_t       erp_done_wqh;
+       struct zfcp_erp_action  erp_action;        /* pending error recovery */
+        atomic_t                erp_counter;
+       struct zfcp_port        *nameserver_port;  /* adapter's nameserver */
+        debug_info_t            *erp_dbf;          /* S/390 debug features */
+       debug_info_t            *abort_dbf;
+       debug_info_t            *req_dbf;
+       debug_info_t            *in_els_dbf;
+       debug_info_t            *cmd_dbf;
+       rwlock_t                cmd_dbf_lock;
+       struct zfcp_adapter_mempool     pool;      /* Adapter memory pools */
+       struct qdio_initialize  qdio_init_data;    /* for qdio_establish */
+};
+
+struct zfcp_port {
+       u32                    common_magic;   /* driver wide common magic */
+       u32                    specific_magic; /* structure specific magic */
+       struct list_head       list;           /* list of remote ports */
+       atomic_t               refcount;       /* reference count */
+       wait_queue_head_t      remove_wq;      /* can be used to wait for
+                                                 refcount drop to zero */
+       struct zfcp_adapter    *adapter;       /* adapter used to access port */
+       struct list_head       unit_list_head; /* head of logical unit list */
+       struct list_head       unit_remove_lh; /* head of luns to be removed
+                                                 list */
+       u32                    units;          /* # of logical units in list */
+       atomic_t               status;         /* status of this remote port */
+       scsi_id_t              scsi_id;        /* own SCSI ID */
+       wwn_t                  wwnn;           /* WWNN if known */
+       wwn_t                  wwpn;           /* WWPN */
+       fc_id_t                d_id;           /* D_ID */
+       scsi_lun_t             max_scsi_lun;   /* largest SCSI LUN */
+       u32                    handle;         /* handle assigned by FSF */
+       struct zfcp_erp_action erp_action;     /* pending error recovery */
+        atomic_t               erp_counter;
+       struct device          sysfs_device;   /* sysfs device */
+};
+
+struct zfcp_unit {
+       u32                    common_magic;   /* driver wide common magic */
+       u32                    specific_magic; /* structure specific magic */
+       struct list_head       list;           /* list of logical units */
+       atomic_t               refcount;       /* reference count */
+       wait_queue_head_t      remove_wq;      /* can be used to wait for
+                                                 refcount drop to zero */
+       struct zfcp_port       *port;          /* remote port of unit */
+       atomic_t               status;         /* status of this logical unit */
+       scsi_lun_t             scsi_lun;       /* own SCSI LUN */
+       fcp_lun_t              fcp_lun;        /* own FCP_LUN */
+       u32                    handle;         /* handle assigned by FSF */
+        Scsi_Device            *device;        /* scsi device struct pointer */
+       struct zfcp_erp_action erp_action;     /* pending error recovery */
+        atomic_t               erp_counter;
+       struct device          sysfs_device;   /* sysfs device */
+};
+
+/* FSF request */
+struct zfcp_fsf_req {
+       u32                    common_magic;   /* driver wide common magic */
+       u32                    specific_magic; /* structure specific magic */
+       struct list_head       list;           /* list of FSF requests */
+       struct zfcp_adapter    *adapter;       /* adapter request belongs to */
+       u8                     sbal_count;     /* # of SBALs in FSF request */
+       u8                     sbal_index;     /* position of 1st SBAL */
+       wait_queue_head_t      completion_wq;  /* can be used by a routine
+                                                 to wait for completion */
+       volatile u32           status;         /* status of this request */
+       u32                    fsf_command;    /* FSF Command copy */
+       struct fsf_qtcb        *qtcb;          /* address of associated QTCB */
+       u32                    seq_no;         /* Sequence number of request */
+        union zfcp_req_data    data;           /* Info fields of request */ 
+       struct zfcp_erp_action *erp_action;    /* used if this request is
+                                                 issued on behalf of erp */
+};
+
+typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
+
+/* driver data */
+struct zfcp_data {
+       Scsi_Host_Template      scsi_host_template;
+        atomic_t                status;             /* Module status flags */
+       struct list_head        adapter_list_head;  /* head of adapter list */
+       struct list_head        adapter_remove_lh;  /* head of adapters to be
+                                                      removed */
+        rwlock_t                status_read_lock;   /* for status read thread */
+        struct list_head        status_read_receive_head;
+        struct list_head        status_read_send_head;
+        struct semaphore        status_read_sema;
+       wait_queue_head_t       status_read_thread_wqh;
+       u16                     adapters;           /* # of adapters in list */
+       rwlock_t                config_lock;        /* serialises changes
+                                                      to adapter/port/unit
+                                                      lists */
+       struct semaphore        config_sema;        /* serialises configuration
+                                                      changes */
+       struct notifier_block   reboot_notifier;     /* used to register cleanup
+                                                       functions */
+       atomic_t                loglevel;            /* current loglevel */
+#ifdef ZFCP_STAT_REQSIZES                            /* Statistical accounting
+                                                       of processed data */
+       struct list_head        read_req_head;
+       struct list_head        write_req_head;
+       struct list_head        read_sg_head;
+       struct list_head        write_sg_head;
+       struct list_head        read_sguse_head;
+       struct list_head        write_sguse_head;
+       unsigned long           stat_errors;
+       rwlock_t                stat_lock;
+#endif
+#ifdef ZFCP_STAT_QUEUES
+        atomic_t                outbound_queue_full;
+       atomic_t                outbound_total;
+#endif
+};
+
+#ifdef ZFCP_STAT_REQSIZES
+struct zfcp_statistics {
+        struct list_head list;
+        u32 num;
+        u32 occurrence;
+};
+#endif
+
+/********************** ZFCP SPECIFIC DEFINES ********************************/
+
+#define ZFCP_FSFREQ_CLEANUP_TIMEOUT    HZ/10
+
+#define ZFCP_KNOWN              0x00000001
+#define ZFCP_REQ_AUTO_CLEANUP  0x00000002
+#define ZFCP_WAIT_FOR_SBAL     0x00000004
+
+#define ZFCP_SET                0x00000100
+#define ZFCP_CLEAR              0x00000200
+
+#define ZFCP_INTERRUPTIBLE     1
+#define ZFCP_UNINTERRUPTIBLE   0
+
+/* some magics which may be used to authenticate data structures */
+#define ZFCP_MAGIC             0xFCFCFCFC
+#define ZFCP_MAGIC_ADAPTER     0xAAAAAAAA
+#define ZFCP_MAGIC_PORT                0xBBBBBBBB
+#define ZFCP_MAGIC_UNIT                0xCCCCCCCC
+#define ZFCP_MAGIC_FSFREQ      0xEEEEEEEE
+
+#ifndef atomic_test_mask
+#define atomic_test_mask(mask, target) \
+           (atomic_read(target) & mask)
+#endif
+
+extern void _zfcp_hex_dump(char *, int);
+#define ZFCP_HEX_DUMP(level, addr, count) \
+               if (ZFCP_LOG_CHECK(level)) { \
+                       _zfcp_hex_dump(addr, count); \
+               }
+/*
+ * Not yet optimal but useful:
+ * Waits until the condition is met or the timeout occurs.
+ * The condition may be a function call. This allows to
+ * execute some additional instructions in addition
+ * to a simple condition check.
+ * The timeout is modified on exit and holds the remaining time.
+ * Thus it is zero if a timeout ocurred, i.e. the condition was 
+ * not met in the specified interval.
+ */
+#define __ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
+do { \
+       set_current_state(TASK_UNINTERRUPTIBLE); \
+       while (!(condition) && timeout) \
+               timeout = schedule_timeout(timeout); \
+       current->state = TASK_RUNNING; \
+} while (0);
+
+#define ZFCP_WAIT_EVENT_TIMEOUT(waitqueue, timeout, condition) \
+do { \
+       wait_queue_t entry; \
+       init_waitqueue_entry(&entry, current); \
+       add_wait_queue(&waitqueue, &entry); \
+       __ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
+       remove_wait_queue(&waitqueue, &entry); \
+} while (0);
+
+#define zfcp_get_busid_by_adapter(adapter) (adapter->ccw_device->dev.bus_id)
+#define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter))
+#define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port))
+
+/*
+ *  functions needed for reference/usage counting
+ */
+
+static inline void
+zfcp_unit_get(struct zfcp_unit *unit)
+{
+       atomic_inc(&unit->refcount);
+}
+
+static inline void
+zfcp_unit_put(struct zfcp_unit *unit)
+{
+       if (atomic_dec_return(&unit->refcount) == 0)
+               wake_up(&unit->remove_wq);
+}
+
+static inline void
+zfcp_unit_wait(struct zfcp_unit *unit)
+{
+       wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0);
+}
+
+static inline void
+zfcp_port_get(struct zfcp_port *port)
+{
+       atomic_inc(&port->refcount);
+}
+
+static inline void
+zfcp_port_put(struct zfcp_port *port)
+{
+       if (atomic_dec_return(&port->refcount) == 0)
+               wake_up(&port->remove_wq);
+}
+
+static inline void
+zfcp_port_wait(struct zfcp_port *port)
+{
+       wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
+}
+
+static inline void
+zfcp_adapter_get(struct zfcp_adapter *adapter)
+{
+       atomic_inc(&adapter->refcount);
+}
+
+static inline void
+zfcp_adapter_put(struct zfcp_adapter *adapter)
+{
+       if (atomic_dec_return(&adapter->refcount) == 0)
+               wake_up(&adapter->remove_wq);
+}
+
+static inline void
+zfcp_adapter_wait(struct zfcp_adapter *adapter)
+{
+       wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
+}
+
+#endif /* __KERNEL_- */
+#endif /* ZFCP_DEF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
new file mode 100644 (file)
index 0000000..2d80b3e
--- /dev/null
@@ -0,0 +1,3305 @@
+/* 
+ * 
+ * linux/drivers/s390/scsi/zfcp_erp.c 
+ * 
+ * FCP adapter driver for IBM eServer zSeries 
+ * 
+ * Copyright 2002 IBM Corporation 
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com> 
+ *            Raimund Schroeder <raimund.schroeder@de.ibm.com> 
+ *            Aron Zeh <arzeh@de.ibm.com> 
+ *            Wolfgang Taphorn <taphorn@de.ibm.com> 
+ *            Stefan Bader <stefan.bader@de.ibm.com> 
+ *            Heiko Carstens <heiko.carstens@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_LOG_AREA_PREFIX           ZFCP_LOG_AREA_PREFIX_ERP
+/* this drivers version (do not edit !!! generated and updated by cvs) */
+#define ZFCP_ERP_REVISION "$Revision: 1.33 $"
+
+#include "zfcp_ext.h"
+
+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_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 *);
+
+/*
+ * function:   zfcp_erp_adapter_shutdown_all
+ *
+ * purpose:    recursively calls zfcp_erp_adapter_shutdown to stop all
+ *              IO on each adapter, return all outstanding packets and 
+ *              relinquish all IRQs
+ *              Note: This function waits for completion of all shutdowns
+ *
+ * returns:     0 in all cases
+ */
+int
+zfcp_erp_adapter_shutdown_all(void)
+{
+       int retval = 0;
+       unsigned long flags;
+       struct zfcp_adapter *adapter;
+
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list)
+           zfcp_erp_adapter_shutdown(adapter, 0);
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+       /*
+        * FIXME : need to take config_lock but cannot, since we schedule here.
+        */
+       /* start all shutdowns first before any waiting to allow for concurreny */
+       list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list)
+           zfcp_erp_wait(adapter);
+
+       return retval;
+}
+
+/*
+ * function:     zfcp_erp_scsi_low_mem_buffer_timeout_handler
+ *
+ * purpose:      This function needs to be called whenever the SCSI command
+ *               in the low memory buffer 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_erp_scsi_low_mem_buffer_timeout_handler(unsigned long data)
+{
+       struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+
+       ZFCP_LOG_NORMAL("warning: Emergency buffer for SCSI I/O timed out. "
+                       "Restarting all operations on the adapter %s.\n",
+                       zfcp_get_busid_by_adapter(adapter));
+       debug_text_event(adapter->erp_dbf, 1, "scsi_lmem_tout");
+       zfcp_erp_adapter_reopen(adapter, 0);
+
+       return;
+}
+
+/*
+ * 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: Emergency buffer for SCSI error handling 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 on the 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 on the 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;
+}
+
+/*
+ * 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 the port with WWPN 0x%Lx "
+                      "on the 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 on the failed port "
+                              "with WWPN 0x%Lx on the 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 the port with WWPN 0x%Lx "
+                      "on the 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 on the failed port with WWPN 0x%Lx "
+                    "on the 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;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    Wrappper for zfcp_erp_port_reopen_internal
+ *              used to ensure the correct locking
+ *
+ * returns:    0       - initiated action succesfully
+ *             <0      - failed to initiate action
+ */
+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 the unit with FCP LUN 0x%Lx on the "
+                      "port with WWPN 0x%Lx "
+                      "on the 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 on the failed unit with FCP LUN 0x%Lx "
+                    "on the port with WWPN 0x%Lx " "on the 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;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    Wrappper for zfcp_erp_unit_reopen_internal
+ *              used to ensure the correct locking
+ *
+ * returns:    0       - initiated action succesfully
+ *             <0      - failed to initiate action
+ */
+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 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;
+                               /* some special things for time out conditions */
+                               if (erp_action-> status & ZFCP_STATUS_ERP_TIMEDOUT) {
+                                       ZFCP_LOG_NORMAL
+                                           ("error: Error Recovery Procedure step timed out. "
+                                            "The action flag is 0x%x. The FSF request "
+                                            "is at 0x%lx\n", erp_action->action,
+                                            (unsigned long) erp_action->fsf_req);
+                                       /* fight for low memory buffer, if required */
+                                       if (fsf_req->
+                                           status & ZFCP_STATUS_FSFREQ_POOL) {
+                                               debug_text_event(adapter->erp_dbf, 3,
+                                                                "a_ca_lowmem");
+                                               ZFCP_LOG_NORMAL
+                                                   ("error: The error recovery action using the "
+                                                    "low memory pool timed out. Restarting IO on "
+                                                    "the adapter %s to free it.\n",
+                                                    zfcp_get_busid_by_adapter
+                                                    (adapter));
+                                               zfcp_erp_adapter_reopen_internal(adapter, 0);
+                                       }
+                               }
+                       }
+               } 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_sync(&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
+ */
+static 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 finished FSF requests related to erp,
+ *             moves concerned erp action to 'ready' queue and
+ *             signals erp thread to process it,
+ *             besides it cancels a timeout
+ */
+void
+zfcp_erp_fsf_req_handler(struct zfcp_fsf_req *fsf_req)
+{
+       struct zfcp_erp_action *erp_action = fsf_req->erp_action;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+
+       debug_text_event(adapter->erp_dbf, 3, "a_frh");
+       debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
+
+       if (erp_action) {
+               debug_event(adapter->erp_dbf, 3, &erp_action->action,
+                           sizeof (int));
+               zfcp_erp_async_handler(erp_action, 0);
+       }
+}
+
+/*
+ * 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: Out of resources. Could not create an "
+                               "error recovery procedure thread "
+                               "for the 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
+ */
+static int
+zfcp_erp_thread(void *data)
+{
+       struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+       struct list_head *next;
+       struct zfcp_erp_action *erp_action;
+       unsigned long flags;
+
+       daemonize("zfcperp%s", zfcp_get_busid_by_adapter(adapter));
+       /* Block all signals */
+       siginitsetinv(&current->blocked, 0);
+       atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+       debug_text_event(adapter->erp_dbf, 5, "a_th_run");
+       wake_up(&adapter->erp_thread_wqh);
+
+       while (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
+                                &adapter->status)) {
+
+               write_lock_irqsave(&adapter->erp_lock, flags);
+               next = adapter->erp_ready_head.prev;
+               write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+               if (next != &adapter->erp_ready_head) {
+                       erp_action =
+                           list_entry(next, struct zfcp_erp_action, list);
+                       /*
+                        * process action (incl. [re]moving it
+                        * from 'ready' queue)
+                        */
+                       zfcp_erp_strategy(erp_action);
+               }
+
+               /*
+                * sleep as long as there is nothing to do, i.e.
+                * no action in 'ready' queue to be processed and
+                * thread is not to be killed
+                */
+               down_interruptible(&adapter->erp_ready_sem);
+               debug_text_event(adapter->erp_dbf, 5, "a_th_woken");
+       }
+
+       atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+       debug_text_event(adapter->erp_dbf, 5, "a_th_stop");
+       wake_up(&adapter->erp_thread_wqh);
+
+       return 0;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    drives single error recovery action and schedules higher and
+ *             subordinate actions, if necessary
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_SUCCEEDED      - action finished successfully (deqd)
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully (deqd)
+ *             ZFCP_ERP_EXIT           - action finished (dequeued), offline
+ *             ZFCP_ERP_DISMISSED      - action canceled (dequeued)
+ */
+static int
+zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_port *port = erp_action->port;
+       struct zfcp_unit *unit = erp_action->unit;
+       int action = erp_action->action;
+       u32 status = erp_action->status;
+       unsigned long flags;
+
+       /* serialise dismissing, timing out, moving, enqueueing */
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       write_lock(&adapter->erp_lock);
+
+       /* dequeue dismissed action and leave, if required */
+       retval = zfcp_erp_strategy_check_action(erp_action, retval);
+       if (retval == ZFCP_ERP_DISMISSED) {
+               debug_text_event(adapter->erp_dbf, 4, "a_st_dis1");
+               goto unlock;
+       }
+
+       /*
+        * move action to 'running' queue before processing it
+        * (to avoid a race condition regarding moving the
+        * action to the 'running' queue and back)
+        */
+       zfcp_erp_action_to_running(erp_action);
+
+       /*
+        * try to process action as far as possible,
+        * no lock to allow for blocking operations (kmalloc, qdio, ...),
+        * afterwards the lock is required again for the following reasons:
+        * - dequeueing of finished action and enqueueing of
+        *   follow-up actions must be atomic so that any other
+        *   reopen-routine does not believe there is nothing to do
+        *   and that it is safe to enqueue something else,
+        * - we want to force any control thread which is dismissing
+        *   actions to finish this before we decide about
+        *   necessary steps to be taken here further
+        */
+       write_unlock(&adapter->erp_lock);
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       retval = zfcp_erp_strategy_do_action(erp_action);
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       write_lock(&adapter->erp_lock);
+
+       /*
+        * check for dismissed status again to avoid follow-up actions,
+        * failing of targets and so on for dismissed actions
+        */
+       retval = zfcp_erp_strategy_check_action(erp_action, retval);
+
+       switch (retval) {
+       case ZFCP_ERP_DISMISSED:
+               /* leave since this action has ridden to its ancestors */
+               debug_text_event(adapter->erp_dbf, 6, "a_st_dis2");
+               goto unlock;
+       case ZFCP_ERP_NOMEM:
+               /* no memory to continue immediately, let it sleep */
+               debug_text_event(adapter->erp_dbf, 2, "a_st_memw");
+               retval = zfcp_erp_strategy_memwait(erp_action);
+               /* fall through, waiting for memory means action continues */
+       case ZFCP_ERP_CONTINUES:
+               /* leave since this action runs asynchronously */
+               debug_text_event(adapter->erp_dbf, 6, "a_st_cont");
+               goto unlock;
+       }
+       /* ok, finished action (whatever its result is) */
+
+       /* check for unrecoverable targets */
+       retval = zfcp_erp_strategy_check_target(erp_action, retval);
+
+       /* action must be dequeued (here to allow for further ones) */
+       zfcp_erp_action_dequeue(erp_action);
+
+       /*
+        * put this target through the erp mill again if someone has
+        * requested to change the status of a target being online 
+        * to offline or the other way around
+        * (old retval is preserved if nothing has to be done here)
+        */
+       retval = zfcp_erp_strategy_statechange(action, status, adapter,
+                                              port, unit, retval);
+
+       /*
+        * leave if target is in permanent error state or if
+        * action is repeated in order to process state change
+        */
+       if (retval == ZFCP_ERP_EXIT) {
+               debug_text_event(adapter->erp_dbf, 2, "a_st_exit");
+               goto unlock;
+       }
+
+       /* trigger follow up actions */
+       zfcp_erp_strategy_followup_actions(action, adapter, port, unit, retval);
+
+ unlock:
+       write_unlock(&adapter->erp_lock);
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       
+       /*
+        * a few tasks remain when the erp queues are empty
+        * (don't do that if the last action evaluated was dismissed
+        * since this clearly indicates that there is more to come) :
+        * - close the name server port if it is open yet
+        *   (enqueues another [probably] final action)
+        * - otherwise, wake up whoever wants to be woken when we are
+        *   done with erp
+        */
+       if (retval != ZFCP_ERP_DISMISSED)
+               zfcp_erp_strategy_check_queues(adapter);
+
+       debug_text_event(adapter->erp_dbf, 6, "a_st_done");
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_DISMISSED      - if action has been dismissed
+ *             retval                  - otherwise
+ */
+static int
+zfcp_erp_strategy_check_action(struct zfcp_erp_action *erp_action, int retval)
+{
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       zfcp_erp_strategy_check_fsfreq(erp_action);
+
+       debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof (int));
+       if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
+               debug_text_event(adapter->erp_dbf, 3, "a_stcd_dis");
+               zfcp_erp_action_dequeue(erp_action);
+               retval = ZFCP_ERP_DISMISSED;
+       } else
+               debug_text_event(adapter->erp_dbf, 5, "a_stcd_nodis");
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_FAILED;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       /*
+        * try to execute/continue action as far as possible,
+        * note: no lock in subsequent strategy routines
+        * (this allows these routine to call schedule, e.g.
+        * kmalloc with such flags or qdio_initialize & friends)
+        * Note: in case of timeout, the seperate strategies will fail
+        * anyhow. No need for a special action. Even worse, a nameserver
+        * failure would not wake up waiting ports without the call.
+        */
+       switch (erp_action->action) {
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               retval = zfcp_erp_adapter_strategy(erp_action);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               retval = zfcp_erp_port_forced_strategy(erp_action);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               retval = zfcp_erp_port_strategy(erp_action);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_UNIT:
+               retval = zfcp_erp_unit_strategy(erp_action);
+               break;
+
+       default:
+               debug_text_exception(adapter->erp_dbf, 1, "a_stda_bug");
+               debug_event(adapter->erp_dbf, 1, &erp_action->action,
+                           sizeof (int));
+               ZFCP_LOG_NORMAL("bug: Unknown error recovery procedure "
+                               "action requested on the adapter %s "
+                               "(debug info %d)\n",
+                               zfcp_get_busid_by_adapter(erp_action->adapter),
+                               erp_action->action);
+       }
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    triggers retry of this action after a certain amount of time
+ *             by means of timer provided by erp_action
+ *
+ * returns:    ZFCP_ERP_CONTINUES - erp_action sleeps in erp running queue
+ */
+static int
+zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_CONTINUES;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       debug_text_event(adapter->erp_dbf, 6, "a_mwinit");
+       debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof (int));
+       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 + ZFCP_ERP_MEMWAIT_TIMEOUT;
+       add_timer(&erp_action->timer);
+
+       return retval;
+}
+
+/* 
+ * function:    zfcp_erp_adapter_failed
+ *
+ * purpose:     sets the adapter and all underlying devices to ERP_FAILED
+ *
+ */
+void
+zfcp_erp_adapter_failed(struct zfcp_adapter *adapter)
+{
+       zfcp_erp_modify_adapter_status(adapter,
+                                      ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+       ZFCP_LOG_NORMAL("Adapter recovery failed on the "
+                       "adapter %s.\n", zfcp_get_busid_by_adapter(adapter));
+       debug_text_event(adapter->erp_dbf, 2, "a_afail");
+}
+
+/* 
+ * function:    zfcp_erp_port_failed
+ *
+ * purpose:     sets the port and all underlying devices to ERP_FAILED
+ *
+ */
+void
+zfcp_erp_port_failed(struct zfcp_port *port)
+{
+       zfcp_erp_modify_port_status(port,
+                                   ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+
+       ZFCP_LOG_NORMAL("Port recovery failed on the "
+                       "port with WWPN 0x%Lx at the "
+                       "adapter %s.\n",
+                       port->wwpn, zfcp_get_busid_by_port(port));
+       debug_text_event(port->adapter->erp_dbf, 2, "p_pfail");
+       debug_event(port->adapter->erp_dbf, 2, &port->wwpn, sizeof (wwn_t));
+}
+
+/* 
+ * function:    zfcp_erp_unit_failed
+ *
+ * purpose:     sets the unit to ERP_FAILED
+ *
+ */
+void
+zfcp_erp_unit_failed(struct zfcp_unit *unit)
+{
+       zfcp_erp_modify_unit_status(unit,
+                                   ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+
+       ZFCP_LOG_NORMAL("Unit recovery failed on the unit with FCP LUN 0x%Lx "
+                       "connected to the port with WWPN 0x%Lx at the "
+                       "adapter %s.\n",
+                       unit->fcp_lun,
+                       unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+       debug_text_event(unit->port->adapter->erp_dbf, 2, "u_ufail");
+       debug_event(unit->port->adapter->erp_dbf, 2,
+                   &unit->fcp_lun, sizeof (fcp_lun_t));
+}
+
+/*
+ * function:   zfcp_erp_strategy_check_target
+ *
+ * purpose:    increments the erp action count on the device currently in
+ *              recovery if the action failed or resets the count in case of
+ *              success. If a maximum count is exceeded the device is marked
+ *              as ERP_FAILED.
+ *             The 'blocked' state of a target which has been recovered
+ *              successfully is reset.
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (not considered)
+ *             ZFCP_ERP_SUCCEEDED      - action finished successfully 
+ *             ZFCP_ERP_EXIT           - action failed and will not continue
+ */
+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 zfcp_unit *unit = erp_action->unit;
+
+       debug_text_event(adapter->erp_dbf, 5, "a_stct_norm");
+       debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof (int));
+       debug_event(adapter->erp_dbf, 5, &result, sizeof (int));
+
+       switch (erp_action->action) {
+
+       case ZFCP_ERP_ACTION_REOPEN_UNIT:
+               result = zfcp_erp_strategy_check_unit(unit, 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;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_strategy_statechange(int action,
+                             u32 status,
+                             struct zfcp_adapter *adapter,
+                             struct zfcp_port *port,
+                             struct zfcp_unit *unit, int retval)
+{
+       debug_text_event(adapter->erp_dbf, 3, "a_stsc");
+       debug_event(adapter->erp_dbf, 3, &action, sizeof (int));
+
+       switch (action) {
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               if (zfcp_erp_strategy_statechange_detected(&adapter->status,
+                                                          status)) {
+                       zfcp_erp_adapter_reopen_internal(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
+                       retval = ZFCP_ERP_EXIT;
+               }
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               if (zfcp_erp_strategy_statechange_detected(&port->status,
+                                                          status)) {
+                       zfcp_erp_port_reopen_internal(port, ZFCP_STATUS_COMMON_ERP_FAILED);
+                       retval = ZFCP_ERP_EXIT;
+               }
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_UNIT:
+               if (zfcp_erp_strategy_statechange_detected(&unit->status,
+                                                          status)) {
+                       zfcp_erp_unit_reopen_internal(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
+                       retval = ZFCP_ERP_EXIT;
+               }
+               break;
+       }
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static inline int
+zfcp_erp_strategy_statechange_detected(atomic_t * target_status, u32 erp_status)
+{
+       return
+           /* take it online */
+           (atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
+            (ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status)) ||
+           /* take it offline */
+           (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
+            !(ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status));
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result)
+{
+       debug_text_event(unit->port->adapter->erp_dbf, 5, "u_stct");
+       debug_event(unit->port->adapter->erp_dbf, 5, &unit->fcp_lun,
+                   sizeof (fcp_lun_t));
+
+       if (result == ZFCP_ERP_SUCCEEDED) {
+               atomic_set(&unit->erp_counter, 0);
+               zfcp_erp_unit_unblock(unit);
+               /* register unit with scsi stack */
+               if (!unit->device)
+                       scsi_add_device(unit->port->adapter->scsi_host,
+                                       0, unit->port->scsi_id, unit->scsi_lun);
+       } else {
+               /* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */
+               atomic_inc(&unit->erp_counter);
+               if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS) {
+                       zfcp_erp_unit_failed(unit);
+                       result = ZFCP_ERP_EXIT;
+               }
+       }
+
+       return result;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
+{
+       debug_text_event(port->adapter->erp_dbf, 5, "p_stct");
+       debug_event(port->adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+
+       if (result == ZFCP_ERP_SUCCEEDED) {
+               atomic_set(&port->erp_counter, 0);
+               zfcp_erp_port_unblock(port);
+       } else {
+               /* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */
+               atomic_inc(&port->erp_counter);
+               if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) {
+                       zfcp_erp_port_failed(port);
+                       result = ZFCP_ERP_EXIT;
+               }
+       }
+
+       return result;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter, int result)
+{
+       debug_text_event(adapter->erp_dbf, 5, "a_stct");
+
+       if (result == ZFCP_ERP_SUCCEEDED) {
+               atomic_set(&adapter->erp_counter, 0);
+               zfcp_erp_adapter_unblock(adapter);
+       } else {
+               /* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */
+               atomic_inc(&adapter->erp_counter);
+               if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) {
+                       zfcp_erp_adapter_failed(adapter);
+                       result = ZFCP_ERP_EXIT;
+               }
+       }
+
+       return result;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    remaining things in good cases,
+ *             escalation in bad cases
+ *
+ * returns:
+ */
+static int
+zfcp_erp_strategy_followup_actions(int action,
+                                  struct zfcp_adapter *adapter,
+                                  struct zfcp_port *port,
+                                  struct zfcp_unit *unit, int status)
+{
+       debug_text_event(adapter->erp_dbf, 5, "a_stfol");
+       debug_event(adapter->erp_dbf, 5, &action, sizeof (int));
+
+       /* initiate follow-up actions depending on success of finished action */
+       switch (action) {
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               if (status == ZFCP_ERP_SUCCEEDED)
+                       zfcp_erp_port_reopen_all_internal(adapter, 0);
+               else
+                       zfcp_erp_adapter_reopen_internal(adapter, 0);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               if (status == ZFCP_ERP_SUCCEEDED)
+                       zfcp_erp_port_reopen_internal(port, 0);
+               else
+                       zfcp_erp_adapter_reopen_internal(adapter, 0);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               if (status == ZFCP_ERP_SUCCEEDED)
+                       zfcp_erp_unit_reopen_all_internal(port, 0);
+               else
+                       zfcp_erp_port_forced_reopen_internal(port, 0);
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_UNIT:
+               if (status == ZFCP_ERP_SUCCEEDED) ;     /* no further action */
+               else
+                       zfcp_erp_port_reopen_internal(unit->port, 0);
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_strategy_check_queues(struct zfcp_adapter *adapter)
+{
+       int retval = 0;
+       unsigned long flags;
+       struct zfcp_port *nport = adapter->nameserver_port;
+
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       read_lock(&adapter->erp_lock);
+       if (list_empty(&adapter->erp_ready_head) &&
+           list_empty(&adapter->erp_running_head)) {
+               if (nport
+                   && atomic_test_mask(ZFCP_STATUS_COMMON_OPEN,
+                                       &nport->status)) {
+                       debug_text_event(adapter->erp_dbf, 4, "a_cq_nspsd");
+                       /* taking down nameserver port */
+                       zfcp_erp_port_reopen_internal(nport,
+                                                     ZFCP_STATUS_COMMON_RUNNING |
+                                                     ZFCP_STATUS_COMMON_ERP_FAILED);
+               } else {
+                       debug_text_event(adapter->erp_dbf, 4, "a_cq_wake");
+                       atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
+                                         &adapter->status);
+                       wake_up(&adapter->erp_done_wqh);
+               }
+       } else
+               debug_text_event(adapter->erp_dbf, 5, "a_cq_notempty");
+       read_unlock(&adapter->erp_lock);
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+int
+zfcp_erp_wait(struct zfcp_adapter *adapter)
+{
+       int retval = 0;
+
+       wait_event(adapter->erp_done_wqh,
+                  !atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
+                                    &adapter->status));
+
+       return retval;
+}
+
+/*
+ * function:   zfcp_erp_modify_adapter_status
+ *
+ * purpose:    
+ *
+ */
+void
+zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter,
+                              u32 mask, int set_or_clear)
+{
+       struct zfcp_port *port;
+       u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+
+       if (set_or_clear == ZFCP_SET) {
+               atomic_set_mask(mask, &adapter->status);
+               debug_text_event(adapter->erp_dbf, 3, "a_mod_as_s");
+       } else {
+               atomic_clear_mask(mask, &adapter->status);
+               if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
+                       atomic_set(&adapter->erp_counter, 0);
+               debug_text_event(adapter->erp_dbf, 3, "a_mod_as_c");
+       }
+       debug_event(adapter->erp_dbf, 3, &mask, sizeof (u32));
+
+       /* Deal with all underlying devices, only pass common_mask */
+       if (common_mask)
+               list_for_each_entry(port, &adapter->port_list_head, list)
+                   zfcp_erp_modify_port_status(port, common_mask,
+                                               set_or_clear);
+}
+
+/*
+ * function:   zfcp_erp_modify_port_status
+ *
+ * purpose:    sets the port and all underlying devices to ERP_FAILED
+ *
+ */
+void
+zfcp_erp_modify_port_status(struct zfcp_port *port, u32 mask, int set_or_clear)
+{
+       struct zfcp_unit *unit;
+       u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+
+       if (set_or_clear == ZFCP_SET) {
+               atomic_set_mask(mask, &port->status);
+               debug_text_event(port->adapter->erp_dbf, 3, "p_mod_ps_s");
+       } else {
+               atomic_clear_mask(mask, &port->status);
+               if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
+                       atomic_set(&port->erp_counter, 0);
+               debug_text_event(port->adapter->erp_dbf, 3, "p_mod_ps_c");
+       }
+       debug_event(port->adapter->erp_dbf, 3, &port->wwpn, sizeof (wwn_t));
+       debug_event(port->adapter->erp_dbf, 3, &mask, sizeof (u32));
+
+       /* Modify status of all underlying devices, only pass common mask */
+       if (common_mask)
+               list_for_each_entry(unit, &port->unit_list_head, list)
+                   zfcp_erp_modify_unit_status(unit, common_mask,
+                                               set_or_clear);
+}
+
+/*
+ * function:   zfcp_erp_modify_unit_status
+ *
+ * purpose:    sets the unit to ERP_FAILED
+ *
+ */
+void
+zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u32 mask, int set_or_clear)
+{
+       if (set_or_clear == ZFCP_SET) {
+               atomic_set_mask(mask, &unit->status);
+               debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_s");
+       } else {
+               atomic_clear_mask(mask, &unit->status);
+               if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
+                       atomic_set(&unit->erp_counter, 0);
+               }
+               debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_c");
+       }
+       debug_event(unit->port->adapter->erp_dbf, 3, &unit->fcp_lun,
+                   sizeof (fcp_lun_t));
+       debug_event(unit->port->adapter->erp_dbf, 3, &mask, sizeof (u32));
+}
+
+/*
+ * function:   
+ *
+ * purpose:    Wrappper for zfcp_erp_port_reopen_all_internal
+ *              used to ensure the correct locking
+ *
+ * returns:    0       - initiated action succesfully
+ *             <0      - failed to initiate action
+ */
+int
+zfcp_erp_port_reopen_all(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_port_reopen_all_internal(adapter, clear_mask);
+       write_unlock(&adapter->erp_lock);
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    FIXME
+ */
+static int
+zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *adapter, int clear_mask)
+{
+       int retval = 0;
+       struct zfcp_port *port;
+
+       list_for_each_entry(port, &adapter->port_list_head, list)
+           if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &port->status)
+               != ZFCP_STATUS_PORT_NAMESERVER)
+               zfcp_erp_port_reopen_internal(port, clear_mask);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    FIXME
+ */
+static int
+zfcp_erp_unit_reopen_all_internal(struct zfcp_port *port, int clear_mask)
+{
+       int retval = 0;
+       struct zfcp_unit *unit;
+
+       list_for_each_entry(unit, &port->unit_list_head, list)
+           zfcp_erp_unit_reopen_internal(unit, clear_mask);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    this routine executes the 'Reopen Adapter' action
+ *             (the entire action is processed synchronously, since
+ *             there are no actions which might be run concurrently
+ *             per definition)
+ *
+ * returns:    ZFCP_ERP_SUCCEEDED      - action finished successfully
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_adapter_strategy(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       unsigned long timeout;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       retval = zfcp_erp_adapter_strategy_close(erp_action);
+       if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+               retval = ZFCP_ERP_EXIT;
+       else
+               retval = zfcp_erp_adapter_strategy_open(erp_action);
+
+       debug_text_event(adapter->erp_dbf, 3, "a_ast/ret");
+       debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
+       debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
+
+       if (retval == ZFCP_ERP_FAILED) {
+               ZFCP_LOG_INFO("Waiting to allow the adapter %s "
+                             "to recover itself\n.",
+                             zfcp_get_busid_by_adapter(adapter));
+               /*
+                * SUGGESTION: substitute by
+                * timeout = ZFCP_TYPE2_RECOVERY_TIME;
+                * __ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
+                */
+               timeout = ZFCP_TYPE2_RECOVERY_TIME;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(timeout);
+       }
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_SUCCEEDED      - action finished successfully
+ *              ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+
+       atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING,
+                       &erp_action->adapter->status);
+       retval = zfcp_erp_adapter_strategy_generic(erp_action, 1);
+       atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING,
+                         &erp_action->adapter->status);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_SUCCEEDED      - action finished successfully
+ *              ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+
+       atomic_set_mask(ZFCP_STATUS_COMMON_OPENING,
+                       &erp_action->adapter->status);
+       retval = zfcp_erp_adapter_strategy_generic(erp_action, 0);
+       atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING,
+                         &erp_action->adapter->status);
+
+       return retval;
+}
+
+/*
+ * function:    zfcp_register_adapter
+ *
+ * purpose:    allocate the irq associated with this devno and register
+ *             the FSF adapter with the SCSI stack
+ *
+ * returns:    
+ */
+static int
+zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close)
+{
+       int retval = ZFCP_ERP_SUCCEEDED;
+
+       if (close)
+               goto close_only;
+
+       retval = zfcp_erp_adapter_strategy_open_qdio(erp_action);
+       if (retval != ZFCP_ERP_SUCCEEDED)
+               goto failed_qdio;
+
+       retval = zfcp_erp_adapter_strategy_open_fsf(erp_action);
+       if (retval != ZFCP_ERP_SUCCEEDED)
+               goto failed_openfcp;
+
+       atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &erp_action->adapter->status);
+       goto out;
+
+ close_only:
+       atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
+                         &erp_action->adapter->status);
+
+ failed_openfcp:
+       zfcp_erp_adapter_strategy_close_qdio(erp_action);
+       zfcp_erp_adapter_strategy_close_fsf(erp_action);
+ failed_qdio:
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_qdio_init
+ *
+ * purpose:    setup QDIO operation for specified adapter
+ *
+ * returns:    0 - successful setup
+ *             !0 - failed setup
+ */
+int
+zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       int i;
+       volatile struct qdio_buffer_element *buffere;
+       int retval_cleanup = 0;
+
+       if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
+               ZFCP_LOG_NORMAL
+                   ("bug: QDIO (data transfer mechanism) start-up on "
+                    "adapter %s attempted twice. Second attempt ignored.\n",
+                    zfcp_get_busid_by_adapter(adapter));
+               goto failed_sanity;
+       }
+
+       if (qdio_establish(&adapter->qdio_init_data) != 0) {
+               ZFCP_LOG_INFO
+                   ("error: Could not establish queues for QDIO (data "
+                    "transfer mechanism) operation on adapter %s\n.",
+                    zfcp_get_busid_by_adapter(adapter));
+               goto failed_qdio_establish;
+       }
+       ZFCP_LOG_DEBUG("queues established\n");
+
+       if (qdio_activate(adapter->ccw_device, 0) != 0) {
+               ZFCP_LOG_INFO("error: Could not activate queues for QDIO (data "
+                             "transfer mechanism) operation on adapter %s\n.",
+                             zfcp_get_busid_by_adapter(adapter));
+               goto failed_qdio_activate;
+       }
+       ZFCP_LOG_DEBUG("queues activated\n");
+
+       /*
+        * put buffers into response queue,
+        */
+       for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
+               buffere = &(adapter->response_queue.buffer[i]->element[0]);
+               buffere->length = 0;
+               buffere->flags = SBAL_FLAGS_LAST_ENTRY;
+               buffere->addr = 0;
+       }
+
+       ZFCP_LOG_TRACE("Calling do QDIO busid=%s, flags=0x%x, queue_no=%i, "
+                      "index_in_queue=%i, count=%i\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q);
+
+       retval = do_QDIO(adapter->ccw_device,
+                        QDIO_FLAG_SYNC_INPUT,
+                        0, 0, QDIO_MAX_BUFFERS_PER_Q, NULL);
+
+       if (retval) {
+               ZFCP_LOG_NORMAL
+                   ("bug: QDIO (data transfer mechanism) inobund transfer "
+                    "structures could not be set-up (debug info %d)\n",
+                    retval);
+               goto failed_do_qdio;
+       } else {
+               adapter->response_queue.free_index = 0;
+               atomic_set(&adapter->response_queue.free_count, 0);
+               ZFCP_LOG_DEBUG
+                   ("%i buffers successfully enqueued to response queue\n",
+                    QDIO_MAX_BUFFERS_PER_Q);
+       }
+       /* set index of first avalable SBALS / number of available SBALS */
+       adapter->request_queue.free_index = 0;
+       atomic_set(&adapter->request_queue.free_count, QDIO_MAX_BUFFERS_PER_Q);
+       adapter->request_queue.distance_from_int = 0;
+
+       /* initialize waitqueue used to wait for free SBALs in requests queue */
+       init_waitqueue_head(&adapter->request_wq);
+
+       /* ok, we did it - skip all cleanups for different failures */
+       atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+       retval = ZFCP_ERP_SUCCEEDED;
+       goto out;
+
+ failed_do_qdio:
+       /* NOP */
+
+ failed_qdio_activate:
+       /* DEBUG */
+       //__ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
+       /* cleanup queues previously established */
+       retval_cleanup = qdio_shutdown(adapter->ccw_device,
+                                      QDIO_FLAG_CLEANUP_USING_CLEAR);
+       if (retval_cleanup) {
+               ZFCP_LOG_NORMAL
+                   ("bug: Could not clean QDIO (data transfer mechanism) "
+                    "queues. (debug info %i).\n", retval_cleanup);
+       }
+#ifdef ZFCP_DEBUG_REQUESTS
+       else
+               debug_text_event(adapter->req_dbf, 1, "q_clean");
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+
+ failed_qdio_establish:
+       atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+
+ failed_sanity:
+       retval = ZFCP_ERP_FAILED;
+
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_qdio_cleanup
+ *
+ * purpose:    cleans up QDIO operation for the specified adapter
+ *
+ * returns:    0 - successful cleanup
+ *             !0 - failed cleanup
+ */
+int
+zfcp_erp_adapter_strategy_close_qdio(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_SUCCEEDED;
+       int first_used;
+       int used_count;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
+               ZFCP_LOG_DEBUG("Termination of QDIO (data transfer operation) "
+                              "attempted for an inactive qdio on the "
+                              "adapter %s....ignored.\n",
+                              zfcp_get_busid_by_adapter(adapter));
+               retval = ZFCP_ERP_FAILED;
+               goto out;
+       }
+
+       /* cleanup queues previously established */
+
+       /*
+        * MUST NOT LOCK - qdio_cleanup might call schedule
+        * FIXME: need another way to make cleanup safe
+        */
+       /* Note:
+        * We need the request_queue lock here, otherwise there exists the 
+        * following race:
+        * 
+        * queuecommand calls create_fcp_commmand_task...calls req_create, 
+        * gets sbal x to x+y - meanwhile adapter reopen is called, completes 
+        * - req_send calls do_QDIO for sbal x to x+y, i.e. wrong indices.
+        *
+        * with lock:
+        * queuecommand calls create_fcp_commmand_task...calls req_create, 
+        * gets sbal x to x+y - meanwhile adapter reopen is called, waits 
+        * - req_send calls do_QDIO for sbal x to x+y, i.e. wrong indices 
+        * but do_QDIO fails as adapter_reopen is still waiting for the lock
+        * OR
+        * queuecommand calls create_fcp_commmand_task...calls req_create 
+        * - meanwhile adapter reopen is called...completes,
+        * - gets sbal 0 to 0+y, - req_send calls do_QDIO for sbal 0 to 0+y, 
+        * i.e. correct indices...though an fcp command is called before 
+        * exchange config data...that should be fine, however
+        */
+       if (qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR)) {
+               /*
+                * FIXME(design):
+                * What went wrong? What to do best? Proper retval?
+                */
+               ZFCP_LOG_NORMAL
+                   ("error: Clean-up of QDIO (data transfer mechanism) "
+                    "structures failed for adapter %s.\n",
+                    zfcp_get_busid_by_adapter(adapter));
+       } else {
+               ZFCP_LOG_DEBUG("queues cleaned up\n");
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 1, "q_clean");
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+       }
+
+       /*
+        * First we had to stop QDIO operation.
+        * Now it is safe to take the following actions.
+        */
+
+       /* Cleanup only necessary when there are unacknowledged buffers */
+       if (atomic_read(&adapter->request_queue.free_count)
+           < QDIO_MAX_BUFFERS_PER_Q) {
+               first_used = (adapter->request_queue.free_index +
+                             atomic_read(&adapter->request_queue.free_count))
+                       % QDIO_MAX_BUFFERS_PER_Q;
+               used_count = QDIO_MAX_BUFFERS_PER_Q -
+                       atomic_read(&adapter->request_queue.free_count);
+               zfcp_qdio_zero_sbals(adapter->request_queue.buffer,
+                                    first_used, used_count);
+       }
+       adapter->response_queue.free_index = 0;
+       atomic_set(&adapter->response_queue.free_count, 0);
+       adapter->request_queue.free_index = 0;
+       atomic_set(&adapter->request_queue.free_count, 0);
+       adapter->request_queue.distance_from_int = 0;
+
+       atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_init
+ *
+ * purpose:    initializes FSF operation for the specified adapter
+ *
+ * returns:    0 - succesful initialization of FSF operation
+ *             !0 - failed to initialize FSF operation
+ */
+static int
+zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+
+       /* do 'exchange configuration data' */
+       retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action);
+       if (retval == ZFCP_ERP_FAILED)
+               return retval;
+
+       /* start the desired number of Status Reads */
+       retval = zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action);
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_SUCCEEDED;
+       int retries;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
+       retries = ZFCP_EXCHANGE_CONFIG_DATA_RETRIES;
+
+       do {
+               atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+                                 &adapter->status);
+               ZFCP_LOG_DEBUG("Doing exchange config data\n");
+               zfcp_erp_timeout_init(erp_action);
+               if (zfcp_fsf_exchange_config_data(erp_action)) {
+                       retval = ZFCP_ERP_FAILED;
+                       debug_text_event(adapter->erp_dbf, 5, "a_fstx_xf");
+                       ZFCP_LOG_INFO("error: Out of resources. Could not "
+                                     "start exchange of configuration data "
+                                     "between the adapter %s "
+                                     "and the device driver.\n",
+                                     zfcp_get_busid_by_adapter(adapter));
+                       break;
+               }
+               debug_text_event(adapter->erp_dbf, 6, "a_fstx_xok");
+               ZFCP_LOG_DEBUG("Xchange underway\n");
+
+               /*
+                * Why this works:
+                * Both the normal completion handler as well as the timeout
+                * handler will do an 'up' when the 'exchange config data'
+                * request completes or times out. Thus, the signal to go on
+                * won't be lost utilizing this semaphore.
+                * Furthermore, this 'adapter_reopen' action is
+                * guaranteed to be the only action being there (highest action
+                * which prevents other actions from being created).
+                * Resulting from that, the wake signal recognized here
+                * _must_ be the one belonging to the 'exchange config
+                * data' request.
+                */
+               down_interruptible(&adapter->erp_ready_sem);
+               if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
+                       ZFCP_LOG_INFO
+                           ("error: Exchange of configuration data between "
+                            "the adapter with %s and the device "
+                            "driver timed out\n",
+                            zfcp_get_busid_by_adapter(adapter));
+                       break;
+               }
+               if (atomic_test_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+                                    &adapter->status)) {
+                       ZFCP_LOG_DEBUG("Host connection still initialising... "
+                                      "waiting and retrying....\n");
+                       /* sleep a little bit before retry */
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(ZFCP_EXCHANGE_CONFIG_DATA_SLEEP);
+               }
+       } while ((retries--) &&
+                atomic_test_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+                                 &adapter->status));
+
+       if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+                             &adapter->status)) {
+               ZFCP_LOG_INFO("error: Exchange of configuration data between "
+                             "the adapter %s and the device driver failed.\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               retval = ZFCP_ERP_FAILED;;
+       }
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_adapter_strategy_open_fsf_statusread(struct zfcp_erp_action
+                                             *erp_action)
+{
+       int retval = ZFCP_ERP_SUCCEEDED;
+       int temp_ret;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       int i;
+
+       adapter->status_read_failed = 0;
+       for (i = 0; i < ZFCP_STATUS_READS_RECOM; i++) {
+               temp_ret = zfcp_fsf_status_read(adapter, ZFCP_WAIT_FOR_SBAL);
+               if (temp_ret < 0) {
+                       ZFCP_LOG_INFO("error: Out of resources. Could not "
+                                     "set-up the infrastructure for "
+                                     "unsolicited status presentation "
+                                     "for the adapter %s.\n",
+                                     zfcp_get_busid_by_adapter(adapter));
+                       retval = ZFCP_ERP_FAILED;
+                       i--;
+                       break;
+               }
+       }
+
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_cleanup
+ *
+ * purpose:    cleanup FSF operation for specified adapter
+ *
+ * returns:    0 - FSF operation successfully cleaned up
+ *             !0 - failed to cleanup FSF operation for this adapter
+ */
+static int
+zfcp_erp_adapter_strategy_close_fsf(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_SUCCEEDED;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       /*
+        * wake waiting initiators of requests,
+        * return SCSI commands (with error status),
+        * clean up all requests (synchronously)
+        */
+       zfcp_fsf_req_dismiss_all(adapter);
+       /* reset FSF request sequence number */
+       adapter->fsf_req_seq_no = 0;
+       /* all ports and units are closed */
+       zfcp_erp_modify_adapter_status(adapter,
+                                      ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    this routine executes the 'Reopen Physical Port' action
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_SUCCEEDED      - action finished successfully
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_FAILED;
+       struct zfcp_port *port = erp_action->port;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       switch (erp_action->step) {
+
+               /*
+                * FIXME:
+                * the ULP spec. begs for waiting for oustanding commands
+                */
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+               zfcp_erp_port_strategy_clearstati(port);
+               /*
+                * it would be sufficient to test only the normal open flag
+                * since the phys. open flag cannot be set if the normal
+                * open flag is unset - however, this is for readabilty ...
+                */
+               if (atomic_test_mask((ZFCP_STATUS_PORT_PHYS_OPEN |
+                                     ZFCP_STATUS_COMMON_OPEN), &port->status)
+                   == (ZFCP_STATUS_PORT_PHYS_OPEN | ZFCP_STATUS_COMMON_OPEN)) {
+                       ZFCP_LOG_DEBUG("Port wwpn=0x%Lx is open -> trying "
+                                      " close physical\n",
+                                      port->wwpn);
+                       retval =
+                           zfcp_erp_port_forced_strategy_close(erp_action);
+               } else
+                       retval = ZFCP_ERP_FAILED;
+               break;
+
+       case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+               if (atomic_test_mask(ZFCP_STATUS_PORT_PHYS_OPEN,
+                                    &port->status)) {
+                       ZFCP_LOG_DEBUG
+                               ("failed to close physical port wwpn=0x%Lx\n",
+                                port->wwpn);
+                       retval = ZFCP_ERP_FAILED;
+               } else
+                       retval = ZFCP_ERP_SUCCEEDED;
+               break;
+       }
+
+       debug_text_event(adapter->erp_dbf, 3, "p_pfst/ret");
+       debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof (wwn_t));
+       debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
+       debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    this routine executes the 'Reopen Port' action
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_SUCCEEDED      - action finished successfully
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_FAILED;
+       struct zfcp_port *port = erp_action->port;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       switch (erp_action->step) {
+
+               /*
+                * FIXME:
+                * the ULP spec. begs for waiting for oustanding commands
+                */
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+               zfcp_erp_port_strategy_clearstati(port);
+               if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
+                       ZFCP_LOG_DEBUG
+                           ("port wwpn=0x%Lx is open -> trying close\n",
+                            port->wwpn);
+                       retval = zfcp_erp_port_strategy_close(erp_action);
+                       goto out;
+               }               /* else it's already closed, open it */
+               break;
+
+       case ZFCP_ERP_STEP_PORT_CLOSING:
+               if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
+                       ZFCP_LOG_DEBUG("failed to close port wwpn=0x%Lx\n",
+                                      port->wwpn);
+                       retval = ZFCP_ERP_FAILED;
+                       goto out;
+               }               /* else it's closed now, open it */
+               break;
+       }
+       if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+               retval = ZFCP_ERP_EXIT;
+       else
+               retval = zfcp_erp_port_strategy_open(erp_action);
+
+ out:
+       debug_text_event(adapter->erp_dbf, 3, "p_pst/ret");
+       debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof (wwn_t));
+       debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
+       debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_port_strategy_open(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+
+       if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER,
+                            &erp_action->port->status)
+           == ZFCP_STATUS_PORT_NAMESERVER)
+               retval = zfcp_erp_port_strategy_open_nameserver(erp_action);
+       else
+               retval = zfcp_erp_port_strategy_open_common(erp_action);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ *
+ * FIXME(design):      currently only prepared for fabric (nameserver!)
+ */
+static int
+zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_port *port = erp_action->port;
+
+       switch (erp_action->step) {
+
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+       case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+       case ZFCP_ERP_STEP_PORT_CLOSING:
+               if (!(adapter->nameserver_port)) {
+                       retval = zfcp_nameserver_enqueue(adapter);
+                       if (retval != 0) {
+                               ZFCP_LOG_NORMAL
+                                   ("error: nameserver port not available "
+                                    "(adapter with busid %s)\n",
+                                    zfcp_get_busid_by_adapter(adapter));
+                               retval = ZFCP_ERP_FAILED;
+                               break;
+                       }
+               }
+               if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
+                                     &adapter->nameserver_port->status)) {
+                       ZFCP_LOG_DEBUG
+                           ("nameserver port is not open -> open "
+                            "nameserver port\n");
+                       /* nameserver port may live again */
+                       atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING,
+                                       &adapter->nameserver_port->status);
+                       zfcp_erp_port_reopen(adapter->nameserver_port, 0);
+                       erp_action->step = ZFCP_ERP_STEP_NAMESERVER_OPEN;
+                       retval = ZFCP_ERP_CONTINUES;
+                       break;
+               }
+               /* else nameserver port is already open, fall through */
+       case ZFCP_ERP_STEP_NAMESERVER_OPEN:
+               if (!atomic_test_mask(ZFCP_STATUS_COMMON_OPEN,
+                                     &adapter->nameserver_port->status)) {
+                       ZFCP_LOG_DEBUG("failed to open nameserver port\n");
+                       retval = ZFCP_ERP_FAILED;
+               } else {
+                       ZFCP_LOG_DEBUG("nameserver port is open -> "
+                                      "ask nameserver for current D_ID of "
+                                      "port with WWPN 0x%Lx\n",
+                                      port->wwpn);
+                       retval = zfcp_erp_port_strategy_open_common_lookup
+                               (erp_action);
+               }
+               break;
+
+       case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
+               if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) {
+                       if (atomic_test_mask
+                           (ZFCP_STATUS_PORT_INVALID_WWPN, &port->status)) {
+                               ZFCP_LOG_DEBUG
+                                   ("failed to look up the D_ID of the port wwpn=0x%Lx "
+                                    "(misconfigured WWPN?)\n", port->wwpn);
+                               zfcp_erp_port_failed(port);
+                               retval = ZFCP_ERP_EXIT;
+                       } else {
+                               ZFCP_LOG_DEBUG
+                                   ("failed to look up the D_ID of the port wwpn=0x%Lx\n",
+                                    port->wwpn);
+                               retval = ZFCP_ERP_FAILED;
+                       }
+               } else {
+                       ZFCP_LOG_DEBUG
+                           ("port wwpn=0x%Lx has D_ID=0x%6.6x -> trying open\n",
+                            port->wwpn, (unsigned int) port->d_id);
+                       retval = zfcp_erp_port_strategy_open_port(erp_action);
+               }
+               break;
+
+       case ZFCP_ERP_STEP_PORT_OPENING:
+               /* D_ID might have changed during open */
+               if (atomic_test_mask((ZFCP_STATUS_COMMON_OPEN |
+                                     ZFCP_STATUS_PORT_DID_DID),
+                                    &port->status)) {
+                       ZFCP_LOG_DEBUG("port wwpn=0x%Lx is open ", port->wwpn);
+                       retval = ZFCP_ERP_SUCCEEDED;
+               } else {
+                       ZFCP_LOG_DEBUG("failed to open port wwpn=0x%Lx\n",
+                                      port->wwpn);
+                       retval = ZFCP_ERP_FAILED;
+               }
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: unkown erp step 0x%x\n",
+                               erp_action->step);
+               retval = ZFCP_ERP_FAILED;
+       }
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       struct zfcp_port *port = erp_action->port;
+
+       switch (erp_action->step) {
+
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+       case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+       case ZFCP_ERP_STEP_PORT_CLOSING:
+               ZFCP_LOG_DEBUG
+                   ("port wwpn=0x%Lx has D_ID=0x%6.6x -> trying open\n",
+                    port->wwpn, (unsigned int) port->d_id);
+               retval = zfcp_erp_port_strategy_open_port(erp_action);
+               break;
+
+       case ZFCP_ERP_STEP_PORT_OPENING:
+               if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
+                       ZFCP_LOG_DEBUG("nameserver port is open\n");
+                       retval = ZFCP_ERP_SUCCEEDED;
+               } else {
+                       ZFCP_LOG_DEBUG("failed to open nameserver port\n");
+                       retval = ZFCP_ERP_FAILED;
+               }
+               /* this is needed anyway (dont care for retval of wakeup) */
+               ZFCP_LOG_DEBUG("continue other open port operations\n");
+               zfcp_erp_port_strategy_open_nameserver_wakeup(erp_action);
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: unkown erp step 0x%x\n",
+                               erp_action->step);
+               retval = ZFCP_ERP_FAILED;
+       }
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    makes the erp thread continue with reopen (physical) port
+ *             actions which have been paused until the name server port
+ *             is opened (or failed)
+ *
+ * returns:    0       (a kind of void retval, its not used)
+ */
+static int
+zfcp_erp_port_strategy_open_nameserver_wakeup(struct zfcp_erp_action
+                                             *ns_erp_action)
+{
+       int retval = 0;
+       unsigned long flags;
+       struct zfcp_adapter *adapter = ns_erp_action->adapter;
+       struct zfcp_erp_action *erp_action, *tmp;
+
+       read_lock_irqsave(&adapter->erp_lock, flags);
+       list_for_each_entry_safe(erp_action, tmp, &adapter->erp_running_head,
+                                list) {
+               debug_text_event(adapter->erp_dbf, 4, "p_pstnsw_n");
+               debug_event(adapter->erp_dbf, 4, &erp_action->port->wwpn,
+                           sizeof (wwn_t));
+               if (erp_action->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
+                       debug_text_event(adapter->erp_dbf, 3, "p_pstnsw_w");
+                       debug_event(adapter->erp_dbf, 3,
+                                   &erp_action->port->wwpn, sizeof (wwn_t));
+                       zfcp_erp_action_ready(erp_action);
+               }
+       }
+       read_unlock_irqrestore(&adapter->erp_lock, flags);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_port *port = erp_action->port;
+
+       zfcp_erp_timeout_init(erp_action);
+       retval = zfcp_fsf_close_physical_port(erp_action);
+       if (retval == -ENOMEM) {
+               debug_text_event(adapter->erp_dbf, 5, "o_pfstc_nomem");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               retval = ZFCP_ERP_NOMEM;
+               goto out;
+       }
+       erp_action->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
+       if (retval != 0) {
+               debug_text_event(adapter->erp_dbf, 5, "o_pfstc_cpf");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               /* could not send 'open', fail */
+               retval = ZFCP_ERP_FAILED;
+               goto out;
+       }
+       debug_text_event(adapter->erp_dbf, 6, "o_pfstc_cpok");
+       debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+       retval = ZFCP_ERP_CONTINUES;
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = port->adapter;
+
+       debug_text_event(adapter->erp_dbf, 5, "p_pstclst");
+       debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+
+       atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
+                         ZFCP_STATUS_COMMON_CLOSING |
+                         ZFCP_STATUS_PORT_DID_DID |
+                         ZFCP_STATUS_PORT_PHYS_CLOSING |
+                         ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_port *port = erp_action->port;
+
+       zfcp_erp_timeout_init(erp_action);
+       retval = zfcp_fsf_close_port(erp_action);
+       if (retval == -ENOMEM) {
+               debug_text_event(adapter->erp_dbf, 5, "p_pstc_nomem");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               retval = ZFCP_ERP_NOMEM;
+               goto out;
+       }
+       erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
+       if (retval != 0) {
+               debug_text_event(adapter->erp_dbf, 5, "p_pstc_cpf");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               /* could not send 'close', fail */
+               retval = ZFCP_ERP_FAILED;
+               goto out;
+       }
+       debug_text_event(adapter->erp_dbf, 6, "p_pstc_cpok");
+       debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+       retval = ZFCP_ERP_CONTINUES;
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_port *port = erp_action->port;
+
+       zfcp_erp_timeout_init(erp_action);
+       retval = zfcp_fsf_open_port(erp_action);
+       if (retval == -ENOMEM) {
+               debug_text_event(adapter->erp_dbf, 5, "p_psto_nomem");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               retval = ZFCP_ERP_NOMEM;
+               goto out;
+       }
+       erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
+       if (retval != 0) {
+               debug_text_event(adapter->erp_dbf, 5, "p_psto_opf");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               /* could not send 'open', fail */
+               retval = ZFCP_ERP_FAILED;
+               goto out;
+       }
+       debug_text_event(adapter->erp_dbf, 6, "p_psto_opok");
+       debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+       retval = ZFCP_ERP_CONTINUES;
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_port *port = erp_action->port;
+
+       zfcp_erp_timeout_init(erp_action);
+       retval = zfcp_nameserver_request(erp_action);
+       if (retval == -ENOMEM) {
+               debug_text_event(adapter->erp_dbf, 5, "p_pstn_nomem");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               retval = ZFCP_ERP_NOMEM;
+               goto out;
+       }
+       erp_action->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
+       if (retval != 0) {
+               debug_text_event(adapter->erp_dbf, 5, "p_pstn_ref");
+               debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+               /* could not send nameserver request, fail */
+               retval = ZFCP_ERP_FAILED;
+               goto out;
+       }
+       debug_text_event(adapter->erp_dbf, 6, "p_pstn_reok");
+       debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+       retval = ZFCP_ERP_CONTINUES;
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    this routine executes the 'Reopen Unit' action
+ *             currently no retries
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_SUCCEEDED      - action finished successfully
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_unit_strategy(struct zfcp_erp_action *erp_action)
+{
+       int retval = ZFCP_ERP_FAILED;
+       struct zfcp_unit *unit = erp_action->unit;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       switch (erp_action->step) {
+
+               /*
+                * FIXME:
+                * the ULP spec. begs for waiting for oustanding commands
+                */
+       case ZFCP_ERP_STEP_UNINITIALIZED:
+               zfcp_erp_unit_strategy_clearstati(unit);
+               if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
+                       ZFCP_LOG_DEBUG
+                           ("unit fcp_lun=0x%Lx is open -> trying close\n",
+                            unit->fcp_lun);
+                       retval = zfcp_erp_unit_strategy_close(erp_action);
+                       break;
+               }
+               /* else it's already closed, fall through */
+       case ZFCP_ERP_STEP_UNIT_CLOSING:
+               if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
+                       ZFCP_LOG_DEBUG("failed to close unit fcp_lun=0x%Lx\n",
+                                      unit->fcp_lun);
+                       retval = ZFCP_ERP_FAILED;
+               } else {
+                       if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+                               retval = ZFCP_ERP_EXIT;
+                       else {
+                               ZFCP_LOG_DEBUG("unit fcp_lun=0x%Lx is not "
+                                              "open -> trying open\n",
+                                              unit->fcp_lun);
+                               retval =
+                                   zfcp_erp_unit_strategy_open(erp_action);
+                       }
+               }
+               break;
+
+       case ZFCP_ERP_STEP_UNIT_OPENING:
+               if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
+                       ZFCP_LOG_DEBUG("unit fcp_lun=0x%Lx is open\n",
+                                      unit->fcp_lun);
+                       retval = ZFCP_ERP_SUCCEEDED;
+               } else {
+                       ZFCP_LOG_DEBUG("failed to open unit fcp_lun=0x%Lx\n",
+                                      unit->fcp_lun);
+                       retval = ZFCP_ERP_FAILED;
+               }
+               break;
+       }
+
+       debug_text_event(adapter->erp_dbf, 3, "u_ust/ret");
+       debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof (fcp_lun_t));
+       debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
+       debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
+       return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+static int
+zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = unit->port->adapter;
+
+       debug_text_event(adapter->erp_dbf, 5, "u_ustclst");
+       debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
+
+       atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
+                         ZFCP_STATUS_COMMON_CLOSING, &unit->status);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_unit_strategy_close(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_unit *unit = erp_action->unit;
+
+       zfcp_erp_timeout_init(erp_action);
+       retval = zfcp_fsf_close_unit(erp_action);
+       if (retval == -ENOMEM) {
+               debug_text_event(adapter->erp_dbf, 5, "u_ustc_nomem");
+               debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
+                           sizeof (fcp_lun_t));
+               retval = ZFCP_ERP_NOMEM;
+               goto out;
+       }
+       erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
+       if (retval != 0) {
+               debug_text_event(adapter->erp_dbf, 5, "u_ustc_cuf");
+               debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
+                           sizeof (fcp_lun_t));
+               /* could not send 'close', fail */
+               retval = ZFCP_ERP_FAILED;
+               goto out;
+       }
+       debug_text_event(adapter->erp_dbf, 6, "u_ustc_cuok");
+       debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
+       retval = ZFCP_ERP_CONTINUES;
+
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    ZFCP_ERP_CONTINUES      - action continues (asynchronously)
+ *             ZFCP_ERP_FAILED         - action finished unsuccessfully
+ */
+static int
+zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
+{
+       int retval;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+       struct zfcp_unit *unit = erp_action->unit;
+
+       zfcp_erp_timeout_init(erp_action);
+       retval = zfcp_fsf_open_unit(erp_action);
+       if (retval == -ENOMEM) {
+               debug_text_event(adapter->erp_dbf, 5, "u_usto_nomem");
+               debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
+                           sizeof (fcp_lun_t));
+               retval = ZFCP_ERP_NOMEM;
+               goto out;
+       }
+       erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
+       if (retval != 0) {
+               debug_text_event(adapter->erp_dbf, 5, "u_usto_ouf");
+               debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
+                           sizeof (fcp_lun_t));
+               /* could not send 'open', fail */
+               retval = ZFCP_ERP_FAILED;
+               goto out;
+       }
+       debug_text_event(adapter->erp_dbf, 6, "u_usto_ouok");
+       debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
+       retval = ZFCP_ERP_CONTINUES;
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static inline void
+zfcp_erp_timeout_init(struct zfcp_erp_action *erp_action)
+{
+       init_timer(&erp_action->timer);
+       erp_action->timer.function = zfcp_erp_timeout_handler;
+       erp_action->timer.data = (unsigned long) erp_action;
+       /* jiffies will be added in zfcp_fsf_req_send */
+       erp_action->timer.expires = ZFCP_ERP_FSFREQ_TIMEOUT;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    enqueue the specified error recovery action, if needed
+ *
+ * returns:
+ */
+static int
+zfcp_erp_action_enqueue(int action,
+                       struct zfcp_adapter *adapter,
+                       struct zfcp_port *port, struct zfcp_unit *unit)
+{
+       int retval = -1;
+       struct zfcp_erp_action *erp_action = NULL;
+       int stronger_action = 0;
+       u32 status = 0;
+
+       /*
+        * We need some rules here which check whether we really need
+        * this action or whether we should just drop it.
+        * E.g. if there is a unfinished 'Reopen Port' request then we drop a
+        * 'Reopen Unit' request for an associated unit since we can't
+        * satisfy this request now. A 'Reopen Port' action will trigger
+        * 'Reopen Unit' actions when it completes.
+        * Thus, there are only actions in the queue which can immediately be
+        * executed. This makes the processing of the action queue more
+        * efficient.
+        */
+
+       debug_event(adapter->erp_dbf, 4, &action, sizeof (int));
+       /* check whether we really need this */
+       switch (action) {
+       case ZFCP_ERP_ACTION_REOPEN_UNIT:
+               if (atomic_test_mask
+                   (ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
+                       debug_text_event(adapter->erp_dbf, 4, "u_actenq_drp");
+                       debug_event(adapter->erp_dbf, 4, &port->wwpn,
+                                   sizeof (wwn_t));
+                       debug_event(adapter->erp_dbf, 4, &unit->fcp_lun,
+                                   sizeof (fcp_lun_t));
+                       goto out;
+               }
+               if (!atomic_test_mask
+                   (ZFCP_STATUS_COMMON_UNBLOCKED, &port->status)) {
+                       stronger_action = ZFCP_ERP_ACTION_REOPEN_PORT;
+                       unit = NULL;
+               }
+               /* fall through !!! */
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+               if (atomic_test_mask
+                   (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
+                       debug_text_event(adapter->erp_dbf, 4, "p_actenq_drp");
+                       debug_event(adapter->erp_dbf, 4, &port->wwpn,
+                                   sizeof (wwn_t));
+                       goto out;
+               }
+               /* fall through !!! */
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               if (atomic_test_mask
+                   (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)
+                   && port->erp_action.action ==
+                   ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) {
+                       debug_text_event(adapter->erp_dbf, 4, "pf_actenq_drp");
+                       debug_event(adapter->erp_dbf, 4, &port->wwpn,
+                                   sizeof (wwn_t));
+                       goto out;
+               }
+               if (!atomic_test_mask
+                   (ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status)) {
+                       stronger_action = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
+                       port = NULL;
+               }
+               /* fall through !!! */
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               if (atomic_test_mask
+                   (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) {
+                       debug_text_event(adapter->erp_dbf, 4, "a_actenq_drp");
+                       goto out;
+               }
+               break;
+
+       default:
+               debug_text_exception(adapter->erp_dbf, 1, "a_actenq_bug");
+               debug_event(adapter->erp_dbf, 1, &action, sizeof (int));
+               ZFCP_LOG_NORMAL("bug: Unknown error recovery procedure "
+                               "action requested on the adapter %s "
+                               "(debug info %d)\n",
+                               zfcp_get_busid_by_adapter(adapter), action);
+               goto out;
+       }
+
+       /* check whether we need something stronger first */
+       if (stronger_action) {
+               debug_text_event(adapter->erp_dbf, 4, "a_actenq_str");
+               debug_event(adapter->erp_dbf, 4, &stronger_action,
+                           sizeof (int));
+               ZFCP_LOG_DEBUG("shortcut: need erp action %i before "
+                              "erp action %i (adapter busid=%s)\n",
+                              stronger_action, action,
+                              zfcp_get_busid_by_adapter(adapter));
+               action = stronger_action;
+       }
+
+       /* mark adapter to have some error recovery pending */
+       atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
+
+       /* setup error recovery action */
+       switch (action) {
+
+       case ZFCP_ERP_ACTION_REOPEN_UNIT:
+               zfcp_unit_get(unit);
+               atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
+               erp_action = &unit->erp_action;
+               if (!atomic_test_mask
+                   (ZFCP_STATUS_COMMON_RUNNING, &unit->status))
+                       status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_PORT:
+       case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+               zfcp_port_get(port);
+               zfcp_erp_action_dismiss_port(port);
+               atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
+               erp_action = &port->erp_action;
+               if (!atomic_test_mask
+                   (ZFCP_STATUS_COMMON_RUNNING, &port->status))
+                       status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+               break;
+
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               zfcp_adapter_get(adapter);
+               zfcp_erp_action_dismiss_adapter(adapter);
+               atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
+               erp_action = &adapter->erp_action;
+               if (!atomic_test_mask
+                   (ZFCP_STATUS_COMMON_RUNNING, &adapter->status))
+                       status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+               break;
+       }
+
+       debug_text_event(adapter->erp_dbf, 4, "a_actenq");
+
+       memset(erp_action, 0, sizeof (struct zfcp_erp_action));
+       erp_action->adapter = adapter;
+       erp_action->port = port;
+       erp_action->unit = unit;
+       erp_action->action = action;
+       erp_action->status = status;
+
+       /* finally put it into 'ready' queue and kick erp thread */
+       list_add(&erp_action->list, &adapter->erp_ready_head);
+       up(&adapter->erp_ready_sem);
+       retval = 0;
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static int
+zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       debug_text_event(adapter->erp_dbf, 4, "a_actdeq");
+       debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof (int));
+       list_del(&erp_action->list);
+       switch (erp_action->action) {
+       case ZFCP_ERP_ACTION_REOPEN_UNIT:
+               atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+                                 &erp_action->unit->status);
+               zfcp_unit_put(erp_action->unit);
+               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);
+               zfcp_port_put(erp_action->port);
+               break;
+       case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+               atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+                                 &erp_action->adapter->status);
+               zfcp_adapter_put(adapter);
+               break;
+       default:
+               /* bug */
+               break;
+       }
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    FIXME
+ */
+static int
+zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+{
+       int retval = 0;
+       struct zfcp_port *port;
+
+       debug_text_event(adapter->erp_dbf, 5, "a_actab");
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
+               zfcp_erp_action_dismiss(&adapter->erp_action);
+       else
+               list_for_each_entry(port, &adapter->port_list_head, list)
+                   zfcp_erp_action_dismiss_port(port);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    FIXME
+ */
+static int
+zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+{
+       int retval = 0;
+       struct zfcp_unit *unit;
+       struct zfcp_adapter *adapter = port->adapter;
+
+       debug_text_event(adapter->erp_dbf, 5, "p_actab");
+       debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
+               zfcp_erp_action_dismiss(&port->erp_action);
+       else
+               list_for_each_entry(unit, &port->unit_list_head, list)
+                   zfcp_erp_action_dismiss_unit(unit);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:    FIXME
+ */
+static int
+zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = unit->port->adapter;
+
+       debug_text_event(adapter->erp_dbf, 5, "u_actab");
+       debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
+               zfcp_erp_action_dismiss(&unit->erp_action);
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    moves erp_action to 'erp running list'
+ *
+ * returns:
+ */
+static inline void
+zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
+{
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       debug_text_event(adapter->erp_dbf, 6, "a_toru");
+       debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof (int));
+       list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
+}
+
+/*
+ * function:   
+ *
+ * purpose:    moves erp_action to 'erp ready list'
+ *
+ * returns:
+ */
+static inline void
+zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action)
+{
+       struct zfcp_adapter *adapter = erp_action->adapter;
+
+       debug_text_event(adapter->erp_dbf, 6, "a_tore");
+       debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof (int));
+       list_move(&erp_action->list, &erp_action->adapter->erp_ready_head);
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
new file mode 100644 (file)
index 0000000..90169f9
--- /dev/null
@@ -0,0 +1,163 @@
+/* 
+ * 
+ * linux/drivers/s390/scsi/zfcp_ext.h
+ * 
+ * FCP adapter driver for IBM eServer zSeries 
+ * 
+ * Copyright 2002 IBM Corporation 
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com> 
+ *            Raimund Schroeder <raimund.schroeder@de.ibm.com> 
+ *            Aron Zeh <arzeh@de.ibm.com> 
+ *            Wolfgang Taphorn <taphorn@de.ibm.com> 
+ *            Stefan Bader <stefan.bader@de.ibm.com> 
+ *            Heiko Carstens <heiko.carstens@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. 
+ */
+
+#ifndef ZFCP_EXT_H
+#define ZFCP_EXT_H
+/* this drivers version (do not edit !!! generated and updated by cvs) */
+#define ZFCP_EXT_REVISION "$Revision: 1.33 $"
+
+#ifdef __KERNEL__
+
+#include "zfcp_def.h"
+
+extern struct zfcp_data zfcp_data;
+
+/******************************** SYSFS  *************************************/
+extern int  zfcp_sysfs_driver_create_files(struct device_driver *);
+extern void zfcp_sysfs_driver_remove_files(struct device_driver *);
+extern int  zfcp_sysfs_adapter_create_files(struct device *);
+extern void zfcp_sysfs_adapter_remove_files(struct device *);
+extern int  zfcp_sysfs_port_create_files(struct device *, u32);
+extern int  zfcp_sysfs_unit_create_files(struct device *);
+extern void zfcp_sysfs_port_release(struct device *);
+extern int  zfcp_sysfs_port_shutdown(struct zfcp_port *);
+extern void zfcp_sysfs_unit_release(struct device *);
+
+/**************************** CONFIGURATION  *********************************/
+extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *,
+                                             fcp_lun_t fcp_lun);
+extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *,
+                                              wwn_t wwpn);
+extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
+extern void   zfcp_adapter_dequeue(struct zfcp_adapter *);
+extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, u32);
+extern void   zfcp_port_dequeue(struct zfcp_port *);
+extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t);
+extern void   zfcp_unit_dequeue(struct zfcp_unit *);
+
+/******************************* S/390 IO ************************************/
+extern int  zfcp_ccw_register(void);
+extern void zfcp_ccw_unregister(void);
+
+extern int  zfcp_initialize_with_0copy(struct zfcp_adapter *);
+extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int);
+extern int  zfcp_qdio_allocate(struct zfcp_adapter *);
+extern int  zfcp_qdio_allocate_queues(struct zfcp_adapter *);
+extern void zfcp_qdio_free_queues(struct zfcp_adapter *);
+extern int  zfcp_qdio_determine_pci(struct zfcp_qdio_queue *,
+                                   struct zfcp_fsf_req *);
+extern int  zfcp_qdio_reqid_check(struct zfcp_adapter *, void *);
+
+/******************************** FSF ****************************************/
+extern int  zfcp_fsf_open_port(struct zfcp_erp_action *);
+extern int  zfcp_fsf_close_port(struct zfcp_erp_action *);
+extern int  zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
+
+extern int  zfcp_fsf_open_unit(struct zfcp_erp_action *);
+extern int  zfcp_fsf_close_unit(struct zfcp_erp_action *);
+
+extern int  zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
+extern void zfcp_fsf_scsi_er_timeout_handler(unsigned long);
+extern int  zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
+extern int  zfcp_fsf_status_read(struct zfcp_adapter *, int);
+extern int  zfcp_fsf_req_create(struct zfcp_adapter *,u32, unsigned long *,
+                               int, struct zfcp_fsf_req **);
+extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
+extern int  zfcp_fsf_send_generic(struct zfcp_fsf_req *, unsigned char,
+                                 unsigned long *, struct timer_list *);
+extern int  zfcp_fsf_req_wait_and_cleanup(struct zfcp_fsf_req *, int, u32 *);
+extern int  zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
+                                          struct zfcp_unit *, Scsi_Cmnd *,
+                                          int);
+extern int  zfcp_fsf_req_complete(struct zfcp_fsf_req *);
+extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *);
+extern void zfcp_fsf_req_cleanup(struct zfcp_fsf_req *);
+extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_command_task_management(
+       struct zfcp_adapter *, struct zfcp_unit *, u8, int);
+extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(
+       unsigned long, struct zfcp_adapter *, struct zfcp_unit *, int);
+
+/******************************** FCP ****************************************/
+extern int  zfcp_nameserver_enqueue(struct zfcp_adapter *);
+extern int  zfcp_nameserver_request(struct zfcp_erp_action *);
+extern void zfcp_fsf_els_processing(struct zfcp_fsf_req *);
+
+/******************************* SCSI ****************************************/
+extern int  zfcp_adapter_scsi_register(struct zfcp_adapter *);
+extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
+extern void zfcp_scsi_block_requests(struct Scsi_Host *);
+extern void zfcp_scsi_insert_into_fake_queue(struct zfcp_adapter *,
+                                            Scsi_Cmnd *);
+extern void zfcp_scsi_process_and_clear_fake_queue(unsigned long);
+extern int  zfcp_create_sbals_from_sg(struct zfcp_fsf_req *,
+                                    Scsi_Cmnd *, char, int, int);
+extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
+extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *);
+extern void set_host_byte(u32 *, char);
+extern void set_driver_byte(u32 *, char);
+extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
+extern void zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *);
+extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *);
+
+/******************************** ERP ****************************************/
+extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u32, int);
+extern int  zfcp_erp_adapter_reopen(struct zfcp_adapter *, int);
+extern int  zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int);
+extern int  zfcp_erp_adapter_shutdown_all(void);
+extern void zfcp_erp_adapter_failed(struct zfcp_adapter *);
+
+extern void zfcp_erp_modify_port_status(struct zfcp_port *, u32, int);
+extern int  zfcp_erp_port_reopen(struct zfcp_port *, int);
+extern int  zfcp_erp_port_shutdown(struct zfcp_port *, int);
+extern int  zfcp_erp_port_forced_reopen(struct zfcp_port *, int);
+extern void zfcp_erp_port_failed(struct zfcp_port *);
+extern int  zfcp_erp_port_reopen_all(struct zfcp_adapter *, int);
+
+extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u32, int);
+extern int  zfcp_erp_unit_reopen(struct zfcp_unit *, int);
+extern int  zfcp_erp_unit_shutdown(struct zfcp_unit *, int);
+extern void zfcp_erp_unit_failed(struct zfcp_unit *);
+
+extern void zfcp_erp_scsi_low_mem_buffer_timeout_handler(unsigned long);
+extern int  zfcp_erp_thread_setup(struct zfcp_adapter *);
+extern int  zfcp_erp_thread_kill(struct zfcp_adapter *);
+extern int  zfcp_erp_wait(struct zfcp_adapter *);
+extern void zfcp_erp_fsf_req_handler(struct zfcp_fsf_req *);
+
+/******************************** AUX ****************************************/
+extern void zfcp_cmd_dbf_event_fsf(const char *, struct zfcp_fsf_req *,
+                                  void *, int);
+extern void zfcp_cmd_dbf_event_scsi(const char *, Scsi_Cmnd *);
+extern void zfcp_in_els_dbf_event(struct zfcp_adapter *, const char *,
+                                 struct fsf_status_read_buffer *, int);
+#ifdef ZFCP_STAT_REQSIZES
+extern int  zfcp_statistics_inc(struct list_head *, u32);
+#endif
+#endif /* __KERNEL__ */
+#endif /* ZFCP_EXT_H */
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
new file mode 100644 (file)
index 0000000..faba1a9
--- /dev/null
@@ -0,0 +1,4284 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_fsf.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * Copyright 2002 IBM Corporation
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ *            Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ *            Aron Zeh <arzeh@de.ibm.com>
+ *            Wolfgang Taphorn <taphorn@de.ibm.com>
+ *            Stefan Bader <stefan.bader@de.ibm.com>
+ *            Heiko Carstens <heiko.carstens@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.
+ */
+
+/* this drivers version (do not edit !!! generated and updated by cvs) */
+#define ZFCP_FSF_C_REVISION "$Revision: 1.12 $"
+
+#include "zfcp_ext.h"
+
+static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_open_port_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_close_port_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_send_fcp_command_task_management_handler(
+       struct zfcp_fsf_req *);
+static int zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_send_generic_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_status_read_handler(struct zfcp_fsf_req *);
+static inline int zfcp_fsf_req_create_sbal_check(
+       unsigned long *, struct zfcp_qdio_queue *, int);
+static struct zfcp_fsf_req *zfcp_fsf_req_get(int, mempool_t *);
+static struct zfcp_fsf_req *zfcp_fsf_req_alloc(struct zfcp_adapter *, u32, int);
+static int zfcp_fsf_req_send(struct zfcp_fsf_req *, struct timer_list *);
+static int zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *);
+static int zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *);
+static int zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *);
+static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *);
+static void zfcp_fsf_req_dismiss(struct zfcp_fsf_req *);
+
+/* association between FSF command and FSF QTCB type */
+static u32 fsf_qtcb_type[] = {
+       [FSF_QTCB_FCP_CMND] =             FSF_IO_COMMAND,
+       [FSF_QTCB_ABORT_FCP_CMND] =       FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_OPEN_PORT_WITH_DID] =   FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_OPEN_LUN] =             FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_CLOSE_LUN] =            FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_CLOSE_PORT] =           FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_CLOSE_PHYSICAL_PORT] =  FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_SEND_ELS] =             FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_SEND_GENERIC] =         FSF_SUPPORT_COMMAND,
+       [FSF_QTCB_EXCHANGE_CONFIG_DATA] = FSF_CONFIG_COMMAND
+};
+
+/****************************************************************/
+/*************** FSF related Functions  *************************/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA                  ZFCP_LOG_AREA_FSF
+#define ZFCP_LOG_AREA_PREFIX           ZFCP_LOG_AREA_PREFIX_FSF
+
+/*
+ * function:   zfcp_fsf_req_alloc
+ *
+ * purpose:     Obtains an fsf_req and potentially a qtcb (for all but 
+ *              unsolicited requests) via helper functions
+ *              Does some initial fsf request set-up.
+ *              
+ * returns:    pointer to allocated fsf_req if successfull
+ *              NULL otherwise
+ *
+ * locks:       none
+ *
+ */
+static struct zfcp_fsf_req *
+zfcp_fsf_req_alloc(struct zfcp_adapter *adapter, u32 fsf_cmd, int kmalloc_flags)
+{
+       struct zfcp_fsf_req *fsf_req = NULL;
+
+       switch (fsf_cmd) {
+
+       case FSF_QTCB_FCP_CMND:
+       case FSF_QTCB_ABORT_FCP_CMND:
+               fsf_req = zfcp_fsf_req_get(kmalloc_flags,
+                                          adapter->pool.fcp_command_fsf);
+               if (fsf_req && (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL)) {
+                       /*
+                        * watch low mem buffer
+                        * Note: If the command is reset or aborted, two
+                        * timeouts (this and the SCSI ER one) will be started
+                        * for the command. There is no problem however as
+                        * the first expired timer will call adapter_reopen
+                        * which will delete the other 
+                        */
+                       adapter->pool.fcp_command_fsf_timer.expires =
+                           jiffies + ZFCP_ERP_SCSI_LOW_MEM_TIMEOUT;
+                       add_timer(&adapter->pool.fcp_command_fsf_timer);
+               }
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 5, "fsfa_fcp");
+               if (fsf_req && (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL))
+                       debug_text_event(adapter->req_dbf, 5, "fsfa_pl");
+#endif /* ZFCP_DEBUG_REQUESTS */
+               break;
+
+       case FSF_QTCB_OPEN_PORT_WITH_DID:
+       case FSF_QTCB_OPEN_LUN:
+       case FSF_QTCB_CLOSE_LUN:
+       case FSF_QTCB_CLOSE_PORT:
+       case FSF_QTCB_CLOSE_PHYSICAL_PORT:
+       case FSF_QTCB_SEND_ELS:
+       case FSF_QTCB_EXCHANGE_CONFIG_DATA:
+       case FSF_QTCB_SEND_GENERIC:
+               fsf_req =
+                   zfcp_fsf_req_get(kmalloc_flags, adapter->pool.erp_fsf);
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 5, "fsfa_erp");
+               if (fsf_req && (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL))
+                       debug_text_event(adapter->req_dbf, 5, "fsfa_pl");
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+               break;
+
+       case FSF_QTCB_UNSOLICITED_STATUS:
+               fsf_req =
+                   mempool_alloc(adapter->pool.status_read_fsf, GFP_ATOMIC);
+               if (fsf_req) {
+                       memset(fsf_req, 0, sizeof (struct zfcp_fsf_req));
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_POOL;
+               } else
+                       ZFCP_LOG_NORMAL("bug: could not find free fsf_req\n");
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 5, "fsfa_sr");
+               debug_text_event(adapter->req_dbf, 5, "fsfa_pl");
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An attempt to send an unsupported "
+                               "command has been detected. "
+                               "(debug info 0x%x)\n", fsf_cmd);
+       }                       //switch(fsf_cmd)
+
+       if (!fsf_req) {
+               ZFCP_LOG_DEBUG("error: Out of memory. Allocation of FSF "
+                              "request structure failed\n");
+       } else {
+               ZFCP_LOG_TRACE("FSF request allocated at 0x%lx, "
+                              "adapter 0x%lx (%s)\n",
+                              (unsigned long) fsf_req,
+                              (unsigned long) adapter,
+                              zfcp_get_busid_by_adapter(adapter));
+       }
+
+#ifdef ZFCP_DEBUG_REQUESTS
+       debug_event(adapter->req_dbf, 5, &fsf_req, sizeof (unsigned long));
+       if (fsf_req->qtcb)
+               debug_event(adapter->req_dbf, 5, &fsf_req->qtcb,
+                           sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+
+       return fsf_req;
+}
+
+/*
+ * function:   zfcp_fsf_req_free
+ *
+ * purpose:     Frees the memory of an fsf_req (and potentially a qtcb) or
+ *              returns it into the pool via helper functions.
+ *
+ * returns:     sod all
+ *
+ * locks:       none
+ */
+void
+zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req)
+{
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+
+       switch (fsf_req->fsf_command) {
+
+       case FSF_QTCB_FCP_CMND:
+       case FSF_QTCB_ABORT_FCP_CMND:
+               if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL) {
+                       del_timer(&adapter->pool.fcp_command_fsf_timer);
+                       mempool_free(fsf_req, adapter->pool.fcp_command_fsf);
+               } else
+                       kfree(fsf_req);
+               break;
+
+       case FSF_QTCB_OPEN_PORT_WITH_DID:
+       case FSF_QTCB_OPEN_LUN:
+       case FSF_QTCB_CLOSE_LUN:
+       case FSF_QTCB_CLOSE_PORT:
+       case FSF_QTCB_CLOSE_PHYSICAL_PORT:
+       case FSF_QTCB_SEND_ELS:
+       case FSF_QTCB_EXCHANGE_CONFIG_DATA:
+       case FSF_QTCB_SEND_GENERIC:
+               if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL)
+                       mempool_free(fsf_req, adapter->pool.erp_fsf);
+               else
+                       kfree(fsf_req);
+               break;
+
+       case FSF_QTCB_UNSOLICITED_STATUS:
+               mempool_free(fsf_req, adapter->pool.status_read_fsf);
+               break;
+       }
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ *
+ * note: qdio queues shall be down (no ongoing inbound processing)
+ */
+int
+zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
+{
+       int retval = 0;
+       struct zfcp_fsf_req *fsf_req, *tmp;
+
+       list_for_each_entry_safe(fsf_req, tmp, &adapter->fsf_req_list_head,
+                                list)
+           zfcp_fsf_req_dismiss(fsf_req);
+       /* wait_event_timeout? */
+       while (!list_empty(&adapter->fsf_req_list_head)) {
+               ZFCP_LOG_DEBUG("fsf req list of adapter %s not yet empty\n",
+                              zfcp_get_busid_by_adapter(adapter));
+               /* wait for woken intiators to clean up their requests */
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(ZFCP_FSFREQ_CLEANUP_TIMEOUT);
+       }
+
+       /* consistency check */
+       if (atomic_read(&adapter->fsf_reqs_active)) {
+               ZFCP_LOG_NORMAL("bug: There are still %d FSF requests pending "
+                               "on adapter %s after cleanup.\n",
+                               atomic_read(&adapter->fsf_reqs_active),
+                               zfcp_get_busid_by_adapter(adapter));
+               atomic_set(&adapter->fsf_reqs_active, 0);
+       }
+
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+static void
+zfcp_fsf_req_dismiss(struct zfcp_fsf_req *fsf_req)
+{
+       fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+       zfcp_fsf_req_complete(fsf_req);
+}
+
+/*
+ * function:    zfcp_fsf_req_complete
+ *
+ * purpose:    Updates active counts and timers for openfcp-reqs
+ *              May cleanup request after req_eval returns
+ *
+ * returns:    0 - success
+ *             !0 - failure
+ *
+ * context:    
+ */
+int
+zfcp_fsf_req_complete(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+       int cleanup;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+
+       /* do some statistics */
+       atomic_dec(&adapter->fsf_reqs_active);
+
+       if (fsf_req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS) {
+               ZFCP_LOG_DEBUG("Status read response received\n");
+               /*
+                * Note: all cleanup handling is done in the callchain of
+                * the function call-chain below.
+                */
+               zfcp_fsf_status_read_handler(fsf_req);
+               goto out;
+       } else
+               zfcp_fsf_protstatus_eval(fsf_req);
+
+       /*
+        * fsf_req may be deleted due to waking up functions, so 
+        * cleanup is saved here and used later 
+        */
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_CLEANUP)
+               cleanup = 1;
+       else
+               cleanup = 0;
+
+       fsf_req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
+
+       /* cleanup request if requested by initiator */
+       if (cleanup) {
+               ZFCP_LOG_TRACE("removing FSF request 0x%lx\n",
+                              (unsigned long) fsf_req);
+               /*
+                * lock must not be held here since it will be
+                * grabed by the called routine, too
+                */
+               zfcp_fsf_req_cleanup(fsf_req);
+       } else {
+               /* notify initiator waiting for the requests completion */
+               ZFCP_LOG_TRACE("waking initiator of FSF request 0x%lx\n",
+                              (unsigned long) fsf_req);
+               wake_up(&fsf_req->completion_wq);
+       }
+
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_protstatus_eval
+ *
+ * purpose:    evaluates the QTCB of the finished FSF request
+ *             and initiates appropriate actions
+ *             (usually calling FSF command specific handlers)
+ *
+ * returns:    
+ *
+ * context:    
+ *
+ * locks:
+ */
+static int
+zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+
+       ZFCP_LOG_DEBUG("QTCB is at 0x%lx\n", (unsigned long) fsf_req->qtcb);
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
+               ZFCP_LOG_DEBUG("fsf_req 0x%lx has been dismissed\n",
+                              (unsigned long) fsf_req);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+                       ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */
+               zfcp_cmd_dbf_event_fsf("dismiss", fsf_req, NULL, 0);
+               goto skip_protstatus;
+       }
+
+       /* log additional information provided by FSF (if any) */
+       if (fsf_req->qtcb->header.log_length) {
+               /* do not trust them ;-) */
+               if (fsf_req->qtcb->header.log_start > ZFCP_QTCB_SIZE) {
+                       ZFCP_LOG_NORMAL
+                           ("bug: ULP (FSF logging) log data starts "
+                            "beyond end of packet header. Ignored. "
+                            "(start=%i, size=%li)\n",
+                            fsf_req->qtcb->header.log_start, ZFCP_QTCB_SIZE);
+                       goto forget_log;
+               }
+               if ((fsf_req->qtcb->header.log_start +
+                    fsf_req->qtcb->header.log_length)
+                   > ZFCP_QTCB_SIZE) {
+                       ZFCP_LOG_NORMAL("bug: ULP (FSF logging) log data ends "
+                                       "beyond end of packet header. Ignored. "
+                                       "(start=%i, length=%i, size=%li)\n",
+                                       fsf_req->qtcb->header.log_start,
+                                       fsf_req->qtcb->header.log_length,
+                                       ZFCP_QTCB_SIZE);
+                       goto forget_log;
+               }
+               ZFCP_LOG_TRACE("ULP log data: \n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
+                             (char *) fsf_req->qtcb +
+                             fsf_req->qtcb->header.log_start,
+                             fsf_req->qtcb->header.log_length);
+       }
+ forget_log:
+
+       /* evaluate FSF Protocol Status */
+       switch (fsf_req->qtcb->prefix.prot_status) {
+
+       case FSF_PROT_GOOD:
+               ZFCP_LOG_TRACE("FSF_PROT_GOOD\n");
+               break;
+
+       case FSF_PROT_FSF_STATUS_PRESENTED:
+               ZFCP_LOG_TRACE("FSF_PROT_FSF_STATUS_PRESENTED\n");
+               break;
+
+       case FSF_PROT_QTCB_VERSION_ERROR:
+               ZFCP_LOG_FLAGS(0, "FSF_PROT_QTCB_VERSION_ERROR\n");
+               /* DEBUG */
+               ZFCP_LOG_NORMAL("fsf_req=0x%lx, qtcb=0x%lx (0x%lx, 0x%lx)\n",
+                               (unsigned long) fsf_req,
+                               (unsigned long) fsf_req->qtcb,
+                               ((unsigned long) fsf_req) & 0xFFFFFF00,
+                               (unsigned
+                                long) ((struct zfcp_fsf_req
+                                        *) (((unsigned long) fsf_req) &
+                                            0xFFFFFF00))->qtcb);
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) (((unsigned long) fsf_req) & 0xFFFFFF00),
+                             sizeof (struct zfcp_fsf_req));
+               ZFCP_LOG_NORMAL("error: The adapter %s contains "
+                               "microcode of version 0x%x, the device driver "
+                               "only supports 0x%x. Aborting.\n",
+                               zfcp_get_busid_by_adapter(adapter),
+                               fsf_req->qtcb->prefix.prot_status_qual.
+                               version_error.fsf_version, ZFCP_QTCB_VERSION);
+               /* stop operation for this adapter */
+               debug_text_exception(adapter->erp_dbf, 0, "prot_ver_err");
+               zfcp_erp_adapter_shutdown(adapter, 0);
+               zfcp_cmd_dbf_event_fsf("qverserr", fsf_req,
+                                      &fsf_req->qtcb->prefix.prot_status_qual,
+                                      sizeof (union fsf_prot_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PROT_SEQ_NUMB_ERROR:
+               ZFCP_LOG_FLAGS(0, "FSF_PROT_SEQ_NUMB_ERROR\n");
+               ZFCP_LOG_NORMAL("bug: Sequence number mismatch between "
+                               "driver (0x%x) and adapter %s (0x%x). "
+                               "Restarting all operations on this adapter.\n",
+                               fsf_req->qtcb->prefix.req_seq_no,
+                               zfcp_get_busid_by_adapter(adapter),
+                               fsf_req->qtcb->prefix.prot_status_qual.
+                               sequence_error.exp_req_seq_no);
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 1, "exp_seq!");
+               debug_event(adapter->req_dbf, 1,
+                           &fsf_req->qtcb->prefix.prot_status_qual.
+                           sequence_error.exp_req_seq_no, 4);
+               debug_text_event(adapter->req_dbf, 1, "qtcb_seq!");
+               debug_exception(adapter->req_dbf, 1,
+                               &fsf_req->qtcb->prefix.req_seq_no, 4);
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+               debug_text_exception(adapter->erp_dbf, 0, "prot_seq_err");
+               /* restart operation on this adapter */
+               zfcp_erp_adapter_reopen(adapter, 0);
+               zfcp_cmd_dbf_event_fsf("seqnoerr", fsf_req,
+                                      &fsf_req->qtcb->prefix.prot_status_qual,
+                                      sizeof (union fsf_prot_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PROT_UNSUPP_QTCB_TYPE:
+               ZFCP_LOG_FLAGS(0, "FSF_PROT_UNSUP_QTCB_TYPE\n");
+               ZFCP_LOG_NORMAL("error: Packet header type used by the "
+                               "device driver is incompatible with "
+                               "that used on adapter %s. "
+                               "Stopping all operations on this adapter.\n",
+                               zfcp_get_busid_by_adapter(adapter));
+               ZFCP_LOG_NORMAL("fsf_req=0x%lx, qtcb=0x%lx (0x%lx, 0x%lx)\n",
+                               (unsigned long) fsf_req,
+                               (unsigned long) fsf_req->qtcb,
+                               ((unsigned long) fsf_req) & 0xFFFFFF00,
+                               (unsigned long) (
+                                       (struct zfcp_fsf_req *) (
+                                               ((unsigned long)
+                                                fsf_req) & 0xFFFFFF00))->qtcb);
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) (((unsigned long) fsf_req) & 0xFFFFFF00),
+                             sizeof (struct zfcp_fsf_req));
+               debug_text_exception(adapter->erp_dbf, 0, "prot_unsup_qtcb");
+               zfcp_erp_adapter_shutdown(adapter, 0);
+               zfcp_cmd_dbf_event_fsf("unsqtcbt", fsf_req,
+                                      &fsf_req->qtcb->prefix.prot_status_qual,
+                                      sizeof (union fsf_prot_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PROT_HOST_CONNECTION_INITIALIZING:
+               ZFCP_LOG_FLAGS(1, "FSF_PROT_HOST_CONNECTION_INITIALIZING\n");
+               zfcp_cmd_dbf_event_fsf("hconinit", fsf_req,
+                                      &fsf_req->qtcb->prefix.prot_status_qual,
+                                      sizeof (union fsf_prot_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+                               &(adapter->status));
+               debug_text_event(adapter->erp_dbf, 4, "prot_con_init");
+               break;
+
+       case FSF_PROT_DUPLICATE_REQUEST_ID:
+               ZFCP_LOG_FLAGS(0, "FSF_PROT_DUPLICATE_REQUEST_IDS\n");
+               if (fsf_req->qtcb) {
+                       ZFCP_LOG_NORMAL("bug: The request identifier  0x%Lx "
+                                       "to the adapter %s is ambiguous. "
+                                       "Stopping all operations on this "
+                                       "adapter.\n",
+                                       *(unsigned long long *)
+                                       (&fsf_req->qtcb->bottom.support.
+                                        req_handle),
+                                       zfcp_get_busid_by_adapter(adapter));
+               } else {
+                       ZFCP_LOG_NORMAL("bug: The request identifier  0x%lx "
+                                       "to the adapter %s is ambiguous. "
+                                       "Stopping all operations on this "
+                                       "adapter. "
+                                       "(bug: got this for an unsolicited "
+                                       "status read request)\n",
+                                       (unsigned long) fsf_req,
+                                       zfcp_get_busid_by_adapter(adapter));
+               }
+               debug_text_exception(adapter->erp_dbf, 0, "prot_dup_id");
+               zfcp_erp_adapter_shutdown(adapter, 0);
+               zfcp_cmd_dbf_event_fsf("dupreqid", fsf_req,
+                                      &fsf_req->qtcb->prefix.prot_status_qual,
+                                      sizeof (union fsf_prot_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PROT_LINK_DOWN:
+               ZFCP_LOG_FLAGS(1, "FSF_PROT_LINK_DOWN\n");
+               /*
+                * 'test and set' is not atomic here -
+                * it's ok as long as calls to our response queue handler
+                * (and thus execution of this code here) are serialized
+                * by the qdio module
+                */
+               if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+                                     &adapter->status)) {
+                       switch (fsf_req->qtcb->prefix.prot_status_qual.
+                               locallink_error.code) {
+                       case FSF_PSQ_LINK_NOLIGHT:
+                               ZFCP_LOG_INFO("The local link to adapter %s "
+                                             "is down (no light detected).\n",
+                                             zfcp_get_busid_by_adapter(
+                                                     adapter));
+                               break;
+                       case FSF_PSQ_LINK_WRAPPLUG:
+                               ZFCP_LOG_INFO("The local link to adapter %s "
+                                             "is down (wrap plug detected).\n",
+                                             zfcp_get_busid_by_adapter(
+                                                     adapter));
+                               break;
+                       case FSF_PSQ_LINK_NOFCP:
+                               ZFCP_LOG_INFO("The local link to adapter %s "
+                                             "is down (adjacent node on "
+                                             "link does not support FCP).\n",
+                                             zfcp_get_busid_by_adapter(
+                                                     adapter));
+                               break;
+                       default:
+                               ZFCP_LOG_INFO("The local link to adapter %s "
+                                             "is down "
+                                             "(warning: unknown reason "
+                                             "code).\n",
+                                             zfcp_get_busid_by_adapter(
+                                                     adapter));
+                               break;
+
+                       }
+                       /*
+                        * Due to the 'erp failed' flag the adapter won't
+                        * be recovered but will be just set to 'blocked'
+                        * state. All subordinary devices will have state
+                        * 'blocked' and 'erp failed', too.
+                        * Thus the adapter is still able to provide
+                        * 'link up' status without being flooded with
+                        * requests.
+                        * (note: even 'close port' is not permitted)
+                        */
+                       ZFCP_LOG_INFO("Stopping all operations for adapter "
+                                     "%s.\n",
+                                     zfcp_get_busid_by_adapter(adapter));
+                       atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+                                       ZFCP_STATUS_COMMON_ERP_FAILED,
+                                       &adapter->status);
+                       zfcp_erp_adapter_reopen(adapter, 0);
+                       debug_text_event(adapter->erp_dbf, 1, "prot_link_down");
+               }
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PROT_REEST_QUEUE:
+               ZFCP_LOG_FLAGS(1, "FSF_PROT_REEST_QUEUE\n");
+               debug_text_event(adapter->erp_dbf, 1, "prot_reest_queue");
+               ZFCP_LOG_INFO("The local link to adapter with "
+                             "%s was re-plugged. "
+                             "Re-starting operations on this adapter.\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               /* All ports should be marked as ready to run again */
+               zfcp_erp_modify_adapter_status(adapter,
+                                              ZFCP_STATUS_COMMON_RUNNING,
+                                              ZFCP_SET);
+               zfcp_erp_adapter_reopen(adapter,
+                                       ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
+                                       | ZFCP_STATUS_COMMON_ERP_FAILED);
+               zfcp_cmd_dbf_event_fsf("reestque", fsf_req,
+                                      &fsf_req->qtcb->prefix.prot_status_qual,
+                                      sizeof (union fsf_prot_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PROT_ERROR_STATE:
+               ZFCP_LOG_FLAGS(0, "FSF_PROT_ERROR_STATE\n");
+               ZFCP_LOG_NORMAL("error: The adapter %s "
+                               "has entered the error state. "
+                               "Restarting all operations on this "
+                               "adapter.\n",
+                               zfcp_get_busid_by_adapter(adapter));
+               debug_text_event(adapter->erp_dbf, 0, "prot_err_sta");
+               /* restart operation on this adapter */
+               zfcp_erp_adapter_reopen(adapter, 0);
+               zfcp_cmd_dbf_event_fsf("proterrs", fsf_req,
+                                      &fsf_req->qtcb->prefix.prot_status_qual,
+                                      sizeof (union fsf_prot_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: Transfer protocol status information "
+                               "provided by the adapter %s "
+                               "is not compatible with the device driver. "
+                               "Stopping all operations on this adapter. "
+                               "(debug info 0x%x).\n",
+                               zfcp_get_busid_by_adapter(adapter),
+                               fsf_req->qtcb->prefix.prot_status);
+               ZFCP_LOG_NORMAL("fsf_req=0x%lx, qtcb=0x%lx (0x%lx, 0x%lx)\n",
+                               (unsigned long) fsf_req,
+                               (unsigned long) fsf_req->qtcb,
+                               ((unsigned long) fsf_req) & 0xFFFFFF00,
+                               (unsigned
+                                long) ((struct zfcp_fsf_req
+                                        *) (((unsigned long) fsf_req) &
+                                            0xFFFFFF00))->qtcb);
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) (((unsigned long) fsf_req) & 0xFFFFFF00),
+                             sizeof (struct zfcp_fsf_req));
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *) fsf_req->qtcb,
+                             ZFCP_QTCB_SIZE);
+               debug_text_event(adapter->erp_dbf, 0, "prot_inval:");
+               debug_exception(adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->prefix.prot_status,
+                               sizeof (u32));
+               zfcp_erp_adapter_shutdown(adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+       }
+
+ skip_protstatus:
+       /*
+        * always call specific handlers to give them a chance to do
+        * something meaningful even in error cases
+        */
+       zfcp_fsf_fsfstatus_eval(fsf_req);
+       return retval;
+}
+
+/*
+ * function:   zfcp_fsf_fsfstatus_eval
+ *
+ * purpose:    evaluates FSF status of completed FSF request
+ *             and acts accordingly
+ *
+ * returns:
+ */
+static int
+zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF Status */
+       switch (fsf_req->qtcb->header.fsf_status) {
+       case FSF_UNKNOWN_COMMAND:
+               ZFCP_LOG_FLAGS(0, "FSF_UNKNOWN_COMMAND\n");
+               ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
+                               "not known by the adapter %s "
+                               "Stopping all operations on this adapter. "
+                               "(debug info 0x%x).\n",
+                               zfcp_get_busid_by_adapter(fsf_req->adapter),
+                               fsf_req->qtcb->header.fsf_command);
+               debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                    "fsf_s_unknown");
+               zfcp_erp_adapter_shutdown(fsf_req->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("unknownc", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_FCP_RSP_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_FCP_RSP_AVAILABLE\n");
+               ZFCP_LOG_DEBUG("FCP Sense data will be presented to the "
+                              "SCSI stack.\n");
+               debug_text_event(fsf_req->adapter->erp_dbf, 3, "fsf_s_rsp");
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_astatus");
+               zfcp_fsf_fsfstatus_qual_eval(fsf_req);
+               break;
+
+       default:
+               break;
+       }
+
+ skip_fsfstatus:
+       /*
+        * always call specific handlers to give them a chance to do
+        * something meaningful even in error cases
+        */
+       zfcp_fsf_req_dispatch(fsf_req);
+
+       return retval;
+}
+
+/*
+ * function:   zfcp_fsf_fsfstatus_qual_eval
+ *
+ * purpose:    evaluates FSF status-qualifier of completed FSF request
+ *             and acts accordingly
+ *
+ * returns:
+ */
+static int
+zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+
+       switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+       case FSF_SQ_FCP_RSP_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_SQ_FCP_RSP_AVAILABLE\n");
+               debug_text_event(fsf_req->adapter->erp_dbf, 4, "fsf_sq_rsp");
+               break;
+       case FSF_SQ_RETRY_IF_POSSIBLE:
+               ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n");
+               /* The SCSI-stack may now issue retries or escalate */
+               debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_sq_retry");
+               zfcp_cmd_dbf_event_fsf("sqretry", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+       case FSF_SQ_COMMAND_ABORTED:
+               ZFCP_LOG_FLAGS(2, "FSF_SQ_COMMAND_ABORTED\n");
+               /* Carry the aborted state on to upper layer */
+               debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_sq_abort");
+               zfcp_cmd_dbf_event_fsf("sqabort", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+       case FSF_SQ_NO_RECOM:
+               ZFCP_LOG_FLAGS(0, "FSF_SQ_NO_RECOM\n");
+               debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                    "fsf_sq_no_rec");
+               ZFCP_LOG_NORMAL("bug: No recommendation could be given for a"
+                               "problem on the adapter %s "
+                               "Stopping all operations on this adapter. ",
+                               zfcp_get_busid_by_adapter(fsf_req->adapter));
+               zfcp_erp_adapter_shutdown(fsf_req->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("sqnrecom", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+       case FSF_SQ_ULP_PROGRAMMING_ERROR:
+               ZFCP_LOG_FLAGS(0, "FSF_SQ_ULP_PROGRAMMING_ERROR\n");
+               ZFCP_LOG_NORMAL("bug: An illegal amount of data was attempted "
+                               "to be sent to the adapter %s "
+                               "Stopping all operations on this adapter. ",
+                               zfcp_get_busid_by_adapter(fsf_req->adapter));
+               debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                    "fsf_sq_ulp_err");
+               zfcp_erp_adapter_shutdown(fsf_req->adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+       case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+       case FSF_SQ_NO_RETRY_POSSIBLE:
+       case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+               /* dealt with in the respective functions */
+               break;
+       default:
+               ZFCP_LOG_NORMAL("bug: Additional status info could "
+                               "not be interpreted properly.\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status_qual.word[0],
+                               sizeof (u32));
+               zfcp_cmd_dbf_event_fsf("squndef", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+       }
+
+       return retval;
+}
+
+/*
+ * function:   zfcp_fsf_req_dispatch
+ *
+ * purpose:    calls the appropriate command specific handler
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               ZFCP_LOG_TRACE("fsf_req=0x%lx, QTCB=0x%lx\n",
+                              (unsigned long) fsf_req,
+                              (unsigned long) (fsf_req->qtcb));
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
+                             (char *) fsf_req->qtcb, ZFCP_QTCB_SIZE);
+       }
+
+       switch (fsf_req->fsf_command) {
+
+       case FSF_QTCB_FCP_CMND:
+               ZFCP_LOG_FLAGS(3, "FSF_QTCB_FCP_CMND\n");
+               zfcp_fsf_send_fcp_command_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_ABORT_FCP_CMND:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_ABORT_FCP_CMND\n");
+               zfcp_fsf_abort_fcp_command_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_SEND_GENERIC:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_GENERIC\n");
+               zfcp_fsf_send_generic_handler(fsf_req);
+               zfcp_erp_fsf_req_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_OPEN_PORT_WITH_DID:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_PORT_WITH_DID\n");
+               zfcp_fsf_open_port_handler(fsf_req);
+               zfcp_erp_fsf_req_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_OPEN_LUN:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_LUN\n");
+               zfcp_fsf_open_unit_handler(fsf_req);
+               zfcp_erp_fsf_req_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_CLOSE_LUN:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_LUN\n");
+               zfcp_fsf_close_unit_handler(fsf_req);
+               zfcp_erp_fsf_req_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_CLOSE_PORT:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PORT\n");
+               zfcp_fsf_close_port_handler(fsf_req);
+               zfcp_erp_fsf_req_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_CLOSE_PHYSICAL_PORT:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PHYSICAL_PORT\n");
+               zfcp_fsf_close_physical_port_handler(fsf_req);
+               zfcp_erp_fsf_req_handler(fsf_req);
+               break;
+
+       case FSF_QTCB_EXCHANGE_CONFIG_DATA:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_CONFIG_DATA\n");
+               zfcp_fsf_exchange_config_data_handler(fsf_req);
+               zfcp_erp_fsf_req_handler(fsf_req);
+               break;
+
+       default:
+               ZFCP_LOG_FLAGS(2, "FSF_QTCB_UNKNOWN\n");
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
+                               "not supported by the adapter %s "
+                               "(debug info 0x%lx 0x%x).\n",
+                               zfcp_get_busid_by_adapter(fsf_req->adapter),
+                               (unsigned long) fsf_req, fsf_req->fsf_command);
+               if (fsf_req->fsf_command != fsf_req->qtcb->header.fsf_command)
+                       ZFCP_LOG_NORMAL
+                           ("bug: Command issued by the device driver differs "
+                            "from the command returned by the adapter %s "
+                            "(debug info 0x%x, 0x%x).\n",
+                            zfcp_get_busid_by_adapter(fsf_req->adapter),
+                            fsf_req->fsf_command,
+                            fsf_req->qtcb->header.fsf_command);
+       }
+
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_status_read
+ *
+ * purpose:    initiates a Status Read command at the specified adapter
+ *
+ * returns:
+ */
+int
+zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags)
+{
+       struct zfcp_fsf_req *fsf_req;
+       struct fsf_status_read_buffer *status_buffer;
+       unsigned long lock_flags;
+       volatile struct qdio_buffer_element *buffere;
+       struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
+       int retval = 0;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(adapter,
+                                    FSF_QTCB_UNSOLICITED_STATUS,
+                                    &lock_flags, req_flags, &fsf_req);
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+                             "unsolicited status buffer for "
+                             "the adapter %s.\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               goto failed_req_create;
+       }
+
+       status_buffer =
+           mempool_alloc(adapter->pool.status_read_buf, GFP_ATOMIC);
+       if (!status_buffer) {
+               ZFCP_LOG_NORMAL("bug: could not get some buffer\n");
+               goto failed_buf;
+       }
+       memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer));
+       fsf_req->data.status_read.buffer = status_buffer;
+
+       /* insert pointer to respective buffer */
+       buffere = req_queue->buffer[fsf_req->sbal_index]->element;
+       buffere[2].addr = (void *) status_buffer;
+       buffere[2].length = sizeof (struct fsf_status_read_buffer);
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(fsf_req, NULL);
+       if (retval) {
+               ZFCP_LOG_DEBUG("error: Could not set-up unsolicited status "
+                              "environment.\n");
+               goto failed_req_send;
+       }
+
+       ZFCP_LOG_TRACE("Status Read request initiated "
+                      "(adapter busid=%s)\n",
+                      zfcp_get_busid_by_adapter(adapter));
+#ifdef ZFCP_DEBUG_REQUESTS
+       debug_text_event(adapter->req_dbf, 1, "unso");
+#endif
+       goto out;
+
+ failed_req_send:
+       mempool_free(status_buffer, adapter->pool.status_read_buf);
+
+ failed_buf:
+       zfcp_fsf_req_free(fsf_req);
+ failed_req_create:
+ out:
+       write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+       return retval;
+}
+
+static int
+zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req)
+{
+       struct fsf_status_read_buffer *status_buffer;
+       struct zfcp_adapter *adapter;
+       struct zfcp_port *port;
+       unsigned long flags;
+
+       status_buffer = fsf_req->data.status_read.buffer;
+       adapter = fsf_req->adapter;
+
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       list_for_each_entry(port, &adapter->port_list_head, list)
+           if (port->d_id == (status_buffer->d_id & ZFCP_DID_MASK))
+               break;
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+       if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) {
+               ZFCP_LOG_NORMAL("bug: Re-open port indication received for the "
+                               "non-existing port with DID 0x%3.3x, on "
+                               "the adapter %s. Ignored.\n",
+                               status_buffer->d_id & ZFCP_DID_MASK,
+                               zfcp_get_busid_by_adapter(adapter));
+               goto out;
+       }
+
+       switch (status_buffer->status_subtype) {
+
+       case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
+               ZFCP_LOG_FLAGS(2, "FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT\n");
+               debug_text_event(adapter->erp_dbf, 3, "unsol_pc_phys:");
+               zfcp_erp_port_reopen(port, 0);
+               break;
+
+       case FSF_STATUS_READ_SUB_ERROR_PORT:
+               ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_SUB_ERROR_PORT\n");
+               debug_text_event(adapter->erp_dbf, 1, "unsol_pc_err:");
+               zfcp_erp_port_shutdown(port, 0);
+               break;
+
+       default:
+               debug_text_event(adapter->erp_dbf, 0, "unsol_unk_sub:");
+               debug_exception(adapter->erp_dbf, 0,
+                               &status_buffer->status_subtype, sizeof (u32));
+               ZFCP_LOG_NORMAL("bug: Undefined status subtype received "
+                               "for a re-open indication on the port with "
+                               "DID 0x%3.3x, on the adapter "
+                               "%s. Ignored. (debug info 0x%x)\n",
+                               status_buffer->d_id,
+                               zfcp_get_busid_by_adapter(adapter),
+                               status_buffer->status_subtype);
+       }
+ out:
+       return 0;
+}
+
+/*
+ * function:    zfcp_fsf_status_read_handler
+ *
+ * purpose:    is called for finished Open Port command
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+       struct fsf_status_read_buffer *status_buffer =
+           fsf_req->data.status_read.buffer;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
+               mempool_free(status_buffer, adapter->pool.status_read_buf);
+               zfcp_fsf_req_cleanup(fsf_req);
+               goto out;
+       }
+
+       switch (status_buffer->status_type) {
+
+       case FSF_STATUS_READ_PORT_CLOSED:
+               ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_PORT_CLOSED\n");
+               debug_text_event(adapter->erp_dbf, 3, "unsol_pclosed:");
+               debug_event(adapter->erp_dbf, 3,
+                           &status_buffer->d_id, sizeof (u32));
+               zfcp_fsf_status_read_port_closed(fsf_req);
+               break;
+
+       case FSF_STATUS_READ_INCOMING_ELS:
+               ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_INCOMING_ELS\n");
+               debug_text_event(adapter->erp_dbf, 3, "unsol_els:");
+               zfcp_fsf_incoming_els(fsf_req);
+               break;
+
+       case FSF_STATUS_READ_BIT_ERROR_THRESHOLD:
+               ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_BIT_ERROR_THRESHOLD\n");
+               debug_text_event(adapter->erp_dbf, 3, "unsol_bit_err:");
+               ZFCP_LOG_NORMAL("Bit error threshold data received:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) status_buffer,
+                             sizeof (struct fsf_status_read_buffer));
+               break;
+
+       case FSF_STATUS_READ_LINK_DOWN:
+               ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_LINK_DOWN\n");
+               /* Unneccessary, ignoring.... */
+               break;
+
+       case FSF_STATUS_READ_LINK_UP:
+               ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_LINK_UP\n");
+               debug_text_event(adapter->erp_dbf, 2, "unsol_link_up:");
+               ZFCP_LOG_INFO("The local link to the adapter %s "
+                             "was re-plugged. "
+                             "Re-starting operations on this adapter..\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               /* All ports should be marked as ready to run again */
+               zfcp_erp_modify_adapter_status(adapter,
+                                              ZFCP_STATUS_COMMON_RUNNING,
+                                              ZFCP_SET);
+               zfcp_erp_adapter_reopen(adapter,
+                                       ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
+                                       | ZFCP_STATUS_COMMON_ERP_FAILED);
+               break;
+
+       default:
+               debug_text_event(adapter->erp_dbf, 0, "unsol_unknown:");
+               debug_exception(adapter->erp_dbf, 0,
+                               &status_buffer->status_type, sizeof (u32));
+               ZFCP_LOG_NORMAL("bug: An unsolicited status packet of unknown "
+                               "type was received by the zfcp-driver "
+                               "(debug info 0x%x)\n",
+                               status_buffer->status_type);
+               ZFCP_LOG_DEBUG("Dump of status_read_buffer 0x%lx:\n",
+                              (unsigned long) status_buffer);
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) status_buffer,
+                             sizeof (struct fsf_status_read_buffer));
+               break;
+       }
+       mempool_free(status_buffer, adapter->pool.status_read_buf);
+       zfcp_fsf_req_cleanup(fsf_req);
+       /*
+        * recycle buffer and start new request repeat until outbound
+        * queue is empty or adapter shutdown is requested
+        */
+       /*
+        * FIXME(qdio):
+        * we may wait in the req_create for 5s during shutdown, so
+        * qdio_cleanup will have to wait at least that long before returning
+        * with failure to allow us a proper cleanup under all circumstances
+        */
+       /*
+        * FIXME:
+        * allocation failure possible? (Is this code needed?)
+        */
+       retval = zfcp_fsf_status_read(adapter, 0);
+       if (retval < 0) {
+               ZFCP_LOG_INFO("Outbound queue busy. "
+                             "Could not create use an "
+                             "unsolicited status read request for "
+                             "the adapter %s.\n",
+                             zfcp_get_busid_by_adapter(adapter));
+               /* temporary fix to avoid status read buffer shortage */
+               adapter->status_read_failed++;
+               if ((ZFCP_STATUS_READS_RECOM - adapter->status_read_failed)
+                   < ZFCP_STATUS_READ_FAILED_THRESHOLD) {
+                       ZFCP_LOG_INFO("restart adapter due to status read "
+                                     "buffer shortage (busid %s)\n",
+                                     zfcp_get_busid_by_adapter(adapter));
+                       zfcp_erp_adapter_reopen(adapter, 0);
+               }
+       }
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_abort_fcp_command
+ *
+ * purpose:    tells FSF to abort a running SCSI command
+ *
+ * returns:    address of initiated FSF request
+ *             NULL - request could not be initiated
+ *
+ * FIXME(design): should be watched by a timeout !!! 
+ * FIXME(design) shouldn't this be modified to return an int
+ *               also...don't know how though
+ */
+struct zfcp_fsf_req *
+zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
+                          struct zfcp_adapter *adapter,
+                          struct zfcp_unit *unit, int req_flags)
+{
+       struct zfcp_fsf_req *new_fsf_req = NULL;
+       int retval = 0;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
+                                    &lock_flags, req_flags, &new_fsf_req);
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+                             "abort command request on the device with "
+                             "the FCP-LUN 0x%Lx connected to "
+                             "the port with WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_adapter(adapter));
+               goto out;
+       }
+
+       new_fsf_req->data.abort_fcp_command.unit = unit;
+
+       /* set handles of unit and its parent port in QTCB */
+       new_fsf_req->qtcb->header.lun_handle = unit->handle;
+       new_fsf_req->qtcb->header.port_handle = unit->port->handle;
+
+       /* set handle of request which should be aborted */
+       new_fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id;
+
+       /* start QDIO request for this FSF request */
+
+       zfcp_fsf_start_scsi_er_timer(adapter);
+       retval = zfcp_fsf_req_send(new_fsf_req, NULL);
+       if (retval) {
+               del_timer(&adapter->scsi_er_timer);
+               ZFCP_LOG_INFO("error: Could not send an abort command request "
+                             "for a command on the adapter %s, "
+                             "port WWPN 0x%Lx and unit LUN 0x%Lx\n",
+                             zfcp_get_busid_by_adapter(adapter),
+                             unit->port->wwpn, unit->fcp_lun);
+               zfcp_fsf_req_free(new_fsf_req);
+               new_fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_DEBUG("Abort FCP Command request initiated "
+                      "(adapter busid=%s, port d_id=0x%x, "
+                      "unit fcp_lun=0x%Lx, old_req_id=0x%lx)\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      (unsigned int) unit->port->d_id,
+                      unit->fcp_lun, old_req_id);
+ out:
+       write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+       return new_fsf_req;
+}
+
+/*
+ * function:    zfcp_fsf_abort_fcp_command_handler
+ *
+ * purpose:    is called for finished Abort FCP Command request
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_unit *unit = new_fsf_req->data.abort_fcp_command.unit;
+       unsigned char status_qual =
+           new_fsf_req->qtcb->header.fsf_status_qual.word[0];
+
+       del_timer(&new_fsf_req->adapter->scsi_er_timer);
+
+       if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (new_fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_HANDLE_NOT_VALID:
+               if (status_qual >> 4 != status_qual % 0xf) {
+                       ZFCP_LOG_FLAGS(2, "FSF_PORT_HANDLE_NOT_VALID\n");
+                       debug_text_event(new_fsf_req->adapter->erp_dbf, 3,
+                                        "fsf_s_phand_nv0");
+                       /*
+                        * In this case a command that was sent prior to a port
+                        * reopen was aborted (handles are different). This is
+                        * fine.
+                        */
+               } else {
+                       ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
+                       ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
+                                     "for the port with WWPN 0x%Lx connected "
+                                     "to the adapter %s is not valid. This "
+                                     "may happen occasionally.\n",
+                                     unit->port->handle,
+                                     unit->port->wwpn,
+                                     zfcp_get_busid_by_unit(unit));
+                       ZFCP_LOG_INFO("status qualifier:\n");
+                       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
+                                     (char *) &new_fsf_req->qtcb->header.
+                                     fsf_status_qual,
+                                     sizeof (union fsf_status_qual));
+                       /* Let's hope this sorts out the mess */
+                       debug_text_event(new_fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_s_phand_nv1");
+                       zfcp_erp_adapter_reopen(unit->port->adapter, 0);
+                       new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               }
+               break;
+
+       case FSF_LUN_HANDLE_NOT_VALID:
+               if (status_qual >> 4 != status_qual % 0xf) {
+                       /* 2 */
+                       ZFCP_LOG_FLAGS(0, "FSF_LUN_HANDLE_NOT_VALID\n");
+                       debug_text_event(new_fsf_req->adapter->erp_dbf, 3,
+                                        "fsf_s_lhand_nv0");
+                       /*
+                        * In this case a command that was sent prior to a unit
+                        * reopen was aborted (handles are different).
+                        * This is fine.
+                        */
+               } else {
+                       ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
+                       ZFCP_LOG_INFO
+                           ("Warning: Temporary LUN identifier (handle) 0x%x "
+                            "of the logical unit with FCP-LUN 0x%Lx at "
+                            "the remote port with WWPN 0x%Lx connected "
+                            "to the adapter %s is "
+                            "not valid. This may happen in rare cases."
+                            "Trying to re-establish link.\n",
+                            unit->handle,
+                            unit->fcp_lun,
+                            unit->port->wwpn,
+                            zfcp_get_busid_by_unit(unit));
+                       ZFCP_LOG_DEBUG("Status qualifier data:\n");
+                       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                                     (char *) &new_fsf_req->qtcb->header.
+                                     fsf_status_qual,
+                                     sizeof (union fsf_status_qual));
+                       /* Let's hope this sorts out the mess */
+                       debug_text_event(new_fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_s_lhand_nv1");
+                       zfcp_erp_port_reopen(unit->port, 0);
+                       new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               }
+               break;
+
+       case FSF_FCP_COMMAND_DOES_NOT_EXIST:
+               ZFCP_LOG_FLAGS(2, "FSF_FCP_COMMAND_DOES_NOT_EXIST\n");
+               retval = 0;
+#ifdef ZFCP_DEBUG_REQUESTS
+               /*
+                * debug feature area which records
+                * fsf request sequence numbers
+                */
+               debug_text_event(new_fsf_req->adapter->req_dbf, 3, "no_exist");
+               debug_event(new_fsf_req->adapter->req_dbf, 3,
+                           &new_fsf_req->qtcb->bottom.support.req_handle,
+                           sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+               debug_text_event(new_fsf_req->adapter->erp_dbf, 3,
+                                "fsf_s_no_exist");
+               new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
+               break;
+
+       case FSF_PORT_BOXED:
+               /* 2 */
+               ZFCP_LOG_FLAGS(0, "FSF_PORT_BOXED\n");
+               ZFCP_LOG_DEBUG("The remote port "
+                              "with WWPN 0x%Lx on the adapter %s "
+                              "needs to be reopened\n",
+                              unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+               debug_text_event(new_fsf_req->adapter->erp_dbf, 2,
+                                "fsf_s_pboxed");
+               zfcp_erp_port_reopen(unit->port, 0);
+               new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
+                   | ZFCP_STATUS_FSFREQ_RETRY;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               /* 2 */
+               ZFCP_LOG_FLAGS(0, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               switch (new_fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+               case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
+                       debug_text_event(new_fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ltest");
+                       /* reopening link to port */
+                       zfcp_erp_port_reopen(unit->port, 0);
+                       new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
+                       /* SCSI stack will escalate */
+                       debug_text_event(new_fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ulp");
+                       new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               default:
+                       ZFCP_LOG_NORMAL
+                           ("bug: Wrong status qualifier 0x%x arrived.\n",
+                            new_fsf_req->qtcb->header.fsf_status_qual.word[0]);
+                       debug_text_event(new_fsf_req->adapter->erp_dbf, 0,
+                                        "fsf_sq_inval:");
+                       debug_exception(new_fsf_req->adapter->erp_dbf, 0,
+                                       &new_fsf_req->qtcb->header.
+                                       fsf_status_qual.word[0], sizeof (u32));
+                       break;
+               }
+               break;
+
+       case FSF_GOOD:
+               /* 3 */
+               ZFCP_LOG_FLAGS(0, "FSF_GOOD\n");
+               retval = 0;
+               new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+                               "(debug info 0x%x)\n",
+                               new_fsf_req->qtcb->header.fsf_status);
+               debug_text_event(new_fsf_req->adapter->erp_dbf, 0,
+                                "fsf_s_inval:");
+               debug_exception(new_fsf_req->adapter->erp_dbf, 0,
+                               &new_fsf_req->qtcb->header.fsf_status,
+                               sizeof (u32));
+               break;
+       }
+ skip_fsfstatus:
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_send_generic
+ *
+ * purpose:    sends a FC request according to FC-GS-3
+ *
+ * returns:    address of initiated FSF request
+ *             NULL - request could not be initiated 
+ */
+int
+zfcp_fsf_send_generic(struct zfcp_fsf_req *fsf_req, unsigned char timeout,
+                     unsigned long *lock_flags, struct timer_list *timer)
+{
+       int retval = 0;
+       struct qdio_buffer *buffer;
+       volatile struct qdio_buffer_element *buffer_element = NULL;
+       struct zfcp_port *port = fsf_req->data.send_generic.port;
+       struct zfcp_adapter *adapter = port->adapter;
+
+       /* put buffers to the 2 SBALEs after the QTCB */
+       buffer = (adapter->request_queue.buffer[fsf_req->sbal_index]);
+       buffer_element = &(buffer->element[2]);
+       buffer_element->addr = fsf_req->data.send_generic.outbuf;
+       buffer_element->length = fsf_req->data.send_generic.outbuf_length;
+       buffer_element++;
+       buffer_element->addr = fsf_req->data.send_generic.inbuf;
+       buffer_element->length = fsf_req->data.send_generic.inbuf_length;
+       buffer_element->flags |= SBAL_FLAGS_LAST_ENTRY;
+
+       /* settings in QTCB */
+       fsf_req->qtcb->header.port_handle = port->handle;
+       fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class;
+       fsf_req->qtcb->bottom.support.timeout = timeout;
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(fsf_req, timer);
+       if (retval) {
+               ZFCP_LOG_DEBUG("error: Out of resources. could not send a "
+                              "generic services "
+                              "command via the adapter %s, port "
+                              "WWPN 0x%Lx\n",
+                              zfcp_get_busid_by_adapter(adapter), port->wwpn);
+               /*
+                * fsf_req structure will be cleaned up by higher layer handler
+                */
+               goto out;
+       }
+
+       ZFCP_LOG_DEBUG("Send Generic request initiated "
+                      "(adapter busido=%s, port d_id=0x%x)\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      (unsigned int) port->d_id);
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_send_generic_handler
+ *
+ * purpose:    is called for finished Send Generic request
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_send_generic_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_port *port = fsf_req->data.send_generic.port;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_DEBUG("Temporary port identifier (handle) 0x%x "
+                              "for the port with WWPN 0x%Lx connected to "
+                              "the adapter %s is "
+                              "not valid. This may happen occasionally.\n",
+                              port->handle,
+                              port->wwpn, zfcp_get_busid_by_port(port));
+               ZFCP_LOG_INFO("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_phandle_nv");
+               zfcp_erp_adapter_reopen(port->adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+               ZFCP_LOG_FLAGS(0, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
+               if (fsf_req->adapter->fc_service_class <= 3) {
+                       ZFCP_LOG_NORMAL("error: The adapter %s does "
+                                       "not support fibre-channel class %d.\n",
+                                       zfcp_get_busid_by_port(port),
+                                       fsf_req->adapter->fc_service_class);
+               } else {
+                       ZFCP_LOG_NORMAL
+                           ("bug: The fibre channel class at the adapter "
+                            "%s is invalid. " "(debug info %d)\n",
+                            zfcp_get_busid_by_port(port),
+                            fsf_req->adapter->fc_service_class);
+               }
+               /* stop operation for this adapter */
+               debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                    "fsf_s_class_nsup");
+               zfcp_erp_adapter_shutdown(port->adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_GENERIC_COMMAND_REJECTED:
+               ZFCP_LOG_FLAGS(1, "FSF_GENERIC_COMMAND_REJECTED\n");
+               ZFCP_LOG_INFO("warning: The port with WWPN 0x%Lx connected to "
+                             "the adapter %s is"
+                             "rejected a generic services command.\n",
+                             port->wwpn, zfcp_get_busid_by_port(port));
+               ZFCP_LOG_INFO("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_gcom_rej");
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_REQUEST_BUF_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_REQUEST_BUF_NOT_VALID\n");
+               ZFCP_LOG_NORMAL("error: The port with WWPN 0x%Lx connected to "
+                               "the adapter %s is"
+                               "rejected a generic services command "
+                               "due to invalid request buffer.\n",
+                               port->wwpn, zfcp_get_busid_by_port(port));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_reqiv");
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_RESPONSE_BUF_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_RESPONSE_BUF_NOT_VALID\n");
+               ZFCP_LOG_NORMAL("error: The port with WWPN 0x%Lx connected to "
+                               "the adapter %s is"
+                               "rejected a generic services command "
+                               "due to invalid response buffer.\n",
+                               port->wwpn, zfcp_get_busid_by_port(port));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_resiv");
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PORT_BOXED:
+               ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
+               ZFCP_LOG_DEBUG("The remote port "
+                              "with WWPN 0x%Lx on the adapter %s "
+                              "needs to be reopened\n",
+                              port->wwpn, zfcp_get_busid_by_port(port));
+               debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed");
+               zfcp_erp_port_reopen(port, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
+                   | ZFCP_STATUS_FSFREQ_RETRY;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+               case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
+                       /* reopening link to port */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ltest");
+                       zfcp_erp_port_forced_reopen(port, 0);
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+                       /* ERP strategy will escalate */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ulp");
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+
+               default:
+                       ZFCP_LOG_NORMAL
+                           ("bug: Wrong status qualifier 0x%x arrived.\n",
+                            fsf_req->qtcb->header.fsf_status_qual.word[0]);
+                       break;
+               }
+               break;
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
+               retval = 0;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+                               "(debug info 0x%x)\n",
+                               fsf_req->qtcb->header.fsf_status);
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status_qual.word[0],
+                               sizeof (u32));
+               break;
+       }
+ skip_fsfstatus:
+       /* callback */
+       (fsf_req->data.send_generic.handler)(fsf_req);
+       return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:    address of initiated FSF request
+ *             NULL - request could not be initiated
+ */
+int
+zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(erp_action->adapter,
+                                    FSF_QTCB_EXCHANGE_CONFIG_DATA,
+                                    &lock_flags,
+                                    ZFCP_REQ_AUTO_CLEANUP,
+                                    &(erp_action->fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+                             "exchange configuration data request for"
+                             "the adapter %s.\n",
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto out;
+       }
+
+       erp_action->fsf_req->erp_action = erp_action;
+       /* no information from us to adapter, set nothing */
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+       if (retval) {
+               ZFCP_LOG_INFO
+                   ("error: Could not send an exchange configuration data "
+                    "command on the adapter %s\n",
+                    zfcp_get_busid_by_adapter(erp_action->adapter));
+               zfcp_fsf_req_free(erp_action->fsf_req);
+               erp_action->fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_DEBUG("Exchange Configuration Data request initiated "
+                      "(adapter busid=%s)\n",
+                      zfcp_get_busid_by_adapter(erp_action->adapter));
+
+ out:
+       write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
+                               lock_flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_exchange_config_data_handler
+ *
+ * purpose:     is called for finished Exchange Configuration Data command
+ *
+ * returns:
+ */
+static int
+zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) {
+       int retval = -EIO;
+       struct fsf_qtcb_bottom_config *bottom;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* don't set any value, stay with the old (unitialized) ones */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
+               bottom = &fsf_req->qtcb->bottom.config;
+               /* only log QTCB versions for now */
+               ZFCP_LOG_DEBUG("low QTCB version 0x%x of FSF, "
+                              "high QTCB version 0x%x of FSF, \n",
+                              bottom->low_qtcb_version,
+                              bottom->high_qtcb_version);
+               adapter->wwnn = bottom->nport_serv_param.wwnn;
+               adapter->wwpn = bottom->nport_serv_param.wwpn;
+               adapter->s_id = bottom->s_id & ZFCP_DID_MASK;
+               adapter->hydra_version = bottom->adapter_type;
+               adapter->fsf_lic_version = bottom->lic_version;
+               adapter->fc_topology = bottom->fc_topology;
+               adapter->fc_link_speed = bottom->fc_link_speed;
+               ZFCP_LOG_INFO("The adapter %s reported "
+                             "the following characteristics:\n"
+                             "WWNN 0x%16.16Lx, "
+                             "WWPN 0x%16.16Lx, "
+                             "S_ID 0x%6.6x,\n"
+                             "adapter version 0x%x, "
+                             "LIC version 0x%x, "
+                             "FC link speed %d Gb/s\n",
+                             zfcp_get_busid_by_adapter(adapter),
+                             adapter->wwnn,
+                             adapter->wwpn,
+                             (unsigned int) adapter->s_id,
+                             adapter->hydra_version,
+                             adapter->fsf_lic_version,
+                             adapter->fc_link_speed);
+               if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) {
+                       ZFCP_LOG_NORMAL("error: the adapter %s "
+                                       "only supports newer control block "
+                                       "versions in comparison to this device "
+                                       "driver (try updated device driver)\n",
+                                       zfcp_get_busid_by_adapter(adapter));
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "low_qtcb_ver");
+                       zfcp_erp_adapter_shutdown(adapter, 0);
+                       goto skip_fsfstatus;
+               }
+               if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) {
+                       ZFCP_LOG_NORMAL("error: the adapter %s "
+                                       "only supports older control block "
+                                       "versions than this device driver uses"
+                                       "(consider a microcode upgrade)\n",
+                                       zfcp_get_busid_by_adapter(adapter));
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "high_qtcb_ver");
+                       zfcp_erp_adapter_shutdown(adapter, 0);
+                       goto skip_fsfstatus;
+               }
+               switch (adapter->fc_topology) {
+               case FSF_TOPO_P2P:
+                       ZFCP_LOG_FLAGS(1, "FSF_TOPO_P2P\n");
+                       ZFCP_LOG_NORMAL("error: Point-to-point fibre-channel "
+                                       "configuration detected "
+                                       "at the adapter %s, not "
+                                       "supported, shutting down adapter\n",
+                                       zfcp_get_busid_by_adapter(adapter));
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "top-p-to-p");
+                       zfcp_erp_adapter_shutdown(adapter, 0);
+                       goto skip_fsfstatus;
+               case FSF_TOPO_AL:
+                       ZFCP_LOG_FLAGS(1, "FSF_TOPO_AL\n");
+                       ZFCP_LOG_NORMAL("error: Arbitrated loop fibre-channel "
+                                       "topology detected "
+                                       "at the adapter %s, not "
+                                       "supported, shutting down adapter\n",
+                                       zfcp_get_busid_by_adapter(adapter));
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "top-al");
+                       zfcp_erp_adapter_shutdown(adapter, 0);
+                       goto skip_fsfstatus;
+               case FSF_TOPO_FABRIC:
+                       ZFCP_LOG_FLAGS(1, "FSF_TOPO_FABRIC\n");
+                       ZFCP_LOG_INFO("Switched fabric fibre-channel "
+                                     "network detected "
+                                     "at the adapter %s.\n",
+                                     zfcp_get_busid_by_adapter(adapter));
+                       break;
+               default:
+                       ZFCP_LOG_NORMAL("bug: The fibre-channel topology "
+                                       "reported by the exchange "
+                                       "configuration command for "
+                                       "the adapter %s is not "
+                                       "of a type known to the zfcp "
+                                       "driver, shutting down adapter\n",
+                                       zfcp_get_busid_by_adapter(adapter));
+                       debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                            "unknown-topo");
+                       zfcp_erp_adapter_shutdown(adapter, 0);
+                       goto skip_fsfstatus;
+               }
+               if (bottom->max_qtcb_size < ZFCP_QTCB_SIZE) {
+                       ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) "
+                                       "allowed by the adapter %s "
+                                       "is lower than the minimum "
+                                       "required by the driver (%ld bytes).\n",
+                                       bottom->max_qtcb_size,
+                                       zfcp_get_busid_by_adapter(adapter),
+                                       ZFCP_QTCB_SIZE);
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "qtcb-size");
+                       debug_event(fsf_req->adapter->erp_dbf, 0,
+                                   &bottom->max_qtcb_size, sizeof (u32));
+                       zfcp_erp_adapter_shutdown(adapter, 0);
+                       goto skip_fsfstatus;
+               }
+               atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+                               &adapter->status);
+               retval = 0;
+               break;
+       default:
+               /* retval is -EIO by default */
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf-stat-ng");
+               debug_event(fsf_req->adapter->erp_dbf, 0,
+                           &fsf_req->qtcb->header.fsf_status, sizeof (u32));
+               zfcp_erp_adapter_shutdown(adapter, 0);
+       }
+ skip_fsfstatus:
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_open_port
+ *
+ * purpose:    
+ *
+ * returns:    address of initiated FSF request
+ *             NULL - request could not be initiated 
+ */
+int
+zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(erp_action->adapter,
+                                    FSF_QTCB_OPEN_PORT_WITH_DID,
+                                    &lock_flags,
+                                    ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+                                    &(erp_action->fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+                             "open port request for "
+                             "the port with WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto out;
+       }
+
+       erp_action->fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id;
+       atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
+       erp_action->fsf_req->data.open_port.port = erp_action->port;
+       erp_action->fsf_req->erp_action = erp_action;
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+       if (retval) {
+               ZFCP_LOG_INFO("error: Could not send an "
+                             "open port request for "
+                             "the port with WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               zfcp_fsf_req_free(erp_action->fsf_req);
+               erp_action->fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_DEBUG("Open Port request initiated "
+                      "(adapter busid=%s, port wwpn=0x%Lx)\n",
+                      zfcp_get_busid_by_adapter(erp_action->adapter),
+                      erp_action->port->wwpn);
+ out:
+       write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
+                               lock_flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_open_port_handler
+ *
+ * purpose:    is called for finished Open Port command
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_port *port;
+       struct fsf_plogi *plogi;
+
+       port = fsf_req->data.open_port.port;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* don't change port status in our bookkeeping */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_ALREADY_OPEN:
+               ZFCP_LOG_FLAGS(0, "FSF_PORT_ALREADY_OPEN\n");
+               ZFCP_LOG_NORMAL("bug: The remote port with WWPN=0x%Lx "
+                               "connected to the adapter %s "
+                               "is already open.\n",
+                               port->wwpn, zfcp_get_busid_by_port(port));
+               debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                    "fsf_s_popen");
+               /*
+                * This is a bug, however operation should continue normally
+                * if it is simply ignored
+                */
+               break;
+
+       case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED:
+               ZFCP_LOG_FLAGS(1, "FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED\n");
+               ZFCP_LOG_INFO("error: The FSF adapter is out of resources. "
+                             "The remote port with WWPN=0x%Lx "
+                             "connected to the adapter %s "
+                             "could not be opened. Disabling it.\n",
+                             port->wwpn, zfcp_get_busid_by_port(port));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_max_ports");
+               zfcp_erp_port_failed(port);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+               case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ltest");
+                       /* ERP strategy will escalate */
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+                       /* ERP strategy will escalate */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ulp");
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_NO_RETRY_POSSIBLE:
+                       ZFCP_LOG_FLAGS(0, "FSF_SQ_NO_RETRY_POSSIBLE\n");
+                       ZFCP_LOG_NORMAL("The remote port with WWPN=0x%Lx "
+                                       "connected to the adapter %s "
+                                       "could not be opened. Disabling it.\n",
+                                       port->wwpn,
+                                       zfcp_get_busid_by_port(port));
+                       debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                            "fsf_sq_no_retry");
+                       zfcp_erp_port_failed(port);
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               default:
+                       ZFCP_LOG_NORMAL
+                           ("bug: Wrong status qualifier 0x%x arrived.\n",
+                            fsf_req->qtcb->header.fsf_status_qual.word[0]);
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "fsf_sq_inval:");
+                       debug_exception(
+                               fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status_qual.word[0],
+                               sizeof (u32));
+                       break;
+               }
+               break;
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
+               /* save port handle assigned by FSF */
+               port->handle = fsf_req->qtcb->header.port_handle;
+               ZFCP_LOG_INFO("The remote port (WWPN=0x%Lx) via adapter "
+                             "(busid=%s) was opened, it's "
+                             "port handle is 0x%x\n",
+                             port->wwpn,
+                             zfcp_get_busid_by_port(port),
+                             port->handle);
+               /* mark port as open */
+               atomic_set_mask(ZFCP_STATUS_COMMON_OPEN |
+                               ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
+               retval = 0;
+               /* check whether D_ID has changed during open */
+               plogi = (struct fsf_plogi *) fsf_req->qtcb->bottom.support.els;
+               if (!atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN, &port->status))
+               {
+                       if (fsf_req->qtcb->bottom.support.els1_length <
+                           ((((unsigned long) &plogi->serv_param.wwpn) -
+                             ((unsigned long) plogi)) + sizeof (u64))) {
+                               ZFCP_LOG_INFO(
+                                       "warning: insufficient length of "
+                                       "PLOGI payload (%i)\n",
+                                       fsf_req->qtcb->bottom.support.els1_length);
+                               debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                                "fsf_s_short_plogi:");
+                               /* skip sanity check and assume wwpn is ok */
+                       } else {
+                               if (plogi->serv_param.wwpn != port->wwpn) {
+                                       ZFCP_LOG_INFO("warning: D_ID of port "
+                                                     "with WWPN 0x%Lx changed "
+                                                     "during open\n", port->wwpn);
+                                       debug_text_event(
+                                               fsf_req->adapter->erp_dbf, 0,
+                                               "fsf_s_did_change:");
+                                       atomic_clear_mask(
+                                               ZFCP_STATUS_PORT_DID_DID,
+                                               &port->status);
+                               } else
+                                       port->wwnn = plogi->serv_param.wwnn;
+                       }
+               }
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+                               "(debug info 0x%x)\n",
+                               fsf_req->qtcb->header.fsf_status);
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status,
+                               sizeof (u32));
+               break;
+       }
+
+ skip_fsfstatus:
+       atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &port->status);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_close_port
+ *
+ * purpose:     submit FSF command "close port"
+ *
+ * returns:     address of initiated FSF request
+ *              NULL - request could not be initiated
+ */
+int
+zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(erp_action->adapter,
+                                    FSF_QTCB_CLOSE_PORT,
+                                    &lock_flags,
+                                    ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+                                    &(erp_action->fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create a "
+                             "close port request for WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto out;
+       }
+
+       atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
+       erp_action->fsf_req->data.close_port.port = erp_action->port;
+       erp_action->fsf_req->erp_action = erp_action;
+       erp_action->fsf_req->qtcb->header.port_handle =
+           erp_action->port->handle;
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+       if (retval) {
+               ZFCP_LOG_INFO("error: Could not send a "
+                             "close port request for WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               zfcp_fsf_req_free(erp_action->fsf_req);
+               erp_action->fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_TRACE("Close Port request initiated "
+                      "(adapter busid=%s, port wwpn=0x%Lx)\n",
+                      zfcp_get_busid_by_adapter(erp_action->adapter),
+                      erp_action->port->wwpn);
+ out:
+       write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
+                               lock_flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_close_port_handler
+ *
+ * purpose:     is called for finished Close Port FSF command
+ *
+ * returns:
+ */
+static int
+zfcp_fsf_close_port_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_port *port;
+
+       port = fsf_req->data.close_port.port;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* don't change port status in our bookkeeping */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
+                             "for the port with WWPN 0x%Lx connected to "
+                             "the adapter %s is"
+                             "not valid. This may happen occasionally.\n",
+                             port->handle,
+                             port->wwpn, zfcp_get_busid_by_port(port));
+               ZFCP_LOG_DEBUG("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_phand_nv");
+               zfcp_erp_adapter_reopen(port->adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               /* Note: FSF has actually closed the port in this case.
+                * The status code is just daft. Fingers crossed for a change
+                */
+               retval = 0;
+               break;
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
+               ZFCP_LOG_TRACE("remote port (WWPN=0x%Lx) via adapter "
+                              "(busid=%s) closed, port handle 0x%x\n",
+                              port->wwpn,
+                              zfcp_get_busid_by_port(port),
+                              port->handle);
+               zfcp_erp_modify_port_status(port,
+                                           ZFCP_STATUS_COMMON_OPEN,
+                                           ZFCP_CLEAR);
+               retval = 0;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+                               "(debug info 0x%x)\n",
+                               fsf_req->qtcb->header.fsf_status);
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status,
+                               sizeof (u32));
+               break;
+       }
+
+ skip_fsfstatus:
+       atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &port->status);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_close_physical_port
+ *
+ * purpose:     submit FSF command "close physical port"
+ *
+ * returns:     address of initiated FSF request
+ *              NULL - request could not be initiated
+ */
+int
+zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(erp_action->adapter,
+                                    FSF_QTCB_CLOSE_PHYSICAL_PORT,
+                                    &lock_flags,
+                                    ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+                                    &erp_action->fsf_req);
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create a "
+                             "close physical port request for "
+                             "the port with WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto out;
+       }
+
+       /* mark port as being closed */
+       atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING,
+                       &erp_action->port->status);
+       /* save a pointer to this port */
+       erp_action->fsf_req->data.close_physical_port.port = erp_action->port;
+       /* port to be closeed */
+       erp_action->fsf_req->qtcb->header.port_handle =
+           erp_action->port->handle;
+       erp_action->fsf_req->erp_action = erp_action;
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+       if (retval) {
+               ZFCP_LOG_INFO("error: Could not send an close physical port "
+                             "request for the port with WWPN 0x%Lx connected "
+                             "to the adapter %s.\n",
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               zfcp_fsf_req_free(erp_action->fsf_req);
+               erp_action->fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_TRACE("Close Physical Port request initiated "
+                      "(adapter busid=%s, port wwpn=0x%Lx)\n",
+                      zfcp_get_busid_by_adapter(erp_action->adapter),
+                      erp_action->port->wwpn);
+ out:
+       write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
+                               lock_flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_close_physical_port_handler
+ *
+ * purpose:     is called for finished Close Physical Port FSF command
+ *
+ * returns:
+ */
+static int
+zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_port *port;
+       struct zfcp_unit *unit;
+
+       port = fsf_req->data.close_physical_port.port;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* don't change port status in our bookkeeping */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
+                             "for the port with WWPN 0x%Lx connected to "
+                             "the adapter %s is not valid. This may happen "
+                             "occasionally.\n",
+                             port->handle,
+                             port->wwpn,
+                             zfcp_get_busid_by_port(port));
+               ZFCP_LOG_DEBUG("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_phand_nv");
+               zfcp_erp_adapter_reopen(port->adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PORT_BOXED:
+               ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
+               ZFCP_LOG_DEBUG("The remote port with WWPN 0x%Lx on the adapter "
+                              "%s needs to be reopened but it was attempted "
+                              "to close it physically.\n",
+                              port->wwpn,
+                              zfcp_get_busid_by_port(port));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_pboxed");
+               zfcp_erp_port_reopen(port, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+                       ZFCP_STATUS_FSFREQ_RETRY;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+               case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ltest");
+                       /* This will now be escalated by ERP */
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
+                       /* ERP strategy will escalate */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ulp");
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               default:
+                       ZFCP_LOG_NORMAL
+                           ("bug: Wrong status qualifier 0x%x arrived.\n",
+                            fsf_req->qtcb->header.fsf_status_qual.word[0]);
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "fsf_sq_inval:");
+                       debug_exception(
+                               fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status_qual.word[0],
+                               sizeof (u32));
+                       break;
+               }
+               break;
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
+               ZFCP_LOG_DEBUG("Remote port (WWPN=0x%Lx) via adapter "
+                              "(busid=%s) physically closed, "
+                              "port handle 0x%x\n",
+                              port->wwpn,
+                              zfcp_get_busid_by_port(port), port->handle);
+               /* can't use generic zfcp_erp_modify_port_status because
+                * ZFCP_STATUS_COMMON_OPEN must not be reset for the port
+                */
+               atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
+               list_for_each_entry(unit, &port->unit_list_head, list)
+                   atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
+               retval = 0;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+                               "(debug info 0x%x)\n",
+                               fsf_req->qtcb->header.fsf_status);
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status,
+                               sizeof (u32));
+               break;
+       }
+
+ skip_fsfstatus:
+       atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &port->status);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_open_unit
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * assumptions:        This routine does not check whether the associated
+ *             remote port has already been opened. This should be
+ *             done by calling routines. Otherwise some status
+ *             may be presented by FSF
+ */
+int
+zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(erp_action->adapter,
+                                    FSF_QTCB_OPEN_LUN,
+                                    &lock_flags,
+                                    ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+                                    &(erp_action->fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+                             "open unit request for FCP-LUN 0x%Lx connected "
+                             "to the port with WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             erp_action->unit->fcp_lun,
+                             erp_action->unit->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto out;
+       }
+
+       erp_action->fsf_req->qtcb->header.port_handle =
+           erp_action->port->handle;
+       *(fcp_lun_t *) & (erp_action->fsf_req->qtcb->bottom.support.fcp_lun)
+           = erp_action->unit->fcp_lun;
+       atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
+       erp_action->fsf_req->data.open_unit.unit = erp_action->unit;
+       erp_action->fsf_req->erp_action = erp_action;
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+       if (retval) {
+               ZFCP_LOG_INFO("error: Could not send an open unit request "
+                             "on the adapter %s, port WWPN 0x%Lx for "
+                             "unit LUN 0x%Lx\n",
+                             zfcp_get_busid_by_adapter(erp_action->adapter),
+                             erp_action->port->wwpn,
+                             erp_action->unit->fcp_lun);
+               zfcp_fsf_req_free(erp_action->fsf_req);
+               erp_action->fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_TRACE("Open LUN request initiated (adapter busid=%s, "
+                      "port wwpn=0x%Lx, unit fcp_lun=0x%Lx)\n",
+                      zfcp_get_busid_by_adapter(erp_action->adapter),
+                      erp_action->port->wwpn, erp_action->unit->fcp_lun);
+ out:
+       write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
+                               lock_flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_open_unit_handler
+ *
+ * purpose:    is called for finished Open LUN command
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_unit *unit;
+
+       unit = fsf_req->data.open_unit.unit;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* don't change unit status in our bookkeeping */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
+                             "for the port with WWPN 0x%Lx connected to "
+                             "the adapter %s is"
+                             "not valid. This may happen occasionally.\n",
+                             unit->port->handle,
+                             unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+               ZFCP_LOG_DEBUG("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_ph_nv");
+               zfcp_erp_adapter_reopen(unit->port->adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_LUN_ALREADY_OPEN:
+               ZFCP_LOG_FLAGS(0, "FSF_LUN_ALREADY_OPEN\n");
+               ZFCP_LOG_NORMAL("bug: Attempted to open the logical unit "
+                               "with FCP-LUN 0x%Lx at "
+                               "the remote port with WWPN 0x%Lx connected "
+                               "to the adapter %s twice.\n",
+                               unit->fcp_lun,
+                               unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+               debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                    "fsf_s_uopen");
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PORT_BOXED:
+               ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
+               ZFCP_LOG_DEBUG("The remote port "
+                              "with WWPN 0x%Lx on the adapter %s "
+                              "needs to be reopened\n",
+                              unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+               debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed");
+               zfcp_erp_port_reopen(unit->port, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+                       ZFCP_STATUS_FSFREQ_RETRY;
+               break;
+
+       case FSF_LUN_IN_USE:
+               ZFCP_LOG_FLAGS(0, "FSF_LUN_IN_USE\n");
+               ZFCP_LOG_NORMAL("error: FCP-LUN 0x%Lx at "
+                               "the remote port with WWPN 0x%Lx connected "
+                               "to the adapter %s "
+                               "is already owned by another operating system "
+                               "instance (LPAR or VM guest)\n",
+                               unit->fcp_lun,
+                               unit->port->wwpn,
+                               zfcp_get_busid_by_unit(unit));
+               ZFCP_LOG_NORMAL("Additional sense data is presented:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 2,
+                                "fsf_s_l_in_use");
+               zfcp_erp_unit_failed(unit);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED:
+               ZFCP_LOG_FLAGS(1, "FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED\n");
+               ZFCP_LOG_INFO("error: The adapter ran out of resources. "
+                             "There is no handle (temporary port identifier) "
+                             "available for the unit with FCP-LUN 0x%Lx "
+                             "at the remote port with WWPN 0x%Lx connected "
+                             "to the adapter %s\n",
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_unit(unit));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_max_units");
+               zfcp_erp_unit_failed(unit);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+               case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
+                       /* Re-establish link to port */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ltest");
+                       zfcp_erp_port_reopen(unit->port, 0);
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
+                       /* ERP strategy will escalate */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ulp");
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               default:
+                       ZFCP_LOG_NORMAL
+                           ("bug: Wrong status qualifier 0x%x arrived.\n",
+                            fsf_req->qtcb->header.fsf_status_qual.word[0]);
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "fsf_sq_inval:");
+                       debug_exception(
+                               fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status_qual.word[0],
+                               sizeof (u32));
+               }
+               break;
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
+               /* save LUN handle assigned by FSF */
+               unit->handle = fsf_req->qtcb->header.lun_handle;
+               ZFCP_LOG_TRACE("unit (FCP_LUN=0x%Lx) of remote port "
+                              "(WWPN=0x%Lx) via adapter (busid=%s) opened, "
+                              "port handle 0x%x \n",
+                              unit->fcp_lun,
+                              unit->port->wwpn,
+                              zfcp_get_busid_by_unit(unit),
+                              unit->handle);
+               /* mark unit as open */
+               atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
+               retval = 0;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+                               "(debug info 0x%x)\n",
+                               fsf_req->qtcb->header.fsf_status);
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status,
+                               sizeof (u32));
+               break;
+       }
+
+      skip_fsfstatus:
+       atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &unit->status);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_close_unit
+ *
+ * purpose:
+ *
+ * returns:    address of fsf_req - request successfully initiated
+ *             NULL - 
+ *
+ * assumptions: This routine does not check whether the associated
+ *              remote port/lun has already been opened. This should be
+ *              done by calling routines. Otherwise some status
+ *              may be presented by FSF
+ */
+int
+zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(erp_action->adapter,
+                                    FSF_QTCB_CLOSE_LUN,
+                                    &lock_flags,
+                                    ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+                                    &(erp_action->fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create a "
+                             "close unit request for FCP-LUN 0x%Lx "
+                             "connected to the port with WWPN 0x%Lx connected "
+                             "to the adapter %s.\n",
+                             erp_action->unit->fcp_lun,
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               goto out;
+       }
+
+       erp_action->fsf_req->qtcb->header.port_handle =
+           erp_action->port->handle;
+       erp_action->fsf_req->qtcb->header.lun_handle = erp_action->unit->handle;
+       atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status);
+       erp_action->fsf_req->data.close_unit.unit = erp_action->unit;
+       erp_action->fsf_req->erp_action = erp_action;
+
+       /* start QDIO request for this FSF request */
+       retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+       if (retval) {
+               ZFCP_LOG_INFO("error: Could not send a close unit request for "
+                             "FCP-LUN 0x%Lx connected to the port with "
+                             "WWPN 0x%Lx connected to the adapter %s.\n",
+                             erp_action->unit->fcp_lun,
+                             erp_action->port->wwpn,
+                             zfcp_get_busid_by_adapter(erp_action->adapter));
+               zfcp_fsf_req_free(erp_action->fsf_req);
+               erp_action->fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_TRACE("Close LUN request initiated (adapter busid=%s, "
+                      "port wwpn=0x%Lx, unit fcp_lun=0x%Lx)\n",
+                      zfcp_get_busid_by_adapter(erp_action->adapter),
+                      erp_action->port->wwpn, erp_action->unit->fcp_lun);
+ out:
+       write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
+                               lock_flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_close_unit_handler
+ *
+ * purpose:     is called for finished Close LUN FSF command
+ *
+ * returns:
+ */
+static int
+zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_unit *unit;
+
+       unit = fsf_req->data.close_unit.unit;   /* restore unit */
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* don't change unit status in our bookkeeping */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
+                             "for the port with WWPN 0x%Lx connected to "
+                             "the adapter %s is not valid. This may "
+                             "happen in rare circumstances\n",
+                             unit->port->handle,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_unit(unit));
+               ZFCP_LOG_DEBUG("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_phand_nv");
+               zfcp_erp_adapter_reopen(unit->port->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("porthinv", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_LUN_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_INFO("Temporary LUN identifier (handle) 0x%x "
+                             "of the logical unit with FCP-LUN 0x%Lx at "
+                             "the remote port with WWPN 0x%Lx connected "
+                             "to the adapter %s is "
+                             "not valid. This may happen occasionally.\n",
+                             unit->handle,
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_unit(unit));
+               ZFCP_LOG_DEBUG("Status qualifier data:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_lhand_nv");
+               zfcp_erp_port_reopen(unit->port, 0);
+               zfcp_cmd_dbf_event_fsf("lunhinv", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PORT_BOXED:
+               ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
+               ZFCP_LOG_DEBUG("The remote port "
+                              "with WWPN 0x%Lx on the adapter %s "
+                              "needs to be reopened\n",
+                              unit->port->wwpn,
+                              zfcp_get_busid_by_unit(unit));
+               debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed");
+               zfcp_erp_port_reopen(unit->port, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+                       ZFCP_STATUS_FSFREQ_RETRY;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+               case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
+                       /* re-establish link to port */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ltest");
+                       zfcp_erp_port_reopen(unit->port, 0);
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
+                       /* ERP strategy will escalate */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ulp");
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               default:
+                       ZFCP_LOG_NORMAL
+                           ("bug: Wrong status qualifier 0x%x arrived.\n",
+                            fsf_req->qtcb->header.fsf_status_qual.word[0]);
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "fsf_sq_inval:");
+                       debug_exception(
+                               fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status_qual.word[0],
+                               sizeof (u32));
+                       break;
+               }
+               break;
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
+               ZFCP_LOG_TRACE("unit (FCP_LUN=0x%Lx) of remote port "
+                              "(WWPN=0x%Lx) via adapter (busid=%s) closed, "
+                              "port handle 0x%x \n",
+                              unit->fcp_lun,
+                              unit->port->wwpn,
+                              zfcp_get_busid_by_unit(unit),
+                              unit->handle);
+               /* mark unit as closed */
+               atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
+               retval = 0;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+                               "(debug info 0x%x)\n",
+                               fsf_req->qtcb->header.fsf_status);
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status,
+                               sizeof (u32));
+               break;
+       }
+
+ skip_fsfstatus:
+       atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &unit->status);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_send_fcp_command_task
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * note: we do not employ linked commands (not supported by HBA anyway)
+ */
+int
+zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
+                              struct zfcp_unit *unit,
+                              Scsi_Cmnd * scsi_cmnd, int req_flags)
+{
+       struct zfcp_fsf_req *fsf_req = NULL;
+       struct fcp_cmnd_iu *fcp_cmnd_iu;
+       volatile struct qdio_buffer_element *buffere;
+       unsigned int sbtype;
+       unsigned long lock_flags;
+       int real_bytes = 0;
+       int retval = 0;
+
+       /* setup new FSF request */
+
+       retval = zfcp_fsf_req_create(adapter,
+                                    FSF_QTCB_FCP_CMND,
+                                    &lock_flags, req_flags, &(fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_DEBUG("error: Out of resources. Could not create an "
+                              "FCP command request for FCP-LUN 0x%Lx "
+                              "connected to the port with WWPN 0x%Lx "
+                              "connected to the adapter %s.\n",
+                              unit->fcp_lun,
+                              unit->port->wwpn,
+                              zfcp_get_busid_by_adapter(adapter));
+               goto failed_req_create;
+       }
+
+       /*
+        * associate FSF request with SCSI request
+        * (need this for look up on abort)
+        */
+       fsf_req->data.send_fcp_command_task.fsf_req = fsf_req;
+       scsi_cmnd->host_scribble = (char *) &(fsf_req->data);
+
+       /*
+        * associate SCSI command with FSF request
+        * (need this for look up on normal command completion)
+        */
+       fsf_req->data.send_fcp_command_task.scsi_cmnd = scsi_cmnd;
+#ifdef ZFCP_DEBUG_REQUESTS
+       debug_text_event(adapter->req_dbf, 3, "fsf/sc");
+       debug_event(adapter->req_dbf, 3, &fsf_req, sizeof (unsigned long));
+       debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+#ifdef ZFCP_DEBUG_ABORTS
+       fsf_req->data.send_fcp_command_task.start_jiffies = jiffies;
+#endif
+
+       fsf_req->data.send_fcp_command_task.unit = unit;
+       ZFCP_LOG_DEBUG("unit=0x%lx, unit_fcp_lun=0x%Lx\n",
+                      (unsigned long) unit, unit->fcp_lun);
+
+       /* set handles of unit and its parent port in QTCB */
+       fsf_req->qtcb->header.lun_handle = unit->handle;
+       fsf_req->qtcb->header.port_handle = unit->port->handle;
+
+       /* FSF does not define the structure of the FCP_CMND IU */
+       fcp_cmnd_iu = (struct fcp_cmnd_iu *)
+           &(fsf_req->qtcb->bottom.io.fcp_cmnd);
+
+       /*
+        * set depending on data direction:
+        *      data direction bits in SBALE (SB Type)
+        *      data direction bits in QTCB
+        *      data direction bits in FCP_CMND IU
+        */
+       switch (scsi_cmnd->sc_data_direction) {
+       case SCSI_DATA_NONE:
+               ZFCP_LOG_FLAGS(3, "SCSI_DATA_NONE\n");
+               fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
+               /*
+                * FIXME(qdio):
+                * what is the correct type for commands
+                * without 'real' data buffers?
+                */
+               sbtype = SBAL_FLAGS0_TYPE_READ;
+               break;
+       case SCSI_DATA_READ:
+               ZFCP_LOG_FLAGS(3, "SCSI_DATA_READ\n");
+               fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
+               sbtype = SBAL_FLAGS0_TYPE_READ;
+               fcp_cmnd_iu->rddata = 1;
+               break;
+       case SCSI_DATA_WRITE:
+               ZFCP_LOG_FLAGS(3, "SCSI_DATA_WRITE\n");
+               fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
+               sbtype = SBAL_FLAGS0_TYPE_WRITE;
+               fcp_cmnd_iu->wddata = 1;
+               break;
+       case SCSI_DATA_UNKNOWN:
+               ZFCP_LOG_FLAGS(0, "SCSI_DATA_UNKNOWN not supported\n");
+       default:
+               /*
+                * dummy, catch this condition earlier
+                * in zfcp_scsi_queuecommand
+                */
+               goto failed_scsi_cmnd;
+       }
+       buffere =
+           &(adapter->request_queue.buffer[fsf_req->sbal_index]->element[0]);
+       buffere->flags |= sbtype;
+
+       /* set FC service class in QTCB (3 per default) */
+       fsf_req->qtcb->bottom.io.service_class = adapter->fc_service_class;
+
+       /* set FCP_LUN in FCP_CMND IU in QTCB */
+       fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
+
+       /* set task attributes in FCP_CMND IU in QTCB */
+       if (scsi_cmnd->device->simple_tags) {
+               fcp_cmnd_iu->task_attribute = SIMPLE_Q;
+               ZFCP_LOG_TRACE("setting SIMPLE_Q task attribute\n");
+       } else {
+               fcp_cmnd_iu->task_attribute = UNTAGGED;
+               ZFCP_LOG_TRACE("setting UNTAGGED task attribute\n");
+       }
+
+       /* set additional length of FCP_CDB in FCP_CMND IU in QTCB, if needed */
+       if (scsi_cmnd->cmd_len > FCP_CDB_LENGTH) {
+               fcp_cmnd_iu->add_fcp_cdb_length
+                   = (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
+               ZFCP_LOG_TRACE("SCSI CDB length is 0x%x, "
+                              "additional FCP_CDB length is 0x%x "
+                              "(shifted right 2 bits)\n",
+                              scsi_cmnd->cmd_len,
+                              fcp_cmnd_iu->add_fcp_cdb_length);
+       }
+       /*
+        * copy SCSI CDB (including additional length, if any) to
+        * FCP_CDB in FCP_CMND IU in QTCB
+        */
+       memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
+
+       /* FCP CMND IU length in QTCB */
+       fsf_req->qtcb->bottom.io.fcp_cmnd_length =
+               sizeof (struct fcp_cmnd_iu) +
+               fcp_cmnd_iu->add_fcp_cdb_length + sizeof (fcp_dl_t);
+
+       /* generate SBALEs from data buffer */
+       real_bytes = zfcp_create_sbals_from_sg(fsf_req,
+                                              scsi_cmnd,
+                                              sbtype,
+                                              0, ZFCP_MAX_SBALS_PER_REQ);
+       /* Note: >= and not = because the combined scatter-gather entries
+        * may be larger than request_bufflen according to the mailing list
+        */
+       if (real_bytes >= scsi_cmnd->request_bufflen) {
+               ZFCP_LOG_TRACE("Data fits\n");
+       } else if (real_bytes == 0) {
+               ZFCP_LOG_DEBUG("Data did not fit into available buffer(s), "
+                              "waiting for more...\n");
+               retval = -EIO;
+               goto no_fit;
+       } else {
+               ZFCP_LOG_NORMAL("error: No truncation implemented but "
+                               "required. Shutting down unit (busid=%s, "
+                               "WWPN=0x%16.16Lx, FCP_LUN=0x%16.16Lx)\n",
+                               zfcp_get_busid_by_unit(unit),
+                               unit->port->wwpn,
+                               unit->fcp_lun);
+               zfcp_erp_unit_shutdown(unit, 0);
+               retval = -EINVAL;
+               goto no_fit;
+       }
+
+       /* set length of FCP data length in FCP_CMND IU in QTCB */
+       zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
+
+       ZFCP_LOG_DEBUG("Sending SCSI command:\n");
+       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                     (char *) scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
+
+       /*
+        * start QDIO request for this FSF request
+        *  covered by an SBALE)
+        */
+       retval = zfcp_fsf_req_send(fsf_req, NULL);
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Could not send an FCP command request "
+                             "for a command on the adapter %s, "
+                             "port WWPN 0x%Lx and unit LUN 0x%Lx\n",
+                             zfcp_get_busid_by_adapter(adapter),
+                             unit->port->wwpn,
+                             unit->fcp_lun);
+               goto send_failed;
+       }
+
+       ZFCP_LOG_TRACE("Send FCP Command initiated (adapter busid=%s, "
+                      "port wwpn=0x%Lx, unit fcp_lun=0x%Lx)\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      unit->port->wwpn,
+                      unit->fcp_lun);
+       goto success;
+
+ send_failed:
+ no_fit:
+ failed_scsi_cmnd:
+       /* dequeue new FSF request previously enqueued */
+#ifdef ZFCP_DEBUG_REQUESTS
+       debug_text_event(adapter->req_dbf, 3, "fail_sc");
+       debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+
+       zfcp_fsf_req_free(fsf_req);
+       fsf_req = NULL;
+ success:
+ failed_req_create:
+       write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_send_fcp_command_task_management
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * FIXME(design): should be watched by a timeout!!!
+ * FIXME(design) shouldn't this be modified to return an int
+ *               also...don't know how though
+ *
+ */
+struct zfcp_fsf_req *
+zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter,
+                                         struct zfcp_unit *unit,
+                                         u8 tm_flags, int req_flags)
+{
+       struct zfcp_fsf_req *fsf_req = NULL;
+       int retval = 0;
+       struct fcp_cmnd_iu *fcp_cmnd_iu;
+       unsigned long lock_flags;
+       volatile struct qdio_buffer_element *buffere;
+
+       /* setup new FSF request */
+       retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND,
+                                    &lock_flags, req_flags, &(fsf_req));
+       if (retval < 0) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+                             "FCP command (task management) request for "
+                             "the adapter %s, port with "
+                             "WWPN 0x%Lx and FCP_LUN 0x%Lx.\n",
+                             zfcp_get_busid_by_adapter(adapter),
+                             unit->port->wwpn, unit->fcp_lun);
+               goto out;
+       }
+
+       /*
+        * Used to decide on proper handler in the return path,
+        * could be either zfcp_fsf_send_fcp_command_task_handler or
+        * zfcp_fsf_send_fcp_command_task_management_handler */
+
+       fsf_req->status |= ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
+
+       /*
+        * hold a pointer to the unit being target of this
+        * task management request
+        */
+       fsf_req->data.send_fcp_command_task_management.unit = unit;
+
+       /* set FSF related fields in QTCB */
+       fsf_req->qtcb->header.lun_handle = unit->handle;
+       fsf_req->qtcb->header.port_handle = unit->port->handle;
+       fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
+       fsf_req->qtcb->bottom.io.service_class = adapter->fc_service_class;
+       fsf_req->qtcb->bottom.io.fcp_cmnd_length =
+               sizeof (struct fcp_cmnd_iu) + sizeof (fcp_dl_t);
+
+       buffere =
+           &(adapter->request_queue.buffer[fsf_req->sbal_index]->element[0]);
+       buffere[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
+       buffere[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+       /* set FCP related fields in FCP_CMND IU in QTCB */
+       fcp_cmnd_iu = (struct fcp_cmnd_iu *)
+               &(fsf_req->qtcb->bottom.io.fcp_cmnd);
+       fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
+       fcp_cmnd_iu->task_management_flags = tm_flags;
+
+       /* start QDIO request for this FSF request */
+       zfcp_fsf_start_scsi_er_timer(adapter);
+       retval = zfcp_fsf_req_send(fsf_req, NULL);
+       if (retval) {
+               del_timer(&adapter->scsi_er_timer);
+               ZFCP_LOG_INFO("error: Could not send an FCP-command (task "
+                             "management) on the adapter %s, port WWPN "
+                             "0x%Lx for unit LUN 0x%Lx\n",
+                             zfcp_get_busid_by_adapter(adapter),
+                             unit->port->wwpn,
+                             unit->fcp_lun);
+               zfcp_fsf_req_free(fsf_req);
+               fsf_req = NULL;
+               goto out;
+       }
+
+       ZFCP_LOG_TRACE("Send FCP Command (task management function) initiated "
+                      "(adapter busid=%s, port wwpn=0x%Lx, "
+                      "unit fcp_lun=0x%Lx, tm_flags=0x%x)\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      unit->port->wwpn,
+                      unit->fcp_lun,
+                      tm_flags);
+ out:
+       write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+       return fsf_req;
+}
+
+/*
+ * function:    zfcp_fsf_send_fcp_command_handler
+ *
+ * purpose:    is called for finished Send FCP Command
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = -EINVAL;
+       struct zfcp_unit *unit;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)
+               unit = fsf_req->data.send_fcp_command_task_management.unit;
+       else
+               unit = fsf_req->data.send_fcp_command_task.unit;
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               /* go directly to calls of special handlers */
+               goto skip_fsfstatus;
+       }
+
+       /* evaluate FSF status in QTCB */
+       switch (fsf_req->qtcb->header.fsf_status) {
+
+       case FSF_PORT_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
+                             "for the port with WWPN 0x%Lx connected to "
+                             "the adapter %s is not valid.\n",
+                             unit->port->handle,
+                             unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_phand_nv");
+               zfcp_erp_adapter_reopen(unit->port->adapter, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_LUN_HANDLE_NOT_VALID:
+               ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
+               ZFCP_LOG_INFO("Temporary LUN identifier (handle) 0x%x "
+                             "of the logical unit with FCP-LUN 0x%Lx at "
+                             "the remote port with WWPN 0x%Lx connected "
+                             "to the adapter %s is "
+                             "not valid. This may happen occasionally.\n",
+                             unit->handle,
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_unit(unit));
+               ZFCP_LOG_NORMAL("Status qualifier data:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_uhand_nv");
+               zfcp_erp_port_reopen(unit->port, 0);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_HANDLE_MISMATCH:
+               ZFCP_LOG_FLAGS(0, "FSF_HANDLE_MISMATCH\n");
+               ZFCP_LOG_NORMAL("bug: The port handle (temporary port "
+                               "identifier) 0x%x has changed unexpectedly. "
+                               "This was detected upon receiveing the "
+                               "response of a command send to the unit with "
+                               "FCP-LUN 0x%Lx at the remote port with WWPN "
+                               "0x%Lx connected to the adapter %s.\n",
+                               unit->port->handle,
+                               unit->fcp_lun,
+                               unit->port->wwpn,
+                               zfcp_get_busid_by_unit(unit));
+               ZFCP_LOG_NORMAL("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_hand_mis");
+               zfcp_erp_adapter_reopen(unit->port->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("handmism",
+                                      fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+               ZFCP_LOG_FLAGS(0, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
+               if (fsf_req->adapter->fc_service_class <= 3) {
+                       ZFCP_LOG_NORMAL("error: The adapter %s does "
+                                       "not support fibre-channel class %d.\n",
+                                       zfcp_get_busid_by_unit(unit),
+                                       fsf_req->adapter->fc_service_class);
+               } else {
+                       ZFCP_LOG_NORMAL("bug: The fibre channel class at the "
+                                       "adapter %s is invalid. "
+                                       "(debug info %d)\n",
+                                       zfcp_get_busid_by_unit(unit),
+                                       fsf_req->adapter->fc_service_class);
+               }
+               /* stop operation for this adapter */
+               debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+                                    "fsf_s_class_nsup");
+               zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("unsclass",
+                                      fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_FCPLUN_NOT_VALID:
+               ZFCP_LOG_FLAGS(0, "FSF_FCPLUN_NOT_VALID\n");
+               ZFCP_LOG_NORMAL("bug: The FCP LUN 0x%Lx behind the remote port "
+                               "of WWPN0x%Lx via the adapter %s does not have "
+                               "the correct unit handle (temporary unit "
+                               "identifier) 0x%x\n",
+                               unit->fcp_lun,
+                               unit->port->wwpn,
+                               zfcp_get_busid_by_unit(unit),
+                               unit->handle);
+               ZFCP_LOG_DEBUG("status qualifier:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                             (char *) &fsf_req->qtcb->header.fsf_status_qual,
+                             sizeof (union fsf_status_qual));
+               debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                "fsf_s_fcp_lun_nv");
+               zfcp_erp_port_reopen(unit->port, 0);
+               zfcp_cmd_dbf_event_fsf("fluninv",
+                                      fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_DIRECTION_INDICATOR_NOT_VALID:
+               ZFCP_LOG_FLAGS(0, "FSF_DIRECTION_INDICATOR_NOT_VALID\n");
+               ZFCP_LOG_INFO("bug: Invalid data direction given for the unit "
+                             "with FCP LUN 0x%Lx at the remote port with "
+                             "WWPN 0x%Lx via the adapter %s "
+                             "(debug info %d)\n",
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_unit(unit),
+                             fsf_req->qtcb->bottom.io.data_direction);
+               /* stop operation for this adapter */
+               debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                "fsf_s_dir_ind_nv");
+               zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("dirinv",
+                                      fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+               /* FIXME: this should be obsolete, isn' it? */
+       case FSF_INBOUND_DATA_LENGTH_NOT_VALID:
+               ZFCP_LOG_FLAGS(0, "FSF_INBOUND_DATA_LENGTH_NOT_VALID\n");
+               ZFCP_LOG_NORMAL("bug: An invalid inbound data length field "
+                               "was found in a command for the unit with "
+                               "FCP LUN 0x%Lx of the remote port "
+                               "with WWPN 0x%Lx via the adapter %s.\n",
+                               unit->fcp_lun,
+                               unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+               /* stop operation for this adapter */
+               debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                "fsf_s_in_dl_nv");
+               zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("idleninv",
+                                      fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+               /* FIXME: this should be obsolete, isn' it? */
+       case FSF_OUTBOUND_DATA_LENGTH_NOT_VALID:
+               ZFCP_LOG_FLAGS(0, "FSF_OUTBOUND_DATA_LENGTH_NOT_VALID\n");
+               ZFCP_LOG_NORMAL("bug: An invalid outbound data length field "
+                               "was found in a command for the unit with "
+                               "FCP LUN 0x%Lx of the remote port "
+                               "with WWPN 0x%Lx via the adapter %s\n.",
+                               unit->fcp_lun,
+                               unit->port->wwpn,
+                               zfcp_get_busid_by_unit(unit));
+               /* stop operation for this adapter */
+               debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                "fsf_s_out_dl_nv");
+               zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("odleninv", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_CMND_LENGTH_NOT_VALID:
+               ZFCP_LOG_FLAGS(0, "FSF_CMND_LENGTH_NOT_VALID\n");
+               ZFCP_LOG_NORMAL
+                   ("bug: An invalid control-data-block length field "
+                    "was found in a command for the unit with "
+                    "FCP LUN 0x%Lx of the remote port "
+                    "with WWPN 0x%Lx via the adapter %s " "(debug info %d)\n",
+                    unit->fcp_lun, unit->port->wwpn,
+                    zfcp_get_busid_by_unit(unit),
+                    fsf_req->qtcb->bottom.io.fcp_cmnd_length);
+               /* stop operation for this adapter */
+               debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                "fsf_s_cmd_len_nv");
+               zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
+               zfcp_cmd_dbf_event_fsf("cleninv",
+                                      fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               break;
+
+       case FSF_PORT_BOXED:
+               ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
+               ZFCP_LOG_DEBUG("The remote port "
+                              "with WWPN 0x%Lx on the adapter %s "
+                              "needs to be reopened\n",
+                              unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+               debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed");
+               zfcp_erp_port_reopen(unit->port, 0);
+               zfcp_cmd_dbf_event_fsf("portbox", fsf_req,
+                                      &fsf_req->qtcb->header.fsf_status_qual,
+                                      sizeof (union fsf_status_qual));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+                       ZFCP_STATUS_FSFREQ_RETRY;
+               break;
+
+       case FSF_ADAPTER_STATUS_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+               switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+               case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+                       ZFCP_LOG_FLAGS(2,
+                                      "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
+                       /* re-establish link to port */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ltest");
+                       zfcp_erp_port_reopen(unit->port, 0);
+                       zfcp_cmd_dbf_event_fsf(
+                               "sqltest",
+                               fsf_req,
+                               &fsf_req->qtcb->header.fsf_status_qual,
+                               sizeof (union fsf_status_qual));
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+                       ZFCP_LOG_FLAGS(3,
+                                      "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
+                       /* FIXME(hw) need proper specs for proper action */
+                       /* let scsi stack deal with retries and escalation */
+                       debug_text_event(fsf_req->adapter->erp_dbf, 1,
+                                        "fsf_sq_ulp");
+                       zfcp_cmd_dbf_event_fsf(
+                               "sqdeperp",
+                               fsf_req,
+                               &fsf_req->qtcb->header.fsf_status_qual,
+                               sizeof (union fsf_status_qual));
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+                       break;
+               default:
+                       /* FIXME: shall we consider this a successful transfer? */
+                       ZFCP_LOG_NORMAL
+                           ("bug: Wrong status qualifier 0x%x arrived.\n",
+                            fsf_req->qtcb->header.fsf_status_qual.word[0]);
+                       debug_text_event(fsf_req->adapter->erp_dbf, 0,
+                                        "fsf_sq_inval:");
+                       debug_exception(
+                               fsf_req->adapter->erp_dbf,
+                               0,
+                               &fsf_req->qtcb->header.fsf_status_qual.word[0],
+                               sizeof (u32));
+                       break;
+               }
+               break;
+
+       case FSF_GOOD:
+               ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
+               break;
+
+       case FSF_FCP_RSP_AVAILABLE:
+               ZFCP_LOG_FLAGS(2, "FSF_FCP_RSP_AVAILABLE\n");
+               break;
+
+       default:
+               debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+               debug_exception(fsf_req->adapter->erp_dbf, 0,
+                               &fsf_req->qtcb->header.fsf_status,
+                               sizeof (u32));
+               break;
+       }
+
+ skip_fsfstatus:
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) {
+               retval =
+                   zfcp_fsf_send_fcp_command_task_management_handler(fsf_req);
+       } else {
+               retval = zfcp_fsf_send_fcp_command_task_handler(fsf_req);
+       }
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_send_fcp_command_task_handler
+ *
+ * purpose:    evaluates FCP_RSP IU
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+
+       Scsi_Cmnd *scpnt;
+       struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
+           &(fsf_req->qtcb->bottom.io.fcp_rsp);
+       struct fcp_cmnd_iu *fcp_cmnd_iu = (struct fcp_cmnd_iu *)
+           &(fsf_req->qtcb->bottom.io.fcp_cmnd);
+       u32 sns_len;
+       char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
+       unsigned long flags;
+       struct zfcp_unit *unit = fsf_req->data.send_fcp_command_task.unit;
+
+       read_lock_irqsave(&fsf_req->adapter->abort_lock, flags);
+       scpnt = fsf_req->data.send_fcp_command_task.scsi_cmnd;
+       if (!scpnt) {
+               ZFCP_LOG_DEBUG
+                   ("Command with fsf_req 0x%lx is not associated to "
+                    "a scsi command anymore. Aborted?\n",
+                    (unsigned long) fsf_req);
+               goto out;
+       }
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTED) {
+               /* FIXME: (design) mid-layer should handle DID_ABORT like
+                *        DID_SOFT_ERROR by retrying the request for devices
+                *        that allow retries.
+                */
+               ZFCP_LOG_DEBUG("Setting DID_SOFT_ERROR and SUGGEST_RETRY\n");
+               set_host_byte(&scpnt->result, DID_SOFT_ERROR);
+               set_driver_byte(&scpnt->result, SUGGEST_RETRY);
+               goto skip_fsfstatus;
+       }
+
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               ZFCP_LOG_DEBUG("Setting DID_ERROR\n");
+               set_host_byte(&scpnt->result, DID_ERROR);
+               goto skip_fsfstatus;
+       }
+
+       /* set message byte of result in SCSI command */
+       scpnt->result |= COMMAND_COMPLETE << 8;
+
+       /*
+        * copy SCSI status code of FCP_STATUS of FCP_RSP IU to status byte
+        * of result in SCSI command
+        */
+       scpnt->result |= fcp_rsp_iu->scsi_status;
+       if (fcp_rsp_iu->scsi_status) {
+               /* DEBUG */
+               ZFCP_LOG_NORMAL("status for SCSI Command:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             scpnt->cmnd, scpnt->cmd_len);
+
+               ZFCP_LOG_NORMAL("SCSI status code 0x%x\n",
+                               fcp_rsp_iu->scsi_status);
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (void *) fcp_rsp_iu, sizeof (struct fcp_rsp_iu));
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
+                             fcp_rsp_iu->fcp_sns_len);
+       }
+
+       /* check FCP_RSP_INFO */
+       if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid) {
+               ZFCP_LOG_DEBUG("rsp_len is valid\n");
+               switch (fcp_rsp_info[3]) {
+               case RSP_CODE_GOOD:
+                       ZFCP_LOG_FLAGS(3, "RSP_CODE_GOOD\n");
+                       /* ok, continue */
+                       ZFCP_LOG_TRACE("no failure or Task Management "
+                                      "Function complete\n");
+                       set_host_byte(&scpnt->result, DID_OK);
+                       break;
+               case RSP_CODE_LENGTH_MISMATCH:
+                       ZFCP_LOG_FLAGS(0, "RSP_CODE_LENGTH_MISMATCH\n");
+                       /* hardware bug */
+                       ZFCP_LOG_NORMAL("bug: FCP response code indictates "
+                                       " that the fibre-channel protocol data "
+                                       "length differs from the burst length. "
+                                       "The problem occured on the unit "
+                                       "with FCP LUN 0x%Lx connected to the "
+                                       "port with WWPN 0x%Lx at the "
+                                       "adapter %s",
+                                       unit->fcp_lun,
+                                       unit->port->wwpn,
+                                       zfcp_get_busid_by_unit(unit));
+                       /* dump SCSI CDB as prepared by zfcp */
+                       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                                     (char *) &fsf_req->qtcb->
+                                     bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
+                       zfcp_cmd_dbf_event_fsf("clenmis", fsf_req, NULL, 0);
+                       set_host_byte(&scpnt->result, DID_ERROR);
+                       goto skip_fsfstatus;
+               case RSP_CODE_FIELD_INVALID:
+                       ZFCP_LOG_FLAGS(0, "RSP_CODE_FIELD_INVALID\n");
+                       /* driver or hardware bug */
+                       ZFCP_LOG_NORMAL("bug: FCP response code indictates "
+                                       "that the fibre-channel protocol data "
+                                       "fields were incorrectly set-up. "
+                                       "The problem occured on the unit "
+                                       "with FCP LUN 0x%Lx connected to the "
+                                       "port with WWPN 0x%Lx at the "
+                                       "adapter %s",
+                                       unit->fcp_lun,
+                                       unit->port->wwpn,
+                                       zfcp_get_busid_by_unit(unit));
+                       /* dump SCSI CDB as prepared by zfcp */
+                       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                                     (char *) &fsf_req->qtcb->
+                                     bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
+                       set_host_byte(&scpnt->result, DID_ERROR);
+                       zfcp_cmd_dbf_event_fsf("codeinv", fsf_req, NULL, 0);
+                       goto skip_fsfstatus;
+               case RSP_CODE_RO_MISMATCH:
+                       ZFCP_LOG_FLAGS(0, "RSP_CODE_RO_MISMATCH\n");
+                       /* hardware bug */
+                       ZFCP_LOG_NORMAL("bug: The FCP response code indicates "
+                                       "that conflicting  values for the "
+                                       "fibre-channel payload offset from the "
+                                       "header were found. "
+                                       "The problem occured on the unit "
+                                       "with FCP LUN 0x%Lx connected to the "
+                                       "port with WWPN 0x%Lx at the "
+                                       "adapter %s.\n",
+                                       unit->fcp_lun,
+                                       unit->port->wwpn,
+                                       zfcp_get_busid_by_unit(unit));
+                       /* dump SCSI CDB as prepared by zfcp */
+                       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                                     (char *) &fsf_req->qtcb->
+                                     bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
+                       zfcp_cmd_dbf_event_fsf("codemism", fsf_req, NULL, 0);
+                       set_host_byte(&scpnt->result, DID_ERROR);
+                       goto skip_fsfstatus;
+               default:
+                       ZFCP_LOG_NORMAL("bug: An invalid FCP response "
+                                       "code was detected for a command. "
+                                       "The problem occured on the unit "
+                                       "with FCP LUN 0x%Lx connected to the "
+                                       "port with WWPN 0x%Lx at the "
+                                       "adapter %s "
+                                       "(debug info 0x%x)\n",
+                                       unit->fcp_lun,
+                                       unit->port->wwpn,
+                                       zfcp_get_busid_by_unit(unit),
+                                       fcp_rsp_info[3]);
+                       /* dump SCSI CDB as prepared by zfcp */
+                       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                                     (char *) &fsf_req->qtcb->
+                                     bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
+                       zfcp_cmd_dbf_event_fsf("undeffcp", fsf_req, NULL, 0);
+                       set_host_byte(&scpnt->result, DID_ERROR);
+               }
+       }
+
+       /* check for sense data */
+       if (fcp_rsp_iu->validity.bits.fcp_sns_len_valid) {
+               sns_len = FSF_FCP_RSP_SIZE -
+                   sizeof (struct fcp_rsp_iu) + fcp_rsp_iu->fcp_rsp_len;
+               ZFCP_LOG_TRACE("room for %i bytes sense data in QTCB\n",
+                              sns_len);
+               sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE);
+               ZFCP_LOG_TRACE("room for %i bytes sense data in SCSI command\n",
+                              SCSI_SENSE_BUFFERSIZE);
+               sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
+               ZFCP_LOG_TRACE("scpnt->result =0x%x, command was:\n",
+                              scpnt->result);
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
+                             (void *) &scpnt->cmnd, scpnt->cmd_len);
+
+               ZFCP_LOG_TRACE("%i bytes sense data provided by FCP\n",
+                              fcp_rsp_iu->fcp_sns_len);
+               memcpy(&scpnt->sense_buffer,
+                      zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len);
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
+                             (void *) &scpnt->sense_buffer, sns_len);
+       }
+
+       /* check for overrun */
+       if (fcp_rsp_iu->validity.bits.fcp_resid_over) {
+               ZFCP_LOG_INFO("A data overrun was detected for a command. "
+                             "This happened for a command to the unit "
+                             "with FCP LUN 0x%Lx connected to the "
+                             "port with WWPN 0x%Lx at the adapter %s. "
+                             "The response data length is "
+                             "%d, the original length was %d.\n",
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_unit(unit),
+                             fcp_rsp_iu->fcp_resid,
+                             (int) zfcp_get_fcp_dl(fcp_cmnd_iu));
+       }
+
+       /* check for underrun */
+       if (fcp_rsp_iu->validity.bits.fcp_resid_under) {
+               ZFCP_LOG_DEBUG("A data underrun was detected for a command. "
+                              "This happened for a command to the unit "
+                              "with FCP LUN 0x%Lx connected to the "
+                              "port with WWPN 0x%Lx at the adapter %s. "
+                              "The response data length is "
+                              "%d, the original length was %d.\n",
+                              unit->fcp_lun,
+                              unit->port->wwpn,
+                              zfcp_get_busid_by_unit(unit),
+                              fcp_rsp_iu->fcp_resid,
+                              (int) zfcp_get_fcp_dl(fcp_cmnd_iu));
+               /*
+                * It may not have been possible to send all data and the
+                * underrun on send may already be in scpnt->resid, so it's add
+                * not equals in the below statement.
+                */
+               scpnt->resid += fcp_rsp_iu->fcp_resid;
+               ZFCP_LOG_TRACE("scpnt->resid=0x%x\n", scpnt->resid);
+       }
+
+ skip_fsfstatus:
+#if 0
+       /*
+        * This nasty chop at the problem is not working anymore
+        * as we do not adjust the retry count anylonger in order
+        * to have a number of retries that avoids I/O errors.
+        * The manipulation of the retry count has been removed
+        * in favour of a safe tape device handling. We must not
+        * sent SCSI commands more than once to a device if no
+        * retries are permitted by the high level driver. Generally
+        * speaking, it was a mess to change retry counts. So it is
+        * fine that this sort of workaround is gone.
+        * Then, we had to face a certain number of immediate retries in case of
+        * busy and queue full conditions (see below).
+        * This is not acceptable
+        * for the latter. Queue full conditions are used
+        * by devices to indicate to a host that the host can rely
+        * on the completion (or timeout) of at least one outstanding
+        * command as a suggested trigger for command retries.
+        * Busy conditions require a different trigger since
+        * no commands are outstanding for that initiator from the
+        * devices perspective.
+        * The drawback of mapping a queue full condition to a
+        * busy condition is the chance of wasting all retries prior
+        * to the time when the device indicates that a command
+        * rejected due to a queue full condition should be re-driven.
+        * This case would lead to unnecessary I/O errors that
+        * have to be considered fatal if for example ext3's
+        * journaling would be torpedoed by such an avoidable
+        * I/O error.
+        * So, what issues are there with not mapping a queue-full
+        * condition to a busy condition?
+        * Due to the 'exclusive LUN'
+        * policy enforced by the zSeries FCP channel, this 
+        * Linux instance is the only initiator with regard to
+        * this adapter. It is safe to rely on the information
+        * 'don't disturb me now ... and btw. no other commands
+        * pending for you' (= queue full) sent by the LU,
+        * since no other Linux can use this LUN via this adapter
+        * at the same time. If there is a potential race
+        * introduced by the FCP channel by not inhibiting Linux A
+        * to give up a LU with commands pending while Linux B
+        * grabs this LU and sends commands  - thus providing
+        * an exploit at the 'exclusive LUN' policy - then this
+        * issue has to be considered a hardware problem. It should
+        * be tracked as such if it really occurs. Even if the
+        * FCP Channel spec. begs exploiters to wait for the
+        * completion of all request sent to a LU prior to
+        * closing this LU connection.
+        * This spec. statement in conjunction with
+        * the 'exclusive LUN' policy is not consistent design.
+        * Another issue is how resource constraints for SCSI commands
+        * might be handled by the FCP channel (just guessing for now).
+        * If the FCP channel would always map resource constraints,
+        * e.g. no free FC exchange ID due to I/O stress caused by
+        * other sharing Linux instances, to faked queue-full
+        * conditions then this would be a misinterpretation and
+        * violation of SCSI standards.
+        * If there are SCSI stack races as indicated below
+        * then they need to be fixed just there.
+        * Providing all issue above are not applicable or will
+        * be fixed appropriately, removing the following hack
+        * is the right thing to do.
+        */
+
+       /*
+        * Note: This is a rather nasty chop at the problem. We cannot 
+        * risk adding to the mlqueue however as this will block the 
+        * device. If it is the last outstanding command for this host
+        * it will remain blocked indefinitely. This would be quite possible
+        * on the zSeries FCP adapter.
+        * Also, there exists a race with scsi_insert_special relying on 
+        * scsi_request_fn to recalculate some command data which may not 
+        * happen when q->plugged is true in scsi_request_fn
+        */
+       if (status_byte(scpnt->result) == QUEUE_FULL) {
+               ZFCP_LOG_DEBUG("Changing QUEUE_FULL to BUSY....\n");
+               scpnt->result &= ~(QUEUE_FULL << 1);
+               scpnt->result |= (BUSY << 1);
+       }
+#endif
+
+       ZFCP_LOG_DEBUG("scpnt->result =0x%x\n", scpnt->result);
+
+       zfcp_cmd_dbf_event_scsi("response", scpnt);
+
+       /* cleanup pointer (need this especially for abort) */
+       scpnt->host_scribble = NULL;
+
+       /*
+        * NOTE:
+        * according to the outcome of a discussion on linux-scsi we
+        * don't need to grab the io_request_lock here since we use
+        * the new eh
+        */
+       /* always call back */
+       (scpnt->scsi_done) (scpnt);
+       atomic_dec(&adapter->scsi_reqs_active);
+       wake_up(&adapter->scsi_reqs_active_wq);
+#ifdef ZFCP_DEBUG_REQUESTS
+       debug_text_event(fsf_req->adapter->req_dbf, 2, "ok_done:");
+       debug_event(fsf_req->adapter->req_dbf, 2, &scpnt,
+                   sizeof (unsigned long));
+       debug_event(fsf_req->adapter->req_dbf, 2, &scpnt->scsi_done,
+                   sizeof (unsigned long));
+       debug_event(fsf_req->adapter->req_dbf, 2, &fsf_req,
+                   sizeof (unsigned long));
+#endif /* ZFCP_DEBUG_REQUESTS */
+       (scpnt->scsi_done) (scpnt);
+       atomic_dec(&adapter->scsi_reqs_active);
+       wake_up(&adapter->scsi_reqs_active_wq);
+       /*
+        * We must hold this lock until scsi_done has been called.
+        * Otherwise we may call scsi_done after abort regarding this
+        * command has completed.
+        * Note: scsi_done must not block!
+        */
+ out:
+       read_unlock_irqrestore(&fsf_req->adapter->abort_lock, flags);
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_send_fcp_command_task_management_handler
+ *
+ * purpose:    evaluates FCP_RSP IU
+ *
+ * returns:    
+ */
+static int
+zfcp_fsf_send_fcp_command_task_management_handler(struct zfcp_fsf_req *fsf_req)
+{
+       int retval = 0;
+       struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
+           &(fsf_req->qtcb->bottom.io.fcp_rsp);
+       char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
+       struct zfcp_unit *unit =
+           fsf_req->data.send_fcp_command_task_management.unit;
+
+       del_timer(&fsf_req->adapter->scsi_er_timer);
+       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
+               goto skip_fsfstatus;
+       }
+
+       /* check FCP_RSP_INFO */
+       switch (fcp_rsp_info[3]) {
+       case RSP_CODE_GOOD:
+               ZFCP_LOG_FLAGS(3, "RSP_CODE_GOOD\n");
+               /* ok, continue */
+               ZFCP_LOG_DEBUG("no failure or Task Management "
+                              "Function complete\n");
+               break;
+       case RSP_CODE_TASKMAN_UNSUPP:
+               ZFCP_LOG_FLAGS(0, "RSP_CODE_TASKMAN_UNSUPP\n");
+               ZFCP_LOG_NORMAL("bug: A reuested task management function "
+                               "is not supported on the target device "
+                               "The corresponding device is the unit with "
+                               "FCP LUN 0x%Lx at the port "
+                               "with WWPN 0x%Lx at the adapter %s\n ",
+                               unit->fcp_lun,
+                               unit->port->wwpn,
+                               zfcp_get_busid_by_unit(unit));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP;
+               break;
+       case RSP_CODE_TASKMAN_FAILED:
+               ZFCP_LOG_FLAGS(0, "RSP_CODE_TASKMAN_FAILED\n");
+               ZFCP_LOG_NORMAL("bug: A reuested task management function "
+                               "failed to complete successfully. "
+                               "The corresponding device is the unit with "
+                               "FCP LUN 0x%Lx at the port "
+                               "with WWPN 0x%Lx at the adapter %s.\n",
+                               unit->fcp_lun,
+                               unit->port->wwpn,
+                               zfcp_get_busid_by_unit(unit));
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
+               break;
+       default:
+               ZFCP_LOG_NORMAL("bug: An invalid FCP response "
+                               "code was detected for a command. "
+                               "The problem occured on the unit "
+                               "with FCP LUN 0x%Lx connected to the "
+                               "port with WWPN 0x%Lx at the adapter %s "
+                               "(debug info 0x%x)\n",
+                               unit->fcp_lun,
+                               unit->port->wwpn,
+                               zfcp_get_busid_by_unit(unit),
+                               fcp_rsp_info[3]);
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
+       }
+
+      skip_fsfstatus:
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_req_wait_and_cleanup
+ *
+ * purpose:
+ *
+ * FIXME(design): signal seems to be <0 !!!
+ * returns:    0       - request completed (*status is valid), cleanup succ.
+ *             <0      - request completed (*status is valid), cleanup failed
+ *             >0      - signal which interrupted waiting (*status invalid),
+ *                       request not completed, no cleanup
+ *
+ *             *status is a copy of status of completed fsf_req
+ */
+int
+zfcp_fsf_req_wait_and_cleanup(struct zfcp_fsf_req *fsf_req,
+                             int interruptible, u32 * status)
+{
+       int retval = 0;
+       int signal = 0;
+
+       if (interruptible) {
+               __wait_event_interruptible(fsf_req->completion_wq,
+                                          fsf_req->status &
+                                          ZFCP_STATUS_FSFREQ_COMPLETED,
+                                          signal);
+               if (signal) {
+                       ZFCP_LOG_DEBUG("Caught signal %i while waiting for the "
+                                      "completion of the request at 0x%lx\n",
+                                      signal, (unsigned long) fsf_req);
+                       retval = signal;
+                       goto out;
+               }
+       } else {
+               __wait_event(fsf_req->completion_wq,
+                            fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+       }
+
+       *status = fsf_req->status;
+
+       /* cleanup request */
+       zfcp_fsf_req_cleanup(fsf_req);
+ out:
+       return retval;
+}
+
+static inline int
+zfcp_fsf_req_create_sbal_check(unsigned long *flags,
+                              struct zfcp_qdio_queue *queue, int needed)
+{
+       write_lock_irqsave(&queue->queue_lock, *flags);
+       if (atomic_read(&queue->free_count) >= needed)
+               return 1;
+       write_unlock_irqrestore(&queue->queue_lock, *flags);
+       return 0;
+}
+
+/*
+ * function:    zfcp_fsf_req_create
+ *
+ * purpose:    create an FSF request at the specified adapter and
+ *             setup common fields
+ *
+ * returns:    -ENOMEM if there was insufficient memory for a request
+ *              -EIO if no qdio buffers could be allocate to the request
+ *              -EINVAL/-EPERM on bug conditions in req_dequeue
+ *              0 in success
+ *
+ * note:        The created request is returned by reference.
+ *
+ * locks:      lock of concerned request queue must not be held,
+ *             but is held on completion (write, irqsave)
+ */
+int
+zfcp_fsf_req_create(struct zfcp_adapter *adapter,
+                   u32 fsf_cmd,
+                   unsigned long *lock_flags,
+                   int req_flags,
+                   struct zfcp_fsf_req **fsf_req_p)
+{
+       struct zfcp_fsf_req *fsf_req = NULL;
+       int retval = 0;
+       struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
+       volatile struct qdio_buffer_element *buffere;
+       unsigned long timeout;
+       int condition;
+
+       /* allocate new FSF request */
+       fsf_req = zfcp_fsf_req_alloc(adapter, fsf_cmd, GFP_ATOMIC);
+       if (!fsf_req) {
+               ZFCP_LOG_DEBUG("error: Could not put an FSF request into"
+                              "the outbound (send) queue.\n");
+               retval = -ENOMEM;
+               goto failed_fsf_req;
+       }
+       /* save pointer to "parent" adapter */
+       fsf_req->adapter = adapter;
+
+       /* initialize waitqueue which may be used to wait on 
+          this request completion */
+       init_waitqueue_head(&fsf_req->completion_wq);
+
+       /* set magics */
+       fsf_req->common_magic = ZFCP_MAGIC;
+       fsf_req->specific_magic = ZFCP_MAGIC_FSFREQ;
+
+       fsf_req->fsf_command = fsf_cmd;
+       if (req_flags & ZFCP_REQ_AUTO_CLEANUP)
+               fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+
+       /* initialize QTCB */
+       if (fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS) {
+               ZFCP_LOG_TRACE("fsf_req->qtcb=0x%lx\n",
+                              (unsigned long) fsf_req->qtcb);
+               fsf_req->qtcb->prefix.req_id = (unsigned long) fsf_req;
+               fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION;
+               fsf_req->qtcb->prefix.qtcb_type = fsf_qtcb_type[fsf_cmd];
+               fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION;
+               fsf_req->qtcb->header.req_handle = (unsigned long) fsf_req;
+               fsf_req->qtcb->header.fsf_command = fsf_cmd;
+               /*
+                * Request Sequence Number is set later when the request is
+                * actually sent.
+                */
+       }
+
+       /*
+        * try to get needed SBALs in request queue (get queue lock on success)
+        */
+       ZFCP_LOG_TRACE("try to get free BUFFER in request queue\n");
+       if (req_flags & ZFCP_WAIT_FOR_SBAL) {
+               timeout = ZFCP_SBAL_TIMEOUT;
+               ZFCP_WAIT_EVENT_TIMEOUT(adapter->request_wq,
+                                       timeout,
+                                       (condition =
+                                        (zfcp_fsf_req_create_sbal_check)
+                                        (lock_flags, req_queue, 1)));
+               if (!condition) {
+                       retval = -EIO;
+                       goto failed_sbals;
+               }
+       } else {
+               if (!zfcp_fsf_req_create_sbal_check(lock_flags, req_queue, 1)) {
+                       retval = -EIO;
+                       goto failed_sbals;
+               }
+       }
+       fsf_req->sbal_count = 1;
+       fsf_req->sbal_index = req_queue->free_index;
+
+       ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n",
+                      fsf_req->sbal_count, fsf_req->sbal_index);
+       buffere = req_queue->buffer[fsf_req->sbal_index]->element;
+       /* setup common SBALE fields */
+       buffere[0].addr = fsf_req;
+       buffere[0].flags |= SBAL_FLAGS0_COMMAND;
+       if (fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS) {
+               buffere[1].addr = (void *) fsf_req->qtcb;
+               buffere[1].length = ZFCP_QTCB_SIZE;
+       }
+
+       /* set specific common SBALE and QTCB fields */
+       switch (fsf_cmd) {
+       case FSF_QTCB_FCP_CMND:
+               ZFCP_LOG_FLAGS(3, "FSF_QTCB_FCP_CMND\n");
+               /*
+                * storage-block type depends on actual
+                * SCSI command and is set by calling
+                * routine according to transfer direction
+                * of data buffers associated with SCSI
+                * command
+                */
+               break;
+       case FSF_QTCB_ABORT_FCP_CMND:
+       case FSF_QTCB_OPEN_PORT_WITH_DID:
+       case FSF_QTCB_OPEN_LUN:
+       case FSF_QTCB_CLOSE_LUN:
+       case FSF_QTCB_CLOSE_PORT:
+       case FSF_QTCB_CLOSE_PHYSICAL_PORT:
+       case FSF_QTCB_SEND_ELS: /* FIXME: ELS needs separate case */
+               ZFCP_LOG_FLAGS(3, "FSF_QTCB_*\n");
+               /*
+                * FIXME(qdio):
+                * what is the correct type for commands
+                * without 'real' data buffers?
+                */
+               buffere[0].flags |= SBAL_FLAGS0_TYPE_READ;
+               buffere[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+               break;
+       case FSF_QTCB_EXCHANGE_CONFIG_DATA:
+               ZFCP_LOG_FLAGS(3, "FSF_QTCB_EXCHANGE_CONFIG_DATA\n");
+               buffere[0].flags |= SBAL_FLAGS0_TYPE_READ;
+               buffere[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+               break;
+
+       case FSF_QTCB_SEND_GENERIC:
+               ZFCP_LOG_FLAGS(3, "FSF_QTCB_SEND_GENERIC\n");
+               buffere[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+               break;
+
+       case FSF_QTCB_UNSOLICITED_STATUS:
+               ZFCP_LOG_FLAGS(3, "FSF_QTCB_UNSOLICITED_STATUS\n");
+               buffere[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
+               buffere[2].flags |= SBAL_FLAGS_LAST_ENTRY;
+               break;
+
+       default:
+               ZFCP_LOG_NORMAL("bug: An attempt to send an unsupported "
+                               "command has been detected. "
+                               "(debug info 0x%x)\n", fsf_cmd);
+               goto unsupported_fsf_cmd;
+       }
+
+       /* yes, we did it - skip all cleanups for different failures */
+       goto out;
+
+ unsupported_fsf_cmd:
+
+ failed_sbals:
+#ifdef ZFCP_STAT_QUEUES
+       atomic_inc(&zfcp_data.outbound_queue_full);
+#endif
+/* dequeue new FSF request previously enqueued */
+       zfcp_fsf_req_free(fsf_req);
+       fsf_req = NULL;
+
+ failed_fsf_req:
+       write_lock_irqsave(&req_queue->queue_lock, *lock_flags);
+ out:
+       *fsf_req_p = fsf_req;
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_req_send
+ *
+ * purpose:    start transfer of FSF request via QDIO
+ *
+ * returns:    0 - request transfer succesfully started
+ *             !0 - start of request transfer failed
+ */
+static int
+zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer)
+{
+       int retval = 0;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+       struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
+       volatile struct qdio_buffer_element *buffere;
+       int inc_seq_no = 1;
+       int new_distance_from_int;
+       unsigned long flags;
+
+       u8 sbal_index = fsf_req->sbal_index;
+
+       /* FIXME(debug): remove it later */
+       buffere = &(req_queue->buffer[sbal_index]->element[0]);
+       ZFCP_LOG_DEBUG("zeroeth BUFFERE flags=0x%x \n ", buffere->flags);
+       buffere = &(req_queue->buffer[sbal_index]->element[1]);
+       ZFCP_LOG_TRACE("HEX DUMP OF 0eth BUFFERE PAYLOAD:\n");
+       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) buffere->addr,
+                     buffere->length);
+
+       /* set sequence counter in QTCB */
+       if (fsf_req->qtcb) {
+               fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
+               fsf_req->seq_no = adapter->fsf_req_seq_no;
+               ZFCP_LOG_TRACE("FSF request 0x%lx of adapter 0x%lx gets "
+                              "FSF sequence counter value of %i\n",
+                              (unsigned long) fsf_req,
+                              (unsigned long) adapter,
+                              fsf_req->qtcb->prefix.req_seq_no);
+       } else
+               inc_seq_no = 0;
+
+       /* put allocated FSF request at list tail */
+       write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
+       list_add_tail(&fsf_req->list, &adapter->fsf_req_list_head);
+       write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
+
+       /* figure out expiration time of timeout and start timeout */
+       if (timer) {
+               timer->expires += jiffies;
+               add_timer(timer);
+       }
+
+       ZFCP_LOG_TRACE("request queue of adapter with busid=%s: "
+                      "next free SBAL is %i, %i free SBALs\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      req_queue->free_index,
+                      atomic_read(&req_queue->free_count));
+
+       ZFCP_LOG_DEBUG("Calling do QDIO busid=%s, flags=0x%x, queue_no=%i, "
+                      "index_in_queue=%i, count=%i, buffers=0x%lx\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      QDIO_FLAG_SYNC_OUTPUT,
+                      0,
+                      sbal_index,
+                      fsf_req->sbal_count,
+                      (unsigned long) &req_queue->buffer[sbal_index]);
+
+       /*
+        * adjust the number of free SBALs in request queue as well as
+        * position of first one
+        */
+       atomic_sub(fsf_req->sbal_count, &req_queue->free_count);
+       ZFCP_LOG_TRACE("free_count=%d\n", atomic_read(&req_queue->free_count));
+       req_queue->free_index += fsf_req->sbal_count;     /* increase */
+       req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;  /* wrap if needed */
+       new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req);
+
+       retval = do_QDIO(adapter->ccw_device,
+                        QDIO_FLAG_SYNC_OUTPUT,
+                        0, fsf_req->sbal_index, fsf_req->sbal_count, NULL);
+
+       if (retval) {
+               /* Queues are down..... */
+               retval = -EIO;
+               /*
+                * FIXME(potential race):
+                * timer might be expired (absolutely unlikely)
+                */
+               if (timer)
+                       del_timer_sync(timer);
+               write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
+               list_del(&fsf_req->list);
+               write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
+               /*
+                * adjust the number of free SBALs in request queue as well as
+                * position of first one
+                */
+               zfcp_qdio_zero_sbals(req_queue->buffer,
+                                    fsf_req->sbal_index, fsf_req->sbal_count);
+               atomic_add(fsf_req->sbal_count, &req_queue->free_count);
+               req_queue->free_index -= fsf_req->sbal_count;    /* increase */
+               req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q;
+               req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */
+               ZFCP_LOG_DEBUG
+                       ("error: do_QDIO failed. Buffers could not be enqueued "
+                        "to request queue.\n");
+       } else {
+               req_queue->distance_from_int = new_distance_from_int;
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 1, "o:a/seq");
+               debug_event(adapter->req_dbf, 1, &fsf_req,
+                           sizeof (unsigned long));
+               if (inc_seq_no) {
+                       debug_event(adapter->req_dbf, 1,
+                                   &adapter->fsf_req_seq_no, sizeof (u32));
+               } else {
+                       debug_text_event(adapter->req_dbf, 1, "nocb");
+               }
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+               /*
+                * increase FSF sequence counter -
+                * this must only be done for request successfully enqueued to
+                * QDIO this rejected requests may be cleaned up by calling
+                * routines  resulting in missing sequence counter values
+                * otherwise,
+                */
+               /* Don't increase for unsolicited status */
+               if (inc_seq_no) {
+                       adapter->fsf_req_seq_no++;
+                       ZFCP_LOG_TRACE
+                           ("FSF sequence counter value of adapter 0x%lx "
+                            "increased to %i\n", (unsigned long) adapter,
+                            adapter->fsf_req_seq_no);
+               }
+               /* count FSF requests pending */
+               atomic_inc(&adapter->fsf_reqs_active);
+#ifdef ZFCP_STAT_QUEUES
+               atomic_inc(&zfcp_data.outbound_total);
+#endif
+       }
+       return retval;
+}
+
+/*
+ * function:    zfcp_fsf_req_cleanup
+ *
+ * purpose:    cleans up an FSF request and removes it from the specified list
+ *
+ * returns:
+ *
+ * assumption: no pending SB in SBALEs other than QTCB
+ */
+void
+zfcp_fsf_req_cleanup(struct zfcp_fsf_req *fsf_req)
+{
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+       unsigned long flags;
+
+       write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
+       list_del(&fsf_req->list);
+       write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
+       zfcp_fsf_req_free(fsf_req);
+}
+
+/*
+ * try to allocate fsf_req with QTCB,
+ * alternately try to get hold of fsf_req+QTCB provided by the specified memory
+ * pool element, this routine is called for all kinds of fsf requests other than
+ * status read since status read does neither require kmalloc involvement
+ * nor a QTCB
+ */
+static struct zfcp_fsf_req *
+zfcp_fsf_req_get(int kmalloc_flags, mempool_t * pool)
+{
+       struct zfcp_fsf_req *fsf_req;
+
+       fsf_req = kmalloc(ZFCP_QTCB_AND_REQ_SIZE, kmalloc_flags);
+       if (fsf_req) {
+               memset(fsf_req, 0, ZFCP_QTCB_AND_REQ_SIZE);
+       } else {
+               fsf_req = mempool_alloc(pool, kmalloc_flags);
+               if (fsf_req) {
+                       memset(fsf_req, 0, ZFCP_QTCB_AND_REQ_SIZE);
+                       fsf_req->status |= ZFCP_STATUS_FSFREQ_POOL;
+               }
+       }
+       if (fsf_req)
+               fsf_req->qtcb =
+                   (struct fsf_qtcb *) ((unsigned long) fsf_req +
+                                        sizeof (struct zfcp_fsf_req));
+
+       return fsf_req;
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
new file mode 100644 (file)
index 0000000..0ec7166
--- /dev/null
@@ -0,0 +1,358 @@
+/* 
+ * 
+ * linux/drivers/s390/scsi/zfcp_fsf.h
+ * 
+ * FCP adapter driver for IBM eServer zSeries 
+ * 
+ * Copyright 2002 IBM Corporation 
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com> 
+ *            Raimund Schroeder <raimund.schroeder@de.ibm.com> 
+ *            Aron Zeh <arzeh@de.ibm.com> 
+ *            Wolfgang Taphorn <taphorn@de.ibm.com> 
+ *            Stefan Bader <stefan.bader@de.ibm.com> 
+ *            Heiko Carstens <heiko.carstens@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. 
+ */
+
+#ifndef FSF_H
+#define FSF_H
+
+#ifdef __KERNEL__
+
+#define FSF_QTCB_VERSION1                      0x00000001
+#define FSF_QTCB_CURRENT_VERSION               FSF_QTCB_VERSION1
+
+/* FSF commands */
+#define        FSF_QTCB_FCP_CMND                       0x00000001
+#define        FSF_QTCB_ABORT_FCP_CMND                 0x00000002
+#define        FSF_QTCB_OPEN_PORT_WITH_DID             0x00000005
+#define        FSF_QTCB_OPEN_LUN                       0x00000006
+#define        FSF_QTCB_CLOSE_LUN                      0x00000007
+#define        FSF_QTCB_CLOSE_PORT                     0x00000008
+#define        FSF_QTCB_CLOSE_PHYSICAL_PORT            0x00000009
+#define        FSF_QTCB_SEND_ELS                       0x0000000B
+#define        FSF_QTCB_SEND_GENERIC                   0x0000000C
+#define        FSF_QTCB_EXCHANGE_CONFIG_DATA           0x0000000D
+
+/* FSF QTCB types */
+#define FSF_IO_COMMAND                         0x00000001
+#define FSF_SUPPORT_COMMAND                    0x00000002
+#define FSF_CONFIG_COMMAND                     0x00000003
+
+/* FSF protocol stati */
+#define FSF_PROT_GOOD                          0x00000001
+#define FSF_PROT_QTCB_VERSION_ERROR            0x00000010
+#define FSF_PROT_SEQ_NUMB_ERROR                        0x00000020
+#define FSF_PROT_UNSUPP_QTCB_TYPE              0x00000040
+#define FSF_PROT_HOST_CONNECTION_INITIALIZING  0x00000080
+#define FSF_PROT_FSF_STATUS_PRESENTED          0x00000100
+#define FSF_PROT_DUPLICATE_REQUEST_ID          0x00000200
+#define FSF_PROT_LINK_DOWN                      0x00000400
+#define FSF_PROT_REEST_QUEUE                    0x00000800
+#define FSF_PROT_ERROR_STATE                   0x01000000
+
+/* FSF stati */
+#define FSF_GOOD                               0x00000000
+#define FSF_PORT_ALREADY_OPEN                  0x00000001
+#define FSF_LUN_ALREADY_OPEN                   0x00000002
+#define FSF_PORT_HANDLE_NOT_VALID              0x00000003
+#define FSF_LUN_HANDLE_NOT_VALID               0x00000004
+#define FSF_HANDLE_MISMATCH                    0x00000005
+#define FSF_SERVICE_CLASS_NOT_SUPPORTED                0x00000006
+#define FSF_FCPLUN_NOT_VALID                   0x00000009
+//#define FSF_ACCESS_DENIED                     0x00000010
+#define FSF_ACCESS_TYPE_NOT_VALID              0x00000011
+#define FSF_LUN_IN_USE                         0x00000012
+#define FSF_COMMAND_ABORTED_ULP                        0x00000020
+#define FSF_COMMAND_ABORTED_ADAPTER            0x00000021
+#define FSF_FCP_COMMAND_DOES_NOT_EXIST         0x00000022
+#define FSF_DIRECTION_INDICATOR_NOT_VALID      0x00000030
+#define FSF_INBOUND_DATA_LENGTH_NOT_VALID      0x00000031 /* FIX: obsolete? */
+#define FSF_OUTBOUND_DATA_LENGTH_NOT_VALID     0x00000032 /* FIX: obsolete? */
+#define FSF_CMND_LENGTH_NOT_VALID              0x00000033
+#define FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED   0x00000040
+#define FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED    0x00000041
+#define FSF_REQUEST_BUF_NOT_VALID              0x00000042
+#define FSF_RESPONSE_BUF_NOT_VALID             0x00000043
+#define FSF_ELS_COMMAND_REJECTED               0x00000050
+#define FSF_GENERIC_COMMAND_REJECTED           0x00000051
+//#define FSF_AUTHORIZATION_FAILURE             0x00000053
+#define FSF_PORT_BOXED                         0x00000059
+//#define FSF_LUN_BOXED                         0x0000005A
+#define FSF_ADAPTER_STATUS_AVAILABLE           0x000000AD
+#define FSF_FCP_RSP_AVAILABLE                  0x000000AF
+#define FSF_UNKNOWN_COMMAND                    0x000000E2
+//#define FSF_ERROR                             0x000000FF 
+
+#define FSF_STATUS_QUALIFIER_SIZE              16
+
+/* FSF status qualifier, recommendations */
+#define FSF_SQ_NO_RECOM                                0x00
+#define FSF_SQ_FCP_RSP_AVAILABLE               0x01
+#define FSF_SQ_RETRY_IF_POSSIBLE               0x02
+#define FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED      0x03
+#define FSF_SQ_INVOKE_LINK_TEST_PROCEDURE      0x04
+#define FSF_SQ_ULP_PROGRAMMING_ERROR           0x05
+#define FSF_SQ_COMMAND_ABORTED                 0x06
+#define FSF_SQ_NO_RETRY_POSSIBLE               0x07
+
+/* FSF status qualifier (most significant 4 bytes), local link down */
+#define FSF_PSQ_LINK_NOLIGHT                   0x00000004
+#define FSF_PSQ_LINK_WRAPPLUG                  0x00000008
+#define FSF_PSQ_LINK_NOFCP                     0x00000010
+
+/* payload size in status read buffer */
+#define FSF_STATUS_READ_PAYLOAD_SIZE           4032
+
+/* number of status read buffers that should be sent by ULP */
+#define FSF_STATUS_READS_RECOM                 16
+
+/* status types in status read buffer */
+#define FSF_STATUS_READ_PORT_CLOSED            0x00000001
+#define FSF_STATUS_READ_INCOMING_ELS           0x00000002
+#define FSF_STATUS_READ_BIT_ERROR_THRESHOLD    0x00000004
+#define FSF_STATUS_READ_LINK_DOWN              0x00000005 /* FIXME: really? */
+#define FSF_STATUS_READ_LINK_UP                0x00000006
+
+/* status subtypes in status read buffer */
+#define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT    0x00000001
+#define FSF_STATUS_READ_SUB_ERROR_PORT         0x00000002
+
+/* topologie that is detected by the adapter */
+#define FSF_TOPO_ERROR                         0x00000000
+#define FSF_TOPO_P2P                           0x00000001
+#define FSF_TOPO_FABRIC                                0x00000002
+#define FSF_TOPO_AL                            0x00000003
+#define FSF_TOPO_FABRIC_VIRT                   0x00000004
+
+/* data direction for FCP commands */
+#define FSF_DATADIR_WRITE                      0x00000001
+#define FSF_DATADIR_READ                       0x00000002
+#define FSF_DATADIR_READ_WRITE                 0x00000003
+#define FSF_DATADIR_CMND                       0x00000004
+
+/* fc service class */
+#define FSF_CLASS_1                            0x00000001
+#define FSF_CLASS_2                            0x00000002
+#define FSF_CLASS_3                            0x00000003
+
+/* SBAL chaining */
+#define FSF_MAX_SBALS_PER_REQ                  36
+
+/* logging space behind QTCB */
+#define FSF_QTCB_LOG_SIZE                      1024
+
+struct fsf_queue_designator;
+struct fsf_status_read_buffer;
+struct fsf_port_closed_payload;
+struct fsf_bit_error_payload;
+union  fsf_prot_status_qual;
+struct fsf_qual_version_error;
+struct fsf_qual_sequence_error;
+struct fsf_qtcb_prefix;
+struct fsf_qtcb_header;
+struct fsf_qtcb_bottom_config;
+struct fsf_qtcb_bottom_support;
+struct fsf_qtcb_bottom_io;
+union  fsf_qtcb_bottom;
+
+struct fsf_queue_designator {
+       u8  cssid;
+       u8  chpid;
+       u8  hla;
+       u8  ua;
+       u32 res1;
+} __attribute__ ((packed));
+
+struct fsf_port_closed_payload {
+       struct fsf_queue_designator queue_designator;
+       u32                         port_handle;
+} __attribute__ ((packed));
+
+struct fsf_bit_error_payload {
+       u32 res1;
+       u32 link_failure_error_count;
+       u32 loss_of_sync_error_count;
+       u32 loss_of_signal_error_count;
+       u32 primitive_sequence_error_count;
+       u32 invalid_transmission_word_error_count;
+       u32 crc_error_count;
+       u32 primitive_sequence_event_timeout_count;
+       u32 elastic_buffer_overrun_error_count;
+       u32 fcal_arbitration_timeout_count;
+       u32 advertised_receive_b2b_credit;
+       u32 current_receive_b2b_credit;
+       u32 advertised_transmit_b2b_credit;
+       u32 current_transmit_b2b_credit;
+} __attribute__ ((packed));
+
+struct fsf_status_read_buffer {
+       u32 status_type;
+       u32 status_subtype;
+       u32 length;
+       u32 res1;
+       struct fsf_queue_designator queue_designator;
+       u32 d_id;
+       u32 class;
+       u64 fcp_lun;
+       u8  res3[24];
+       u8  payload[FSF_STATUS_READ_PAYLOAD_SIZE];
+} __attribute__ ((packed));
+
+struct fsf_qual_version_error {
+       u32 fsf_version;
+       u32 res1[3];
+} __attribute__ ((packed));
+
+struct fsf_qual_sequence_error {
+       u32 exp_req_seq_no;
+       u32 res1[3];
+} __attribute__ ((packed));
+
+struct fsf_qual_locallink_error {
+       u32 code;
+       u32 res1[3];
+} __attribute__ ((packed));
+
+union fsf_prot_status_qual {
+       struct fsf_qual_version_error   version_error;
+       struct fsf_qual_sequence_error  sequence_error;
+       struct fsf_qual_locallink_error locallink_error;
+} __attribute__ ((packed));
+
+struct fsf_qtcb_prefix {
+       u64 req_id;
+       u32 qtcb_version;
+       u32 ulp_info;
+       u32 qtcb_type;
+       u32 req_seq_no;
+       u32 prot_status;
+       union fsf_prot_status_qual prot_status_qual;
+       u8  res1[20];
+} __attribute__ ((packed));
+
+union fsf_status_qual {
+       u8  byte[FSF_STATUS_QUALIFIER_SIZE];
+       u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)];
+       u32 word[FSF_STATUS_QUALIFIER_SIZE / sizeof (u32)];
+       struct {
+               u32 this_cmd;
+               u32 aborted_cmd;
+       } port_handle;
+       struct {
+               u32 this_cmd;
+               u32 aborted_cmd;
+       } lun_handle;
+       struct {
+               u64 found;
+               u64 expected;
+       } fcp_lun;
+} __attribute__ ((packed));
+
+struct fsf_qtcb_header {
+       u64 req_handle;
+       u32 fsf_command;
+       u32 res1;
+       u32 port_handle;
+       u32 lun_handle;
+       u32 res2;
+       u32 fsf_status;
+       union fsf_status_qual fsf_status_qual;
+       u8  res3[28];
+       u16 log_start;
+       u16 log_length;
+       u8  res4[16];
+} __attribute__ ((packed));
+
+struct fsf_nport_serv_param {
+       u8  common_serv_param[16];
+       u64 wwpn;
+       u64 wwnn;
+       u8  class1_serv_param[16];
+       u8  class2_serv_param[16];
+       u8  class3_serv_param[16];
+       u8  class4_serv_param[16];
+       u8  vendor_version_level[16];
+       u8  res1[16];
+} __attribute__ ((packed));
+
+struct fsf_plogi {
+       u32    code;
+       struct fsf_nport_serv_param serv_param;
+} __attribute__ ((packed));
+
+#define FSF_FCP_CMND_SIZE      288
+#define FSF_FCP_RSP_SIZE       128
+
+struct fsf_qtcb_bottom_io {
+       u32 data_direction;
+       u32 service_class;
+       u8  res1[8];
+       u32 fcp_cmnd_length;
+       u8  res2[12];
+       u8  fcp_cmnd[FSF_FCP_CMND_SIZE];
+       u8  fcp_rsp[FSF_FCP_RSP_SIZE];
+       u8  res3[64];
+} __attribute__ ((packed));
+
+struct fsf_qtcb_bottom_support {
+       u8  res1[16];
+       u32 d_id;
+       u32 res2;
+       u64 fcp_lun;
+       u64 res3;
+       u64 req_handle;
+       u32 service_class;
+       u8  res4[3];
+       u8  timeout;
+       u8  res5[184];
+       u32 els1_length;
+       u32 els2_length;
+       u64 res6;
+       u8  els[256];
+} __attribute__ ((packed));
+
+struct fsf_qtcb_bottom_config {
+       u32 lic_version;
+       u32 res1;
+       u32 high_qtcb_version;
+       u32 low_qtcb_version;
+       u32 max_qtcb_size;
+       u8  res2[12];
+       u32 fc_topology;
+       u32 fc_link_speed;
+       u32 adapter_type;
+       u32 peer_d_id;
+       u8  res3[12];
+       u32 s_id;
+       struct fsf_nport_serv_param nport_serv_param;
+       u8  res4[320];
+} __attribute__ ((packed));
+
+union fsf_qtcb_bottom {
+       struct fsf_qtcb_bottom_io      io;
+       struct fsf_qtcb_bottom_support support;
+       struct fsf_qtcb_bottom_config  config;
+};
+
+struct fsf_qtcb {
+       struct fsf_qtcb_prefix prefix;
+       struct fsf_qtcb_header header;
+       union  fsf_qtcb_bottom bottom;
+} __attribute__ ((packed));
+
+#endif                         /* __KERNEL__ */
+#endif                         /* FSF_H */
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
new file mode 100644 (file)
index 0000000..d5df69e
--- /dev/null
@@ -0,0 +1,557 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_qdio.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * QDIO related routines
+ *
+ * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
+ * Authors:
+ *      Martin Peschke <mpeschke@de.ibm.com>
+ *      Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ *      Wolfgang Taphorn <taphorn@de.ibm.com>
+ *      Heiko Carstens <heiko.carstens@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_QDIO_C_REVISION "$Revision: 1.7 $"
+
+#include "zfcp_ext.h"
+
+static qdio_handler_t zfcp_qdio_request_handler;
+static qdio_handler_t zfcp_qdio_response_handler;
+static int zfcp_qdio_handler_error_check(struct zfcp_adapter *,
+                                        unsigned int,
+                                        unsigned int, unsigned int);
+
+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
+
+/*
+ * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t 
+ * array in the adapter struct.
+ * Cur_buf is the pointer array and count can be any number of required 
+ * buffers, the page-fitting arithmetic is done entirely within this funciton.
+ *
+ * returns:    number of buffers allocated
+ * locks:       must only be called with zfcp_data.config_sema taken
+ */
+static int
+zfcp_qdio_buffers_enqueue(struct qdio_buffer **cur_buf, int count)
+{
+       int buf_pos;
+       int qdio_buffers_per_page;
+       int page_pos = 0;
+       struct qdio_buffer *first_in_page = NULL;
+
+       qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer);
+       ZFCP_LOG_TRACE("Buffers per page %d.\n", qdio_buffers_per_page);
+
+       for (buf_pos = 0; buf_pos < count; buf_pos++) {
+               if (page_pos == 0) {
+                       cur_buf[buf_pos] = (struct qdio_buffer *)
+                           get_zeroed_page(GFP_KERNEL);
+                       if (cur_buf[buf_pos] == NULL) {
+                               ZFCP_LOG_INFO("error: Could not allocate "
+                                             "memory for qdio transfer "
+                                             "structures.\n");
+                               goto out;
+                       }
+                       first_in_page = cur_buf[buf_pos];
+               } else {
+                       cur_buf[buf_pos] = first_in_page + page_pos;
+
+               }
+               /* was initialised to zero */
+               page_pos++;
+               page_pos %= qdio_buffers_per_page;
+       }
+ out:
+       return buf_pos;
+}
+
+/*
+ * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array
+ * in the adapter struct cur_buf is the pointer array and count can be any
+ * number of buffers in the array that should be freed starting from buffer 0
+ *
+ * locks:       must only be called with zfcp_data.config_sema taken
+ */
+static void
+zfcp_qdio_buffers_dequeue(struct qdio_buffer **cur_buf, int count)
+{
+       int buf_pos;
+       int qdio_buffers_per_page;
+
+       qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer);
+       ZFCP_LOG_TRACE("Buffers per page %d.\n", qdio_buffers_per_page);
+
+       for (buf_pos = 0; buf_pos < count; buf_pos += qdio_buffers_per_page)
+               free_page((unsigned long) cur_buf[buf_pos]);
+       return;
+}
+
+/* locks:       must only be called with zfcp_data.config_sema taken */
+int
+zfcp_qdio_allocate_queues(struct zfcp_adapter *adapter)
+{
+       int buffer_count;
+       int retval = 0;
+
+       buffer_count =
+           zfcp_qdio_buffers_enqueue(&(adapter->request_queue.buffer[0]),
+                                     QDIO_MAX_BUFFERS_PER_Q);
+       if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
+               ZFCP_LOG_DEBUG("error: Out of memory allocating "
+                              "request queue, only %d buffers got. "
+                              "Binning them.\n", buffer_count);
+               zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]),
+                                         buffer_count);
+               retval = -ENOMEM;
+               goto out;
+       }
+
+       buffer_count =
+           zfcp_qdio_buffers_enqueue(&(adapter->response_queue.buffer[0]),
+                                     QDIO_MAX_BUFFERS_PER_Q);
+       if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
+               ZFCP_LOG_DEBUG("error: Out of memory allocating "
+                              "response queue, only %d buffers got. "
+                              "Binning them.\n", buffer_count);
+               zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]),
+                                         buffer_count);
+               ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
+               zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]),
+                                         QDIO_MAX_BUFFERS_PER_Q);
+               retval = -ENOMEM;
+               goto out;
+       }
+ out:
+       return retval;
+}
+
+/* locks:       must only be called with zfcp_data.config_sema taken */
+void
+zfcp_qdio_free_queues(struct zfcp_adapter *adapter)
+{
+       ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
+       zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]),
+                                 QDIO_MAX_BUFFERS_PER_Q);
+
+       ZFCP_LOG_TRACE("Deallocating response_queue Buffers.\n");
+       zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]),
+                                 QDIO_MAX_BUFFERS_PER_Q);
+}
+
+int
+zfcp_qdio_allocate(struct zfcp_adapter *adapter)
+{
+       struct qdio_initialize *init_data;
+
+       init_data = &adapter->qdio_init_data;
+
+       init_data->cdev = adapter->ccw_device;
+       init_data->q_format = QDIO_SCSI_QFMT;
+       memcpy(init_data->adapter_name, &adapter->name, 8);
+       init_data->qib_param_field_format = 0;
+       init_data->qib_param_field = NULL;
+       init_data->input_slib_elements = NULL;
+       init_data->output_slib_elements = NULL;
+       init_data->min_input_threshold = ZFCP_MIN_INPUT_THRESHOLD;
+       init_data->max_input_threshold = ZFCP_MAX_INPUT_THRESHOLD;
+       init_data->min_output_threshold = ZFCP_MIN_OUTPUT_THRESHOLD;
+       init_data->max_output_threshold = ZFCP_MAX_OUTPUT_THRESHOLD;
+       init_data->no_input_qs = 1;
+       init_data->no_output_qs = 1;
+       init_data->input_handler = zfcp_qdio_response_handler;
+       init_data->output_handler = zfcp_qdio_request_handler;
+       init_data->int_parm = (unsigned long) adapter;
+       init_data->flags = QDIO_INBOUND_0COPY_SBALS |
+           QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
+       init_data->input_sbal_addr_array =
+           (void **) (adapter->response_queue.buffer);
+       init_data->output_sbal_addr_array =
+           (void **) (adapter->request_queue.buffer);
+
+       return qdio_allocate(init_data);
+}
+
+/*
+ * function:           zfcp_qdio_handler_error_check
+ *
+ * purpose:     called by the response handler to determine error condition
+ *
+ * returns:    error flag
+ *
+ */
+static inline int
+zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter,
+                             unsigned int status,
+                             unsigned int qdio_error, unsigned int siga_error)
+{
+       int retval = 0;
+
+       if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE)) {
+               if (status & QDIO_STATUS_INBOUND_INT) {
+                       ZFCP_LOG_TRACE("status is"
+                                      " QDIO_STATUS_INBOUND_INT \n");
+               }
+               if (status & QDIO_STATUS_OUTBOUND_INT) {
+                       ZFCP_LOG_TRACE("status is"
+                                      " QDIO_STATUS_OUTBOUND_INT \n");
+               }
+       }                       // if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE))
+       if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
+               retval = -EIO;
+
+               ZFCP_LOG_FLAGS(1, "QDIO_STATUS_LOOK_FOR_ERROR \n");
+
+               ZFCP_LOG_INFO("A qdio problem occured. The status, qdio_error "
+                             "and siga_error are 0x%x, 0x%x and 0x%x\n",
+                             status, qdio_error, siga_error);
+
+               if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
+                       ZFCP_LOG_FLAGS(2,
+                                      "QDIO_STATUS_ACTIVATE_CHECK_CONDITION\n");
+               }
+               if (status & QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR) {
+                       ZFCP_LOG_FLAGS(2,
+                                      "QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR\n");
+               }
+               if (status & QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR) {
+                       ZFCP_LOG_FLAGS(2,
+                                      "QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR\n");
+               }
+
+               if (siga_error & QDIO_SIGA_ERROR_ACCESS_EXCEPTION) {
+                       ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_ACCESS_EXCEPTION\n");
+               }
+
+               if (siga_error & QDIO_SIGA_ERROR_B_BIT_SET) {
+                       ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_B_BIT_SET\n");
+               }
+
+               switch (qdio_error) {
+               case 0:
+                       ZFCP_LOG_FLAGS(3, "QDIO_OK");
+                       break;
+               case SLSB_P_INPUT_ERROR:
+                       ZFCP_LOG_FLAGS(1, "SLSB_P_INPUT_ERROR\n");
+                       break;
+               case SLSB_P_OUTPUT_ERROR:
+                       ZFCP_LOG_FLAGS(1, "SLSB_P_OUTPUT_ERROR\n");
+                       break;
+               default:
+                       ZFCP_LOG_NORMAL("bug: Unknown qdio error reported "
+                                       "(debug info 0x%x)\n", qdio_error);
+                       break;
+               }
+               /* Restarting IO on the failed adapter from scratch */
+               debug_text_event(adapter->erp_dbf, 1, "qdio_err");
+               zfcp_erp_adapter_reopen(adapter, 0);
+       }
+       return retval;
+}
+
+/*
+ * function:    zfcp_qdio_request_handler
+ *
+ * purpose:    is called by QDIO layer for completed SBALs in request queue
+ *
+ * returns:    (void)
+ */
+static void
+zfcp_qdio_request_handler(struct ccw_device *ccw_device,
+                         unsigned int status,
+                         unsigned int qdio_error,
+                         unsigned int siga_error,
+                         unsigned int queue_number,
+                         int first_element,
+                         int elements_processed,
+                         unsigned long int_parm)
+{
+       struct zfcp_adapter *adapter;
+       struct zfcp_qdio_queue *queue;
+
+       adapter = (struct zfcp_adapter *) int_parm;
+       queue = &adapter->request_queue;
+
+       ZFCP_LOG_DEBUG("busid=%s, first=%d, count=%d\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      first_element, elements_processed);
+
+       if (zfcp_qdio_handler_error_check(adapter, status, qdio_error,
+                                         siga_error))
+               goto out;
+       /*
+        * we stored address of struct zfcp_adapter  data structure
+        * associated with irq in int_parm
+        */
+
+       /* cleanup all SBALs being program-owned now */
+       zfcp_qdio_zero_sbals(queue->buffer, first_element, elements_processed);
+
+       /* increase free space in outbound queue */
+       atomic_add(elements_processed, &queue->free_count);
+       ZFCP_LOG_DEBUG("free_count=%d\n", atomic_read(&queue->free_count));
+       wake_up(&adapter->request_wq);
+       ZFCP_LOG_DEBUG("Elements_processed = %d, free count=%d \n",
+                      elements_processed, atomic_read(&queue->free_count));
+ out:
+       return;
+}
+
+/*
+ * function:           zfcp_qdio_response_handler
+ *
+ * purpose:    is called by QDIO layer for completed SBALs in response queue
+ *
+ * returns:    (void)
+ */
+static void
+zfcp_qdio_response_handler(struct ccw_device *ccw_device,
+                          unsigned int status,
+                          unsigned int qdio_error,
+                          unsigned int siga_error,
+                          unsigned int queue_number,
+                          int first_element,
+                          int elements_processed,
+                          unsigned long int_parm)
+{
+       struct zfcp_adapter *adapter;
+       struct zfcp_qdio_queue *queue;
+       int buffer_index;
+       int i;
+       struct qdio_buffer *buffer;
+       int retval = 0;
+       u8 count;
+       u8 start;
+       volatile struct qdio_buffer_element *buffere = NULL;
+       int buffere_index;
+
+       adapter = (struct zfcp_adapter *) int_parm;
+       queue = &adapter->response_queue;
+
+       if (zfcp_qdio_handler_error_check(adapter, status, qdio_error,
+                                         siga_error))
+               goto out;
+
+       /*
+        * we stored address of struct zfcp_adapter  data structure
+        * associated with irq in int_parm
+        */
+
+       buffere = &(queue->buffer[first_element]->element[0]);
+       ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x \n ", buffere->flags);
+       /*
+        * go through all SBALs from input queue currently
+        * returned by QDIO layer
+        */
+
+       for (i = 0; i < elements_processed; i++) {
+
+               buffer_index = first_element + i;
+               buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
+               buffer = queue->buffer[buffer_index];
+
+               /* go through all SBALEs of SBAL */
+               for (buffere_index = 0;
+                    buffere_index < QDIO_MAX_ELEMENTS_PER_BUFFER;
+                    buffere_index++) {
+
+                       /* look for QDIO request identifiers in SB */
+                       buffere = &buffer->element[buffere_index];
+                       retval = zfcp_qdio_reqid_check(adapter,
+                                                      (void *) buffere->addr);
+
+                       if (retval) {
+                               ZFCP_LOG_NORMAL
+                                   ("bug: Inbound packet seems not to "
+                                    "have been sent at all. It will be "
+                                    "ignored. (debug info 0x%lx, 0x%lx, "
+                                    "%d, %d, %s)\n",
+                                    (unsigned long) buffere->addr,
+                                    (unsigned long) &(buffere->addr),
+                                    first_element, elements_processed,
+                                    zfcp_get_busid_by_adapter(adapter));
+                               ZFCP_LOG_NORMAL("Dump of inbound BUFFER %d "
+                                               "BUFFERE %d at address 0x%lx\n",
+                                               buffer_index, buffere_index,
+                                               (unsigned long) buffer);
+                               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                                             (char *) buffer, SBAL_SIZE);
+                       }
+                       if (buffere->flags & SBAL_FLAGS_LAST_ENTRY)
+                               break;
+               };
+
+               if (!buffere->flags & SBAL_FLAGS_LAST_ENTRY) {
+                       ZFCP_LOG_NORMAL("bug: End of inbound data "
+                                       "not marked!\n");
+               }
+       }
+
+       /*
+        * put range of SBALs back to response queue
+        * (including SBALs which have already been free before)
+        */
+       count = atomic_read(&queue->free_count) + elements_processed;
+       start = queue->free_index;
+
+       ZFCP_LOG_TRACE("Calling do QDIO busid=%s, flags=0x%x, queue_no=%i, "
+                      "index_in_queue=%i, count=%i, buffers=0x%lx\n",
+                      zfcp_get_busid_by_adapter(adapter),
+                      QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
+                      0, start, count, (unsigned long) &queue->buffer[start]);
+
+       retval = do_QDIO(ccw_device,
+                        QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
+                        0, start, count, NULL);
+
+       if (retval) {
+               atomic_set(&queue->free_count, count);
+               ZFCP_LOG_DEBUG("Inbound data regions could not be cleared "
+                              "Transfer queues may be down. "
+                              "(info %d, %d, %d)\n", count, start, retval);
+       } else {
+               queue->free_index += count;
+               queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
+               atomic_set(&queue->free_count, 0);
+               ZFCP_LOG_TRACE("%i buffers successfully enqueued to response "
+                              "queue starting at position %i\n", count, start);
+       }
+ out:
+       return;
+}
+
+/*
+ * function:   zfcp_qdio_reqid_check
+ *
+ * purpose:    checks for valid reqids or unsolicited status
+ *
+ * returns:    0 - valid request id or unsolicited status
+ *             !0 - otherwise
+ */
+int
+zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, void *sbale_addr)
+{
+       struct zfcp_fsf_req *fsf_req;
+       int retval = 0;
+
+#ifdef ZFCP_DEBUG_REQUESTS
+       /* Note: seq is entered later */
+       debug_text_event(adapter->req_dbf, 1, "i:a/seq");
+       debug_event(adapter->req_dbf, 1, &sbale_addr, sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+
+       /* invalid (per convention used in this driver) */
+       if (!sbale_addr) {
+               ZFCP_LOG_NORMAL
+                   ("bug: Inbound data faulty, contains null-pointer!\n");
+               retval = -EINVAL;
+               goto out;
+       }
+
+       /* valid request id and thus (hopefully :) valid fsf_req address */
+       fsf_req = (struct zfcp_fsf_req *) sbale_addr;
+
+       if ((fsf_req->common_magic != ZFCP_MAGIC) ||
+           (fsf_req->specific_magic != ZFCP_MAGIC_FSFREQ)) {
+               ZFCP_LOG_NORMAL("bug: An inbound FSF acknowledgement was "
+                               "faulty (debug info 0x%x, 0x%x, 0x%lx)\n",
+                               fsf_req->common_magic,
+                               fsf_req->specific_magic,
+                               (unsigned long) fsf_req);
+               retval = -EINVAL;
+               goto out;
+       }
+
+       if (adapter != fsf_req->adapter) {
+               ZFCP_LOG_NORMAL("bug: An inbound FSF acknowledgement was not "
+                               "correct (debug info 0x%lx, 0x%lx, 0%lx) \n",
+                               (unsigned long) fsf_req,
+                               (unsigned long) fsf_req->adapter,
+                               (unsigned long) adapter);
+               retval = -EINVAL;
+               goto out;
+       }
+#ifdef ZFCP_DEBUG_REQUESTS
+       /* debug feature stuff (test for QTCB: remember new unsol. status!) */
+       if (fsf_req->qtcb) {
+               debug_event(adapter->req_dbf, 1,
+                           &fsf_req->qtcb->prefix.req_seq_no, sizeof (u32));
+       }
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+
+       ZFCP_LOG_TRACE("fsf_req at 0x%lx, QTCB at 0x%lx\n",
+                      (unsigned long) fsf_req, (unsigned long) fsf_req->qtcb);
+       if (fsf_req->qtcb) {
+               ZFCP_LOG_TRACE("HEX DUMP OF 1ST BUFFERE PAYLOAD (QTCB):\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
+                             (char *) fsf_req->qtcb, ZFCP_QTCB_SIZE);
+       }
+
+       /* finish the FSF request */
+       zfcp_fsf_req_complete(fsf_req);
+ out:
+       return retval;
+}
+
+int
+zfcp_qdio_determine_pci(struct zfcp_qdio_queue *req_queue,
+                       struct zfcp_fsf_req *fsf_req)
+{
+       int new_distance_from_int;
+       int pci_pos;
+
+       new_distance_from_int = req_queue->distance_from_int +
+           fsf_req->sbal_count;
+       if (new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL) {
+               new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL;
+               pci_pos = fsf_req->sbal_index;
+               pci_pos += fsf_req->sbal_count;
+               pci_pos -= new_distance_from_int;
+               pci_pos -= 1;
+               pci_pos %= QDIO_MAX_BUFFERS_PER_Q;
+               req_queue->buffer[pci_pos]->element[0].flags |= SBAL_FLAGS0_PCI;
+               ZFCP_LOG_TRACE("Setting PCI flag at pos %d\n", pci_pos);
+       }
+       return new_distance_from_int;
+}
+
+/*
+ * function:   zfcp_zero_sbals
+ *
+ * purpose:    zeros specified range of SBALs
+ *
+ * returns:
+ */
+void
+zfcp_qdio_zero_sbals(struct qdio_buffer *buf[], int first, int clean_count)
+{
+       int cur_pos;
+       int index;
+
+       for (cur_pos = first; cur_pos < (first + clean_count); cur_pos++) {
+               index = cur_pos % QDIO_MAX_BUFFERS_PER_Q;
+               memset(buf[index], 0, sizeof (struct qdio_buffer));
+               ZFCP_LOG_TRACE("zeroing BUFFER %d at address 0x%lx\n",
+                              index, (unsigned long) buf[index]);
+       }
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
new file mode 100644 (file)
index 0000000..ccb145a
--- /dev/null
@@ -0,0 +1,1476 @@
+/* 
+ * 
+ * linux/drivers/s390/scsi/zfcp_scsi.c
+ * 
+ * FCP adapter driver for IBM eServer zSeries 
+ * 
+ * Copyright 2002 IBM Corporation 
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com> 
+ *            Raimund Schroeder <raimund.schroeder@de.ibm.com> 
+ *            Aron Zeh <arzeh@de.ibm.com> 
+ *            Wolfgang Taphorn <taphorn@de.ibm.com> 
+ *            Stefan Bader <stefan.bader@de.ibm.com> 
+ *            Heiko Carstens <heiko.carstens@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_SCSI
+#define ZFCP_LOG_AREA_PREFIX           ZFCP_LOG_AREA_PREFIX_SCSI
+/* this drivers version (do not edit !!! generated and updated by cvs) */
+#define ZFCP_SCSI_REVISION "$Revision: 1.38 $"
+
+#include <linux/blkdev.h>
+
+#include "zfcp_ext.h"
+
+static void zfcp_scsi_slave_destroy(struct scsi_device *sdp);
+static int zfcp_scsi_slave_alloc(struct scsi_device *sdp);
+static int zfcp_scsi_slave_configure(struct scsi_device *sdp);
+static int zfcp_scsi_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+static int zfcp_scsi_eh_abort_handler(Scsi_Cmnd *);
+static int zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd *);
+static int zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd *);
+static int zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd *);
+static int zfcp_task_management_function(struct zfcp_unit *, u8);
+
+static int zfcp_create_sbales_from_segment(unsigned long, int, int *,
+                                          int, int, int *, int *, int,
+                                          int, struct qdio_buffer **,
+                                          char);
+
+static int zfcp_create_sbale(unsigned long, int, int *, int, int, int *,
+                            int, int, int *, struct qdio_buffer **,
+                            char);
+
+static struct zfcp_unit *zfcp_scsi_determine_unit(struct zfcp_adapter *,
+                                                 Scsi_Cmnd *);
+static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, int, int);
+
+static struct device_attribute *zfcp_sysfs_sdev_attrs[];
+
+struct zfcp_data zfcp_data = {
+       .scsi_host_template = {
+             name:                    ZFCP_NAME,
+             proc_name:               "dummy",
+             proc_info:               NULL,
+             detect:                  NULL,
+             slave_alloc:             zfcp_scsi_slave_alloc,
+             slave_configure:         zfcp_scsi_slave_configure,
+             slave_destroy:           zfcp_scsi_slave_destroy,
+             queuecommand:            zfcp_scsi_queuecommand,
+             eh_abort_handler:        zfcp_scsi_eh_abort_handler,
+             eh_device_reset_handler: zfcp_scsi_eh_device_reset_handler,
+             eh_bus_reset_handler:    zfcp_scsi_eh_bus_reset_handler,
+             eh_host_reset_handler:   zfcp_scsi_eh_host_reset_handler,
+                                      /* FIXME(openfcp): Tune */
+             can_queue:               4096,
+             this_id:                 0,
+             /*
+              * FIXME:
+              * one less? can zfcp_create_sbale cope with it?
+              */
+             sg_tablesize:            ZFCP_MAX_SBALES_PER_REQ,
+             cmd_per_lun:             1,
+             unchecked_isa_dma:       0,
+             use_clustering:          1,
+             sdev_attrs:              zfcp_sysfs_sdev_attrs,
+       }
+       /* rest initialised with zeros */
+};
+
+/* Find start of Response Information in FCP response unit*/
+char *
+zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
+{
+       char *fcp_rsp_info_ptr;
+
+       fcp_rsp_info_ptr =
+           (unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
+
+       return fcp_rsp_info_ptr;
+}
+
+/* Find start of Sense Information in FCP response unit*/
+char *
+zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
+{
+       char *fcp_sns_info_ptr;
+
+       fcp_sns_info_ptr =
+           (unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
+       if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)
+               fcp_sns_info_ptr = (char *) fcp_sns_info_ptr +
+                   fcp_rsp_iu->fcp_rsp_len;
+
+       return fcp_sns_info_ptr;
+}
+
+fcp_dl_t *
+zfcp_get_fcp_dl_ptr(struct fcp_cmnd_iu * fcp_cmd)
+{
+       int additional_length = fcp_cmd->add_fcp_cdb_length << 2;
+       fcp_dl_t *fcp_dl_addr;
+
+       fcp_dl_addr = (fcp_dl_t *)
+               ((unsigned char *) fcp_cmd +
+                sizeof (struct fcp_cmnd_iu) + additional_length);
+       /*
+        * fcp_dl_addr = start address of fcp_cmnd structure + 
+        * size of fixed part + size of dynamically sized add_dcp_cdb field
+        * SEE FCP-2 documentation
+        */
+       return fcp_dl_addr;
+}
+
+fcp_dl_t
+zfcp_get_fcp_dl(struct fcp_cmnd_iu * fcp_cmd)
+{
+       return *zfcp_get_fcp_dl_ptr(fcp_cmd);
+}
+
+void
+zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, fcp_dl_t fcp_dl)
+{
+       *zfcp_get_fcp_dl_ptr(fcp_cmd) = fcp_dl;
+}
+
+/*
+ * note: it's a bit-or operation not an assignment
+ * regarding the specified byte
+ */
+static inline void
+set_byte(u32 * result, char status, char pos)
+{
+       *result |= status << (pos * 8);
+}
+
+void
+set_host_byte(u32 * result, char status)
+{
+       set_byte(result, status, 2);
+}
+
+void
+set_driver_byte(u32 * result, char status)
+{
+       set_byte(result, status, 3);
+}
+
+/*
+ * function:   zfcp_scsi_slave_alloc
+ *
+ * purpose:
+ *
+ * returns:
+ */
+
+static int
+zfcp_scsi_slave_alloc(struct scsi_device *sdp)
+{
+       struct zfcp_adapter *adapter;
+       struct zfcp_unit *unit;
+       unsigned long flags;
+       int retval = -ENODEV;
+
+       adapter = (struct zfcp_adapter *) sdp->host->hostdata[0];
+       if (!adapter)
+               goto out;
+
+       read_lock_irqsave(&zfcp_data.config_lock, flags);
+       unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
+       if (unit) {
+               sdp->hostdata = unit;
+               unit->device = sdp;
+               zfcp_unit_get(unit);
+               retval = 0;
+       }
+       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ out:
+       return retval;
+}
+
+/*
+ * function:   zfcp_scsi_slave_destroy
+ *
+ * purpose:
+ *
+ * returns:
+ */
+
+static void
+zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
+{
+       struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
+
+       if (unit) {
+               sdpnt->hostdata = NULL;
+               unit->device = NULL;
+               zfcp_unit_put(unit);
+       } else {
+               ZFCP_LOG_INFO("no unit associated with SCSI device at "
+                             "address 0x%lx\n", (unsigned long) sdpnt);
+       }
+}
+
+/*
+ * function:    zfcp_scsi_insert_into_fake_queue
+ *
+ * purpose:
+ *             
+ *
+ * returns:
+ *
+ * FIXME:      Is the following scenario possible and - even more interesting -
+ *             a problem? It reminds me of the famous 'no retry for tape' fix
+ *             (no problem for disks, but what is about tapes...)
+ *
+ *             device is unaccessable,
+ *             command A is put into the fake queue,
+ *             device becomes accessable again,
+ *             command B is queued to the device,
+ *             fake queue timer expires
+ *             command A is returned to the mid-layer
+ *             command A is queued to the device
+ */
+void
+zfcp_scsi_insert_into_fake_queue(struct zfcp_adapter *adapter,
+                                Scsi_Cmnd * new_cmnd)
+{
+       unsigned long flags;
+       Scsi_Cmnd *current_cmnd;
+
+       ZFCP_LOG_DEBUG("Faking SCSI command:\n");
+       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+                     (char *) new_cmnd->cmnd, new_cmnd->cmd_len);
+
+       new_cmnd->host_scribble = NULL;
+
+       write_lock_irqsave(&adapter->fake_list_lock, flags);
+       if (adapter->first_fake_cmnd == NULL) {
+               adapter->first_fake_cmnd = new_cmnd;
+               adapter->fake_scsi_timer.function =
+                   zfcp_scsi_process_and_clear_fake_queue;
+               adapter->fake_scsi_timer.data = (unsigned long) adapter;
+               adapter->fake_scsi_timer.expires =
+                   jiffies + ZFCP_FAKE_SCSI_COMPLETION_TIME;
+               add_timer(&adapter->fake_scsi_timer);
+       } else {
+               for (current_cmnd = adapter->first_fake_cmnd;
+                    current_cmnd->host_scribble != NULL;
+                    current_cmnd =
+                    (Scsi_Cmnd *) (current_cmnd->host_scribble)) ;
+               current_cmnd->host_scribble = (char *) new_cmnd;
+       }
+       write_unlock_irqrestore(&adapter->fake_list_lock, flags);
+}
+
+/*
+ * function:    zfcp_scsi_process_and_clear_fake_queue
+ *
+ * purpose:
+ *             
+ *
+ * returns:
+ */
+void
+zfcp_scsi_process_and_clear_fake_queue(unsigned long data)
+{
+       unsigned long flags;
+       struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+       Scsi_Cmnd *cur = adapter->first_fake_cmnd;
+       Scsi_Cmnd *next;
+
+       /*
+        * We need a common lock for scsi_req on command completion
+        * as well as on command abort to avoid race conditions
+        * during completions and aborts taking place at the same time.
+        * It needs to be the outer lock as in the eh_abort_handler.
+        */
+       read_lock_irqsave(&adapter->abort_lock, flags);
+       write_lock(&adapter->fake_list_lock);
+       while (cur) {
+               next = (Scsi_Cmnd *) cur->host_scribble;
+               cur->host_scribble = NULL;
+               zfcp_cmd_dbf_event_scsi("clrfake", cur);
+               cur->scsi_done(cur);
+               cur = next;
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 2, "fk_done:");
+               debug_event(adapter->req_dbf, 2, &cur, sizeof (unsigned long));
+#endif
+       }
+       adapter->first_fake_cmnd = NULL;
+       write_unlock(&adapter->fake_list_lock);
+       read_unlock_irqrestore(&adapter->abort_lock, flags);
+       return;
+}
+
+void
+zfcp_scsi_block_requests(struct Scsi_Host *shpnt)
+{
+       scsi_block_requests(shpnt);
+       /* This is still somewhat racy but the best I could imagine */
+       do {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(ZFCP_SCSI_HOST_FLUSH_TIMEOUT);
+
+       } while (shpnt->host_busy || shpnt->eh_active);
+}
+
+/* 
+ * Tries to associate a zfcp unit with the scsi device.
+ *
+ * returns:       unit pointer   if unit is found
+ *                NULL           otherwise
+ */
+struct zfcp_unit *
+zfcp_scsi_determine_unit(struct zfcp_adapter *adapter, Scsi_Cmnd * scpnt)
+{
+       struct zfcp_unit *unit;
+
+       /*
+        * figure out target device
+        * (stored there by zfcp_scsi_slave_alloc)
+        * ATTENTION: assumes hostdata initialized to NULL by
+        * mid layer (see scsi_scan.c)
+        */
+       unit = (struct zfcp_unit *) scpnt->device->hostdata;
+       if (!unit) {
+               ZFCP_LOG_DEBUG("logical unit (%i %i %i %i) not configured\n",
+                              scpnt->device->host->host_no,
+                              scpnt->device->channel,
+                              scpnt->device->id, scpnt->device->lun);
+               /*
+                * must fake SCSI command execution and scsi_done
+                * callback for non-configured logical unit
+                */
+               /* return this as long as we are unable to process requests */
+               set_host_byte(&scpnt->result, DID_NO_CONNECT);
+               zfcp_cmd_dbf_event_scsi("notconf", scpnt);
+               scpnt->scsi_done(scpnt);
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 2, "nc_done:");
+               debug_event(adapter->req_dbf, 2, &scpnt,
+                           sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+       }
+       return unit;
+}
+
+/*
+ * called from scsi midlayer to allow finetuning of a device.
+ */
+static int
+zfcp_scsi_slave_configure(struct scsi_device *sdp)
+{
+       if (sdp->tagged_supported)
+               scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, ZFCP_CMND_PER_LUN);
+       else
+               scsi_adjust_queue_depth(sdp, 0, 1);
+       return 0;
+}
+
+/* Sends command on a round-trip using SCSI stack */
+static void
+zfcp_scsi_queuecommand_fake(Scsi_Cmnd * scpnt, struct zfcp_adapter *adapter)
+{
+       ZFCP_LOG_DEBUG("Looping SCSI IO on the adapter %s.\n",
+                      zfcp_get_busid_by_adapter(adapter));
+       /* 
+        * Reset everything for devices with retries, allow at least one retry
+        * for others, e.g. tape.
+        */
+       scpnt->retries = 0;
+       if (scpnt->allowed == 1) {
+               scpnt->allowed = 2;
+       }
+       set_host_byte(&scpnt->result, DID_SOFT_ERROR);
+       set_driver_byte(&scpnt->result, SUGGEST_RETRY);
+       zfcp_scsi_insert_into_fake_queue(adapter, scpnt);
+}
+
+/* Complete a command immediately handing back DID_ERROR */
+static void
+zfcp_scsi_queuecommand_stop(Scsi_Cmnd * scpnt,
+                           struct zfcp_adapter *adapter,
+                           struct zfcp_unit *unit)
+{
+       /* Always pass through to upper layer */
+       scpnt->retries = scpnt->allowed - 1;
+       set_host_byte(&scpnt->result, DID_ERROR);
+       zfcp_cmd_dbf_event_scsi("stopping", scpnt);
+       /* return directly */
+       scpnt->scsi_done(scpnt);
+       if (adapter && unit) {
+               ZFCP_LOG_INFO("Stopping SCSI IO on the unit with FCP LUN 0x%Lx "
+                             "connected to the port with WWPN 0x%Lx at the "
+                             "adapter %s.\n",
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_adapter(adapter));
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 2, "de_done:");
+               debug_event(adapter->req_dbf, 2, &scpnt,
+                           sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+       } else {
+               ZFCP_LOG_INFO("There is no adapter registered in the zfcp "
+                             "module for the SCSI host with hostnumber %d. "
+                             "Stopping IO.\n", scpnt->device->host->host_no);
+       }
+}
+
+/*
+ * function:   zfcp_scsi_queuecommand
+ *
+ * purpose:    enqueues a SCSI command to the specified target device
+ *
+ * note:        The scsi_done midlayer function may be called directly from
+ *              within queuecommand provided queuecommand returns with
+ *              success (0).
+ *              If it fails, it is expected that the command could not be sent
+ *              and is still available for processing.
+ *              As we ensure that queuecommand never fails, we have the choice 
+ *              to call done directly wherever we please.
+ *              Thus, any kind of send errors other than those indicating
+ *              'infinite' retries will be reported directly.
+ *              Retry requests are put into a list to be processed under timer 
+ *              control once in a while to allow for other operations to
+ *              complete in the meantime.
+ *
+ * returns:    0 - success, SCSI command enqueued
+ *             !0 - failure, note that we never allow this to happen as the 
+ *              SCSI stack would block indefinitely should a non-zero return
+ *              value be reported if there are no outstanding commands
+ *              (as in when the queues are down)
+ */
+int
+zfcp_scsi_queuecommand(Scsi_Cmnd * scpnt, void (*done) (Scsi_Cmnd *))
+{
+       int temp_ret;
+       struct zfcp_unit *unit;
+       struct zfcp_adapter *adapter;
+
+       /* reset the status for this request */
+       scpnt->result = 0;
+       /* save address of mid layer call back function */
+       scpnt->scsi_done = done;
+       /*
+        * figure out adapter
+        * (previously stored there by the driver when
+        * the adapter was registered)
+        */
+       adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
+       /* NULL when the adapter was removed from the zfcp list */
+       if (adapter == NULL) {
+               zfcp_scsi_queuecommand_stop(scpnt, NULL, NULL);
+               goto out;
+       }
+
+       /* set when we have a unit/port list modification */
+       if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QUEUECOMMAND_BLOCK,
+                            &adapter->status)) {
+               zfcp_scsi_queuecommand_fake(scpnt, adapter);
+               goto out;
+       }
+
+       unit = zfcp_scsi_determine_unit(adapter, scpnt);
+       if (unit == NULL)
+               goto out;
+
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)
+           || !atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status)) {
+               zfcp_scsi_queuecommand_stop(scpnt, adapter, unit);
+               goto out;
+       }
+       if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status)) {
+               ZFCP_LOG_DEBUG("adapter %s not ready or unit with LUN 0x%Lx "
+                              "on the port with WWPN 0x%Lx in recovery.\n",
+                              zfcp_get_busid_by_adapter(adapter),
+                              unit->fcp_lun, unit->port->wwpn);
+               zfcp_scsi_queuecommand_fake(scpnt, adapter);
+               goto out;
+       }
+
+       atomic_inc(&adapter->scsi_reqs_active);
+
+       temp_ret = zfcp_fsf_send_fcp_command_task(adapter,
+                                                 unit,
+                                                 scpnt, ZFCP_REQ_AUTO_CLEANUP);
+
+       if (temp_ret < 0) {
+               ZFCP_LOG_DEBUG("error: Could not send a Send FCP Command\n");
+               atomic_dec(&adapter->scsi_reqs_active);
+               wake_up(&adapter->scsi_reqs_active_wq);
+               zfcp_scsi_queuecommand_fake(scpnt, adapter);
+       } else {
+#ifdef ZFCP_DEBUG_REQUESTS
+               debug_text_event(adapter->req_dbf, 3, "q_scpnt");
+               debug_event(adapter->req_dbf, 3, &scpnt,
+                           sizeof (unsigned long));
+#endif                         /* ZFCP_DEBUG_REQUESTS */
+       }
+ out:
+       return 0;
+}
+
+/*
+ * function:    zfcp_unit_lookup
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * context:    
+ */
+static struct zfcp_unit *
+zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, int id, int lun)
+{
+       struct zfcp_port *port;
+       struct zfcp_unit *unit, *retval = NULL;
+
+       list_for_each_entry(port, &adapter->port_list_head, list) {
+               if (id != port->scsi_id)
+                       continue;
+               list_for_each_entry(unit, &port->unit_list_head, list) {
+                       if (lun == unit->scsi_lun) {
+                               retval = unit;
+                               goto out;
+                       }
+               }
+       }
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_scsi_potential_abort_on_fake
+ *
+ * purpose:
+ *
+ * returns:     0 - no fake request aborted
+ *              1 - fake request was aborted
+ *
+ * context:    both the adapter->abort_lock and the 
+ *              adapter->fake_list_lock are assumed to be held write lock
+ *              irqsave
+ */
+int
+zfcp_scsi_potential_abort_on_fake(struct zfcp_adapter *adapter,
+                                 Scsi_Cmnd * cmnd)
+{
+       Scsi_Cmnd *cur = adapter->first_fake_cmnd;
+       Scsi_Cmnd *pre = NULL;
+       int retval = 0;
+
+       while (cur) {
+               if (cur == cmnd) {
+                       if (pre)
+                               pre->host_scribble = cur->host_scribble;
+                       else
+                               adapter->first_fake_cmnd =
+                                   (Scsi_Cmnd *) cur->host_scribble;
+                       cur->host_scribble = NULL;
+                       if (!adapter->first_fake_cmnd)
+                               del_timer(&adapter->fake_scsi_timer);
+                       retval = 1;
+                       break;
+               }
+               pre = cur;
+               cur = (Scsi_Cmnd *) cur->host_scribble;
+       }
+       return retval;
+}
+
+/*
+ * function:   zfcp_scsi_eh_abort_handler
+ *
+ * purpose:    tries to abort the specified (timed out) SCSI command
+ *
+ * note:       We do not need to care for a SCSI command which completes
+ *             normally but late during this abort routine runs.
+ *             We are allowed to return late commands to the SCSI stack.
+ *             It tracks the state of commands and will handle late commands.
+ *             (Usually, the normal completion of late commands is ignored with
+ *             respect to the running abort operation. Grep for 'done_late'
+ *             in the SCSI stacks sources.)
+ *
+ * returns:    SUCCESS - command has been aborted and cleaned up in internal
+ *                       bookkeeping,
+ *                       SCSI stack won't be called for aborted command
+ *             FAILED  - otherwise
+ */
+int
+zfcp_scsi_eh_abort_handler(Scsi_Cmnd * scpnt)
+{
+       int retval = SUCCESS;
+       struct zfcp_fsf_req *new_fsf_req, *old_fsf_req;
+       struct zfcp_adapter *adapter;
+       struct zfcp_unit *unit;
+       struct zfcp_port *port;
+       struct Scsi_Host *scsi_host;
+       union zfcp_req_data *req_data = NULL;
+       unsigned long flags;
+       u32 status = 0;
+
+       adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
+       scsi_host = scpnt->device->host;
+       unit = (struct zfcp_unit *) scpnt->device->hostdata;
+       port = unit->port;
+
+#ifdef ZFCP_DEBUG_ABORTS
+       /* the components of a abort_dbf record (fixed size record) */
+       u64 dbf_scsi_cmnd = (unsigned long) scpnt;
+       char dbf_opcode[ZFCP_ABORT_DBF_LENGTH];
+       wwn_t dbf_wwn = port->wwpn;
+       fcp_lun_t dbf_fcp_lun = unit->fcp_lun;
+       u64 dbf_retries = scpnt->retries;
+       u64 dbf_allowed = scpnt->allowed;
+       u64 dbf_timeout = 0;
+       u64 dbf_fsf_req = 0;
+       u64 dbf_fsf_status = 0;
+       u64 dbf_fsf_qual[2] = { 0, 0 };
+       char dbf_result[ZFCP_ABORT_DBF_LENGTH] = { "##undef" };
+
+       memset(dbf_opcode, 0, ZFCP_ABORT_DBF_LENGTH);
+       memcpy(dbf_opcode,
+              scpnt->cmnd,
+              min(scpnt->cmd_len, (unsigned char) ZFCP_ABORT_DBF_LENGTH));
+#endif
+
+        /*TRACE*/
+           ZFCP_LOG_INFO
+           ("Aborting for adapter=0x%lx, busid=%s, scsi_cmnd=0x%lx\n",
+            (unsigned long) adapter, zfcp_get_busid_by_adapter(adapter),
+            (unsigned long) scpnt);
+
+       spin_unlock_irq(scsi_host->host_lock);
+
+       /*
+        * Race condition between normal (late) completion and abort has
+        * to be avoided.
+        * The entirity of all accesses to scsi_req have to be atomic.
+        * scsi_req is usually part of the fsf_req (for requests which
+        * are not faked) and thus we block the release of fsf_req
+        * as long as we need to access scsi_req.
+        * For faked commands we use the same lock even if they are not
+        * put into the fsf_req queue. This makes implementation
+        * easier. 
+        */
+       write_lock_irqsave(&adapter->abort_lock, flags);
+
+       /*
+        * Check if we deal with a faked command, which we may just forget
+        * about from now on
+        */
+       write_lock(&adapter->fake_list_lock);
+       /* only need to go through list if there are faked requests */
+       if (adapter->first_fake_cmnd != NULL) {
+               if (zfcp_scsi_potential_abort_on_fake(adapter, scpnt)) {
+                       write_unlock(&adapter->fake_list_lock);
+                       write_unlock_irqrestore(&adapter->abort_lock, flags);
+                       ZFCP_LOG_INFO("A faked command was aborted\n");
+                       retval = SUCCESS;
+                       strncpy(dbf_result, "##faked", ZFCP_ABORT_DBF_LENGTH);
+                       goto out;
+               }
+       }
+       write_unlock(&adapter->fake_list_lock);
+
+       /*
+        * Check whether command has just completed and can not be aborted.
+        * Even if the command has just been completed late, we can access
+        * scpnt since the SCSI stack does not release it at least until
+        * this routine returns. (scpnt is parameter passed to this routine
+        * and must not disappear during abort even on late completion.)
+        */
+       req_data = (union zfcp_req_data *) scpnt->host_scribble;
+       /* DEBUG */
+       ZFCP_LOG_DEBUG("req_data=0x%lx\n", (unsigned long) req_data);
+       if (!req_data) {
+               ZFCP_LOG_DEBUG("late command completion overtook abort\n");
+               /*
+                * That's it.
+                * Do not initiate abort but return SUCCESS.
+                */
+               write_unlock_irqrestore(&adapter->abort_lock, flags);
+               retval = SUCCESS;
+               strncpy(dbf_result, "##late1", ZFCP_ABORT_DBF_LENGTH);
+               goto out;
+       }
+
+       /* Figure out which fsf_req needs to be aborted. */
+       old_fsf_req = req_data->send_fcp_command_task.fsf_req;
+#ifdef ZFCP_DEBUG_ABORTS
+       dbf_fsf_req = (unsigned long) old_fsf_req;
+       dbf_timeout =
+           (jiffies - req_data->send_fcp_command_task.start_jiffies) / HZ;
+#endif
+       /* DEBUG */
+       ZFCP_LOG_DEBUG("old_fsf_req=0x%lx\n", (unsigned long) old_fsf_req);
+       if (!old_fsf_req) {
+               write_unlock_irqrestore(&adapter->abort_lock, flags);
+               ZFCP_LOG_NORMAL("bug: No old fsf request found.\n");
+               ZFCP_LOG_NORMAL("req_data:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) req_data, sizeof (union zfcp_req_data));
+               ZFCP_LOG_NORMAL("scsi_cmnd:\n");
+               ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+                             (char *) scpnt, sizeof (struct scsi_cmnd));
+               retval = FAILED;
+               strncpy(dbf_result, "##bug:r", ZFCP_ABORT_DBF_LENGTH);
+               goto out;
+       }
+       old_fsf_req->data.send_fcp_command_task.scsi_cmnd = NULL;
+       /* mark old request as being aborted */
+       old_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
+       /*
+        * We have to collect all information (e.g. unit) needed by 
+        * zfcp_fsf_abort_fcp_command before calling that routine
+        * since that routine is not allowed to access
+        * fsf_req which it is going to abort.
+        * This is because of we need to release fsf_req_list_lock
+        * before calling zfcp_fsf_abort_fcp_command.
+        * Since this lock will not be held, fsf_req may complete
+        * late and may be released meanwhile.
+        */
+       ZFCP_LOG_DEBUG("unit=0x%lx, unit_fcp_lun=0x%Lx\n",
+                      (unsigned long) unit, unit->fcp_lun);
+
+       /*
+        * The 'Abort FCP Command' routine may block (call schedule)
+        * because it may wait for a free SBAL.
+        * That's why we must release the lock and enable the
+        * interrupts before.
+        * On the other hand we do not need the lock anymore since
+        * all critical accesses to scsi_req are done.
+        */
+       write_unlock_irqrestore(&adapter->abort_lock, flags);
+       /* call FSF routine which does the abort */
+       new_fsf_req = zfcp_fsf_abort_fcp_command((unsigned long) old_fsf_req,
+                                                adapter,
+                                                unit, ZFCP_WAIT_FOR_SBAL);
+       ZFCP_LOG_DEBUG("new_fsf_req=0x%lx\n", (unsigned long) new_fsf_req);
+       if (!new_fsf_req) {
+               retval = FAILED;
+               ZFCP_LOG_DEBUG("warning: Could not abort SCSI command "
+                              "at 0x%lx\n", (unsigned long) scpnt);
+               strncpy(dbf_result, "##nores", ZFCP_ABORT_DBF_LENGTH);
+               goto out;
+       }
+
+       /* wait for completion of abort */
+       ZFCP_LOG_DEBUG("Waiting for cleanup....\n");
+#ifdef ZFCP_DEBUG_ABORTS
+       /*
+        * FIXME:
+        * copying zfcp_fsf_req_wait_and_cleanup code is not really nice
+        */
+       __wait_event(new_fsf_req->completion_wq,
+                    new_fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+       status = new_fsf_req->status;
+       dbf_fsf_status = new_fsf_req->qtcb->header.fsf_status;
+       /*
+        * Ralphs special debug load provides timestamps in the FSF
+        * status qualifier. This might be specified later if being
+        * useful for debugging aborts.
+        */
+       dbf_fsf_qual[0] =
+           *(u64 *) & new_fsf_req->qtcb->header.fsf_status_qual.word[0];
+       dbf_fsf_qual[1] =
+           *(u64 *) & new_fsf_req->qtcb->header.fsf_status_qual.word[2];
+       zfcp_fsf_req_cleanup(new_fsf_req);
+#else
+       retval = zfcp_fsf_req_wait_and_cleanup(new_fsf_req,
+                                              ZFCP_UNINTERRUPTIBLE, &status);
+#endif
+       ZFCP_LOG_DEBUG("Waiting for cleanup complete, status=0x%x\n", status);
+       /* status should be valid since signals were not permitted */
+       if (status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
+               retval = SUCCESS;
+               strncpy(dbf_result, "##succ", ZFCP_ABORT_DBF_LENGTH);
+       } else if (status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
+               retval = SUCCESS;
+               strncpy(dbf_result, "##late2", ZFCP_ABORT_DBF_LENGTH);
+       } else {
+               retval = FAILED;
+               strncpy(dbf_result, "##fail", ZFCP_ABORT_DBF_LENGTH);
+       }
+
+      out:
+#ifdef ZFCP_DEBUG_ABORTS
+       debug_event(adapter->abort_dbf, 1, &dbf_scsi_cmnd, sizeof (u64));
+       debug_event(adapter->abort_dbf, 1, &dbf_opcode, ZFCP_ABORT_DBF_LENGTH);
+       debug_event(adapter->abort_dbf, 1, &dbf_wwn, sizeof (wwn_t));
+       debug_event(adapter->abort_dbf, 1, &dbf_fcp_lun, sizeof (fcp_lun_t));
+       debug_event(adapter->abort_dbf, 1, &dbf_retries, sizeof (u64));
+       debug_event(adapter->abort_dbf, 1, &dbf_allowed, sizeof (u64));
+       debug_event(adapter->abort_dbf, 1, &dbf_timeout, sizeof (u64));
+       debug_event(adapter->abort_dbf, 1, &dbf_fsf_req, sizeof (u64));
+       debug_event(adapter->abort_dbf, 1, &dbf_fsf_status, sizeof (u64));
+       debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[0], sizeof (u64));
+       debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[1], sizeof (u64));
+       debug_text_event(adapter->abort_dbf, 1, dbf_result);
+#endif
+       spin_lock_irq(scsi_host->host_lock);
+       return retval;
+}
+
+/*
+ * function:   zfcp_scsi_eh_device_reset_handler
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd * scpnt)
+{
+       int retval;
+       struct zfcp_unit *unit = (struct zfcp_unit *) scpnt->device->hostdata;
+       struct Scsi_Host *scsi_host = scpnt->device->host;
+
+       spin_unlock_irq(scsi_host->host_lock);
+
+       /*
+        * We should not be called to reset a target which we 'sent' faked SCSI
+        * commands since the abort of faked SCSI commands should always
+        * succeed (simply delete timer). 
+        */
+       if (!unit) {
+               ZFCP_LOG_NORMAL("bug: Tried to reset a non existant unit.\n");
+               retval = SUCCESS;
+               goto out;
+       }
+       ZFCP_LOG_NORMAL("Resetting Device fcp_lun=0x%Lx\n", unit->fcp_lun);
+
+       /*
+        * If we do not know whether the unit supports 'logical unit reset'
+        * then try 'logical unit reset' and proceed with 'target reset'
+        * if 'logical unit reset' fails.
+        * If the unit is known not to support 'logical unit reset' then
+        * skip 'logical unit reset' and try 'target reset' immediately.
+        */
+       if (!atomic_test_mask(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET,
+                             &unit->status)) {
+               retval =
+                   zfcp_task_management_function(unit, LOGICAL_UNIT_RESET);
+               if (retval) {
+                       ZFCP_LOG_DEBUG
+                           ("logical unit reset failed (unit=0x%lx)\n",
+                            (unsigned long) unit);
+                       if (retval == -ENOTSUPP)
+                               atomic_set_mask
+                                   (ZFCP_STATUS_UNIT_NOTSUPPUNITRESET,
+                                    &unit->status);
+                       /* fall through and try 'target reset' next */
+               } else {
+                       ZFCP_LOG_DEBUG
+                           ("logical unit reset succeeded (unit=0x%lx)\n",
+                            (unsigned long) unit);
+                       /* avoid 'target reset' */
+                       retval = SUCCESS;
+                       goto out;
+               }
+       }
+       retval = zfcp_task_management_function(unit, TARGET_RESET);
+       if (retval) {
+               ZFCP_LOG_DEBUG("target reset failed (unit=0x%lx)\n",
+                              (unsigned long) unit);
+               retval = FAILED;
+       } else {
+               ZFCP_LOG_DEBUG("target reset succeeded (unit=0x%lx)\n",
+                              (unsigned long) unit);
+               retval = SUCCESS;
+       }
+ out:
+       spin_lock_irq(scsi_host->host_lock);
+       return retval;
+}
+
+static int
+zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags)
+{
+       struct zfcp_adapter *adapter = unit->port->adapter;
+       int retval;
+       int status;
+       struct zfcp_fsf_req *fsf_req;
+
+       /* issue task management function */
+       fsf_req = zfcp_fsf_send_fcp_command_task_management
+           (adapter, unit, tm_flags, ZFCP_WAIT_FOR_SBAL);
+       if (!fsf_req) {
+               ZFCP_LOG_INFO("error: Out of resources. Could not create a "
+                             "task management (abort, reset, etc) request "
+                             "for the unit with FCP-LUN 0x%Lx connected to "
+                             "the port with WWPN 0x%Lx connected to "
+                             "the adapter %s.\n",
+                             unit->fcp_lun,
+                             unit->port->wwpn,
+                             zfcp_get_busid_by_adapter(adapter));
+               retval = -ENOMEM;
+               goto out;
+       }
+
+       retval = zfcp_fsf_req_wait_and_cleanup(fsf_req,
+                                              ZFCP_UNINTERRUPTIBLE, &status);
+       /*
+        * check completion status of task management function
+        * (status should always be valid since no signals permitted)
+        */
+       if (status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED)
+               retval = -EIO;
+       else if (status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP)
+               retval = -ENOTSUPP;
+       else
+               retval = 0;
+ out:
+       return retval;
+}
+
+/*
+ * function:   zfcp_scsi_eh_bus_reset_handler
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd * scpnt)
+{
+       int retval = 0;
+       struct zfcp_unit *unit;
+       struct Scsi_Host *scsi_host = scpnt->device->host;
+
+       spin_unlock_irq(scsi_host->host_lock);
+
+       unit = (struct zfcp_unit *) scpnt->device->hostdata;
+        /*DEBUG*/
+           ZFCP_LOG_NORMAL("Resetting because of problems with "
+                           "unit=0x%lx, unit_fcp_lun=0x%Lx\n",
+                           (unsigned long) unit, unit->fcp_lun);
+       zfcp_erp_adapter_reopen(unit->port->adapter, 0);
+       zfcp_erp_wait(unit->port->adapter);
+       retval = SUCCESS;
+
+       spin_lock_irq(scsi_host->host_lock);
+       return retval;
+}
+
+/*
+ * function:   zfcp_scsi_eh_host_reset_handler
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd * scpnt)
+{
+       int retval = 0;
+       struct zfcp_unit *unit;
+       struct Scsi_Host *scsi_host = scpnt->device->host;
+
+       spin_unlock_irq(scsi_host->host_lock);
+
+       unit = (struct zfcp_unit *) scpnt->device->hostdata;
+        /*DEBUG*/
+           ZFCP_LOG_NORMAL("Resetting because of problems with "
+                           "unit=0x%lx, unit_fcp_lun=0x%Lx\n",
+                           (unsigned long) unit, unit->fcp_lun);
+       zfcp_erp_adapter_reopen(unit->port->adapter, 0);
+       zfcp_erp_wait(unit->port->adapter);
+       retval = SUCCESS;
+
+       spin_lock_irq(scsi_host->host_lock);
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+int
+zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
+{
+       int retval = 0;
+       static unsigned int unique_id = 0;
+
+       /* register adapter as SCSI host with mid layer of SCSI stack */
+       adapter->scsi_host = scsi_host_alloc(&zfcp_data.scsi_host_template,
+                                            sizeof (struct zfcp_adapter *));
+       if (!adapter->scsi_host) {
+               ZFCP_LOG_NORMAL("error: Not enough free memory. "
+                               "Could not register adapter %s "
+                               "with the SCSI-stack.\n",
+                               zfcp_get_busid_by_adapter(adapter));
+               retval = -EIO;
+               goto out;
+       }
+       atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
+       ZFCP_LOG_DEBUG("host registered, scsi_host at 0x%lx\n",
+                      (unsigned long) adapter->scsi_host);
+
+       /* tell the SCSI stack some characteristics of this adapter */
+       adapter->scsi_host->max_id = adapter->max_scsi_id + 1;
+       adapter->scsi_host->max_lun = adapter->max_scsi_lun + 1;
+       adapter->scsi_host->max_channel = 0;
+       adapter->scsi_host->unique_id = unique_id++;    /* FIXME */
+       adapter->scsi_host->max_cmd_len = ZFCP_MAX_SCSI_CMND_LENGTH;
+       /*
+        * save a pointer to our own adapter data structure within
+        * hostdata field of SCSI host data structure
+        */
+       adapter->scsi_host->hostdata[0] = (unsigned long) adapter;
+
+       scsi_add_host(adapter->scsi_host, &adapter->ccw_device->dev);
+ out:
+       return retval;
+}
+
+/*
+ * function:   
+ *
+ * purpose:    
+ *
+ * returns:
+ */
+void
+zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
+{
+       struct Scsi_Host *shost;
+
+       shost = adapter->scsi_host;
+       if (!shost)
+               return;
+       scsi_remove_host(shost);
+       scsi_host_put(shost);
+
+       adapter->scsi_host = NULL;
+       return;
+}
+
+
+/**
+ * zfcp_create_sbales_from_segment - creates SBALEs
+ * @addr:          begin of this buffer segment
+ * @length_seg:           length of this buffer segment
+ * @length_total:  total length of buffer
+ * @length_min:    roll back if generated buffer smaller than this
+ * @length_max:           sum of all SBALEs (count) not larger than this
+ * @buffer_index:  position of current BUFFER
+ * @buffere_index: position of current BUFFERE
+ * @buffer_first:  first BUFFER used for this buffer
+ * @buffer_last:   last BUFFER in request queue allowed
+ * @buffer:        begin of SBAL array of request queue
+ * @sbtype:        storage-block type
+ */
+static int
+zfcp_create_sbales_from_segment(unsigned long addr, int length_seg,
+                               int *length_total, int length_min,
+                               int length_max, int *buffer_index,
+                               int *buffere_index, int buffer_first,
+                               int buffer_last, struct qdio_buffer *buffer[],
+                               char sbtype)
+{
+       int retval = 0;
+       int length = 0;
+
+       ZFCP_LOG_TRACE
+           ("SCSI data buffer segment with %i bytes from 0x%lx to 0x%lx\n",
+            length_seg, addr, (addr + length_seg) - 1);
+
+       if (!length_seg)
+               goto out;
+
+       if (addr & (PAGE_SIZE - 1)) {
+               length =
+                   min((int) (PAGE_SIZE - (addr & (PAGE_SIZE - 1))),
+                       length_seg);
+               ZFCP_LOG_TRACE
+                   ("address 0x%lx not on page boundary, length=0x%x\n",
+                    (unsigned long) addr, length);
+               retval =
+                   zfcp_create_sbale(addr, length, length_total, length_min,
+                                     length_max, buffer_index, buffer_first,
+                                     buffer_last, buffere_index, buffer,
+                                     sbtype);
+               if (retval) {
+                       /* no resources */
+                       goto out;
+               }
+               addr += length;
+               length = length_seg - length;
+       } else
+               length = length_seg;
+
+       while (length > 0) {
+               retval = zfcp_create_sbale(addr, min((int) PAGE_SIZE, length),
+                                          length_total, length_min, length_max,
+                                          buffer_index, buffer_first,
+                                          buffer_last, buffere_index, buffer,
+                                          sbtype);
+               if (*buffere_index > ZFCP_LAST_SBALE_PER_SBAL)
+                       ZFCP_LOG_NORMAL("bug: Filling output buffers with SCSI "
+                                       "data failed. Index ran out of bounds. "
+                                       "(debug info %d)\n", *buffere_index);
+               if (retval) {
+                       /* no resources */
+                       goto out;
+               }
+               length -= PAGE_SIZE;
+               addr += PAGE_SIZE;
+       }
+ out:
+       return retval;
+}
+
+/**
+ * zfcp_create_sbale - creates a single SBALE
+ * @addr:          begin of this buffer segment
+ * @length:        length of this buffer segment
+ * @length_total:  total length of buffer
+ * @length_min:    roll back if generated buffer smaller than this
+ * @length_max:    sum of all SBALEs (count) not larger than this
+ * @buffer_index:  position of current BUFFER
+ * @buffer_first:  first BUFFER used for this buffer
+ * @buffer_last:   last BUFFER allowed for this buffer
+ * @buffere_index: position of current BUFFERE of current BUFFER
+ * @buffer:        begin of SBAL array of request queue
+ * @sbtype:        storage-block type
+ */
+static int
+zfcp_create_sbale(unsigned long addr, int length, int *length_total,
+                 int length_min, int length_max, int *buffer_index,
+                 int buffer_first, int buffer_last, int *buffere_index,
+                 struct qdio_buffer *buffer[], char sbtype)
+{
+       int retval = 0;
+       int length_real, residual;
+       int buffers_used;
+
+       volatile struct qdio_buffer_element *buffere =
+           &(buffer[*buffer_index]->element[*buffere_index]);
+
+       /* check whether we hit the limit */
+       residual = length_max - *length_total;
+       if (residual == 0) {
+               ZFCP_LOG_TRACE("skip remaining %i bytes since length_max hit\n",
+                              length);
+               goto out;
+       }
+       length_real = min(length, residual);
+
+       /*
+        * figure out next BUFFERE
+        * (first BUFFERE of first BUFFER is skipped - 
+        * this is ok since it is reserved for the QTCB)
+        */
+       if (*buffere_index == ZFCP_LAST_SBALE_PER_SBAL) {
+               /* last BUFFERE in this BUFFER */
+               buffere->flags |= SBAL_FLAGS_LAST_ENTRY;
+               /* need further BUFFER */
+               if (*buffer_index == buffer_last) {
+                       /* queue full or last allowed BUFFER */
+                       buffers_used = (buffer_last - buffer_first) + 1;
+                       /* avoid modulo operation on negative value */
+                       buffers_used += QDIO_MAX_BUFFERS_PER_Q;
+                       buffers_used %= QDIO_MAX_BUFFERS_PER_Q;
+                       ZFCP_LOG_DEBUG("reached limit of number of BUFFERs "
+                                      "allowed for this request\n");
+                       /* FIXME (design) - This check is wrong and enforces the
+                        * use of one SBALE less than possible 
+                        */
+                       if ((*length_total < length_min)
+                           || (buffers_used < ZFCP_MAX_SBALS_PER_REQ)) {
+                               ZFCP_LOG_DEBUG("Rolling back SCSI command as "
+                                              "there are insufficient buffers "
+                                              "to cover the minimum required "
+                                              "amount of data\n");
+                               /*
+                                * roll back complete list of BUFFERs generated
+                                * from the scatter-gather list associated
+                                * with this SCSI command
+                                */
+                               zfcp_qdio_zero_sbals(buffer,
+                                                    buffer_first,
+                                                    buffers_used);
+                               *length_total = 0;
+                       } else {
+                               /* DEBUG */
+                               ZFCP_LOG_NORMAL("Not enough buffers available. "
+                                               "Can only transfer %i bytes of "
+                                               "data\n",
+                                               *length_total);
+                       }
+                       retval = -ENOMEM;
+                       goto out;
+               } else {        /* *buffer_index != buffer_last */
+                       /* chain BUFFERs */
+                       *buffere_index = 0;
+                       buffere =
+                           &(buffer[*buffer_index]->element[*buffere_index]);
+                       buffere->flags |= SBAL_FLAGS0_MORE_SBALS;
+                       (*buffer_index)++;
+                       *buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
+                       buffere =
+                           &(buffer[*buffer_index]->element[*buffere_index]);
+                       buffere->flags |= sbtype;
+                       ZFCP_LOG_DEBUG
+                           ("Chaining previous BUFFER %i to BUFFER %i\n",
+                            ((*buffer_index !=
+                              0) ? *buffer_index - 1 : QDIO_MAX_BUFFERS_PER_Q -
+                             1), *buffer_index);
+               }
+       } else { /* *buffere_index != (QDIO_MAX_ELEMENTS_PER_BUFFER - 1) */
+               (*buffere_index)++;
+               buffere = &(buffer[*buffer_index]->element[*buffere_index]);
+       }
+
+       /* ok, found a place for this piece, put it there */
+       buffere->addr = (void *) addr;
+       buffere->length = length_real;
+
+#ifdef ZFCP_STAT_REQSIZES
+       if (sbtype == SBAL_FLAGS0_TYPE_READ)
+               zfcp_statistics_inc(&zfcp_data.read_sg_head, length_real);
+       else
+               zfcp_statistics_inc(&zfcp_data.write_sg_head, length_real);
+#endif
+
+       ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) addr, length_real);
+       ZFCP_LOG_TRACE("BUFFER no %i (0x%lx) BUFFERE no %i (0x%lx): BUFFERE "
+                      "data addr 0x%lx, BUFFERE length %i, BUFFER type %i\n",
+                      *buffer_index,
+                      (unsigned long) &buffer[*buffer_index], *buffere_index,
+                      (unsigned long) buffere, addr, length_real, sbtype);
+       *length_total += length_real;
+ out:
+       return retval;
+}
+
+/*
+ * function:    zfcp_create_sbals_from_sg
+ *
+ * purpose:    walks through scatter-gather list of specified SCSI command
+ *             and creates a corresponding list of SBALs
+ *
+ * returns:    size of generated buffer in bytes 
+ *
+ * context:    
+ */
+int
+zfcp_create_sbals_from_sg(struct zfcp_fsf_req *fsf_req, Scsi_Cmnd * scpnt,
+                         char sbtype,  /* storage-block type */
+                         int length_min, /* roll back if generated buffer */
+                         int buffer_max) /* max numbers of BUFFERs */
+{
+       int length_total = 0;
+       int buffer_index = 0;
+       int buffer_last = 0;
+       int buffere_index = 1;  /* elements 0 and 1 are req-id and qtcb */
+       volatile struct qdio_buffer_element *buffere = NULL;
+       struct zfcp_qdio_queue *req_q = NULL;
+       int length_max = scpnt->request_bufflen;
+
+       req_q = &fsf_req->adapter->request_queue;
+
+       buffer_index = req_q->free_index;
+       buffer_last = req_q->free_index +
+           min(buffer_max, atomic_read(&req_q->free_count)) - 1;
+       buffer_last %= QDIO_MAX_BUFFERS_PER_Q;
+
+       ZFCP_LOG_TRACE
+           ("total SCSI data buffer size is (scpnt->request_bufflen) %i\n",
+            scpnt->request_bufflen);
+       ZFCP_LOG_TRACE
+           ("BUFFERs from (buffer_index)%i to (buffer_last)%i available\n",
+            buffer_index, buffer_last);
+       ZFCP_LOG_TRACE("buffer_max=%d, req_q->free_count=%d\n", buffer_max,
+                      atomic_read(&req_q->free_count));
+
+       if (scpnt->use_sg) {
+               int sg_index;
+               struct scatterlist *list
+                   = (struct scatterlist *) scpnt->request_buffer;
+
+               ZFCP_LOG_DEBUG("%i (scpnt->use_sg) scatter-gather segments\n",
+                              scpnt->use_sg);
+
+               //                length_max+=0x2100;
+
+#ifdef ZFCP_STAT_REQSIZES
+               if (sbtype == SBAL_FLAGS0_TYPE_READ)
+                       zfcp_statistics_inc(&zfcp_data.read_sguse_head,
+                                           scpnt->use_sg);
+               else
+                       zfcp_statistics_inc(&zfcp_data.write_sguse_head,
+                                           scpnt->use_sg);
+#endif
+
+               for (sg_index = 0; sg_index < scpnt->use_sg; sg_index++, list++)
+               {
+                       if (zfcp_create_sbales_from_segment(
+                                   (page_to_pfn (list->page) << PAGE_SHIFT) +
+                                   list->offset,
+                                   list->length,
+                                   &length_total,
+                                   length_min,
+                                   length_max,
+                                   &buffer_index,
+                                   &buffere_index,
+                                   req_q->free_index,
+                                   buffer_last,
+                                   req_q->buffer,
+                                   sbtype))
+                               break;
+               }
+       } else {
+               ZFCP_LOG_DEBUG("no scatter-gather list\n");
+#ifdef ZFCP_STAT_REQSIZES
+               if (sbtype == SBAL_FLAGS0_TYPE_READ)
+                       zfcp_statistics_inc(&zfcp_data.read_sguse_head, 1);
+               else
+                       zfcp_statistics_inc(&zfcp_data.write_sguse_head, 1);
+#endif
+               zfcp_create_sbales_from_segment(
+                       (unsigned long) scpnt->request_buffer,
+                       scpnt->request_bufflen,
+                       &length_total,
+                       length_min,
+                       length_max,
+                       &buffer_index,
+                       &buffere_index,
+                       req_q->free_index,
+                       buffer_last,
+                       req_q->buffer,
+                       sbtype);
+       }
+
+       fsf_req->sbal_index = req_q->free_index;
+
+       if (buffer_index >= fsf_req->sbal_index) {
+               fsf_req->sbal_count = (buffer_index - fsf_req->sbal_index) + 1;
+       } else {
+               fsf_req->sbal_count =
+                   (QDIO_MAX_BUFFERS_PER_Q - fsf_req->sbal_index) +
+                   buffer_index + 1;
+       }
+       /* HACK */
+       if ((scpnt->request_bufflen != 0) && (length_total == 0))
+               goto out;
+
+#ifdef ZFCP_STAT_REQSIZES
+       if (sbtype == SBAL_FLAGS0_TYPE_READ)
+               zfcp_statistics_inc(&zfcp_data.read_req_head, length_total);
+       else
+               zfcp_statistics_inc(&zfcp_data.write_req_head, length_total);
+#endif
+
+       buffere = &(req_q->buffer[buffer_index]->element[buffere_index]);
+       buffere->flags |= SBAL_FLAGS_LAST_ENTRY;
+ out:
+       ZFCP_LOG_DEBUG("%i BUFFER(s) from %i to %i needed\n",
+                      fsf_req->sbal_count, fsf_req->sbal_index, buffer_index);
+       ZFCP_LOG_TRACE("total QDIO data buffer size is %i\n", length_total);
+
+       return length_total;
+}
+
+void
+zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *adapter)
+{
+       adapter->scsi_er_timer.function = zfcp_fsf_scsi_er_timeout_handler;
+       adapter->scsi_er_timer.data = (unsigned long) adapter;
+       adapter->scsi_er_timer.expires = jiffies + ZFCP_SCSI_ER_TIMEOUT;
+       add_timer(&adapter->scsi_er_timer);
+}
+
+/**
+ * zfcp_sysfs_hba_id_show - display hba_id of scsi device
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * "hba_id" attribute of a scsi device. Displays hba_id (bus_id)
+ * of the adapter belonging to a scsi device.
+ */
+static ssize_t
+zfcp_sysfs_hba_id_show(struct device *dev, char *buf)
+{
+       struct scsi_device *sdev;
+       struct zfcp_unit *unit;
+
+       sdev = to_scsi_device(dev);
+       unit = (struct zfcp_unit *) sdev->hostdata;
+       return sprintf(buf, "%s\n", zfcp_get_busid_by_unit(unit));
+}
+
+static DEVICE_ATTR(hba_id, S_IRUGO, zfcp_sysfs_hba_id_show, NULL);
+
+/**
+ * zfcp_sysfs_wwpn_show - display wwpn of scsi device
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * "wwpn" attribute of a scsi device. Displays wwpn of the port
+ * belonging to a scsi device.
+ */
+static ssize_t
+zfcp_sysfs_wwpn_show(struct device *dev, char *buf)
+{
+       struct scsi_device *sdev;
+       struct zfcp_unit *unit;
+
+       sdev = to_scsi_device(dev);
+       unit = (struct zfcp_unit *) sdev->hostdata;
+       return sprintf(buf, "0x%016llx\n", unit->port->wwpn);
+}
+
+static DEVICE_ATTR(wwpn, S_IRUGO, zfcp_sysfs_wwpn_show, NULL);
+
+/**
+ * zfcp_sysfs_fcp_lun_show - display fcp lun of scsi device
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * "fcp_lun" attribute of a scsi device. Displays fcp_lun of the unit
+ * belonging to a scsi device.
+ */
+static ssize_t
+zfcp_sysfs_fcp_lun_show(struct device *dev, char *buf)
+{
+       struct scsi_device *sdev;
+       struct zfcp_unit *unit;
+
+       sdev = to_scsi_device(dev);
+       unit = (struct zfcp_unit *) sdev->hostdata;
+       return sprintf(buf, "0x%016llx\n", unit->fcp_lun);
+}
+
+static DEVICE_ATTR(fcp_lun, S_IRUGO, zfcp_sysfs_fcp_lun_show, NULL);
+
+static struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
+       &dev_attr_fcp_lun,
+       &dev_attr_wwpn,
+       &dev_attr_hba_id,
+       NULL
+};
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c
new file mode 100644 (file)
index 0000000..48182df
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_adapter.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs adapter related routines
+ *
+ * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
+ * Authors:
+ *      Martin Peschke <mpeschke@de.ibm.com>
+ *     Heiko Carstens <heiko.carstens@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_SYSFS_ADAPTER_C_REVISION "$Revision: 1.21 $"
+
+#include <asm/ccwdev.h>
+#include "zfcp_ext.h"
+#include "zfcp_def.h"
+
+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_CONFIG
+
+static const char fc_topologies[5][25] = {
+       {"<error>"},
+       {"point-to-point"},
+       {"fabric"},
+       {"arbitrated loop"},
+       {"fabric (virt. adapter)"}
+};
+
+/**
+ * ZFCP_DEFINE_ADAPTER_ATTR
+ * @_name:   name of show attribute
+ * @_format: format string
+ * @_value:  value to print
+ *
+ * Generates attributes for an adapter.
+ */
+#define ZFCP_DEFINE_ADAPTER_ATTR(_name, _format, _value)                      \
+static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev,          \
+                                                char *buf)                   \
+{                                                                             \
+       struct zfcp_adapter *adapter;                                         \
+                                                                              \
+       adapter = dev_get_drvdata(dev);                                       \
+       return sprintf(buf, _format, _value);                                 \
+}                                                                             \
+                                                                              \
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
+
+ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
+ZFCP_DEFINE_ADAPTER_ATTR(wwnn, "0x%016llx\n", adapter->wwnn);
+ZFCP_DEFINE_ADAPTER_ATTR(wwpn, "0x%016llx\n", adapter->wwpn);
+ZFCP_DEFINE_ADAPTER_ATTR(s_id, "0x%06x\n", adapter->s_id);
+ZFCP_DEFINE_ADAPTER_ATTR(hw_version, "0x%04x\n", adapter->hydra_version);
+ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
+ZFCP_DEFINE_ADAPTER_ATTR(fc_link_speed, "%d Gb/s\n", adapter->fc_link_speed);
+ZFCP_DEFINE_ADAPTER_ATTR(fc_service_class, "%d\n", adapter->fc_service_class);
+ZFCP_DEFINE_ADAPTER_ATTR(fc_topology, "%s\n",
+                        fc_topologies[adapter->fc_topology]);
+
+/**
+ * zfcp_sysfs_adapter_in_recovery_show - recovery state of adapter
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * Show function of "in_recovery" attribute of adapter. Will be
+ * "0" if no error recovery is pending for adapter, otherwise "1".
+ */
+static ssize_t
+zfcp_sysfs_adapter_in_recovery_show(struct device *dev, char *buf)
+{
+       struct zfcp_adapter *adapter;
+
+       adapter = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR(in_recovery, S_IRUGO,
+                  zfcp_sysfs_adapter_in_recovery_show, NULL);
+
+/**
+ * zfcp_sysfs_adapter_scsi_host_no_show - display scsi_host_no of adapter
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * "scsi_host_no" attribute of adapter. Displays the SCSI host number.
+ */
+static ssize_t
+zfcp_sysfs_adapter_scsi_host_no_show(struct device *dev, char *buf)
+{
+       struct zfcp_adapter *adapter;
+       unsigned short host_no = 0;
+
+       down(&zfcp_data.config_sema);
+       adapter = dev_get_drvdata(dev);
+       if (adapter->scsi_host)
+               host_no = adapter->scsi_host->host_no;
+       up(&zfcp_data.config_sema);
+       return sprintf(buf, "0x%x\n", host_no);
+}
+
+static DEVICE_ATTR(scsi_host_no, S_IRUGO, zfcp_sysfs_adapter_scsi_host_no_show,
+                  NULL);
+
+/**
+ * zfcp_sysfs_port_add_store - add a port to sysfs tree
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * @count: number of bytes in buffer
+ *
+ * Store function of the "port_add" attribute of an adapter.
+ */
+static ssize_t
+zfcp_sysfs_port_add_store(struct device *dev, const char *buf, size_t count)
+{
+       wwn_t wwpn;
+       char *endp;
+       struct zfcp_adapter *adapter;
+       struct zfcp_port *port;
+       int retval = -EINVAL;
+
+       down(&zfcp_data.config_sema);
+
+       adapter = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       wwpn = simple_strtoull(buf, &endp, 0);
+       if ((endp + 1) < (buf + count))
+               goto out;
+
+       port = zfcp_port_enqueue(adapter, wwpn, 0);
+       if (!port)
+               goto out;
+
+       retval = 0;
+
+       zfcp_adapter_get(adapter);
+
+       /* try to open port only if adapter is online */
+       if (adapter->ccw_device->online == 1)
+               zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED);
+       zfcp_port_put(port);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval ? retval : count;
+}
+
+static DEVICE_ATTR(port_add, S_IWUSR, NULL, zfcp_sysfs_port_add_store);
+
+/**
+ * zfcp_sysfs_port_remove_store - remove a port from sysfs tree
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * @count: number of bytes in buffer
+ *
+ * Store function of the "port_remove" attribute of an adapter.
+ */
+static ssize_t
+zfcp_sysfs_port_remove_store(struct device *dev, const char *buf, size_t count)
+{
+       struct zfcp_adapter *adapter;
+       struct zfcp_port *port;
+       wwn_t wwpn;
+       char *endp;
+       int retval = 0;
+
+       down(&zfcp_data.config_sema);
+
+       adapter = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       wwpn = simple_strtoull(buf, &endp, 0);
+       if ((endp + 1) < (buf + count)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       write_lock_irq(&zfcp_data.config_lock);
+       port = zfcp_get_port_by_wwpn(adapter, wwpn);
+       if (port && (atomic_read(&port->refcount) == 0)) {
+               zfcp_port_get(port);
+               atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+               list_move(&port->list, &adapter->port_remove_lh);
+       }
+       else {
+               port = NULL;
+       }
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       if (!port) {
+               retval = -ENXIO;
+               goto out;
+       }
+
+       zfcp_erp_port_shutdown(port, 0);
+       zfcp_erp_wait(adapter);
+       zfcp_port_put(port);
+       device_unregister(&port->sysfs_device);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval ? retval : count;
+}
+
+static DEVICE_ATTR(port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store);
+
+/**
+ * zfcp_sysfs_adapter_failed_store - failed state of adapter
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * @count: number of bytes in buffer
+ *
+ * Store function of the "failed" attribute of an adapter.
+ * If a "0" gets written to "failed", error recovery will be
+ * started for the belonging adapter.
+ */
+static ssize_t
+zfcp_sysfs_adapter_failed_store(struct device *dev,
+                               const char *buf, size_t count)
+{
+       struct zfcp_adapter *adapter;
+       unsigned int val;
+       char *endp;
+       int retval = 0;
+
+       down(&zfcp_data.config_sema);
+
+       adapter = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val != 0)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       /* restart error recovery only if adapter is online */
+       if (adapter->ccw_device->online != 1) {
+               retval = -ENXIO;
+               goto out;
+       }
+       zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING,
+                                      ZFCP_SET);
+       zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval ? retval : count;
+}
+
+/**
+ * zfcp_sysfs_adapter_failed_show - failed state of adapter
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * Show function of "failed" attribute of adapter. Will be
+ * "0" if adapter is working, otherwise "1".
+ */
+static ssize_t
+zfcp_sysfs_adapter_failed_show(struct device *dev, char *buf)
+{
+       struct zfcp_adapter *adapter;
+
+       adapter = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_adapter_failed_show,
+                  zfcp_sysfs_adapter_failed_store);
+
+static struct attribute *zfcp_adapter_attrs[] = {
+       &dev_attr_failed.attr,
+       &dev_attr_in_recovery.attr,
+       &dev_attr_port_remove.attr,
+       &dev_attr_port_add.attr,
+       &dev_attr_wwnn.attr,
+       &dev_attr_wwpn.attr,
+       &dev_attr_s_id.attr,
+       &dev_attr_hw_version.attr,
+       &dev_attr_lic_version.attr,
+       &dev_attr_fc_link_speed.attr,
+       &dev_attr_fc_service_class.attr,
+       &dev_attr_fc_topology.attr,
+       &dev_attr_scsi_host_no.attr,
+       &dev_attr_status.attr,
+       NULL
+};
+
+static struct attribute_group zfcp_adapter_attr_group = {
+       .attrs = zfcp_adapter_attrs,
+};
+
+/**
+ * zfcp_sysfs_create_adapter_files - create sysfs adapter files
+ * @dev: pointer to belonging device
+ *
+ * Create all attributes of the sysfs representation of an adapter.
+ */
+int
+zfcp_sysfs_adapter_create_files(struct device *dev)
+{
+       return sysfs_create_group(&dev->kobj, &zfcp_adapter_attr_group);
+}
+
+/**
+ * zfcp_sysfs_remove_adapter_files - remove sysfs adapter files
+ * @dev: pointer to belonging device
+ *
+ * Remove all attributes of the sysfs representation of an adapter.
+ */
+void
+zfcp_sysfs_adapter_remove_files(struct device *dev)
+{
+       sysfs_remove_group(&dev->kobj, &zfcp_adapter_attr_group);
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_sysfs_driver.c b/drivers/s390/scsi/zfcp_sysfs_driver.c
new file mode 100644 (file)
index 0000000..9908f25
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_driver.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs driver related routines
+ *
+ * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
+ * Authors:
+ *      Martin Peschke <mpeschke@de.ibm.com>
+ *     Heiko Carstens <heiko.carstens@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_SYSFS_DRIVER_C_REVISION "$Revision: 1.8 $"
+
+#include <asm/ccwdev.h>
+#include "zfcp_ext.h"
+#include "zfcp_def.h"
+
+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_CONFIG
+
+/**
+ * ZFCP_DEFINE_DRIVER_ATTR - define for all loglevels sysfs attributes
+ * @_name:       name of attribute
+ * @_define:     name of ZFCP loglevel define
+ *
+ * Generates store function for a sysfs loglevel attribute of zfcp driver.
+ */
+#define ZFCP_DEFINE_DRIVER_ATTR(_name, _define)                               \
+static ssize_t zfcp_sysfs_loglevel_##_name##_store(struct device_driver *drv, \
+                                                  const char *buf,           \
+                                                  size_t count)              \
+{                                                                             \
+       unsigned int loglevel;                                                \
+       unsigned int new_loglevel;                                            \
+       char *endp;                                                           \
+                                                                              \
+       new_loglevel = simple_strtoul(buf, &endp, 0);                         \
+       if ((endp + 1) < (buf + count))                                       \
+               return -EINVAL;                                               \
+       if (new_loglevel > 3)                                                 \
+               return -EINVAL;                                               \
+       down(&zfcp_data.config_sema);                                         \
+       loglevel = atomic_read(&zfcp_data.loglevel);                          \
+       loglevel &= ~((unsigned int) 0xf << (ZFCP_LOG_AREA_##_define << 2));  \
+       loglevel |= new_loglevel << (ZFCP_LOG_AREA_##_define << 2);           \
+       atomic_set(&zfcp_data.loglevel, loglevel);                            \
+       up(&zfcp_data.config_sema);                                           \
+       return count;                                                         \
+}                                                                             \
+                                                                              \
+static ssize_t zfcp_sysfs_loglevel_##_name##_show(struct device_driver *dev,  \
+                                                 char *buf)                  \
+{                                                                             \
+       return sprintf(buf,"%d\n", ZFCP_LOG_VALUE(ZFCP_LOG_AREA_##_define));  \
+}                                                                             \
+                                                                              \
+static DRIVER_ATTR(loglevel_##_name, S_IWUSR | S_IRUGO,                       \
+                  zfcp_sysfs_loglevel_##_name##_show,                        \
+                  zfcp_sysfs_loglevel_##_name##_store);
+
+ZFCP_DEFINE_DRIVER_ATTR(other, OTHER);
+ZFCP_DEFINE_DRIVER_ATTR(scsi, SCSI);
+ZFCP_DEFINE_DRIVER_ATTR(fsf, FSF);
+ZFCP_DEFINE_DRIVER_ATTR(config, CONFIG);
+ZFCP_DEFINE_DRIVER_ATTR(cio, CIO);
+ZFCP_DEFINE_DRIVER_ATTR(qdio, QDIO);
+ZFCP_DEFINE_DRIVER_ATTR(erp, ERP);
+ZFCP_DEFINE_DRIVER_ATTR(fc, FC);
+
+static struct attribute *zfcp_driver_attrs[] = {
+       &driver_attr_loglevel_other.attr,
+       &driver_attr_loglevel_scsi.attr,
+       &driver_attr_loglevel_fsf.attr,
+       &driver_attr_loglevel_config.attr,
+       &driver_attr_loglevel_cio.attr,
+       &driver_attr_loglevel_qdio.attr,
+       &driver_attr_loglevel_erp.attr,
+       &driver_attr_loglevel_fc.attr,
+       NULL
+};
+
+static struct attribute_group zfcp_driver_attr_group = {
+       .attrs = zfcp_driver_attrs,
+};
+
+/**
+ * zfcp_sysfs_create_driver_files - create sysfs driver files
+ * @dev: pointer to belonging device
+ *
+ * Create all sysfs attributes of the zfcp device driver
+ */
+int
+zfcp_sysfs_driver_create_files(struct device_driver *drv)
+{
+       return sysfs_create_group(&drv->kobj, &zfcp_driver_attr_group);
+}
+
+/**
+ * zfcp_sysfs_remove_driver_files - remove sysfs driver files
+ * @dev: pointer to belonging device
+ *
+ * Remove all sysfs attributes of the zfcp device driver
+ */
+void
+zfcp_sysfs_driver_remove_files(struct device_driver *drv)
+{
+       sysfs_remove_group(&drv->kobj, &zfcp_driver_attr_group);
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_sysfs_port.c b/drivers/s390/scsi/zfcp_sysfs_port.c
new file mode 100644 (file)
index 0000000..c7497d6
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_port.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs port related routines
+ *
+ * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
+ * Authors:
+ *      Martin Peschke <mpeschke@de.ibm.com>
+ *     Heiko Carstens <heiko.carstens@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_SYSFS_PORT_C_REVISION "$Revision: 1.26 $"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/ccwdev.h>
+#include "zfcp_ext.h"
+#include "zfcp_def.h"
+
+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_CONFIG
+
+/**
+ * zfcp_sysfs_port_release - gets called when a struct device port is released
+ * @dev: pointer to belonging device
+ */
+void
+zfcp_sysfs_port_release(struct device *dev)
+{
+       struct zfcp_port *port;
+
+       port = dev_get_drvdata(dev);
+       zfcp_port_dequeue(port);
+       return;
+}
+
+/**
+ * ZFCP_DEFINE_PORT_ATTR
+ * @_name:   name of show attribute
+ * @_format: format string
+ * @_value:  value to print
+ *
+ * Generates attributes for a port.
+ */
+#define ZFCP_DEFINE_PORT_ATTR(_name, _format, _value)                    \
+static ssize_t zfcp_sysfs_port_##_name##_show(struct device *dev,        \
+                                              char *buf)                 \
+{                                                                        \
+        struct zfcp_port *port;                                          \
+                                                                         \
+        port = dev_get_drvdata(dev);                                     \
+        return sprintf(buf, _format, _value);                            \
+}                                                                        \
+                                                                         \
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_port_##_name##_show, NULL);
+
+ZFCP_DEFINE_PORT_ATTR(status, "0x%08x\n", atomic_read(&port->status));
+ZFCP_DEFINE_PORT_ATTR(wwnn, "0x%016llx\n", port->wwnn);
+ZFCP_DEFINE_PORT_ATTR(d_id, "0x%06x\n", port->d_id);
+ZFCP_DEFINE_PORT_ATTR(scsi_id, "0x%x\n", port->scsi_id);
+
+/**
+ * zfcp_sysfs_unit_add_store - add a unit to sysfs tree
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * @count: number of bytes in buffer
+ *
+ * Store function of the "unit_add" attribute of a port.
+ */
+static ssize_t
+zfcp_sysfs_unit_add_store(struct device *dev, const char *buf, size_t count)
+{
+       fcp_lun_t fcp_lun;
+       char *endp;
+       struct zfcp_port *port;
+       struct zfcp_unit *unit;
+       int retval = -EINVAL;
+
+       down(&zfcp_data.config_sema);
+
+       port = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       fcp_lun = simple_strtoull(buf, &endp, 0);
+       if ((endp + 1) < (buf + count))
+               goto out;
+
+       unit = zfcp_unit_enqueue(port, fcp_lun);
+       if (!unit)
+               goto out;
+
+       retval = 0;
+
+       zfcp_port_get(port);
+
+       /* try to open unit only if adapter is online */
+       if (port->adapter->ccw_device->online == 1)
+               zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
+       zfcp_unit_put(unit);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval ? retval : count;
+}
+
+static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
+
+/**
+ * zfcp_sysfs_unit_remove_store - remove a unit from sysfs tree
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * @count: number of bytes in buffer
+ */
+static ssize_t
+zfcp_sysfs_unit_remove_store(struct device *dev, const char *buf, size_t count)
+{
+       struct zfcp_port *port;
+       struct zfcp_unit *unit;
+       fcp_lun_t fcp_lun;
+       char *endp;
+       int retval = -EINVAL;
+
+       down(&zfcp_data.config_sema);
+
+       port = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       fcp_lun = simple_strtoull(buf, &endp, 0);
+       if ((endp + 1) < (buf + count))
+               goto out;
+
+       write_lock_irq(&zfcp_data.config_lock);
+       unit = zfcp_get_unit_by_lun(port, fcp_lun);
+       if (unit && (atomic_read(&unit->refcount) == 0)) {
+               zfcp_unit_get(unit);
+               atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+               list_move(&unit->list, &port->unit_remove_lh);
+       }
+       else {
+               unit = NULL;
+       }
+       write_unlock_irq(&zfcp_data.config_lock);
+
+       if (!unit) {
+               retval = -ENXIO;
+               goto out;
+       }
+
+       zfcp_erp_unit_shutdown(unit, 0);
+       zfcp_erp_wait(unit->port->adapter);
+       zfcp_unit_put(unit);
+       device_unregister(&unit->sysfs_device);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval ? retval : count;
+}
+
+static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
+
+/**
+ * zfcp_sysfs_port_failed_store - failed state of port
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * @count: number of bytes in buffer
+ *
+ * Store function of the "failed" attribute of a port.
+ * If a "0" gets written to "failed", error recovery will be
+ * started for the belonging port.
+ */
+static ssize_t
+zfcp_sysfs_port_failed_store(struct device *dev, const char *buf, size_t count)
+{
+       struct zfcp_port *port;
+       unsigned int val;
+       char *endp;
+       int retval = 0;
+
+       down(&zfcp_data.config_sema);
+
+       port = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val != 0)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       /* restart error recovery only if adapter is online */
+       if (port->adapter->ccw_device->online != 1) {
+               retval = -ENXIO;
+               goto out;
+       }
+       zfcp_erp_modify_port_status(port, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
+       zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval ? retval : count;
+}
+
+/**
+ * zfcp_sysfs_port_failed_show - failed state of port
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * Show function of "failed" attribute of port. Will be
+ * "0" if port is working, otherwise "1".
+ */
+static ssize_t
+zfcp_sysfs_port_failed_show(struct device *dev, char *buf)
+{
+       struct zfcp_port *port;
+
+       port = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_port_failed_show,
+                  zfcp_sysfs_port_failed_store);
+
+/**
+ * zfcp_sysfs_port_in_recovery_show - recovery state of port
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * 
+ * Show function of "in_recovery" attribute of port. Will be
+ * "0" if no error recovery is pending for port, otherwise "1".
+ */
+static ssize_t
+zfcp_sysfs_port_in_recovery_show(struct device *dev, char *buf)
+{
+       struct zfcp_port *port;
+
+       port = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR(in_recovery, S_IRUGO, zfcp_sysfs_port_in_recovery_show,
+                  NULL);
+
+static struct attribute *zfcp_port_common_attrs[] = {
+       &dev_attr_failed.attr,
+       &dev_attr_in_recovery.attr,
+       &dev_attr_status.attr,
+       &dev_attr_wwnn.attr,
+       &dev_attr_d_id.attr,
+       NULL
+};
+
+static struct attribute_group zfcp_port_common_attr_group = {
+       .attrs = zfcp_port_common_attrs,
+};
+
+static struct attribute *zfcp_port_no_ns_attrs[] = {
+       &dev_attr_unit_add.attr,
+       &dev_attr_unit_remove.attr,
+       &dev_attr_scsi_id.attr,
+       NULL
+};
+
+static struct attribute_group zfcp_port_no_ns_attr_group = {
+       .attrs = zfcp_port_no_ns_attrs,
+};
+
+/**
+ * zfcp_sysfs_create_port_files - create sysfs port files
+ * @dev: pointer to belonging device
+ *
+ * Create all attributes of the sysfs representation of a port.
+ */
+int
+zfcp_sysfs_port_create_files(struct device *dev, u32 flags)
+{
+       int retval;
+
+       retval = sysfs_create_group(&dev->kobj, &zfcp_port_common_attr_group);
+
+       if ((flags & ZFCP_STATUS_PORT_NAMESERVER) || retval)
+               return retval;
+
+       retval = sysfs_create_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
+       if (retval)
+               sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
+
+       return retval;
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
diff --git a/drivers/s390/scsi/zfcp_sysfs_unit.c b/drivers/s390/scsi/zfcp_sysfs_unit.c
new file mode 100644 (file)
index 0000000..72acb96
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_unit.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs unit related routines
+ *
+ * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
+ * Authors:
+ *      Martin Peschke <mpeschke@de.ibm.com>
+ *     Heiko Carstens <heiko.carstens@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_SYSFS_UNIT_C_REVISION "$Revision: 1.17 $"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/ccwdev.h>
+#include "zfcp_ext.h"
+#include "zfcp_def.h"
+
+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_CONFIG
+
+/**
+ * zfcp_sysfs_unit_release - gets called when a struct device unit is released
+ * @dev: pointer to belonging device
+ */
+void
+zfcp_sysfs_unit_release(struct device *dev)
+{
+       struct zfcp_unit *unit;
+
+       unit = dev_get_drvdata(dev);
+       zfcp_unit_dequeue(unit);
+       return;
+}
+
+/**
+ * ZFCP_DEFINE_UNIT_ATTR
+ * @_name:   name of show attribute
+ * @_format: format string
+ * @_value:  value to print
+ *
+ * Generates attribute for a unit.
+ */
+#define ZFCP_DEFINE_UNIT_ATTR(_name, _format, _value)                    \
+static ssize_t zfcp_sysfs_unit_##_name##_show(struct device *dev,        \
+                                              char *buf)                 \
+{                                                                        \
+        struct zfcp_unit *unit;                                          \
+                                                                         \
+        unit = dev_get_drvdata(dev);                                     \
+        return sprintf(buf, _format, _value);                            \
+}                                                                        \
+                                                                         \
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_unit_##_name##_show, NULL);
+
+ZFCP_DEFINE_UNIT_ATTR(status, "0x%08x\n", atomic_read(&unit->status));
+ZFCP_DEFINE_UNIT_ATTR(scsi_lun, "0x%x\n", unit->scsi_lun);
+
+/**
+ * zfcp_sysfs_unit_failed_store - failed state of unit
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ * @count: number of bytes in buffer
+ *
+ * Store function of the "failed" attribute of a unit.
+ * If a "0" gets written to "failed", error recovery will be
+ * started for the belonging unit.
+ */
+static ssize_t
+zfcp_sysfs_unit_failed_store(struct device *dev, const char *buf, size_t count)
+{
+       struct zfcp_unit *unit;
+       unsigned int val;
+       char *endp;
+       int retval = 0;
+
+       down(&zfcp_data.config_sema);
+       unit = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status)) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val != 0)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       /* restart error recovery only if adapter is online */
+       if (unit->port->adapter->ccw_device->online != 1) {
+               retval = -ENXIO;
+               goto out;
+       }
+       zfcp_erp_modify_unit_status(unit, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
+       zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
+ out:
+       up(&zfcp_data.config_sema);
+       return retval ? retval : count;
+}
+
+/**
+ * zfcp_sysfs_unit_failed_show - failed state of unit
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * Show function of "failed" attribute of unit. Will be
+ * "0" if unit is working, otherwise "1".
+ */
+static ssize_t
+zfcp_sysfs_unit_failed_show(struct device *dev, char *buf)
+{
+       struct zfcp_unit *unit;
+
+       unit = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_unit_failed_show,
+                  zfcp_sysfs_unit_failed_store);
+
+/**
+ * zfcp_sysfs_unit_in_recovery_show - recovery state of unit
+ * @dev: pointer to belonging device
+ * @buf: pointer to input buffer
+ *
+ * Show function of "in_recovery" attribute of unit. Will be
+ * "0" if no error recovery is pending for unit, otherwise "1".
+ */
+static ssize_t
+zfcp_sysfs_unit_in_recovery_show(struct device *dev, char *buf)
+{
+       struct zfcp_unit *unit;
+
+       unit = dev_get_drvdata(dev);
+       if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR(in_recovery, S_IRUGO, zfcp_sysfs_unit_in_recovery_show,
+                  NULL);
+
+static struct attribute *zfcp_unit_attrs[] = {
+       &dev_attr_scsi_lun.attr,
+       &dev_attr_failed.attr,
+       &dev_attr_in_recovery.attr,
+       &dev_attr_status.attr,
+       NULL
+};
+
+static struct attribute_group zfcp_unit_attr_group = {
+       .attrs = zfcp_unit_attrs,
+};
+
+/** 
+ * zfcp_sysfs_create_unit_files - create sysfs unit files
+ * @dev: pointer to belonging device
+ *
+ * Create all attributes of the sysfs representation of a unit.
+ */
+int
+zfcp_sysfs_unit_create_files(struct device *dev)
+{
+       return sysfs_create_group(&dev->kobj, &zfcp_unit_attr_group);
+}
+
+#undef ZFCP_LOG_AREA
+#undef ZFCP_LOG_AREA_PREFIX
index f475289d5131ea6780a4bda345ed6e1d5cf6cba6..55bae98f5a4b415b36c99b039715900b8fbc7329 100644 (file)
@@ -1657,6 +1657,11 @@ config WD33C93_PIO
 
 #      bool 'Cyberstorm Mk III SCSI support (EXPERIMENTAL)' CONFIG_CYBERSTORMIII_SCSI
 #      bool 'GVP Turbo 040/060 SCSI support (EXPERIMENTAL)' CONFIG_GVP_TURBO_SCSI
+
+config ZFCP
+       tristate "IBM z900 OpenFCP/SCSI support"
+       depends on ARCH_S390 && SCSI
+
 endmenu
 
 source "drivers/scsi/pcmcia/Kconfig"