The zfcp scsi host adapater.
#
# 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
#
# 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
#
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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ *
+ * 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 */
--- /dev/null
+/*
+ *
+ * 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(¤t->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
--- /dev/null
+/*
+ *
+ * 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 */
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+/*
+ *
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
# 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"