summaryrefslogtreecommitdiffstats
path: root/drivers/s390/scsi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/scsi
downloadblackbird-op-linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
blackbird-op-linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/s390/scsi')
-rw-r--r--drivers/s390/scsi/Makefile9
-rw-r--r--drivers/s390/scsi/zfcp_aux.c1977
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c312
-rw-r--r--drivers/s390/scsi/zfcp_def.h1121
-rw-r--r--drivers/s390/scsi/zfcp_erp.c3585
-rw-r--r--drivers/s390/scsi/zfcp_ext.h186
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c5087
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h472
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c868
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c949
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_adapter.c298
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_driver.c135
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_port.c311
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_unit.c179
14 files changed, 15489 insertions, 0 deletions
diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile
new file mode 100644
index 000000000000..fc145307a7d4
--- /dev/null
+++ b/drivers/s390/scsi/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the S/390 specific device drivers
+#
+
+zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \
+ zfcp_fsf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \
+ zfcp_sysfs_unit.o zfcp_sysfs_driver.o
+
+obj-$(CONFIG_ZFCP) += zfcp.o
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
new file mode 100644
index 000000000000..6a43322ccb0a
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -0,0 +1,1977 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_aux.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_AUX_REVISION "$Revision: 1.145 $"
+
+#include "zfcp_ext.h"
+
+/* accumulated log level (module parameter) */
+static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
+static char *device;
+/*********************** FUNCTION PROTOTYPES *********************************/
+
+/* written against the module interface */
+static int __init zfcp_module_init(void);
+
+/* FCP related */
+static void zfcp_ns_gid_pn_handler(unsigned long);
+
+/* miscellaneous */
+static inline int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t);
+static inline void zfcp_sg_list_free(struct zfcp_sg_list *);
+static inline int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *,
+ void __user *, size_t);
+static inline int zfcp_sg_list_copy_to_user(void __user *,
+ struct zfcp_sg_list *, size_t);
+
+static int zfcp_cfdc_dev_ioctl(struct inode *, struct file *,
+ unsigned int, unsigned long);
+
+#define ZFCP_CFDC_IOC_MAGIC 0xDD
+#define ZFCP_CFDC_IOC \
+ _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_sense_data)
+
+#ifdef CONFIG_COMPAT
+static struct ioctl_trans zfcp_ioctl_trans = {ZFCP_CFDC_IOC, (void*) sys_ioctl};
+#endif
+
+static struct file_operations zfcp_cfdc_fops = {
+ .ioctl = zfcp_cfdc_dev_ioctl
+};
+
+static struct miscdevice zfcp_cfdc_misc = {
+ .minor = ZFCP_CFDC_DEV_MINOR,
+ .name = ZFCP_CFDC_DEV_NAME,
+ .fops = &zfcp_cfdc_fops
+};
+
+/*********************** KERNEL/MODULE PARAMETERS ***************************/
+
+/* declare driver module init/cleanup functions */
+module_init(zfcp_module_init);
+
+MODULE_AUTHOR("Heiko Carstens <heiko.carstens@de.ibm.com>, "
+ "Andreas Herrman <aherrman@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");
+MODULE_DESCRIPTION
+ ("FCP (SCSI over Fibre Channel) HBA driver for IBM eServer zSeries");
+MODULE_LICENSE("GPL");
+
+module_param(device, charp, 0);
+MODULE_PARM_DESC(device, "specify initial device");
+
+module_param(loglevel, uint, 0);
+MODULE_PARM_DESC(loglevel,
+ "log levels, 8 nibbles: "
+ "FC ERP QDIO CIO 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-1) % 32) != 31)
+ printk("\n");
+}
+
+/****************************************************************/
+/************** Uncategorised Functions *************************/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER
+
+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)
+{
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+ struct scsi_cmnd *scsi_cmnd;
+ int level = 3;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->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));
+ debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd,
+ min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len));
+ 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));
+ }
+ spin_unlock_irqrestore(&adapter->dbf_lock, flags);
+}
+
+/* XXX additionally log unit if available */
+/* ---> introduce new parameter for unit, see 2.4 code */
+void
+zfcp_cmd_dbf_event_scsi(const char *text, struct scsi_cmnd *scsi_cmnd)
+{
+ 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);
+ spin_lock_irqsave(&adapter->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));
+ debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd,
+ min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len));
+ if (likely(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, "");
+ }
+ spin_unlock_irqrestore(&adapter->dbf_lock, flags);
+}
+
+void
+zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text,
+ struct fsf_status_read_buffer *status_buffer, int length)
+{
+ 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));
+}
+
+/**
+ * zfcp_device_setup - setup function
+ * @str: pointer to parameter string
+ *
+ * Parse "device=..." parameter string.
+ */
+static int __init
+zfcp_device_setup(char *str)
+{
+ char *tmp;
+
+ if (!str)
+ return 0;
+
+ tmp = strchr(str, ',');
+ if (!tmp)
+ goto err_out;
+ *tmp++ = '\0';
+ strncpy(zfcp_data.init_busid, str, BUS_ID_SIZE);
+ zfcp_data.init_busid[BUS_ID_SIZE-1] = '\0';
+
+ zfcp_data.init_wwpn = simple_strtoull(tmp, &tmp, 0);
+ if (*tmp++ != ',')
+ goto err_out;
+ if (*tmp == '\0')
+ goto err_out;
+
+ zfcp_data.init_fcp_lun = simple_strtoull(tmp, &tmp, 0);
+ if (*tmp != '\0')
+ goto err_out;
+ return 1;
+
+ err_out:
+ ZFCP_LOG_NORMAL("Parse error for device parameter string %s\n", str);
+ return 0;
+}
+
+static void __init
+zfcp_init_device_configure(void)
+{
+ struct zfcp_adapter *adapter;
+ struct zfcp_port *port;
+ struct zfcp_unit *unit;
+
+ down(&zfcp_data.config_sema);
+ read_lock_irq(&zfcp_data.config_lock);
+ adapter = zfcp_get_adapter_by_busid(zfcp_data.init_busid);
+ if (adapter)
+ zfcp_adapter_get(adapter);
+ read_unlock_irq(&zfcp_data.config_lock);
+
+ if (adapter == NULL)
+ goto out_adapter;
+ port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0, 0);
+ if (!port)
+ goto out_port;
+ unit = zfcp_unit_enqueue(port, zfcp_data.init_fcp_lun);
+ if (!unit)
+ goto out_unit;
+ up(&zfcp_data.config_sema);
+ ccw_device_set_online(adapter->ccw_device);
+ zfcp_erp_wait(adapter);
+ down(&zfcp_data.config_sema);
+ zfcp_unit_put(unit);
+ out_unit:
+ zfcp_port_put(port);
+ out_port:
+ zfcp_adapter_put(adapter);
+ out_adapter:
+ up(&zfcp_data.config_sema);
+ return;
+}
+
+static int __init
+zfcp_module_init(void)
+{
+
+ int retval = 0;
+
+ atomic_set(&zfcp_data.loglevel, loglevel);
+
+ /* 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);
+
+ zfcp_transport_template = fc_attach_transport(&zfcp_transport_functions);
+ if (!zfcp_transport_template)
+ return -ENODEV;
+
+ retval = register_ioctl32_conversion(zfcp_ioctl_trans.cmd,
+ zfcp_ioctl_trans.handler);
+ if (retval != 0) {
+ ZFCP_LOG_INFO("registration of ioctl32 conversion failed\n");
+ goto out;
+ }
+
+ retval = misc_register(&zfcp_cfdc_misc);
+ if (retval != 0) {
+ ZFCP_LOG_INFO("registration of misc device "
+ "zfcp_cfdc failed\n");
+ goto out_misc_register;
+ } else {
+ ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n",
+ ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor);
+ }
+
+ /* Initialise proc semaphores */
+ sema_init(&zfcp_data.config_sema, 1);
+
+ /* initialise configuration rw lock */
+ rwlock_init(&zfcp_data.config_lock);
+
+ /* 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("registration with common I/O layer failed\n");
+ goto out_ccw_register;
+ }
+
+ if (zfcp_device_setup(device))
+ zfcp_init_device_configure();
+
+ goto out;
+
+ out_ccw_register:
+ misc_deregister(&zfcp_cfdc_misc);
+ out_misc_register:
+ unregister_ioctl32_conversion(zfcp_ioctl_trans.cmd);
+ out:
+ return retval;
+}
+
+/*
+ * function: zfcp_cfdc_dev_ioctl
+ *
+ * purpose: Handle control file upload/download transaction via IOCTL
+ * interface
+ *
+ * returns: 0 - Operation completed successfuly
+ * -ENOTTY - Unknown IOCTL command
+ * -EINVAL - Invalid sense data record
+ * -ENXIO - The FCP adapter is not available
+ * -EOPNOTSUPP - The FCP adapter does not have CFDC support
+ * -ENOMEM - Insufficient memory
+ * -EFAULT - User space memory I/O operation fault
+ * -EPERM - Cannot create or queue FSF request or create SBALs
+ * -ERESTARTSYS- Received signal (is mapped to EAGAIN by VFS)
+ */
+static int
+zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file,
+ unsigned int command, unsigned long buffer)
+{
+ struct zfcp_cfdc_sense_data *sense_data, __user *sense_data_user;
+ struct zfcp_adapter *adapter = NULL;
+ struct zfcp_fsf_req *fsf_req = NULL;
+ struct zfcp_sg_list *sg_list = NULL;
+ u32 fsf_command, option;
+ char *bus_id = NULL;
+ int retval = 0;
+
+ sense_data = kmalloc(sizeof(struct zfcp_cfdc_sense_data), GFP_KERNEL);
+ if (sense_data == NULL) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ sg_list = kmalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL);
+ if (sg_list == NULL) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ memset(sg_list, 0, sizeof(*sg_list));
+
+ if (command != ZFCP_CFDC_IOC) {
+ ZFCP_LOG_INFO("IOC request code 0x%x invalid\n", command);
+ retval = -ENOTTY;
+ goto out;
+ }
+
+ if ((sense_data_user = (void __user *) buffer) == NULL) {
+ ZFCP_LOG_INFO("sense data record is required\n");
+ retval = -EINVAL;
+ goto out;
+ }
+
+ retval = copy_from_user(sense_data, sense_data_user,
+ sizeof(struct zfcp_cfdc_sense_data));
+ if (retval) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ if (sense_data->signature != ZFCP_CFDC_SIGNATURE) {
+ ZFCP_LOG_INFO("invalid sense data request signature 0x%08x\n",
+ ZFCP_CFDC_SIGNATURE);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ switch (sense_data->command) {
+
+ case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
+ fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ option = FSF_CFDC_OPTION_NORMAL_MODE;
+ break;
+
+ case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
+ fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ option = FSF_CFDC_OPTION_FORCE;
+ break;
+
+ case ZFCP_CFDC_CMND_FULL_ACCESS:
+ fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ option = FSF_CFDC_OPTION_FULL_ACCESS;
+ break;
+
+ case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
+ fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
+ break;
+
+ case ZFCP_CFDC_CMND_UPLOAD:
+ fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE;
+ option = 0;
+ break;
+
+ default:
+ ZFCP_LOG_INFO("invalid command code 0x%08x\n",
+ sense_data->command);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ bus_id = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
+ if (bus_id == NULL) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ snprintf(bus_id, BUS_ID_SIZE, "%d.%d.%04x",
+ (sense_data->devno >> 24),
+ (sense_data->devno >> 16) & 0xFF,
+ (sense_data->devno & 0xFFFF));
+
+ read_lock_irq(&zfcp_data.config_lock);
+ adapter = zfcp_get_adapter_by_busid(bus_id);
+ if (adapter)
+ zfcp_adapter_get(adapter);
+ read_unlock_irq(&zfcp_data.config_lock);
+
+ kfree(bus_id);
+
+ if (adapter == NULL) {
+ ZFCP_LOG_INFO("invalid adapter\n");
+ retval = -ENXIO;
+ goto out;
+ }
+
+ if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) {
+ retval = zfcp_sg_list_alloc(sg_list,
+ ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
+ if (retval) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ }
+
+ if ((sense_data->command & ZFCP_CFDC_DOWNLOAD) &&
+ (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE)) {
+ retval = zfcp_sg_list_copy_from_user(
+ sg_list, &sense_data_user->control_file,
+ ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
+ if (retval) {
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ retval = zfcp_fsf_control_file(adapter, &fsf_req, fsf_command,
+ option, sg_list);
+ if (retval)
+ goto out;
+
+ if ((fsf_req->qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
+ (fsf_req->qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
+ retval = -ENXIO;
+ goto out;
+ }
+
+ sense_data->fsf_status = fsf_req->qtcb->header.fsf_status;
+ memcpy(&sense_data->fsf_status_qual,
+ &fsf_req->qtcb->header.fsf_status_qual,
+ sizeof(union fsf_status_qual));
+ memcpy(&sense_data->payloads, &fsf_req->qtcb->bottom.support.els, 256);
+
+ retval = copy_to_user(sense_data_user, sense_data,
+ sizeof(struct zfcp_cfdc_sense_data));
+ if (retval) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ if (sense_data->command & ZFCP_CFDC_UPLOAD) {
+ retval = zfcp_sg_list_copy_to_user(
+ &sense_data_user->control_file, sg_list,
+ ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
+ if (retval) {
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ out:
+ if (fsf_req != NULL)
+ zfcp_fsf_req_cleanup(fsf_req);
+
+ if ((adapter != NULL) && (retval != -ENXIO))
+ zfcp_adapter_put(adapter);
+
+ if (sg_list != NULL) {
+ zfcp_sg_list_free(sg_list);
+ kfree(sg_list);
+ }
+
+ if (sense_data != NULL)
+ kfree(sense_data);
+
+ return retval;
+}
+
+
+/**
+ * zfcp_sg_list_alloc - create a scatter-gather list of the specified size
+ * @sg_list: structure describing a scatter gather list
+ * @size: size of scatter-gather list
+ * Return: 0 on success, else -ENOMEM
+ *
+ * In sg_list->sg a pointer to the created scatter-gather list is returned,
+ * or NULL if we run out of memory. sg_list->count specifies the number of
+ * elements of the scatter-gather list. The maximum size of a single element
+ * in the scatter-gather list is PAGE_SIZE.
+ */
+static inline int
+zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size)
+{
+ struct scatterlist *sg;
+ unsigned int i;
+ int retval = 0;
+ void *address;
+
+ BUG_ON(sg_list == NULL);
+
+ sg_list->count = size >> PAGE_SHIFT;
+ if (size & ~PAGE_MASK)
+ sg_list->count++;
+ sg_list->sg = kmalloc(sg_list->count * sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if (sg_list->sg == NULL) {
+ sg_list->count = 0;
+ retval = -ENOMEM;
+ goto out;
+ }
+ memset(sg_list->sg, 0, sg_list->count * sizeof(struct scatterlist));
+
+ for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) {
+ sg->length = min(size, PAGE_SIZE);
+ sg->offset = 0;
+ address = (void *) get_zeroed_page(GFP_KERNEL);
+ if (address == NULL) {
+ sg_list->count = i;
+ zfcp_sg_list_free(sg_list);
+ retval = -ENOMEM;
+ goto out;
+ }
+ zfcp_address_to_sg(address, sg);
+ size -= sg->length;
+ }
+
+ out:
+ return retval;
+}
+
+
+/**
+ * zfcp_sg_list_free - free memory of a scatter-gather list
+ * @sg_list: structure describing a scatter-gather list
+ *
+ * Memory for each element in the scatter-gather list is freed.
+ * Finally sg_list->sg is freed itself and sg_list->count is reset.
+ */
+static inline void
+zfcp_sg_list_free(struct zfcp_sg_list *sg_list)
+{
+ struct scatterlist *sg;
+ unsigned int i;
+
+ BUG_ON(sg_list == NULL);
+
+ for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++)
+ free_page((unsigned long) zfcp_sg_to_address(sg));
+
+ sg_list->count = 0;
+ kfree(sg_list->sg);
+}
+
+/**
+ * zfcp_sg_size - determine size of a scatter-gather list
+ * @sg: array of (struct scatterlist)
+ * @sg_count: elements in array
+ * Return: size of entire scatter-gather list
+ */
+size_t
+zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count)
+{
+ unsigned int i;
+ struct scatterlist *p;
+ size_t size;
+
+ size = 0;
+ for (i = 0, p = sg; i < sg_count; i++, p++) {
+ BUG_ON(p == NULL);
+ size += p->length;
+ }
+
+ return size;
+}
+
+
+/**
+ * zfcp_sg_list_copy_from_user -copy data from user space to scatter-gather list
+ * @sg_list: structure describing a scatter-gather list
+ * @user_buffer: pointer to buffer in user space
+ * @size: number of bytes to be copied
+ * Return: 0 on success, -EFAULT if copy_from_user fails.
+ */
+static inline int
+zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list,
+ void __user *user_buffer,
+ size_t size)
+{
+ struct scatterlist *sg;
+ unsigned int length;
+ void *zfcp_buffer;
+ int retval = 0;
+
+ BUG_ON(sg_list == NULL);
+
+ if (zfcp_sg_size(sg_list->sg, sg_list->count) < size)
+ return -EFAULT;
+
+ for (sg = sg_list->sg; size > 0; sg++) {
+ length = min((unsigned int)size, sg->length);
+ zfcp_buffer = zfcp_sg_to_address(sg);
+ if (copy_from_user(zfcp_buffer, user_buffer, length)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ user_buffer += length;
+ size -= length;
+ }
+
+ out:
+ return retval;
+}
+
+
+/**
+ * zfcp_sg_list_copy_to_user - copy data from scatter-gather list to user space
+ * @user_buffer: pointer to buffer in user space
+ * @sg_list: structure describing a scatter-gather list
+ * @size: number of bytes to be copied
+ * Return: 0 on success, -EFAULT if copy_to_user fails
+ */
+static inline int
+zfcp_sg_list_copy_to_user(void __user *user_buffer,
+ struct zfcp_sg_list *sg_list,
+ size_t size)
+{
+ struct scatterlist *sg;
+ unsigned int length;
+ void *zfcp_buffer;
+ int retval = 0;
+
+ BUG_ON(sg_list == NULL);
+
+ if (zfcp_sg_size(sg_list->sg, sg_list->count) < size)
+ return -EFAULT;
+
+ for (sg = sg_list->sg; size > 0; sg++) {
+ length = min((unsigned int) size, sg->length);
+ zfcp_buffer = zfcp_sg_to_address(sg);
+ if (copy_to_user(user_buffer, zfcp_buffer, length)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ user_buffer += length;
+ size -= length;
+ }
+
+ out:
+ return retval;
+}
+
+
+#undef ZFCP_LOG_AREA
+
+/****************************************************************/
+/****** Functions for configuration/set-up of structures ********/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
+
+/**
+ * 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: FCP LUN to search for
+ * Traverse list of all units of a port and return pointer to a unit
+ * with the given FCP LUN.
+ */
+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 port in port list of adapter by wwpn
+ * @adapter: pointer to adapter to search for port
+ * @wwpn: wwpn to search for
+ * Traverse list of all ports of an adapter and return pointer to a port
+ * with the given wwpn.
+ */
+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_read(&port->status) &
+ (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE))) {
+ found = 1;
+ break;
+ }
+ }
+ return found ? port : NULL;
+}
+
+/**
+ * zfcp_get_port_by_did - find port in port list of adapter by d_id
+ * @adapter: pointer to adapter to search for port
+ * @d_id: d_id to search for
+ * Traverse list of all ports of an adapter and return pointer to a port
+ * with the given d_id.
+ */
+struct zfcp_port *
+zfcp_get_port_by_did(struct zfcp_adapter *adapter, u32 d_id)
+{
+ struct zfcp_port *port;
+ int found = 0;
+
+ list_for_each_entry(port, &adapter->port_list_head, list) {
+ if ((port->d_id == d_id) &&
+ !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
+ {
+ found = 1;
+ break;
+ }
+ }
+ return found ? port : NULL;
+}
+
+/**
+ * zfcp_get_adapter_by_busid - find adpater in adapter list by bus_id
+ * @bus_id: bus_id to search for
+ * Traverse list of all adapters and return pointer to an adapter
+ * with the given bus_id.
+ */
+struct zfcp_adapter *
+zfcp_get_adapter_by_busid(char *bus_id)
+{
+ struct zfcp_adapter *adapter;
+ int found = 0;
+
+ list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) {
+ if ((strncmp(bus_id, zfcp_get_busid_by_adapter(adapter),
+ BUS_ID_SIZE) == 0) &&
+ !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE,
+ &adapter->status)){
+ found = 1;
+ break;
+ }
+ }
+ return found ? adapter : NULL;
+}
+
+/**
+ * zfcp_unit_enqueue - enqueue unit to unit list of a port.
+ * @port: pointer to port where unit is added
+ * @fcp_lun: FCP LUN of unit to be enqueued
+ * Return: pointer to enqueued unit on success, NULL on error
+ * Locks: config_sema must be held to serialize changes to the unit list
+ *
+ * Sets up some unit internal structures and creates sysfs entry.
+ */
+struct zfcp_unit *
+zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
+{
+ struct zfcp_unit *unit, *tmp_unit;
+ scsi_lun_t scsi_lun;
+ int found;
+
+ /*
+ * 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;
+ unit->fcp_lun = fcp_lun;
+
+ /* 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)) {
+ device_unregister(&unit->sysfs_device);
+ return NULL;
+ }
+
+ zfcp_unit_get(unit);
+
+ scsi_lun = 0;
+ found = 0;
+ write_lock_irq(&zfcp_data.config_lock);
+ list_for_each_entry(tmp_unit, &port->unit_list_head, list) {
+ if (tmp_unit->scsi_lun != scsi_lun) {
+ found = 1;
+ break;
+ }
+ scsi_lun++;
+ }
+ unit->scsi_lun = scsi_lun;
+ if (found)
+ list_add_tail(&unit->list, &tmp_unit->list);
+ else
+ list_add_tail(&unit->list, &port->unit_list_head);
+ atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+ atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
+ write_unlock_irq(&zfcp_data.config_lock);
+
+ port->units++;
+ zfcp_port_get(port);
+
+ return unit;
+}
+
+void
+zfcp_unit_dequeue(struct zfcp_unit *unit)
+{
+ zfcp_unit_wait(unit);
+ 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);
+ zfcp_sysfs_unit_remove_files(&unit->sysfs_device);
+ device_unregister(&unit->sysfs_device);
+}
+
+static void *
+zfcp_mempool_alloc(unsigned int __nocast 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.fsf_req_erp =
+ mempool_create(ZFCP_POOL_FSF_REQ_ERP_NR,
+ zfcp_mempool_alloc, zfcp_mempool_free, (void *)
+ sizeof(struct zfcp_fsf_req_pool_element));
+
+ if (NULL == adapter->pool.fsf_req_erp)
+ return -ENOMEM;
+
+ adapter->pool.fsf_req_scsi =
+ mempool_create(ZFCP_POOL_FSF_REQ_SCSI_NR,
+ zfcp_mempool_alloc, zfcp_mempool_free, (void *)
+ sizeof(struct zfcp_fsf_req_pool_element));
+
+ if (NULL == adapter->pool.fsf_req_scsi)
+ return -ENOMEM;
+
+ adapter->pool.fsf_req_abort =
+ mempool_create(ZFCP_POOL_FSF_REQ_ABORT_NR,
+ zfcp_mempool_alloc, zfcp_mempool_free, (void *)
+ sizeof(struct zfcp_fsf_req_pool_element));
+
+ if (NULL == adapter->pool.fsf_req_abort)
+ return -ENOMEM;
+
+ adapter->pool.fsf_req_status_read =
+ mempool_create(ZFCP_POOL_STATUS_READ_NR,
+ zfcp_mempool_alloc, zfcp_mempool_free,
+ (void *) sizeof(struct zfcp_fsf_req));
+
+ if (NULL == adapter->pool.fsf_req_status_read)
+ return -ENOMEM;
+
+ adapter->pool.data_status_read =
+ mempool_create(ZFCP_POOL_STATUS_READ_NR,
+ zfcp_mempool_alloc, zfcp_mempool_free,
+ (void *) sizeof(struct fsf_status_read_buffer));
+
+ if (NULL == adapter->pool.data_status_read)
+ return -ENOMEM;
+
+ adapter->pool.data_gid_pn =
+ mempool_create(ZFCP_POOL_DATA_GID_PN_NR,
+ zfcp_mempool_alloc, zfcp_mempool_free, (void *)
+ sizeof(struct zfcp_gid_pn_data));
+
+ if (NULL == adapter->pool.data_gid_pn)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * zfcp_free_low_mem_buffers - free memory pools of an adapter
+ * @adapter: pointer to zfcp_adapter for which memory pools should be freed
+ * locking: zfcp_data.config_sema must be held
+ */
+static void
+zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+{
+ if (adapter->pool.fsf_req_erp)
+ mempool_destroy(adapter->pool.fsf_req_erp);
+ if (adapter->pool.fsf_req_scsi)
+ mempool_destroy(adapter->pool.fsf_req_scsi);
+ if (adapter->pool.fsf_req_abort)
+ mempool_destroy(adapter->pool.fsf_req_abort);
+ if (adapter->pool.fsf_req_status_read)
+ mempool_destroy(adapter->pool.fsf_req_status_read);
+ if (adapter->pool.data_status_read)
+ mempool_destroy(adapter->pool.data_status_read);
+ if (adapter->pool.data_gid_pn)
+ mempool_destroy(adapter->pool.data_gid_pn);
+}
+
+/**
+ * zfcp_adapter_debug_register - registers debug feature for an adapter
+ * @adapter: pointer to adapter for which debug features should be registered
+ * return: -ENOMEM on error, 0 otherwise
+ */
+int
+zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
+{
+ char dbf_name[20];
+
+ /* debug feature area which records SCSI command failures (hostbyte) */
+ spin_lock_init(&adapter->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);
+ debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL);
+
+ /* 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);
+ debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL);
+
+ /* debug feature area which records incoming ELS commands */
+ 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);
+ debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL);
+
+ /* debug feature area which records erp events */
+ 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);
+ debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL);
+
+ if (!(adapter->cmd_dbf && adapter->abort_dbf &&
+ adapter->in_els_dbf && adapter->erp_dbf)) {
+ zfcp_adapter_debug_unregister(adapter);
+ return -ENOMEM;
+ }
+
+ return 0;
+
+}
+
+/**
+ * zfcp_adapter_debug_unregister - unregisters debug feature for an adapter
+ * @adapter: pointer to adapter for which debug features should be unregistered
+ */
+void
+zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter)
+{
+ debug_unregister(adapter->abort_dbf);
+ debug_unregister(adapter->cmd_dbf);
+ debug_unregister(adapter->erp_dbf);
+ debug_unregister(adapter->in_els_dbf);
+ adapter->abort_dbf = NULL;
+ adapter->cmd_dbf = NULL;
+ adapter->erp_dbf = NULL;
+ adapter->in_els_dbf = NULL;
+}
+
+void
+zfcp_dummy_release(struct device *dev)
+{
+ return;
+}
+
+/*
+ * 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;
+
+ /*
+ * 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) {
+ ZFCP_LOG_INFO("error: pool allocation failed\n");
+ goto failed_low_mem_buffers;
+ }
+
+ /* 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 some erp stuff */
+ init_waitqueue_head(&adapter->erp_thread_wqh);
+ init_waitqueue_head(&adapter->erp_done_wqh);
+
+ /* 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;
+
+ adapter->generic_services.parent = &adapter->ccw_device->dev;
+ adapter->generic_services.release = zfcp_dummy_release;
+ snprintf(adapter->generic_services.bus_id, BUS_ID_SIZE,
+ "generic_services");
+
+ if (device_register(&adapter->generic_services))
+ goto generic_services_failed;
+
+ /* put allocated adapter at list tail */
+ write_lock_irq(&zfcp_data.config_lock);
+ atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
+ list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
+ write_unlock_irq(&zfcp_data.config_lock);
+
+ zfcp_data.adapters++;
+
+ goto out;
+
+ generic_services_failed:
+ zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
+ sysfs_failed:
+ dev_set_drvdata(&ccw_device->dev, NULL);
+ failed_low_mem_buffers:
+ zfcp_free_low_mem_buffers(adapter);
+ if (qdio_free(ccw_device) != 0)
+ ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n",
+ zfcp_get_busid_by_adapter(adapter));
+ 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;
+
+ device_unregister(&adapter->generic_services);
+ 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 (%p) still in use, "
+ "%i requests outstanding\n",
+ zfcp_get_busid_by_adapter(adapter), adapter,
+ atomic_read(&adapter->fsf_reqs_active));
+ 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 %s (%p) removed from list, "
+ "%i adapters still in list\n",
+ zfcp_get_busid_by_adapter(adapter),
+ adapter, zfcp_data.adapters);
+
+ retval = qdio_free(adapter->ccw_device);
+ if (retval)
+ ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n",
+ zfcp_get_busid_by_adapter(adapter));
+
+ 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;
+}
+
+/**
+ * zfcp_port_enqueue - enqueue port to port list of adapter
+ * @adapter: adapter where remote port is added
+ * @wwpn: WWPN of the remote port to be enqueued
+ * @status: initial status for the port
+ * @d_id: destination id of the remote port to be enqueued
+ * Return: pointer to enqueued port on success, NULL on error
+ * Locks: config_sema must be held to serialize changes to the port list
+ *
+ * All port internal structures are set up and the sysfs entry is generated.
+ * d_id is used to enqueue ports with a well known address like the Directory
+ * Service for nameserver lookup.
+ */
+struct zfcp_port *
+zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status,
+ u32 d_id)
+{
+ struct zfcp_port *port, *tmp_port;
+ int check_wwpn;
+ scsi_id_t scsi_id;
+ int found;
+
+ 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_wwpn)
+ port->wwpn = wwpn;
+
+ atomic_set_mask(status, &port->status);
+
+ /* setup for sysfs registration */
+ if (status & ZFCP_STATUS_PORT_WKA) {
+ switch (d_id) {
+ case ZFCP_DID_DIRECTORY_SERVICE:
+ snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
+ "directory");
+ break;
+ case ZFCP_DID_MANAGEMENT_SERVICE:
+ snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
+ "management");
+ break;
+ case ZFCP_DID_KEY_DISTRIBUTION_SERVICE:
+ snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
+ "key_distribution");
+ break;
+ case ZFCP_DID_ALIAS_SERVICE:
+ snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
+ "alias");
+ break;
+ case ZFCP_DID_TIME_SERVICE:
+ snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
+ "time");
+ break;
+ default:
+ kfree(port);
+ return NULL;
+ }
+ port->d_id = d_id;
+ port->sysfs_device.parent = &adapter->generic_services;
+ } 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)) {
+ device_unregister(&port->sysfs_device);
+ return NULL;
+ }
+
+ zfcp_port_get(port);
+
+ scsi_id = 1;
+ found = 0;
+ write_lock_irq(&zfcp_data.config_lock);
+ list_for_each_entry(tmp_port, &adapter->port_list_head, list) {
+ if (atomic_test_mask(ZFCP_STATUS_PORT_NO_SCSI_ID,
+ &tmp_port->status))
+ continue;
+ if (tmp_port->scsi_id != scsi_id) {
+ found = 1;
+ break;
+ }
+ scsi_id++;
+ }
+ port->scsi_id = scsi_id;
+ if (found)
+ list_add_tail(&port->list, &tmp_port->list);
+ else
+ list_add_tail(&port->list, &adapter->port_list_head);
+ atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+ atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
+ if (d_id == ZFCP_DID_DIRECTORY_SERVICE)
+ if (!adapter->nameserver_port)
+ adapter->nameserver_port = port;
+ adapter->ports++;
+ write_unlock_irq(&zfcp_data.config_lock);
+
+ zfcp_adapter_get(adapter);
+
+ return port;
+}
+
+void
+zfcp_port_dequeue(struct zfcp_port *port)
+{
+ zfcp_port_wait(port);
+ write_lock_irq(&zfcp_data.config_lock);
+ list_del(&port->list);
+ port->adapter->ports--;
+ write_unlock_irq(&zfcp_data.config_lock);
+ zfcp_adapter_put(port->adapter);
+ zfcp_sysfs_port_remove_files(&port->sysfs_device,
+ atomic_read(&port->status));
+ device_unregister(&port->sysfs_device);
+}
+
+/* Enqueues a nameserver port */
+int
+zfcp_nameserver_enqueue(struct zfcp_adapter *adapter)
+{
+ struct zfcp_port *port;
+
+ port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_WKA,
+ ZFCP_DID_DIRECTORY_SERVICE);
+ if (!port) {
+ ZFCP_LOG_INFO("error: enqueue of nameserver port for "
+ "adapter %s failed\n",
+ zfcp_get_busid_by_adapter(adapter));
+ return -ENXIO;
+ }
+ zfcp_port_put(port);
+
+ return 0;
+}
+
+#undef ZFCP_LOG_AREA
+
+/****************************************************************/
+/******* Fibre Channel Standard related Functions **************/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_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;
+ u16 i;
+ u16 no_entries;
+ u32 range_mask;
+ 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++) {
+ /* 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;
+ break;
+ case ZFCP_AREA_ADDRESS:
+ ZFCP_LOG_FLAGS(1, "ZFCP_AREA_ADDRESS\n");
+ range_mask = ZFCP_PORTS_RANGE_AREA;
+ break;
+ case ZFCP_DOMAIN_ADDRESS:
+ ZFCP_LOG_FLAGS(1, "ZFCP_DOMAIN_ADDRESS\n");
+ range_mask = ZFCP_PORTS_RANGE_DOMAIN;
+ break;
+ case ZFCP_FABRIC_ADDRESS:
+ ZFCP_LOG_FLAGS(1, "ZFCP_FABRIC_ADDRESS\n");
+ range_mask = ZFCP_PORTS_RANGE_FABRIC;
+ break;
+ default:
+ ZFCP_LOG_INFO("incoming RSCN with unknown "
+ "address format\n");
+ continue;
+ }
+ 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_WKA, &port->status))
+ continue;
+ /* Do we know this port? If not skip it. */
+ if (!atomic_test_mask
+ (ZFCP_STATUS_PORT_DID_DID, &port->status)) {
+ ZFCP_LOG_INFO("incoming RSCN, trying to open "
+ "port 0x%016Lx\n", port->wwpn);
+ debug_text_event(adapter->erp_dbf, 1,
+ "unsol_els_rscnu:");
+ zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_ERP_FAILED);
+ continue;
+ }
+
+ /*
+ * FIXME: race: d_id might being invalidated
+ * (...DID_DID reset)
+ */
+ if ((port->d_id & range_mask)
+ == (fcp_rscn_element->nport_did & range_mask)) {
+ ZFCP_LOG_TRACE("reopen did 0x%08x\n",
+ 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("incoming RSCN, trying to open "
+ "port 0x%016Lx\n", port->wwpn);
+ debug_text_event(adapter->erp_dbf, 1,
+ "unsol_els_rscnk:");
+ zfcp_test_link(port);
+ }
+ }
+ 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("ignored incoming PLOGI for nonexisting port "
+ "with d_id 0x%08x on adapter %s\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("ignored incoming LOGO for nonexisting port "
+ "with d_id 0x%08x on adapter %s\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%08x "
+ "for 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);
+}
+
+
+/**
+ * zfcp_gid_pn_buffers_alloc - allocate buffers for GID_PN nameserver request
+ * @gid_pn: pointer to return pointer to struct zfcp_gid_pn_data
+ * @pool: pointer to mempool_t if non-null memory pool is used for allocation
+ */
+static int
+zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool)
+{
+ struct zfcp_gid_pn_data *data;
+
+ if (pool != NULL) {
+ data = mempool_alloc(pool, GFP_ATOMIC);
+ if (likely(data != NULL)) {
+ data->ct.pool = pool;
+ }
+ } else {
+ data = kmalloc(sizeof(struct zfcp_gid_pn_data), GFP_ATOMIC);
+ }
+
+ if (NULL == data)
+ return -ENOMEM;
+
+ memset(data, 0, sizeof(*data));
+ data->ct.req = &data->req;
+ data->ct.resp = &data->resp;
+ data->ct.req_count = data->ct.resp_count = 1;
+ zfcp_address_to_sg(&data->ct_iu_req, &data->req);
+ zfcp_address_to_sg(&data->ct_iu_resp, &data->resp);
+ data->req.length = sizeof(struct ct_iu_gid_pn_req);
+ data->resp.length = sizeof(struct ct_iu_gid_pn_resp);
+
+ *gid_pn = data;
+ return 0;
+}
+
+/**
+ * zfcp_gid_pn_buffers_free - free buffers for GID_PN nameserver request
+ * @gid_pn: pointer to struct zfcp_gid_pn_data which has to be freed
+ */
+static void
+zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn)
+{
+ if ((gid_pn->ct.pool != 0))
+ mempool_free(gid_pn, gid_pn->ct.pool);
+ else
+ kfree(gid_pn);
+
+ return;
+}
+
+/**
+ * zfcp_ns_gid_pn_request - initiate GID_PN nameserver request
+ * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
+ */
+int
+zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
+{
+ int ret;
+ struct ct_iu_gid_pn_req *ct_iu_req;
+ struct zfcp_gid_pn_data *gid_pn;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ ret = zfcp_gid_pn_buffers_alloc(&gid_pn, adapter->pool.data_gid_pn);
+ if (ret < 0) {
+ ZFCP_LOG_INFO("error: buffer allocation for gid_pn nameserver "
+ "request failed for adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto out;
+ }
+
+ /* setup nameserver request */
+ ct_iu_req = zfcp_sg_to_address(gid_pn->ct.req);
+ ct_iu_req->header.revision = ZFCP_CT_REVISION;
+ ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
+ ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
+ ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS;
+ ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GID_PN;
+ ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE;
+ ct_iu_req->wwpn = erp_action->port->wwpn;
+
+ /* setup parameters for send generic command */
+ gid_pn->ct.port = adapter->nameserver_port;
+ gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
+ gid_pn->ct.handler_data = (unsigned long) gid_pn;
+ gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
+ gid_pn->ct.timer = &erp_action->timer;
+ gid_pn->port = erp_action->port;
+
+ ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
+ erp_action);
+ if (ret) {
+ ZFCP_LOG_INFO("error: initiation of gid_pn nameserver request "
+ "failed for adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+
+ zfcp_gid_pn_buffers_free(gid_pn);
+ }
+
+ out:
+ return ret;
+}
+
+/**
+ * zfcp_ns_gid_pn_handler - handler for GID_PN nameserver request
+ * @data: unsigned long, contains pointer to struct zfcp_gid_pn_data
+ */
+static void zfcp_ns_gid_pn_handler(unsigned long data)
+{
+ struct zfcp_port *port;
+ struct zfcp_send_ct *ct;
+ struct ct_iu_gid_pn_req *ct_iu_req;
+ struct ct_iu_gid_pn_resp *ct_iu_resp;
+ struct zfcp_gid_pn_data *gid_pn;
+
+
+ gid_pn = (struct zfcp_gid_pn_data *) data;
+ port = gid_pn->port;
+ ct = &gid_pn->ct;
+ ct_iu_req = zfcp_sg_to_address(ct->req);
+ ct_iu_resp = zfcp_sg_to_address(ct->resp);
+
+ if ((ct->status != 0) || zfcp_check_ct_response(&ct_iu_resp->header)) {
+ /* FIXME: do we need some specific erp entry points */
+ atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
+ goto failed;
+ }
+ /* paranoia */
+ if (ct_iu_req->wwpn != port->wwpn) {
+ ZFCP_LOG_NORMAL("bug: wwpn 0x%016Lx returned by nameserver "
+ "lookup does not match expected wwpn 0x%016Lx "
+ "for adapter %s\n", ct_iu_req->wwpn, port->wwpn,
+ zfcp_get_busid_by_port(port));
+ goto mismatch;
+ }
+
+ /* looks like a valid d_id */
+ port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
+ atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
+ ZFCP_LOG_DEBUG("adapter %s: wwpn=0x%016Lx ---> d_id=0x%08x\n",
+ zfcp_get_busid_by_port(port), port->wwpn, port->d_id);
+ goto out;
+
+ mismatch:
+ ZFCP_LOG_DEBUG("CT IUs do not match:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_req,
+ sizeof(struct ct_iu_gid_pn_req));
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_resp,
+ sizeof(struct ct_iu_gid_pn_resp));
+
+ failed:
+ ZFCP_LOG_NORMAL("warning: failed gid_pn nameserver request for wwpn "
+ "0x%016Lx for adapter %s\n",
+ port->wwpn, zfcp_get_busid_by_port(port));
+ out:
+ zfcp_gid_pn_buffers_free(gid_pn);
+ return;
+}
+
+/* reject CT_IU reason codes acc. to FC-GS-4 */
+static const struct zfcp_rc_entry zfcp_ct_rc[] = {
+ {0x01, "invalid command code"},
+ {0x02, "invalid version level"},
+ {0x03, "logical error"},
+ {0x04, "invalid CT_IU size"},
+ {0x05, "logical busy"},
+ {0x07, "protocol error"},
+ {0x09, "unable to perform command request"},
+ {0x0b, "command not supported"},
+ {0x0d, "server not available"},
+ {0x0e, "session could not be established"},
+ {0xff, "vendor specific error"},
+ {0, NULL},
+};
+
+/* LS_RJT reason codes acc. to FC-FS */
+static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = {
+ {0x01, "invalid LS_Command code"},
+ {0x03, "logical error"},
+ {0x05, "logical busy"},
+ {0x07, "protocol error"},
+ {0x09, "unable to perform command request"},
+ {0x0b, "command not supported"},
+ {0x0e, "command already in progress"},
+ {0xff, "vendor specific error"},
+ {0, NULL},
+};
+
+/* reject reason codes according to FC-PH/FC-FS */
+static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = {
+ {0x01, "invalid D_ID"},
+ {0x02, "invalid S_ID"},
+ {0x03, "Nx_Port not available, temporary"},
+ {0x04, "Nx_Port not available, permament"},
+ {0x05, "class not supported"},
+ {0x06, "delimiter usage error"},
+ {0x07, "TYPE not supported"},
+ {0x08, "invalid Link_Control"},
+ {0x09, "invalid R_CTL field"},
+ {0x0a, "invalid F_CTL field"},
+ {0x0b, "invalid OX_ID"},
+ {0x0c, "invalid RX_ID"},
+ {0x0d, "invalid SEQ_ID"},
+ {0x0e, "invalid DF_CTL"},
+ {0x0f, "invalid SEQ_CNT"},
+ {0x10, "invalid parameter field"},
+ {0x11, "exchange error"},
+ {0x12, "protocol error"},
+ {0x13, "incorrect length"},
+ {0x14, "unsupported ACK"},
+ {0x15, "class of service not supported by entity at FFFFFE"},
+ {0x16, "login required"},
+ {0x17, "excessive sequences attempted"},
+ {0x18, "unable to establish exchange"},
+ {0x1a, "fabric path not available"},
+ {0x1b, "invalid VC_ID (class 4)"},
+ {0x1c, "invalid CS_CTL field"},
+ {0x1d, "insufficient resources for VC (class 4)"},
+ {0x1f, "invalid class of service"},
+ {0x20, "preemption request rejected"},
+ {0x21, "preemption not enabled"},
+ {0x22, "multicast error"},
+ {0x23, "multicast error terminate"},
+ {0x24, "process login required"},
+ {0xff, "vendor specific reject"},
+ {0, NULL},
+};
+
+/**
+ * zfcp_rc_description - return description for given reaon code
+ * @code: reason code
+ * @rc_table: table of reason codes and descriptions
+ */
+static inline const char *
+zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table)
+{
+ const char *descr = "unknown reason code";
+
+ do {
+ if (code == rc_table->code) {
+ descr = rc_table->description;
+ break;
+ }
+ rc_table++;
+ } while (rc_table->code && rc_table->description);
+
+ return descr;
+}
+
+/**
+ * zfcp_check_ct_response - evaluate reason code for CT_IU
+ * @rjt: response payload to an CT_IU request
+ * Return: 0 for accept CT_IU, 1 for reject CT_IU or invlid response code
+ */
+int
+zfcp_check_ct_response(struct ct_hdr *rjt)
+{
+ if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT)
+ return 0;
+
+ if (rjt->cmd_rsp_code != ZFCP_CT_REJECT) {
+ ZFCP_LOG_NORMAL("error: invalid Generic Service command/"
+ "response code (0x%04hx)\n",
+ rjt->cmd_rsp_code);
+ return 1;
+ }
+
+ ZFCP_LOG_INFO("Generic Service command rejected\n");
+ ZFCP_LOG_INFO("%s (0x%02x, 0x%02x, 0x%02x)\n",
+ zfcp_rc_description(rjt->reason_code, zfcp_ct_rc),
+ (u32) rjt->reason_code, (u32) rjt->reason_code_expl,
+ (u32) rjt->vendor_unique);
+
+ return 1;
+}
+
+/**
+ * zfcp_print_els_rjt - print reject parameter and description for ELS reject
+ * @rjt_par: reject parameter acc. to FC-PH/FC-FS
+ * @rc_table: table of reason codes and descriptions
+ */
+static inline void
+zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par,
+ const struct zfcp_rc_entry *rc_table)
+{
+ ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n",
+ zfcp_rc_description(rjt_par->reason_code, rc_table),
+ (u32) rjt_par->action, (u32) rjt_par->reason_code,
+ (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique);
+}
+
+/**
+ * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject
+ * @sq: status qualifier word
+ * @rjt_par: reject parameter as described in FC-PH and FC-FS
+ * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else
+ */
+int
+zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par)
+{
+ int ret = -EIO;
+
+ if (sq == FSF_IOSTAT_NPORT_RJT) {
+ ZFCP_LOG_INFO("ELS rejected (P_RJT)\n");
+ zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
+ /* invalid d_id */
+ if (rjt_par->reason_code == 0x01)
+ ret = -EREMCHG;
+ } else if (sq == FSF_IOSTAT_FABRIC_RJT) {
+ ZFCP_LOG_INFO("ELS rejected (F_RJT)\n");
+ zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
+ /* invalid d_id */
+ if (rjt_par->reason_code == 0x01)
+ ret = -EREMCHG;
+ } else if (sq == FSF_IOSTAT_LS_RJT) {
+ ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n");
+ zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc);
+ ret = -EREMOTEIO;
+ } else
+ ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq);
+
+ return ret;
+}
+
+#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
new file mode 100644
index 000000000000..0fc46381fc22
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -0,0 +1,312 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_ccw.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * CCW driver related routines
+ *
+ * (C) Copyright IBM Corp. 2003, 2004
+ *
+ * Authors:
+ * Martin Peschke <mpeschke@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_CCW_C_REVISION "$Revision: 1.58 $"
+
+#include "zfcp_ext.h"
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
+
+static int zfcp_ccw_probe(struct ccw_device *);
+static void 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 int zfcp_ccw_notify(struct ccw_device *, int);
+static void zfcp_ccw_shutdown(struct 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)},
+ {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
+ ZFCP_CONTROL_UNIT_MODEL,
+ ZFCP_DEVICE_TYPE,
+ ZFCP_DEVICE_MODEL_PRIV)},
+ {},
+};
+
+static struct ccw_driver zfcp_ccw_driver = {
+ .owner = THIS_MODULE,
+ .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,
+ .notify = zfcp_ccw_notify,
+ .driver = {
+ .shutdown = zfcp_ccw_shutdown,
+ },
+};
+
+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;
+ else
+ ZFCP_LOG_DEBUG("Probed adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ 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 in addition all resources of this
+ * adapter will be freed too.
+ */
+static void
+zfcp_ccw_remove(struct ccw_device *ccw_device)
+{
+ struct zfcp_adapter *adapter;
+ struct zfcp_port *port, *p;
+ struct zfcp_unit *unit, *u;
+
+ ccw_device_set_offline(ccw_device);
+ down(&zfcp_data.config_sema);
+ adapter = dev_get_drvdata(&ccw_device->dev);
+
+ ZFCP_LOG_DEBUG("Removing adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ 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_dequeue(unit);
+ }
+ zfcp_port_dequeue(port);
+ }
+ zfcp_adapter_wait(adapter);
+ zfcp_adapter_dequeue(adapter);
+
+ up(&zfcp_data.config_sema);
+}
+
+/**
+ * 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_debug_register(adapter);
+ if (retval)
+ goto out;
+ retval = zfcp_erp_thread_setup(adapter);
+ if (retval) {
+ ZFCP_LOG_INFO("error: start of error recovery thread for "
+ "adapter %s failed\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto out_erp_thread;
+ }
+
+ retval = zfcp_adapter_scsi_register(adapter);
+ if (retval)
+ goto out_scsi_register;
+ zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING,
+ ZFCP_SET);
+ zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
+ zfcp_erp_wait(adapter);
+ goto out;
+
+ out_scsi_register:
+ zfcp_erp_thread_kill(adapter);
+ out_erp_thread:
+ zfcp_adapter_debug_unregister(adapter);
+ 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);
+ zfcp_erp_thread_kill(adapter);
+ zfcp_adapter_debug_unregister(adapter);
+ up(&zfcp_data.config_sema);
+ return 0;
+}
+
+/**
+ * zfcp_ccw_notify
+ * @ccw_device: pointer to belonging ccw device
+ * @event: indicates if adapter was detached or attached
+ *
+ * This function gets called by the common i/o layer if an adapter has gone
+ * or reappeared.
+ */
+static int
+zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
+{
+ struct zfcp_adapter *adapter;
+
+ down(&zfcp_data.config_sema);
+ adapter = dev_get_drvdata(&ccw_device->dev);
+ switch (event) {
+ case CIO_GONE:
+ ZFCP_LOG_NORMAL("adapter %s: device gone\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf,1,"dev_gone");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ break;
+ case CIO_NO_PATH:
+ ZFCP_LOG_NORMAL("adapter %s: no path\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf,1,"no_path");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ break;
+ case CIO_OPER:
+ ZFCP_LOG_NORMAL("adapter %s: operational again\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf,1,"dev_oper");
+ zfcp_erp_modify_adapter_status(adapter,
+ ZFCP_STATUS_COMMON_RUNNING,
+ ZFCP_SET);
+ zfcp_erp_adapter_reopen(adapter,
+ ZFCP_STATUS_COMMON_ERP_FAILED);
+ break;
+ }
+ zfcp_erp_wait(adapter);
+ up(&zfcp_data.config_sema);
+ return 1;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * zfcp_ccw_shutdown - gets called on reboot/shutdown
+ *
+ * Makes sure that QDIO queues are down when the system gets stopped.
+ */
+static void
+zfcp_ccw_shutdown(struct device *dev)
+{
+ struct zfcp_adapter *adapter;
+
+ down(&zfcp_data.config_sema);
+ adapter = dev_get_drvdata(dev);
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ zfcp_erp_wait(adapter);
+ up(&zfcp_data.config_sema);
+}
+
+#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
new file mode 100644
index 000000000000..53fcccbb424c
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -0,0 +1,1121 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_def.h
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ * Volker Sameske <sameske@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
+
+#define ZFCP_DEF_REVISION "$Revision: 1.111 $"
+
+/*************************** INCLUDES *****************************************/
+
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include "../../fc4/fc.h"
+#include "zfcp_fsf.h"
+#include <asm/ccwdev.h>
+#include <asm/qdio.h>
+#include <asm/debug.h>
+#include <asm/ebcdic.h>
+#include <linux/mempool.h>
+#include <linux/syscalls.h>
+#include <linux/ioctl.h>
+#include <linux/ioctl32.h>
+
+/************************ DEBUG FLAGS *****************************************/
+
+#define ZFCP_PRINT_FLAGS
+
+/********************* GENERAL DEFINES *********************************/
+
+/* zfcp version number, it consists of major, minor, and patch-level number */
+#define ZFCP_VERSION "4.2.0"
+
+/**
+ * zfcp_sg_to_address - determine kernel address from struct scatterlist
+ * @list: struct scatterlist
+ * Return: kernel address
+ */
+static inline void *
+zfcp_sg_to_address(struct scatterlist *list)
+{
+ return (void *) (page_address(list->page) + list->offset);
+}
+
+/**
+ * zfcp_address_to_sg - set up struct scatterlist from kernel address
+ * @address: kernel address
+ * @list: struct scatterlist
+ */
+static inline void
+zfcp_address_to_sg(void *address, struct scatterlist *list)
+{
+ list->page = virt_to_page(address);
+ list->offset = ((unsigned long) address) & (PAGE_SIZE - 1);
+}
+
+/********************* 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_ERP_SCSI_LOW_MEM_TIMEOUT (100*HZ)
+#define ZFCP_SCSI_ER_TIMEOUT (100*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
+#define ZFCP_DEVICE_MODEL_PRIV 0x04
+
+/* allow as many chained SBALs as are supported by hardware */
+#define ZFCP_MAX_SBALS_PER_REQ FSF_MAX_SBALS_PER_REQ
+#define ZFCP_MAX_SBALS_PER_CT_REQ FSF_MAX_SBALS_PER_REQ
+#define ZFCP_MAX_SBALS_PER_ELS_REQ FSF_MAX_SBALS_PER_ELS_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
+
+/* timeout value for "default timer" for fsf requests */
+#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ);
+
+/*************** 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_NS_GID_PN_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 FCP_CLEAR_ACA 0x40
+#define FCP_TARGET_RESET 0x20
+#define FCP_LOGICAL_UNIT_RESET 0x10
+#define FCP_CLEAR_TASK_SET 0x04
+#define FCP_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));
+
+/*
+ * FC-FS stuff
+ */
+#define R_A_TOV 10 /* seconds */
+#define ZFCP_ELS_TIMEOUT (2 * R_A_TOV)
+
+#define ZFCP_LS_RLS 0x0f
+#define ZFCP_LS_ADISC 0x52
+#define ZFCP_LS_RPS 0x56
+#define ZFCP_LS_RSCN 0x61
+#define ZFCP_LS_RNID 0x78
+
+struct zfcp_ls_rjt_par {
+ u8 action;
+ u8 reason_code;
+ u8 reason_expl;
+ u8 vendor_unique;
+} __attribute__ ((packed));
+
+struct zfcp_ls_adisc {
+ u8 code;
+ u8 field[3];
+ u32 hard_nport_id;
+ u64 wwpn;
+ u64 wwnn;
+ u32 nport_id;
+} __attribute__ ((packed));
+
+struct zfcp_ls_adisc_acc {
+ u8 code;
+ u8 field[3];
+ u32 hard_nport_id;
+ u64 wwpn;
+ u64 wwnn;
+ u32 nport_id;
+} __attribute__ ((packed));
+
+struct zfcp_rc_entry {
+ u8 code;
+ const char *description;
+};
+
+/*
+ * FC-GS-2 stuff
+ */
+#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
+#define ZFCP_CT_REJECT 0x8001
+
+/*
+ * FC-GS-4 stuff
+ */
+#define ZFCP_CT_TIMEOUT (3 * R_A_TOV)
+
+
+/***************** 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_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"
+
+/******************** 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"
+
+/* read-only LUN sharing switch initial value */
+#define ZFCP_RO_LUN_SHARING_DEFAULTS 0
+
+/* 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
+
+/*
+ * 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
+
+/* get "loglevel" nibble assignment */
+#define ZFCP_GET_LOG_VALUE(zfcp_lognibble) \
+ ((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
+
+/* set "loglevel" nibble */
+#define ZFCP_SET_LOG_NIBBLE(value, zfcp_lognibble) \
+ (value << (zfcp_lognibble << 2))
+
+/* all log-level defaults are combined to generate initial log-level */
+#define ZFCP_LOG_LEVEL_DEFAULTS \
+ (ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_OTHER) | \
+ ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_SCSI) | \
+ ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FSF) | \
+ ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CONFIG) | \
+ ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CIO) | \
+ ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_QDIO) | \
+ ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_ERP) | \
+ ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FC))
+
+/* check whether we have the right level for logging */
+#define ZFCP_LOG_CHECK(level) \
+ ((ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA)) >= level)
+
+/* logging routine for zfcp */
+#define _ZFCP_LOG(fmt, args...) \
+ printk(KERN_ERR ZFCP_NAME": %s(%d): " fmt, __FUNCTION__, \
+ __LINE__ , ##args)
+
+#define ZFCP_LOG(level, fmt, args...) \
+do { \
+ if (ZFCP_LOG_CHECK(level)) \
+ _ZFCP_LOG(fmt, ##args); \
+} while (0)
+
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
+# define ZFCP_LOG_NORMAL(fmt, args...)
+#else
+# define ZFCP_LOG_NORMAL(fmt, args...) \
+do { \
+ if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_NORMAL)) \
+ printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
+} while (0)
+#endif
+
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
+# define ZFCP_LOG_INFO(fmt, args...)
+#else
+# define ZFCP_LOG_INFO(fmt, args...) \
+do { \
+ if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_INFO)) \
+ printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
+} while (0)
+#endif
+
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
+# define ZFCP_LOG_DEBUG(fmt, args...)
+#else
+# define ZFCP_LOG_DEBUG(fmt, args...) \
+ ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, fmt , ##args)
+#endif
+
+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
+# define ZFCP_LOG_TRACE(fmt, args...)
+#else
+# define ZFCP_LOG_TRACE(fmt, args...) \
+ ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args)
+#endif
+
+#ifndef ZFCP_PRINT_FLAGS
+# define ZFCP_LOG_FLAGS(level, fmt, args...)
+#else
+extern u32 flags_dump;
+# define ZFCP_LOG_FLAGS(level, fmt, args...) \
+do { \
+ if (level <= flags_dump) \
+ _ZFCP_LOG(fmt, ##args); \
+} while (0)
+#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 0xfff00000
+#define ZFCP_SPECIFIC_FLAGS 0x000fffff
+
+/* 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
+#define ZFCP_STATUS_COMMON_ACCESS_DENIED 0x00800000
+
+/* 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_SCSI_UP \
+ (ZFCP_STATUS_COMMON_UNBLOCKED | \
+ ZFCP_STATUS_ADAPTER_REGISTERED)
+
+
+/* FC-PH/FC-GS well-known address identifiers for generic services */
+#define ZFCP_DID_MANAGEMENT_SERVICE 0xFFFFFA
+#define ZFCP_DID_TIME_SERVICE 0xFFFFFB
+#define ZFCP_DID_DIRECTORY_SERVICE 0xFFFFFC
+#define ZFCP_DID_ALIAS_SERVICE 0xFFFFF8
+#define ZFCP_DID_KEY_DISTRIBUTION_SERVICE 0xFFFFF7
+
+/* 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_ACCESS_DENIED 0x00000040
+
+/* for ports with well known addresses */
+#define ZFCP_STATUS_PORT_WKA \
+ (ZFCP_STATUS_PORT_NO_WWPN | \
+ ZFCP_STATUS_PORT_NO_SCSI_ID)
+
+/* logical unit status */
+#define ZFCP_STATUS_UNIT_NOTSUPPUNITRESET 0x00000001
+#define ZFCP_STATUS_UNIT_TEMPORARY 0x00000002
+#define ZFCP_STATUS_UNIT_SHARED 0x00000004
+#define ZFCP_STATUS_UNIT_READONLY 0x00000008
+
+/* 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
+
+/*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/
+
+#define ZFCP_MAX_ERPS 3
+
+#define ZFCP_ERP_FSFREQ_TIMEOUT (30 * 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_STATUS_ERP_LOWMEM 0x00400000
+
+#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
+
+
+/******************** CFDC SPECIFIC STUFF *****************************/
+
+/* Firewall data channel sense data record */
+struct zfcp_cfdc_sense_data {
+ u32 signature; /* Request signature */
+ u32 devno; /* FCP adapter device number */
+ u32 command; /* Command code */
+ u32 fsf_status; /* FSF request status and status qualifier */
+ u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
+ u8 payloads[256]; /* Access conflicts list */
+ u8 control_file[0]; /* Access control table */
+};
+
+#define ZFCP_CFDC_SIGNATURE 0xCFDCACDF
+
+#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001
+#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101
+#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201
+#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401
+#define ZFCP_CFDC_CMND_UPLOAD 0x00010002
+
+#define ZFCP_CFDC_DOWNLOAD 0x00000001
+#define ZFCP_CFDC_UPLOAD 0x00000002
+#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000
+
+#define ZFCP_CFDC_DEV_NAME "zfcp_cfdc"
+#define ZFCP_CFDC_DEV_MAJOR MISC_MAJOR
+#define ZFCP_CFDC_DEV_MINOR MISC_DYNAMIC_MINOR
+
+#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE 127 * 1024
+
+/************************* STRUCTURE DEFINITIONS *****************************/
+
+struct zfcp_fsf_req;
+
+/* holds various memory pools of an adapter */
+struct zfcp_adapter_mempool {
+ mempool_t *fsf_req_erp;
+ mempool_t *fsf_req_scsi;
+ mempool_t *fsf_req_abort;
+ mempool_t *fsf_req_status_read;
+ mempool_t *data_status_read;
+ mempool_t *data_gid_pn;
+};
+
+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;
+ struct 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;
+};
+
+/*
+ * header for CT_IU
+ */
+struct ct_hdr {
+ u8 revision; // 0x01
+ u8 in_id[3]; // 0x00
+ u8 gs_type; // 0xFC Directory Service
+ u8 gs_subtype; // 0x02 Name Server
+ u8 options; // 0x00 single bidirectional exchange
+ u8 reserved0;
+ u16 cmd_rsp_code; // 0x0121 GID_PN, or 0x0100 GA_NXT
+ u16 max_res_size; // <= (4096 - 16) / 4
+ u8 reserved1;
+ u8 reason_code;
+ u8 reason_code_expl;
+ u8 vendor_unique;
+} __attribute__ ((packed));
+
+/* nameserver request CT_IU -- for requests where
+ * a port name is required */
+struct ct_iu_gid_pn_req {
+ struct ct_hdr header;
+ wwn_t wwpn;
+} __attribute__ ((packed));
+
+/* FS_ACC IU and data unit for GID_PN nameserver request */
+struct ct_iu_gid_pn_resp {
+ struct ct_hdr header;
+ fc_id_t d_id;
+} __attribute__ ((packed));
+
+typedef void (*zfcp_send_ct_handler_t)(unsigned long);
+
+/**
+ * struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct
+ * @port: port where the request is sent to
+ * @req: scatter-gather list for request
+ * @resp: scatter-gather list for response
+ * @req_count: number of elements in request scatter-gather list
+ * @resp_count: number of elements in response scatter-gather list
+ * @handler: handler function (called for response to the request)
+ * @handler_data: data passed to handler function
+ * @pool: pointer to memory pool for ct request structure
+ * @timeout: FSF timeout for this request
+ * @timer: timer (e.g. for request initiated by erp)
+ * @completion: completion for synchronization purposes
+ * @status: used to pass error status to calling function
+ */
+struct zfcp_send_ct {
+ struct zfcp_port *port;
+ struct scatterlist *req;
+ struct scatterlist *resp;
+ unsigned int req_count;
+ unsigned int resp_count;
+ zfcp_send_ct_handler_t handler;
+ unsigned long handler_data;
+ mempool_t *pool;
+ int timeout;
+ struct timer_list *timer;
+ struct completion *completion;
+ int status;
+};
+
+/* used for name server requests in error recovery */
+struct zfcp_gid_pn_data {
+ struct zfcp_send_ct ct;
+ struct scatterlist req;
+ struct scatterlist resp;
+ struct ct_iu_gid_pn_req ct_iu_req;
+ struct ct_iu_gid_pn_resp ct_iu_resp;
+ struct zfcp_port *port;
+};
+
+typedef void (*zfcp_send_els_handler_t)(unsigned long);
+
+/**
+ * struct zfcp_send_els - used to pass parameters to function zfcp_fsf_send_els
+ * @adapter: adapter where request is sent from
+ * @d_id: destiniation id of port where request is sent to
+ * @req: scatter-gather list for request
+ * @resp: scatter-gather list for response
+ * @req_count: number of elements in request scatter-gather list
+ * @resp_count: number of elements in response scatter-gather list
+ * @handler: handler function (called for response to the request)
+ * @handler_data: data passed to handler function
+ * @timer: timer (e.g. for request initiated by erp)
+ * @completion: completion for synchronization purposes
+ * @ls_code: hex code of ELS command
+ * @status: used to pass error status to calling function
+ */
+struct zfcp_send_els {
+ struct zfcp_adapter *adapter;
+ fc_id_t d_id;
+ struct scatterlist *req;
+ struct scatterlist *resp;
+ unsigned int req_count;
+ unsigned int resp_count;
+ zfcp_send_els_handler_t handler;
+ unsigned long handler_data;
+ struct timer_list *timer;
+ struct completion *completion;
+ int ls_code;
+ int status;
+};
+
+struct zfcp_status_read {
+ struct fsf_status_read_buffer *buffer;
+};
+
+struct zfcp_fsf_done {
+ struct completion *complete;
+ int status;
+};
+
+/* 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_ct *send_ct;
+ struct zfcp_send_els *send_els;
+ struct zfcp_status_read status_read;
+ struct fsf_qtcb_bottom_port *port_data;
+};
+
+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 {
+ 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;
+ u32 supported_features;/* of FCP channel */
+ u32 hardware_version; /* of FCP channel */
+ u8 serial_number[32]; /* of hardware */
+ struct Scsi_Host *scsi_host; /* Pointer to mid-layer */
+ unsigned short scsi_host_no; /* Assigned host number */
+ 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 */
+ 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 */
+ 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;
+ u32 erp_total_count; /* total nr of enqueued erp
+ actions */
+ u32 erp_low_mem_count; /* nr of erp actions waiting
+ for memory */
+ 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 *in_els_dbf;
+ debug_info_t *cmd_dbf;
+ spinlock_t dbf_lock;
+ struct zfcp_adapter_mempool pool; /* Adapter memory pools */
+ struct qdio_initialize qdio_init_data; /* for qdio_establish */
+ struct device generic_services; /* directory for WKA ports */
+};
+
+/*
+ * the struct device sysfs_device must be at the beginning of this structure.
+ * pointer to struct device is used to free port structure in release function
+ * of the device. don't change!
+ */
+struct zfcp_port {
+ struct device sysfs_device; /* sysfs device */
+ 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 */
+ u32 handle; /* handle assigned by FSF */
+ struct zfcp_erp_action erp_action; /* pending error recovery */
+ atomic_t erp_counter;
+};
+
+/* the struct device sysfs_device must be at the beginning of this structure.
+ * pointer to struct device is used to free unit structure in release function
+ * of the device. don't change!
+ */
+struct zfcp_unit {
+ struct device sysfs_device; /* sysfs device */
+ 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 */
+ struct scsi_device *device; /* scsi device struct pointer */
+ struct zfcp_erp_action erp_action; /* pending error recovery */
+ atomic_t erp_counter;
+};
+
+/* FSF request */
+struct zfcp_fsf_req {
+ struct list_head list; /* list of FSF requests */
+ struct zfcp_adapter *adapter; /* adapter request belongs to */
+ u8 sbal_number; /* nr of SBALs free for use */
+ u8 sbal_first; /* first SBAL for this request */
+ u8 sbal_last; /* last possible SBAL for
+ this reuest */
+ u8 sbal_curr; /* current SBAL during creation
+ of request */
+ u8 sbale_curr; /* current SBALE during creation
+ of request */
+ 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 */
+ mempool_t *pool; /* used if request was alloacted
+ from emergency pool */
+};
+
+typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
+
+/* driver data */
+struct zfcp_data {
+ struct 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;
+ u32 adapters; /* # of adapters in list */
+ rwlock_t config_lock; /* serialises changes
+ to adapter/port/unit
+ lists */
+ struct semaphore config_sema; /* serialises configuration
+ changes */
+ atomic_t loglevel; /* current loglevel */
+ char init_busid[BUS_ID_SIZE];
+ wwn_t init_wwpn;
+ fcp_lun_t init_fcp_lun;
+ char *driver_version;
+};
+
+/**
+ * struct zfcp_sg_list - struct describing a scatter-gather list
+ * @sg: pointer to array of (struct scatterlist)
+ * @count: number of elements in scatter-gather list
+ */
+struct zfcp_sg_list {
+ struct scatterlist *sg;
+ unsigned int count;
+};
+
+/* number of elements for various memory pools */
+#define ZFCP_POOL_FSF_REQ_ERP_NR 1
+#define ZFCP_POOL_FSF_REQ_SCSI_NR 1
+#define ZFCP_POOL_FSF_REQ_ABORT_NR 1
+#define ZFCP_POOL_STATUS_READ_NR ZFCP_STATUS_READS_RECOM
+#define ZFCP_POOL_DATA_GID_PN_NR 1
+
+/* struct used by memory pools for fsf_requests */
+struct zfcp_fsf_req_pool_element {
+ struct zfcp_fsf_req fsf_req;
+ struct fsf_qtcb qtcb;
+};
+
+/********************** 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_REQ_NO_QTCB 0x00000008
+
+#define ZFCP_SET 0x00000100
+#define ZFCP_CLEAR 0x00000200
+
+#define ZFCP_INTERRUPTIBLE 1
+#define ZFCP_UNINTERRUPTIBLE 0
+
+#ifndef atomic_test_mask
+#define atomic_test_mask(mask, target) \
+ ((atomic_read(target) & mask) == 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); \
+ }
+
+#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 /* ZFCP_DEF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
new file mode 100644
index 000000000000..cfc0d8c588df
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -0,0 +1,3585 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_erp.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP
+
+#define ZFCP_ERP_REVISION "$Revision: 1.86 $"
+
+#include "zfcp_ext.h"
+
+static int zfcp_erp_adisc(struct zfcp_adapter *, fc_id_t);
+static void zfcp_erp_adisc_handler(unsigned long);
+
+static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int);
+static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int);
+static int zfcp_erp_port_reopen_internal(struct zfcp_port *, int);
+static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *, int);
+
+static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *, int);
+static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *, int);
+
+static void zfcp_erp_adapter_block(struct zfcp_adapter *, int);
+static void zfcp_erp_adapter_unblock(struct zfcp_adapter *);
+static void zfcp_erp_port_block(struct zfcp_port *, int);
+static void zfcp_erp_port_unblock(struct zfcp_port *);
+static void zfcp_erp_unit_block(struct zfcp_unit *, int);
+static void zfcp_erp_unit_unblock(struct zfcp_unit *);
+
+static int zfcp_erp_thread(void *);
+
+static int zfcp_erp_strategy(struct zfcp_erp_action *);
+
+static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *);
+static int zfcp_erp_strategy_memwait(struct zfcp_erp_action *);
+static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *, int);
+static int zfcp_erp_strategy_check_unit(struct zfcp_unit *, int);
+static int zfcp_erp_strategy_check_port(struct zfcp_port *, int);
+static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *, int);
+static int zfcp_erp_strategy_statechange(int, u32, struct zfcp_adapter *,
+ struct zfcp_port *,
+ struct zfcp_unit *, int);
+static inline int zfcp_erp_strategy_statechange_detected(atomic_t *, u32);
+static int zfcp_erp_strategy_followup_actions(int, struct zfcp_adapter *,
+ struct zfcp_port *,
+ struct zfcp_unit *, int);
+static int zfcp_erp_strategy_check_queues(struct zfcp_adapter *);
+static int zfcp_erp_strategy_check_action(struct zfcp_erp_action *, int);
+
+static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *, int);
+static int zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_close_qdio(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_close_fsf(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *);
+static int zfcp_erp_adapter_strategy_open_fsf_statusread(
+ struct zfcp_erp_action *);
+
+static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *);
+
+static int zfcp_erp_port_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_clearstati(struct zfcp_port *);
+static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_nameserver_wakeup(
+ struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *);
+static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *);
+
+static int zfcp_erp_unit_strategy(struct zfcp_erp_action *);
+static int zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *);
+static int zfcp_erp_unit_strategy_close(struct zfcp_erp_action *);
+static int zfcp_erp_unit_strategy_open(struct zfcp_erp_action *);
+
+static int zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *);
+static int zfcp_erp_action_dismiss_port(struct zfcp_port *);
+static int zfcp_erp_action_dismiss_unit(struct zfcp_unit *);
+static int zfcp_erp_action_dismiss(struct zfcp_erp_action *);
+
+static int zfcp_erp_action_enqueue(int, struct zfcp_adapter *,
+ struct zfcp_port *, struct zfcp_unit *);
+static int zfcp_erp_action_dequeue(struct zfcp_erp_action *);
+static void zfcp_erp_action_cleanup(int, struct zfcp_adapter *,
+ struct zfcp_port *, struct zfcp_unit *,
+ int);
+
+static void zfcp_erp_action_ready(struct zfcp_erp_action *);
+static int zfcp_erp_action_exists(struct zfcp_erp_action *);
+
+static inline void zfcp_erp_action_to_ready(struct zfcp_erp_action *);
+static inline void zfcp_erp_action_to_running(struct zfcp_erp_action *);
+
+static void zfcp_erp_memwait_handler(unsigned long);
+static void zfcp_erp_timeout_handler(unsigned long);
+static inline void zfcp_erp_timeout_init(struct zfcp_erp_action *);
+
+/**
+ * zfcp_fsf_request_timeout_handler - called if a request timed out
+ * @data: pointer to adapter for handler function
+ *
+ * This function needs to be called if requests (ELS, Generic Service,
+ * or SCSI commands) exceed a certain time limit. The assumption is
+ * that after the time limit the adapter get stuck. So we trigger a reopen of
+ * the adapter. This should not be used for error recovery, SCSI abort
+ * commands and SCSI requests from SCSI mid-layer.
+ */
+void
+zfcp_fsf_request_timeout_handler(unsigned long data)
+{
+ struct zfcp_adapter *adapter;
+
+ adapter = (struct zfcp_adapter *) data;
+
+ zfcp_erp_adapter_reopen(adapter, 0);
+}
+
+/*
+ * function: zfcp_fsf_scsi_er_timeout_handler
+ *
+ * purpose: This function needs to be called whenever a SCSI error recovery
+ * action (abort/reset) does not return.
+ * Re-opening the adapter means that the command can be returned
+ * by zfcp (it is guarranteed that it does not return via the
+ * adapter anymore). The buffer can then be used again.
+ *
+ * returns: sod all
+ */
+void
+zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
+{
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+
+ ZFCP_LOG_NORMAL("warning: SCSI error recovery timed out. "
+ "Restarting all operations on the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 1, "eh_lmem_tout");
+ zfcp_erp_adapter_reopen(adapter, 0);
+
+ return;
+}
+
+/*
+ * function:
+ *
+ * purpose: called if an adapter failed,
+ * initiates adapter recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+int
+zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter, int clear_mask)
+{
+ int retval;
+
+ debug_text_event(adapter->erp_dbf, 5, "a_ro");
+ ZFCP_LOG_DEBUG("reopen adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+
+ zfcp_erp_adapter_block(adapter, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
+ ZFCP_LOG_DEBUG("skipped reopen of failed adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 5, "a_ro_f");
+ /* ensure propagation of failed status to new devices */
+ zfcp_erp_adapter_failed(adapter);
+ retval = -EIO;
+ goto out;
+ }
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
+ adapter, NULL, NULL);
+
+ out:
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: Wrappper for zfcp_erp_adapter_reopen_internal
+ * used to ensure the correct locking
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+int
+zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear_mask)
+{
+ int retval;
+
+ retval = zfcp_erp_adapter_reopen(adapter,
+ ZFCP_STATUS_COMMON_RUNNING |
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ clear_mask);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_erp_port_shutdown(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+
+ retval = zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_RUNNING |
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ clear_mask);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+int
+zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask)
+{
+ int retval;
+
+ retval = zfcp_erp_unit_reopen(unit,
+ ZFCP_STATUS_COMMON_RUNNING |
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ clear_mask);
+
+ return retval;
+}
+
+
+/**
+ * zfcp_erp_adisc - send ADISC ELS command
+ * @adapter: adapter structure
+ * @d_id: d_id of port where ADISC is sent to
+ */
+int
+zfcp_erp_adisc(struct zfcp_adapter *adapter, fc_id_t d_id)
+{
+ struct zfcp_send_els *send_els;
+ struct zfcp_ls_adisc *adisc;
+ void *address = NULL;
+ int retval = 0;
+ struct timer_list *timer;
+
+ send_els = kmalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC);
+ if (send_els == NULL)
+ goto nomem;
+ memset(send_els, 0, sizeof(*send_els));
+
+ send_els->req = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
+ if (send_els->req == NULL)
+ goto nomem;
+ memset(send_els->req, 0, sizeof(*send_els->req));
+
+ send_els->resp = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
+ if (send_els->resp == NULL)
+ goto nomem;
+ memset(send_els->resp, 0, sizeof(*send_els->resp));
+
+ address = (void *) get_zeroed_page(GFP_ATOMIC);
+ if (address == NULL)
+ goto nomem;
+
+ zfcp_address_to_sg(address, send_els->req);
+ address += PAGE_SIZE >> 1;
+ zfcp_address_to_sg(address, send_els->resp);
+ send_els->req_count = send_els->resp_count = 1;
+
+ send_els->adapter = adapter;
+ send_els->d_id = d_id;
+ send_els->handler = zfcp_erp_adisc_handler;
+ send_els->handler_data = (unsigned long) send_els;
+
+ adisc = zfcp_sg_to_address(send_els->req);
+ send_els->ls_code = adisc->code = ZFCP_LS_ADISC;
+
+ send_els->req->length = sizeof(struct zfcp_ls_adisc);
+ send_els->resp->length = sizeof(struct zfcp_ls_adisc_acc);
+
+ /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
+ without FC-AL-2 capability, so we don't set it */
+ adisc->wwpn = adapter->wwpn;
+ adisc->wwnn = adapter->wwnn;
+ adisc->nport_id = adapter->s_id;
+ ZFCP_LOG_INFO("ADISC request from s_id 0x%08x to d_id 0x%08x "
+ "(wwpn=0x%016Lx, wwnn=0x%016Lx, "
+ "hard_nport_id=0x%08x, nport_id=0x%08x)\n",
+ adapter->s_id, d_id, (wwn_t) adisc->wwpn,
+ (wwn_t) adisc->wwnn, adisc->hard_nport_id,
+ adisc->nport_id);
+
+ timer = kmalloc(sizeof(struct timer_list), GFP_ATOMIC);
+ if (!timer)
+ goto nomem;
+
+ init_timer(timer);
+ timer->function = zfcp_fsf_request_timeout_handler;
+ timer->data = (unsigned long) adapter;
+ timer->expires = ZFCP_FSF_REQUEST_TIMEOUT;
+ send_els->timer = timer;
+
+ retval = zfcp_fsf_send_els(send_els);
+ if (retval != 0) {
+ ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port "
+ "0x%08x on adapter %s\n", d_id,
+ zfcp_get_busid_by_adapter(adapter));
+ del_timer(send_els->timer);
+ goto freemem;
+ }
+
+ goto out;
+
+ nomem:
+ retval = -ENOMEM;
+ freemem:
+ if (address != NULL)
+ __free_pages(send_els->req->page, 0);
+ if (send_els != NULL) {
+ kfree(send_els->timer);
+ kfree(send_els->req);
+ kfree(send_els->resp);
+ kfree(send_els);
+ }
+ out:
+ return retval;
+}
+
+
+/**
+ * zfcp_erp_adisc_handler - handler for ADISC ELS command
+ * @data: pointer to struct zfcp_send_els
+ *
+ * If ADISC failed (LS_RJT or timed out) forced reopen of the port is triggered.
+ */
+void
+zfcp_erp_adisc_handler(unsigned long data)
+{
+ struct zfcp_send_els *send_els;
+ struct zfcp_port *port;
+ struct zfcp_adapter *adapter;
+ fc_id_t d_id;
+ struct zfcp_ls_adisc_acc *adisc;
+
+ send_els = (struct zfcp_send_els *) data;
+
+ del_timer(send_els->timer);
+
+ adapter = send_els->adapter;
+ d_id = send_els->d_id;
+
+ read_lock(&zfcp_data.config_lock);
+ port = zfcp_get_port_by_did(send_els->adapter, send_els->d_id);
+ read_unlock(&zfcp_data.config_lock);
+
+ BUG_ON(port == NULL);
+
+ /* request rejected or timed out */
+ if (send_els->status != 0) {
+ ZFCP_LOG_NORMAL("ELS request rejected/timed out, "
+ "force physical port reopen "
+ "(adapter %s, port d_id=0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ debug_text_event(adapter->erp_dbf, 3, "forcreop");
+ if (zfcp_erp_port_forced_reopen(port, 0))
+ ZFCP_LOG_NORMAL("failed reopen of port "
+ "(adapter %s, wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_port(port),
+ port->wwpn);
+ goto out;
+ }
+
+ adisc = zfcp_sg_to_address(send_els->resp);
+
+ ZFCP_LOG_INFO("ADISC response from d_id 0x%08x to s_id "
+ "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, "
+ "hard_nport_id=0x%08x, nport_id=0x%08x)\n",
+ d_id, adapter->s_id, (wwn_t) adisc->wwpn,
+ (wwn_t) adisc->wwnn, adisc->hard_nport_id,
+ adisc->nport_id);
+
+ /* set wwnn for port */
+ if (port->wwnn == 0)
+ port->wwnn = adisc->wwnn;
+
+ if (port->wwpn != adisc->wwpn) {
+ ZFCP_LOG_NORMAL("d_id assignment changed, reopening "
+ "port (adapter %s, wwpn=0x%016Lx, "
+ "adisc_resp_wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_port(port),
+ port->wwpn, (wwn_t) adisc->wwpn);
+ if (zfcp_erp_port_reopen(port, 0))
+ ZFCP_LOG_NORMAL("failed reopen of port "
+ "(adapter %s, wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_port(port),
+ port->wwpn);
+ }
+
+ out:
+ zfcp_port_put(port);
+ __free_pages(send_els->req->page, 0);
+ kfree(send_els->timer);
+ kfree(send_els->req);
+ kfree(send_els->resp);
+ kfree(send_els);
+}
+
+
+/**
+ * zfcp_test_link - lightweight link test procedure
+ * @port: port to be tested
+ *
+ * Test status of a link to a remote port using the ELS command ADISC.
+ */
+int
+zfcp_test_link(struct zfcp_port *port)
+{
+ int retval;
+
+ zfcp_port_get(port);
+ retval = zfcp_erp_adisc(port->adapter, port->d_id);
+ if (retval != 0) {
+ zfcp_port_put(port);
+ ZFCP_LOG_NORMAL("reopen needed for port 0x%016Lx "
+ "on adapter %s\n ", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ retval = zfcp_erp_port_forced_reopen(port, 0);
+ if (retval != 0) {
+ ZFCP_LOG_NORMAL("reopen of remote port 0x%016Lx "
+ "on adapter %s failed\n", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ retval = -EPERM;
+ }
+ }
+
+ return retval;
+}
+
+
+/*
+ * function:
+ *
+ * purpose: called if a port failed to be opened normally
+ * initiates Forced Reopen recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+static int
+zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 5, "pf_ro");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+
+ ZFCP_LOG_DEBUG("forced reopen of port 0x%016Lx on adapter %s\n",
+ port->wwpn, zfcp_get_busid_by_port(port));
+
+ zfcp_erp_port_block(port, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
+ ZFCP_LOG_DEBUG("skipped forced reopen of failed port 0x%016Lx "
+ "on adapter %s\n", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ debug_text_event(adapter->erp_dbf, 5, "pf_ro_f");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+ retval = -EIO;
+ goto out;
+ }
+
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
+ port->adapter, port, NULL);
+
+ out:
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: Wrappper for zfcp_erp_port_forced_reopen_internal
+ * used to ensure the correct locking
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+int
+zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+ struct zfcp_adapter *adapter;
+
+ adapter = port->adapter;
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_port_forced_reopen_internal(port, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: called if a port is to be opened
+ * initiates Reopen recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+static int
+zfcp_erp_port_reopen_internal(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 5, "p_ro");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+
+ ZFCP_LOG_DEBUG("reopen of port 0x%016Lx on adapter %s\n",
+ port->wwpn, zfcp_get_busid_by_port(port));
+
+ zfcp_erp_port_block(port, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
+ ZFCP_LOG_DEBUG("skipped reopen of failed port 0x%016Lx "
+ "on adapter %s\n", port->wwpn,
+ zfcp_get_busid_by_port(port));
+ debug_text_event(adapter->erp_dbf, 5, "p_ro_f");
+ debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
+ /* ensure propagation of failed status to new devices */
+ zfcp_erp_port_failed(port);
+ retval = -EIO;
+ goto out;
+ }
+
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
+ port->adapter, port, NULL);
+
+ out:
+ return retval;
+}
+
+/**
+ * zfcp_erp_port_reopen - initiate reopen of a remote port
+ * @port: port to be reopened
+ * @clear_mask: specifies flags in port status to be cleared
+ * Return: 0 on success, < 0 on error
+ *
+ * This is a wrappper function for zfcp_erp_port_reopen_internal. It ensures
+ * correct locking. An error recovery task is initiated to do the reopen.
+ * To wait for the completion of the reopen zfcp_erp_wait should be used.
+ */
+int
+zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+ struct zfcp_adapter *adapter = port->adapter;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_port_reopen_internal(port, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: called if a unit is to be opened
+ * initiates Reopen recovery which is done
+ * asynchronously
+ *
+ * returns: 0 - initiated action succesfully
+ * <0 - failed to initiate action
+ */
+static int
+zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 5, "u_ro");
+ debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
+ ZFCP_LOG_DEBUG("reopen of unit 0x%016Lx on port 0x%016Lx "
+ "on adapter %s\n", unit->fcp_lun,
+ unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+
+ zfcp_erp_unit_block(unit, clear_mask);
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
+ ZFCP_LOG_DEBUG("skipped reopen of failed unit 0x%016Lx "
+ "on port 0x%016Lx on adapter %s\n",
+ unit->fcp_lun, unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit));
+ debug_text_event(adapter->erp_dbf, 5, "u_ro_f");
+ debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
+ sizeof (fcp_lun_t));
+ retval = -EIO;
+ goto out;
+ }
+
+ retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
+ unit->port->adapter, unit->port, unit);
+ out:
+ return retval;
+}
+
+/**
+ * zfcp_erp_unit_reopen - initiate reopen of a unit
+ * @unit: unit to be reopened
+ * @clear_mask: specifies flags in unit status to be cleared
+ * Return: 0 on success, < 0 on error
+ *
+ * This is a wrappper for zfcp_erp_unit_reopen_internal. It ensures correct
+ * locking. An error recovery task is initiated to do the reopen.
+ * To wait for the completion of the reopen zfcp_erp_wait should be used.
+ */
+int
+zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask)
+{
+ int retval;
+ unsigned long flags;
+ struct zfcp_adapter *adapter;
+ struct zfcp_port *port;
+
+ port = unit->port;
+ adapter = port->adapter;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ retval = zfcp_erp_unit_reopen_internal(unit, clear_mask);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ return retval;
+}
+
+/*
+ * function:
+ *
+ * purpose: disable I/O,
+ * return any open requests and clean them up,
+ * aim: no pending and incoming I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
+{
+ debug_text_event(adapter->erp_dbf, 6, "a_bl");
+ zfcp_erp_modify_adapter_status(adapter,
+ ZFCP_STATUS_COMMON_UNBLOCKED |
+ clear_mask, ZFCP_CLEAR);
+}
+
+/*
+ * function:
+ *
+ * purpose: enable I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
+{
+ debug_text_event(adapter->erp_dbf, 6, "a_ubl");
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
+}
+
+/*
+ * function:
+ *
+ * purpose: disable I/O,
+ * return any open requests and clean them up,
+ * aim: no pending and incoming I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_port_block(struct zfcp_port *port, int clear_mask)
+{
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "p_bl");
+ debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+ zfcp_erp_modify_port_status(port,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
+ ZFCP_CLEAR);
+}
+
+/*
+ * function:
+ *
+ * purpose: enable I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_port_unblock(struct zfcp_port *port)
+{
+ struct zfcp_adapter *adapter = port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "p_ubl");
+ debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
+}
+
+/*
+ * function:
+ *
+ * purpose: disable I/O,
+ * return any open requests and clean them up,
+ * aim: no pending and incoming I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "u_bl");
+ debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
+ zfcp_erp_modify_unit_status(unit,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
+ ZFCP_CLEAR);
+}
+
+/*
+ * function:
+ *
+ * purpose: enable I/O
+ *
+ * returns:
+ */
+static void
+zfcp_erp_unit_unblock(struct zfcp_unit *unit)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 6, "u_ubl");
+ debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ */
+static void
+zfcp_erp_action_ready(struct zfcp_erp_action *erp_action)
+{
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 4, "a_ar");
+ debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof (int));
+
+ zfcp_erp_action_to_ready(erp_action);
+ up(&adapter->erp_ready_sem);
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns: <0 erp_action not found in any list
+ * ZFCP_ERP_ACTION_READY erp_action is in ready list
+ * ZFCP_ERP_ACTION_RUNNING erp_action is in running list
+ *
+ * locks: erp_lock must be held
+ */
+static int
+zfcp_erp_action_exists(struct zfcp_erp_action *erp_action)
+{
+ int retval = -EINVAL;
+ struct list_head *entry;
+ struct zfcp_erp_action *entry_erp_action;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ /* search in running list */
+ list_for_each(entry, &adapter->erp_running_head) {
+ entry_erp_action =
+ list_entry(entry, struct zfcp_erp_action, list);
+ if (entry_erp_action == erp_action) {
+ retval = ZFCP_ERP_ACTION_RUNNING;
+ goto out;
+ }
+ }
+ /* search in ready list */
+ list_for_each(entry, &adapter->erp_ready_head) {
+ entry_erp_action =
+ list_entry(entry, struct zfcp_erp_action, list);
+ if (entry_erp_action == erp_action) {
+ retval = ZFCP_ERP_ACTION_READY;
+ goto out;
+ }
+ }
+
+ out:
+ return retval;
+}
+
+/*
+ * purpose: checks current status of action (timed out, dismissed, ...)
+ * and does appropriate preparations (dismiss fsf request, ...)
+ *
+ * locks: called under erp_lock (disabled interrupts)
+ *
+ * returns: 0
+ */
+static int
+zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action)
+{
+ int retval = 0;
+ struct zfcp_fsf_req *fsf_req;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ if (erp_action->fsf_req) {
+ /* take lock to ensure that request is not being deleted meanwhile */
+ write_lock(&adapter->fsf_req_list_lock);
+ /* check whether fsf req does still exist */
+ list_for_each_entry(fsf_req, &adapter->fsf_req_list_head, list)
+ if (fsf_req == erp_action->fsf_req)
+ break;
+ if (fsf_req == erp_action->fsf_req) {
+ /* fsf_req still exists */
+ debug_text_event(adapter->erp_dbf, 3, "a_ca_req");
+ debug_event(adapter->erp_dbf, 3, &fsf_req,
+ sizeof (unsigned long));
+ /* dismiss fsf_req of timed out or dismissed erp_action */
+ if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
+ ZFCP_STATUS_ERP_TIMEDOUT)) {
+ debug_text_event(adapter->erp_dbf, 3,
+ "a_ca_disreq");
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+ }
+ if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
+ ZFCP_LOG_NORMAL("error: erp step timed out "
+ "(action=%d, fsf_req=%p)\n ",
+ erp_action->action,
+ erp_action->fsf_req);
+ }
+ /*
+ * If fsf_req is neither dismissed nor completed
+ * then keep it running asynchronously and don't mess
+ * with the association of erp_action and fsf_req.
+ */
+ if (fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
+ ZFCP_STATUS_FSFREQ_DISMISSED)) {
+ /* forget about association between fsf_req
+ and erp_action */
+ fsf_req->erp_action = NULL;
+ erp_action->fsf_req = NULL;
+ }
+ } else {
+ debug_text_event(adapter->erp_dbf, 3, "a_ca_gonereq");
+ /*
+ * even if this fsf_req has gone, forget about
+ * association between erp_action and fsf_req
+ */
+ erp_action->fsf_req = NULL;
+ }
+ write_unlock(&adapter->fsf_req_list_lock);
+ } else
+ debug_text_event(adapter->erp_dbf, 3, "a_ca_noreq");
+
+ return retval;
+}
+
+/*
+ * purpose: generic handler for asynchronous events related to erp_action events
+ * (normal completion, time-out, dismissing, retry after
+ * low memory condition)
+ *
+ * note: deletion of timer is not required (e.g. in case of a time-out),
+ * but a second try does no harm,
+ * we leave it in here to allow for greater simplification
+ *
+ * returns: 0 - there was an action to handle
+ * !0 - otherwise
+ */
+static int
+zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action,
+ unsigned long set_mask)
+{
+ int retval;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
+ debug_text_event(adapter->erp_dbf, 2, "a_asyh_ex");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action,
+ sizeof (int));
+ if (!(set_mask & ZFCP_STATUS_ERP_TIMEDOUT))
+ del_timer(&erp_action->timer);
+ erp_action->status |= set_mask;
+ zfcp_erp_action_ready(erp_action);
+ retval = 0;
+ } else {
+ /* action is ready or gone - nothing to do */
+ debug_text_event(adapter->erp_dbf, 3, "a_asyh_gone");
+ debug_event(adapter->erp_dbf, 3, &erp_action->action,
+ sizeof (int));
+ retval = 1;
+ }
+
+ return retval;
+}
+
+/*
+ * purpose: generic handler for asynchronous events related to erp_action
+ * events (normal completion, time-out, dismissing, retry after
+ * low memory condition)
+ *
+ * note: deletion of timer is not required (e.g. in case of a time-out),
+ * but a second try does no harm,
+ * we leave it in here to allow for greater simplification
+ *
+ * returns: 0 - there was an action to handle
+ * !0 - otherwise
+ */
+int
+zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
+ unsigned long set_mask)
+{
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ unsigned long flags;
+ int retval;
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ retval = zfcp_erp_async_handler_nolock(erp_action, set_mask);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+ return retval;
+}
+
+/*
+ * purpose: is called for erp_action which was slept waiting for
+ * memory becoming avaliable,
+ * will trigger that this action will be continued
+ */
+static void
+zfcp_erp_memwait_handler(unsigned long data)
+{
+ struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 2, "a_mwh");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
+
+ zfcp_erp_async_handler(erp_action, 0);
+}
+
+/*
+ * purpose: is called if an asynchronous erp step timed out,
+ * action gets an appropriate flag and will be processed
+ * accordingly
+ */
+static void
+zfcp_erp_timeout_handler(unsigned long data)
+{
+ struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 2, "a_th");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
+
+ zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
+}
+
+/*
+ * purpose: is called for an erp_action which needs to be ended
+ * though not being done,
+ * this is usually required if an higher is generated,
+ * action gets an appropriate flag and will be processed
+ * accordingly
+ *
+ * locks: erp_lock held (thus we need to call another handler variant)
+ */
+static int
+zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
+{
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ debug_text_event(adapter->erp_dbf, 2, "a_adis");
+ debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
+
+ zfcp_erp_async_handler_nolock(erp_action, ZFCP_STATUS_ERP_DISMISSED);
+
+ return 0;
+}
+
+int
+zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
+{
+ int retval = 0;
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+
+ rwlock_init(&adapter->erp_lock);
+ INIT_LIST_HEAD(&adapter->erp_ready_head);
+ INIT_LIST_HEAD(&adapter->erp_running_head);
+ sema_init(&adapter->erp_ready_sem, 0);
+
+ retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
+ if (retval < 0) {
+ ZFCP_LOG_NORMAL("error: creation of erp thread failed for "
+ "adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 5, "a_thset_fail");
+ } else {
+ wait_event(adapter->erp_thread_wqh,
+ atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
+ &adapter->status));
+ debug_text_event(adapter->erp_dbf, 5, "a_thset_ok");
+ }
+
+ return (retval < 0);
+}
+
+/*
+ * function:
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * context: process (i.e. proc-fs or rmmod/insmod)
+ *
+ * note: The caller of this routine ensures that the specified
+ * adapter has been shut down and that this operation
+ * has been completed. Thus, there are no pending erp_actions
+ * which would need to be handled here.
+ */
+int
+zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
+{
+ int retval = 0;
+
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
+ up(&adapter->erp_ready_sem);
+
+ wait_event(adapter->erp_thread_wqh,
+ !atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
+ &adapter->status));
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
+ &adapter->status);
+
+ debug_text_event(adapter->erp_dbf, 5, "a_thki_ok");
+
+ return retval;
+}
+
+/*
+ * purpose: is run as a kernel thread,
+ * goes through list of error recovery actions of associated adapter
+ * and delegates single action to execution
+ *
+ * returns: 0
+ */
+static int
+zfcp_erp_thread(void *data)
+{
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+ struct list_head *next;
+ struct zfcp_erp_action *erp_action;
+ unsigned long flags;
+
+ daemonize("zfcperp%s", zfcp_get_busid_by_adapter(adapter));
+ /* Block all signals */
+ siginitsetinv(&current->blocked, 0);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+ debug_text_event(adapter->erp_dbf, 5, "a_th_run");
+ wake_up(&adapter->erp_thread_wqh);
+
+ while (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
+ &adapter->status)) {
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ next = adapter->erp_ready_head.prev;
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+ if (next != &adapter->erp_ready_head) {
+ erp_action =
+ list_entry(next, struct zfcp_erp_action, list);
+ /*
+ * process action (incl. [re]moving it
+ * from 'ready' queue)
+ */
+ zfcp_erp_strategy(erp_action);
+ }
+
+ /*
+ * sleep as long as there is nothing to do, i.e.
+ * no action in 'ready' queue to be processed and
+ * thread is not to be killed
+ */
+ down_interruptible(&adapter->erp_ready_sem);
+ debug_text_event(adapter->erp_dbf, 5, "a_th_woken");
+ }
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+ debug_text_event(adapter->erp_dbf, 5, "a_th_stop");
+ wake_up(&adapter->erp_thread_wqh);
+
+ return 0;
+}
+
+/*
+ * function:
+ *
+ * purpose: drives single error recovery action and schedules higher and
+ * subordinate actions, if necessary
+ *
+ * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
+ * ZFCP_ERP_SUCCEEDED - action finished successfully (deqd)
+ * ZFCP_ERP_FAILED - action finished unsuccessfully (deqd)
+ * ZFCP_ERP_EXIT - action finished (dequeued), offline
+ * ZFCP_ERP_DISMISSED - action canceled (dequeued)
+ */
+static int
+zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
+{
+ int retval = 0;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_port *port = erp_action->port;
+ struct zfcp_unit *unit = erp_action->unit;
+ int action = erp_action->action;
+ u32 status = erp_action->status;
+ unsigned long flags;
+
+ /* serialise dismissing, timing out, moving, enqueueing */
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+
+ /* dequeue dismissed action and leave, if required */
+ retval = zfcp_erp_strategy_check_action(erp_action, retval);
+ if (retval == ZFCP_ERP_DISMISSED) {
+ debug_text_event(adapter->erp_dbf, 4, "a_st_dis1");
+ goto unlock;
+ }
+
+ /*
+ * move action to 'running' queue before processing it
+ * (to avoid a race condition regarding moving the
+ * action to the 'running' queue and back)
+ */
+ zfcp_erp_action_to_running(erp_action);
+
+ /*
+ * try to process action as far as possible,
+ * no lock to allow for blocking operations (kmalloc, qdio, ...),
+ * afterwards the lock is required again for the following reasons:
+ * - dequeueing of finished action and enqueueing of
+ * follow-up actions must be atomic so that any other
+ * reopen-routine does not believe there is nothing to do
+ * and that it is safe to enqueue something else,
+ * - we want to force any control thread which is dismissing
+ * actions to finish this before we decide about
+ * necessary steps to be taken here further
+ */
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ retval = zfcp_erp_strategy_do_action(erp_action);
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+
+ /*
+ * check for dismissed status again to avoid follow-up actions,
+ * failing of targets and so on for dismissed actions
+ */
+ retval = zfcp_erp_strategy_check_action(erp_action, retval);
+
+ switch (retval) {
+ case ZFCP_ERP_DISMISSED:
+ /* leave since this action has ridden to its ancestors */
+ debug_text_event(adapter->erp_dbf, 6, "a_st_dis2");
+ goto unlock;
+ case ZFCP_ERP_NOMEM:
+ /* no memory to continue immediately, let it sleep */
+ if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) {
+ ++adapter->erp_low_mem_count;
+ erp_action->status |= ZFCP_STATUS_ERP_LOWMEM;
+ }
+ /* This condition is true if there is no memory available
+ for any erp_action on this adapter. This implies that there
+ are no elements in the memory pool(s) left for erp_actions.
+ This might happen if an erp_action that used a memory pool
+ element was timed out.
+ */
+ if (adapter->erp_total_count == adapter->erp_low_mem_count) {
+ debug_text_event(adapter->erp_dbf, 3, "a_st_lowmem");
+ ZFCP_LOG_NORMAL("error: no mempool elements available, "
+ "restarting I/O on adapter %s "
+ "to free mempool\n",
+ zfcp_get_busid_by_adapter(adapter));
+ zfcp_erp_adapter_reopen_internal(adapter, 0);
+ } else {
+ debug_text_event(adapter->erp_dbf, 2, "a_st_memw");
+ retval = zfcp_erp_strategy_memwait(erp_action);
+ }
+ goto unlock;
+ case ZFCP_ERP_CONTINUES:
+ /* leave since this action runs asynchronously */
+ debug_text_event(adapter->erp_dbf, 6, "a_st_cont");
+ if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
+ --adapter->erp_low_mem_count;
+ erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
+ }
+ 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);
+
+ if (retval != ZFCP_ERP_CONTINUES)
+ zfcp_erp_action_cleanup(action, adapter, port, unit, retval);
+
+ /*
+ * 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 erp action requested on "
+ "adapter %s (action=%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 erp failed on 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);
+
+ if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
+ ZFCP_LOG_NORMAL("port erp failed (adapter %s, "
+ "port d_id=0x%08x)\n",
+ zfcp_get_busid_by_port(port), port->d_id);
+ else
+ ZFCP_LOG_NORMAL("port erp failed (adapter %s, wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_port(port), port->wwpn);
+
+ 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 erp failed on unit 0x%016Lx on port 0x%016Lx "
+ " on adapter %s\n", unit->fcp_lun,
+ unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+ debug_text_event(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));
+
+ switch (result) {
+ case ZFCP_ERP_SUCCEEDED :
+ atomic_set(&unit->erp_counter, 0);
+ zfcp_erp_unit_unblock(unit);
+ break;
+ case ZFCP_ERP_FAILED :
+ atomic_inc(&unit->erp_counter);
+ if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS)
+ zfcp_erp_unit_failed(unit);
+ break;
+ case ZFCP_ERP_EXIT :
+ /* nothing */
+ break;
+ }
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
+ zfcp_erp_unit_block(unit, 0); /* for ZFCP_ERP_SUCCEEDED */
+ 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));
+
+ switch (result) {
+ case ZFCP_ERP_SUCCEEDED :
+ atomic_set(&port->erp_counter, 0);
+ zfcp_erp_port_unblock(port);
+ break;
+ case ZFCP_ERP_FAILED :
+ atomic_inc(&port->erp_counter);
+ if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS)
+ zfcp_erp_port_failed(port);
+ break;
+ case ZFCP_ERP_EXIT :
+ /* nothing */
+ break;
+ }
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
+ zfcp_erp_port_block(port, 0); /* for ZFCP_ERP_SUCCEEDED */
+ 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");
+
+ switch (result) {
+ case ZFCP_ERP_SUCCEEDED :
+ atomic_set(&adapter->erp_counter, 0);
+ zfcp_erp_adapter_unblock(adapter);
+ break;
+ case ZFCP_ERP_FAILED :
+ atomic_inc(&adapter->erp_counter);
+ if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS)
+ zfcp_erp_adapter_failed(adapter);
+ break;
+ case ZFCP_ERP_EXIT :
+ /* nothing */
+ break;
+ }
+
+ if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
+ zfcp_erp_adapter_block(adapter, 0); /* for ZFCP_ERP_SUCCEEDED */
+ 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)
+{
+ unsigned long flags;
+
+ 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)) {
+ 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 0;
+}
+
+/**
+ * zfcp_erp_wait - wait for completion of error recovery on an adapter
+ * @adapter: adapter for which to wait for completion of its error recovery
+ * Return: 0
+ */
+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_WKA, &port->status))
+ 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;
+ 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));
+ msleep(jiffies_to_msecs(ZFCP_TYPE2_RECOVERY_TIME));
+ }
+
+ 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;
+ int i;
+ volatile struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
+ ZFCP_LOG_NORMAL("bug: second attempt to set up QDIO on "
+ "adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto failed_sanity;
+ }
+
+ if (qdio_establish(&adapter->qdio_init_data) != 0) {
+ ZFCP_LOG_INFO("error: establishment of QDIO queues failed "
+ "on adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto failed_qdio_establish;
+ }
+ debug_text_event(adapter->erp_dbf, 3, "qdio_est");
+
+ if (qdio_activate(adapter->ccw_device, 0) != 0) {
+ ZFCP_LOG_INFO("error: activation of QDIO queues failed "
+ "on adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto failed_qdio_activate;
+ }
+ debug_text_event(adapter->erp_dbf, 3, "qdio_act");
+
+ /*
+ * put buffers into response queue,
+ */
+ for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
+ sbale = &(adapter->response_queue.buffer[i]->element[0]);
+ sbale->length = 0;
+ sbale->flags = SBAL_FLAGS_LAST_ENTRY;
+ sbale->addr = 0;
+ }
+
+ ZFCP_LOG_TRACE("calling do_QDIO on adapter %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: setup of QDIO failed (retval=%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_text_event(adapter->erp_dbf, 3, "qdio_down1a");
+ while (qdio_shutdown(adapter->ccw_device,
+ QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
+ msleep(1000);
+ debug_text_event(adapter->erp_dbf, 3, "qdio_down1b");
+
+ failed_qdio_establish:
+ 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("error: attempt to shut down inactive QDIO "
+ "queues on adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ retval = ZFCP_ERP_FAILED;
+ goto out;
+ }
+
+ /*
+ * Get queue_lock and clear QDIOUP flag. Thus it's guaranteed that
+ * do_QDIO won't be called while qdio_shutdown is in progress.
+ */
+
+ write_lock_irq(&adapter->request_queue.queue_lock);
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+ write_unlock_irq(&adapter->request_queue.queue_lock);
+
+ debug_text_event(adapter->erp_dbf, 3, "qdio_down2a");
+ while (qdio_shutdown(adapter->ccw_device,
+ QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
+ msleep(1000);
+ debug_text_event(adapter->erp_dbf, 3, "qdio_down2b");
+
+ /*
+ * 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;
+ 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_action_to_running(erp_action);
+ 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: initiation of exchange of "
+ "configuration data failed for "
+ "adapter %s\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(&adapter->erp_ready_sem);
+ if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
+ ZFCP_LOG_INFO("error: exchange of configuration data "
+ "for adapter %s 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 */
+ msleep(jiffies_to_msecs(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 for "
+ "adapter %s 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: set-up of unsolicited status "
+ "notification failed on 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_LOG_DEBUG("port 0x%016Lx 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("close physical failed for port "
+ "0x%016Lx\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 0x%016Lx 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("close failed for port 0x%016Lx\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_WKA,
+ &erp_action->port->status))
+ 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 "
+ "unavailable for adapter %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);
+ if (zfcp_erp_port_reopen(adapter->nameserver_port, 0)
+ >= 0) {
+ erp_action->step =
+ ZFCP_ERP_STEP_NAMESERVER_OPEN;
+ retval = ZFCP_ERP_CONTINUES;
+ } else
+ retval = ZFCP_ERP_FAILED;
+ 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("open failed for nameserver port\n");
+ retval = ZFCP_ERP_FAILED;
+ } else {
+ ZFCP_LOG_DEBUG("nameserver port is open -> "
+ "nameserver look-up for port 0x%016Lx\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("nameserver look-up failed "
+ "for port 0x%016Lx "
+ "(misconfigured WWPN?)\n",
+ port->wwpn);
+ zfcp_erp_port_failed(port);
+ retval = ZFCP_ERP_EXIT;
+ } else {
+ ZFCP_LOG_DEBUG("nameserver look-up failed for "
+ "port 0x%016Lx\n", port->wwpn);
+ retval = ZFCP_ERP_FAILED;
+ }
+ } else {
+ ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%08x -> "
+ "trying open\n", port->wwpn, 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 0x%016Lx is open\n", port->wwpn);
+ retval = ZFCP_ERP_SUCCEEDED;
+ } else {
+ ZFCP_LOG_DEBUG("open failed for port 0x%016Lx\n",
+ port->wwpn);
+ retval = ZFCP_ERP_FAILED;
+ }
+ break;
+
+ default:
+ ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\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 0x%016Lx has d_id=0x%08x -> trying open\n",
+ port->wwpn, 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("WKA port is open\n");
+ retval = ZFCP_ERP_SUCCEEDED;
+ } else {
+ ZFCP_LOG_DEBUG("open failed for WKA 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: unknown erp step 0x%08x\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));
+ if (atomic_test_mask(
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ &adapter->nameserver_port->status))
+ zfcp_erp_port_failed(erp_action->port);
+ 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_COMMON_ACCESS_DENIED |
+ 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_ns_gid_pn_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 0x%016Lx 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("close failed for unit 0x%016Lx\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 0x%016Lx 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 0x%016Lx is open\n",
+ unit->fcp_lun);
+ retval = ZFCP_ERP_SUCCEEDED;
+ } else {
+ ZFCP_LOG_DEBUG("open failed for unit 0x%016Lx\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 |
+ ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ ZFCP_STATUS_UNIT_SHARED |
+ ZFCP_STATUS_UNIT_READONLY,
+ &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.
+ */
+
+ if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
+ &adapter->status))
+ return -EIO;
+
+ 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_RUNNING, &port->status) ||
+ atomic_test_mask
+ (ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
+ 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_RUNNING, &adapter->status) ||
+ atomic_test_mask
+ (ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
+ 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 erp action requested "
+ "on adapter %s (action=%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("stronger erp action %d needed before "
+ "erp action %d on adapter %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;
+
+ ++adapter->erp_total_count;
+
+ /* 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;
+
+ --adapter->erp_total_count;
+ if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
+ --adapter->erp_low_mem_count;
+ erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
+ }
+
+ 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);
+ break;
+ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+ &erp_action->port->status);
+ break;
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+ &erp_action->adapter->status);
+ break;
+ default:
+ /* bug */
+ break;
+ }
+ return retval;
+}
+
+/**
+ * zfcp_erp_action_cleanup
+ *
+ * Register unit with scsi stack if appropiate and fix reference counts.
+ * Note: Temporary units are not registered with scsi stack.
+ */
+static void
+zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter,
+ struct zfcp_port *port, struct zfcp_unit *unit,
+ int result)
+{
+ switch (action) {
+ case ZFCP_ERP_ACTION_REOPEN_UNIT:
+ if ((result == ZFCP_ERP_SUCCEEDED)
+ && (!atomic_test_mask(ZFCP_STATUS_UNIT_TEMPORARY,
+ &unit->status))
+ && (!unit->device))
+ scsi_add_device(unit->port->adapter->scsi_host, 0,
+ unit->port->scsi_id, unit->scsi_lun);
+ zfcp_unit_put(unit);
+ break;
+ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ zfcp_port_put(port);
+ break;
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ zfcp_adapter_put(adapter);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ * 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);
+}
+
+/*
+ * function: zfcp_erp_port_access_denied
+ *
+ * purpose:
+ */
+void
+zfcp_erp_port_access_denied(struct zfcp_port *port)
+{
+ struct zfcp_adapter *adapter = port->adapter;
+ unsigned long flags;
+
+ debug_text_event(adapter->erp_dbf, 3, "p_access_block");
+ debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ zfcp_erp_modify_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED |
+ ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+/*
+ * function: zfcp_erp_unit_access_denied
+ *
+ * purpose:
+ */
+void
+zfcp_erp_unit_access_denied(struct zfcp_unit *unit)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 3, "u_access_block");
+ debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t));
+ zfcp_erp_modify_unit_status(unit, ZFCP_STATUS_COMMON_ERP_FAILED |
+ ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET);
+}
+
+/*
+ * function: zfcp_erp_adapter_access_changed
+ *
+ * purpose:
+ */
+void
+zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter)
+{
+ struct zfcp_port *port;
+ unsigned long flags;
+
+ debug_text_event(adapter->erp_dbf, 3, "a_access_unblock");
+ debug_event(adapter->erp_dbf, 3, &adapter->name, 8);
+
+ zfcp_erp_port_access_changed(adapter->nameserver_port);
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ if (port != adapter->nameserver_port)
+ zfcp_erp_port_access_changed(port);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+/*
+ * function: zfcp_erp_port_access_changed
+ *
+ * purpose:
+ */
+void
+zfcp_erp_port_access_changed(struct zfcp_port *port)
+{
+ struct zfcp_adapter *adapter = port->adapter;
+ struct zfcp_unit *unit;
+
+ debug_text_event(adapter->erp_dbf, 3, "p_access_unblock");
+ debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
+
+ if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED,
+ &port->status)) {
+ if (!atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
+ list_for_each_entry(unit, &port->unit_list_head, list)
+ zfcp_erp_unit_access_changed(unit);
+ return;
+ }
+
+ ZFCP_LOG_NORMAL("reopen of port 0x%016Lx on adapter %s "
+ "(due to ACT update)\n",
+ port->wwpn, zfcp_get_busid_by_adapter(adapter));
+ if (zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED) != 0)
+ ZFCP_LOG_NORMAL("failed reopen of port"
+ "(adapter %s, wwpn=0x%016Lx)\n",
+ zfcp_get_busid_by_adapter(adapter), port->wwpn);
+}
+
+/*
+ * function: zfcp_erp_unit_access_changed
+ *
+ * purpose:
+ */
+void
+zfcp_erp_unit_access_changed(struct zfcp_unit *unit)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
+
+ debug_text_event(adapter->erp_dbf, 3, "u_access_unblock");
+ debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t));
+
+ if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &unit->status))
+ return;
+
+ ZFCP_LOG_NORMAL("reopen of unit 0x%016Lx on port 0x%016Lx "
+ " on adapter %s (due to ACT update)\n",
+ unit->fcp_lun, unit->port->wwpn,
+ zfcp_get_busid_by_adapter(adapter));
+ if (zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED) != 0)
+ ZFCP_LOG_NORMAL("failed reopen of unit (adapter %s, "
+ "wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ unit->port->wwpn, unit->fcp_lun);
+}
+
+#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
new file mode 100644
index 000000000000..d5fd43352071
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -0,0 +1,186 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_ext.h
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef ZFCP_EXT_H
+#define ZFCP_EXT_H
+
+#define ZFCP_EXT_REVISION "$Revision: 1.62 $"
+
+#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 void zfcp_sysfs_port_remove_files(struct device *, u32);
+extern int zfcp_sysfs_unit_create_files(struct device *);
+extern void zfcp_sysfs_unit_remove_files(struct device *);
+extern void zfcp_sysfs_port_release(struct device *);
+extern void zfcp_sysfs_unit_release(struct device *);
+
+/**************************** CONFIGURATION *********************************/
+extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, fcp_lun_t);
+extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, wwn_t);
+extern struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *, u32);
+struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
+extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
+extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
+extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
+extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
+extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t,
+ u32, 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 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 *);
+
+extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req
+ (struct zfcp_fsf_req *, int, int);
+extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr
+ (struct zfcp_fsf_req *);
+extern int zfcp_qdio_sbals_from_sg
+ (struct zfcp_fsf_req *, unsigned long, struct scatterlist *, int, int);
+extern int zfcp_qdio_sbals_from_scsicmnd
+ (struct zfcp_fsf_req *, unsigned long, struct scsi_cmnd *);
+
+
+/******************************** 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 int zfcp_fsf_exchange_port_data(struct zfcp_adapter *,
+ struct fsf_qtcb_bottom_port *);
+extern int zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **,
+ u32, u32, struct zfcp_sg_list *);
+extern void zfcp_fsf_request_timeout_handler(unsigned long);
+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, int, mempool_t *,
+ unsigned long *, struct zfcp_fsf_req **);
+extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
+ struct zfcp_erp_action *);
+extern int zfcp_fsf_send_els(struct zfcp_send_els *);
+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 *,
+ struct scsi_cmnd *,
+ struct timer_list*, 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);
+
+/******************************* FC/FCP **************************************/
+extern int zfcp_nameserver_enqueue(struct zfcp_adapter *);
+extern int zfcp_ns_gid_pn_request(struct zfcp_erp_action *);
+extern int zfcp_check_ct_response(struct ct_hdr *);
+extern int zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *);
+
+/******************************* SCSI ****************************************/
+extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
+extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
+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 *);
+
+extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *,
+ struct scsi_cmnd *, struct timer_list *);
+extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *,
+ struct timer_list *);
+extern struct scsi_transport_template *zfcp_transport_template;
+extern struct fc_function_template zfcp_transport_functions;
+
+/******************************** 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 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 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 int zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
+
+extern int zfcp_test_link(struct zfcp_port *);
+
+extern void zfcp_erp_port_access_denied(struct zfcp_port *);
+extern void zfcp_erp_unit_access_denied(struct zfcp_unit *);
+extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *);
+extern void zfcp_erp_port_access_changed(struct zfcp_port *);
+extern void zfcp_erp_unit_access_changed(struct zfcp_unit *);
+
+/******************************** 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 *, struct scsi_cmnd *);
+extern void zfcp_in_els_dbf_event(struct zfcp_adapter *, const char *,
+ struct fsf_status_read_buffer *, int);
+#endif /* ZFCP_EXT_H */
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
new file mode 100644
index 000000000000..578b9fbe5206
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -0,0 +1,5087 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_fsf.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ * Volker Sameske <sameske@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_FSF_C_REVISION "$Revision: 1.92 $"
+
+#include "zfcp_ext.h"
+
+static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *);
+static void zfcp_fsf_exchange_port_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_status_read_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *);
+static int zfcp_fsf_control_file_handler(struct zfcp_fsf_req *);
+static inline int zfcp_fsf_req_sbal_check(
+ unsigned long *, struct zfcp_qdio_queue *, int);
+static inline int zfcp_use_one_sbal(
+ struct scatterlist *, int, struct scatterlist *, int);
+static struct zfcp_fsf_req *zfcp_fsf_req_alloc(mempool_t *, 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 *);
+static void zfcp_fsf_req_free(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_QTCB_EXCHANGE_PORT_DATA] = FSF_PORT_COMMAND,
+ [FSF_QTCB_DOWNLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND,
+ [FSF_QTCB_UPLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND
+};
+
+static const char zfcp_act_subtable_type[5][8] = {
+ "unknown", "OS", "WWPN", "DID", "LUN"
+};
+
+/****************************************************************/
+/*************** FSF related Functions *************************/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_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(mempool_t *pool, int req_flags)
+{
+ size_t size;
+ void *ptr;
+ struct zfcp_fsf_req *fsf_req = NULL;
+
+ if (req_flags & ZFCP_REQ_NO_QTCB)
+ size = sizeof(struct zfcp_fsf_req);
+ else
+ size = sizeof(struct zfcp_fsf_req_pool_element);
+
+ if (likely(pool != NULL))
+ ptr = mempool_alloc(pool, GFP_ATOMIC);
+ else
+ ptr = kmalloc(size, GFP_ATOMIC);
+
+ if (unlikely(NULL == ptr))
+ goto out;
+
+ memset(ptr, 0, size);
+
+ if (req_flags & ZFCP_REQ_NO_QTCB) {
+ fsf_req = (struct zfcp_fsf_req *) ptr;
+ } else {
+ fsf_req = &((struct zfcp_fsf_req_pool_element *) ptr)->fsf_req;
+ fsf_req->qtcb =
+ &((struct zfcp_fsf_req_pool_element *) ptr)->qtcb;
+ }
+
+ fsf_req->pool = pool;
+
+ out:
+ 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
+ */
+static void
+zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req)
+{
+ if (likely(fsf_req->pool != NULL))
+ mempool_free(fsf_req, fsf_req->pool);
+ else
+ kfree(fsf_req);
+}
+
+/*
+ * 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 */
+ msleep(jiffies_to_msecs(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 (unlikely(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 (likely(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 (likely(cleanup)) {
+ ZFCP_LOG_TRACE("removing FSF request %p\n", 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 %p\n",fsf_req);
+ /*
+ * FIXME: Race! We must not access fsf_req here as it might have been
+ * cleaned up already due to the set ZFCP_STATUS_FSFREQ_COMPLETED
+ * flag. It's an improbable case. But, we have the same paranoia for
+ * the cleanup flag already.
+ * Might better be handled using complete()?
+ * (setting the flag and doing wakeup ought to be atomic
+ * with regard to checking the flag as long as waitqueue is
+ * part of the to be released structure)
+ */
+ 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 %p\n", 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 (unlikely(fsf_req->qtcb->header.log_length)) {
+ /* do not trust them ;-) */
+ if (fsf_req->qtcb->header.log_start > sizeof(struct fsf_qtcb)) {
+ 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,
+ sizeof(struct fsf_qtcb));
+ goto forget_log;
+ }
+ if ((size_t) (fsf_req->qtcb->header.log_start +
+ fsf_req->qtcb->header.log_length)
+ > sizeof(struct fsf_qtcb)) {
+ 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,
+ sizeof(struct fsf_qtcb));
+ 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");
+ 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);
+ 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));
+ 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, 3, "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 %p "
+ "to the adapter %s is ambiguous. "
+ "Stopping all operations on this "
+ "adapter. "
+ "(bug: got this for an unsolicited "
+ "status read request)\n",
+ 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);
+ 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 (unlikely(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("error: not enough SBALs for data transfer "
+ "(adapter %s)\n",
+ zfcp_get_busid_by_adapter(fsf_req->adapter));
+ debug_text_exception(fsf_req->adapter->erp_dbf, 0,
+ "fsf_sq_ulp_err");
+ 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)
+{
+ struct zfcp_erp_action *erp_action = fsf_req->erp_action;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+ int retval = 0;
+
+ if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
+ ZFCP_LOG_TRACE("fsf_req=%p, QTCB=%p\n", fsf_req, fsf_req->qtcb);
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
+ (char *) fsf_req->qtcb, sizeof(struct fsf_qtcb));
+ }
+
+ 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_ct_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);
+ break;
+
+ case FSF_QTCB_OPEN_LUN:
+ ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_LUN\n");
+ zfcp_fsf_open_unit_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);
+ break;
+
+ case FSF_QTCB_CLOSE_PORT:
+ ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PORT\n");
+ zfcp_fsf_close_port_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);
+ 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);
+ break;
+
+ case FSF_QTCB_EXCHANGE_PORT_DATA:
+ ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_PORT_DATA\n");
+ zfcp_fsf_exchange_port_data_handler(fsf_req);
+ break;
+
+ case FSF_QTCB_SEND_ELS:
+ ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_ELS\n");
+ zfcp_fsf_send_els_handler(fsf_req);
+ break;
+
+ case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
+ ZFCP_LOG_FLAGS(2, "FSF_QTCB_DOWNLOAD_CONTROL_FILE\n");
+ zfcp_fsf_control_file_handler(fsf_req);
+ break;
+
+ case FSF_QTCB_UPLOAD_CONTROL_FILE:
+ ZFCP_LOG_FLAGS(2, "FSF_QTCB_UPLOAD_CONTROL_FILE\n");
+ zfcp_fsf_control_file_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\n",
+ zfcp_get_busid_by_adapter(fsf_req->adapter));
+ 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);
+ }
+
+ if (!erp_action)
+ return retval;
+
+ debug_text_event(adapter->erp_dbf, 3, "a_frh");
+ debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
+ zfcp_erp_async_handler(erp_action, 0);
+
+ 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 *sbale;
+ int retval = 0;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
+ req_flags | ZFCP_REQ_NO_QTCB,
+ adapter->pool.fsf_req_status_read,
+ &lock_flags, &fsf_req);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create unsolicited status "
+ "buffer for adapter %s.\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto failed_req_create;
+ }
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
+ sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
+ fsf_req->sbale_curr = 2;
+
+ status_buffer =
+ mempool_alloc(adapter->pool.data_status_read, 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 */
+ sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale->addr = (void *) status_buffer;
+ sbale->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%s)\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto out;
+
+ failed_req_send:
+ mempool_free(status_buffer, adapter->pool.data_status_read);
+
+ 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: Reopen port indication received for"
+ "nonexisting port with d_id 0x%08x on "
+ "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 reopen indication on port with "
+ "d_id 0x%08x 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.data_status_read);
+ 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_SENSE_DATA_AVAIL:
+ ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_SENSE_DATA_AVAIL\n");
+ debug_text_event(adapter->erp_dbf, 3, "unsol_sense:");
+ ZFCP_LOG_INFO("unsolicited sense data received (adapter %s)\n",
+ zfcp_get_busid_by_adapter(adapter));
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *) status_buffer,
+ sizeof(struct fsf_status_read_buffer));
+ 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");
+ debug_text_event(adapter->erp_dbf, 0, "unsol_link_down:");
+ ZFCP_LOG_INFO("Local link to adapter %s is down\n",
+ zfcp_get_busid_by_adapter(adapter));
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+ &adapter->status);
+ zfcp_erp_adapter_failed(adapter);
+ 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("Local link to adapter %s was replugged. "
+ "Restarting 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;
+
+ case FSF_STATUS_READ_CFDC_UPDATED:
+ ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_UPDATED\n");
+ debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_update:");
+ ZFCP_LOG_INFO("CFDC has been updated on the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ zfcp_erp_adapter_access_changed(adapter);
+ break;
+
+ case FSF_STATUS_READ_CFDC_HARDENED:
+ ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_HARDENED\n");
+ debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_harden:");
+ switch (status_buffer->status_subtype) {
+ case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE:
+ ZFCP_LOG_INFO("CFDC of adapter %s saved on SE\n",
+ zfcp_get_busid_by_adapter(adapter));
+ break;
+ case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2:
+ ZFCP_LOG_INFO("CFDC of adapter %s has been copied "
+ "to the secondary SE\n",
+ zfcp_get_busid_by_adapter(adapter));
+ break;
+ default:
+ ZFCP_LOG_INFO("CFDC of adapter %s has been hardened\n",
+ zfcp_get_busid_by_adapter(adapter));
+ }
+ 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 (debug info 0x%x)\n",
+ status_buffer->status_type);
+ ZFCP_LOG_DEBUG("Dump of status_read_buffer %p:\n",
+ 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.data_status_read);
+ 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("Failed to create 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 %s due to status read "
+ "buffer shortage\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)
+{
+ volatile struct qdio_buffer_element *sbale;
+ unsigned long lock_flags;
+ struct zfcp_fsf_req *fsf_req = NULL;
+ int retval = 0;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
+ req_flags, adapter->pool.fsf_req_abort,
+ &lock_flags, &fsf_req);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Failed to create an abort command "
+ "request for lun 0x%016Lx on port 0x%016Lx "
+ "on adapter %s.\n",
+ unit->fcp_lun,
+ unit->port->wwpn,
+ zfcp_get_busid_by_adapter(adapter));
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ fsf_req->data.abort_fcp_command.unit = unit;
+
+ /* 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;
+
+ /* set handle of request which should be aborted */
+ 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(fsf_req, NULL);
+ if (retval) {
+ del_timer(&adapter->scsi_er_timer);
+ ZFCP_LOG_INFO("error: Failed to send abort command request "
+ "on adapter %s, port 0x%016Lx, unit 0x%016Lx\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_DEBUG("Abort FCP Command request initiated "
+ "(adapter%s, port d_id=0x%08x, "
+ "unit x%016Lx, old_req_id=0x%lx)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ unit->port->d_id,
+ unit->fcp_lun, old_req_id);
+ out:
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+ return 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 0x%x for "
+ "port 0x%016Lx on adapter %s invalid. "
+ "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 0x%x of LUN "
+ "0x%016Lx on port 0x%016Lx on adapter %s is "
+ "invalid. 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;
+ 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_INFO("Remote port 0x%016Lx on 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_LUN_BOXED:
+ ZFCP_LOG_FLAGS(0, "FSF_LUN_BOXED\n");
+ ZFCP_LOG_INFO(
+ "unit 0x%016Lx on port 0x%016Lx on adapter %s needs "
+ "to be reopened\n",
+ unit->fcp_lun, unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit));
+ debug_text_event(new_fsf_req->adapter->erp_dbf, 1, "fsf_s_lboxed");
+ zfcp_erp_unit_reopen(unit, 0);
+ zfcp_cmd_dbf_event_fsf("unitbox", new_fsf_req,
+ &new_fsf_req->qtcb->header.fsf_status_qual,
+ sizeof(union fsf_status_qual));
+ 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;
+}
+
+/**
+ * zfcp_use_one_sbal - checks whether req buffer and resp bother each fit into
+ * one SBALE
+ * Two scatter-gather lists are passed, one for the reqeust and one for the
+ * response.
+ */
+static inline int
+zfcp_use_one_sbal(struct scatterlist *req, int req_count,
+ struct scatterlist *resp, int resp_count)
+{
+ return ((req_count == 1) &&
+ (resp_count == 1) &&
+ (((unsigned long) zfcp_sg_to_address(&req[0]) &
+ PAGE_MASK) ==
+ ((unsigned long) (zfcp_sg_to_address(&req[0]) +
+ req[0].length - 1) & PAGE_MASK)) &&
+ (((unsigned long) zfcp_sg_to_address(&resp[0]) &
+ PAGE_MASK) ==
+ ((unsigned long) (zfcp_sg_to_address(&resp[0]) +
+ resp[0].length - 1) & PAGE_MASK)));
+}
+
+/**
+ * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS)
+ * @ct: pointer to struct zfcp_send_ct which conatins all needed data for
+ * the request
+ * @pool: pointer to memory pool, if non-null this pool is used to allocate
+ * a struct zfcp_fsf_req
+ * @erp_action: pointer to erp_action, if non-null the Generic Service request
+ * is sent within error recovery
+ */
+int
+zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
+ struct zfcp_erp_action *erp_action)
+{
+ volatile struct qdio_buffer_element *sbale;
+ struct zfcp_port *port;
+ struct zfcp_adapter *adapter;
+ struct zfcp_fsf_req *fsf_req;
+ unsigned long lock_flags;
+ int bytes;
+ int ret = 0;
+
+ port = ct->port;
+ adapter = port->adapter;
+
+ ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
+ ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+ pool, &lock_flags, &fsf_req);
+ if (ret < 0) {
+ ZFCP_LOG_INFO("error: Could not create CT request (FC-GS) for "
+ "adapter: %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ goto failed_req;
+ }
+
+ if (erp_action != NULL) {
+ erp_action->fsf_req = fsf_req;
+ fsf_req->erp_action = erp_action;
+ }
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ if (zfcp_use_one_sbal(ct->req, ct->req_count,
+ ct->resp, ct->resp_count)){
+ /* both request buffer and response buffer
+ fit into one sbale each */
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+ sbale[2].addr = zfcp_sg_to_address(&ct->req[0]);
+ sbale[2].length = ct->req[0].length;
+ sbale[3].addr = zfcp_sg_to_address(&ct->resp[0]);
+ sbale[3].length = ct->resp[0].length;
+ sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+ } else if (adapter->supported_features &
+ FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
+ /* try to use chained SBALs */
+ bytes = zfcp_qdio_sbals_from_sg(fsf_req,
+ SBAL_FLAGS0_TYPE_WRITE_READ,
+ ct->req, ct->req_count,
+ ZFCP_MAX_SBALS_PER_CT_REQ);
+ if (bytes <= 0) {
+ ZFCP_LOG_INFO("error: creation of CT request failed "
+ "on adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ if (bytes == 0)
+ ret = -ENOMEM;
+ else
+ ret = bytes;
+
+ goto failed_send;
+ }
+ fsf_req->qtcb->bottom.support.req_buf_length = bytes;
+ fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
+ bytes = zfcp_qdio_sbals_from_sg(fsf_req,
+ SBAL_FLAGS0_TYPE_WRITE_READ,
+ ct->resp, ct->resp_count,
+ ZFCP_MAX_SBALS_PER_CT_REQ);
+ if (bytes <= 0) {
+ ZFCP_LOG_INFO("error: creation of CT request failed "
+ "on adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ if (bytes == 0)
+ ret = -ENOMEM;
+ else
+ ret = bytes;
+
+ goto failed_send;
+ }
+ fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
+ } else {
+ /* reject send generic request */
+ ZFCP_LOG_INFO(
+ "error: microcode does not support chained SBALs,"
+ "CT request too big (adapter %s)\n",
+ zfcp_get_busid_by_adapter(adapter));
+ ret = -EOPNOTSUPP;
+ goto failed_send;
+ }
+
+ /* 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 = ct->timeout;
+ fsf_req->data.send_ct = ct;
+
+ /* start QDIO request for this FSF request */
+ ret = zfcp_fsf_req_send(fsf_req, ct->timer);
+ if (ret) {
+ ZFCP_LOG_DEBUG("error: initiation of CT request failed "
+ "(adapter %s, port 0x%016Lx)\n",
+ zfcp_get_busid_by_adapter(adapter), port->wwpn);
+ goto failed_send;
+ }
+
+ ZFCP_LOG_DEBUG("CT request initiated (adapter %s, port 0x%016Lx)\n",
+ zfcp_get_busid_by_adapter(adapter), port->wwpn);
+ goto out;
+
+ failed_send:
+ zfcp_fsf_req_free(fsf_req);
+ if (erp_action != NULL) {
+ erp_action->fsf_req = NULL;
+ }
+ failed_req:
+ out:
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock,
+ lock_flags);
+ return ret;
+}
+
+/**
+ * zfcp_fsf_send_ct_handler - handler for Generic Service requests
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ *
+ * Data specific for the Generic Service request is passed by
+ * fsf_req->data.send_ct
+ * Usually a specific handler for the request is called via
+ * fsf_req->data.send_ct->handler at end of this function.
+ */
+static int
+zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req)
+{
+ struct zfcp_port *port;
+ struct zfcp_adapter *adapter;
+ struct zfcp_send_ct *send_ct;
+ struct fsf_qtcb_header *header;
+ struct fsf_qtcb_bottom_support *bottom;
+ int retval = -EINVAL;
+ u16 subtable, rule, counter;
+
+ adapter = fsf_req->adapter;
+ send_ct = fsf_req->data.send_ct;
+ port = send_ct->port;
+ header = &fsf_req->qtcb->header;
+ bottom = &fsf_req->qtcb->bottom.support;
+
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ goto skip_fsfstatus;
+
+ /* evaluate FSF status in QTCB */
+ switch (header->fsf_status) {
+
+ case FSF_GOOD:
+ ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
+ retval = 0;
+ break;
+
+ case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+ ZFCP_LOG_FLAGS(2, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
+ if (adapter->fc_service_class <= 3) {
+ ZFCP_LOG_INFO("error: adapter %s does not support fc "
+ "class %d.\n",
+ zfcp_get_busid_by_port(port),
+ adapter->fc_service_class);
+ } else {
+ ZFCP_LOG_INFO("bug: The fibre channel class at the "
+ "adapter %s is invalid. "
+ "(debug info %d)\n",
+ zfcp_get_busid_by_port(port),
+ adapter->fc_service_class);
+ }
+ /* stop operation for this adapter */
+ debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_ADAPTER_STATUS_AVAILABLE:
+ ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+ switch (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(adapter->erp_dbf, 1, "fsf_sq_ltest");
+ zfcp_test_link(port);
+ 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(adapter->erp_dbf, 1, "fsf_sq_ulp");
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+ default:
+ ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x "
+ "arrived.\n",
+ header->fsf_status_qual.word[0]);
+ break;
+ }
+ break;
+
+ case FSF_ACCESS_DENIED:
+ ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
+ ZFCP_LOG_NORMAL("access denied, cannot send generic service "
+ "command (adapter %s, port d_id=0x%08x)\n",
+ zfcp_get_busid_by_port(port), port->d_id);
+ for (counter = 0; counter < 2; counter++) {
+ subtable = header->fsf_status_qual.halfword[counter * 2];
+ rule = header->fsf_status_qual.halfword[counter * 2 + 1];
+ switch (subtable) {
+ case FSF_SQ_CFDC_SUBTABLE_OS:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
+ case FSF_SQ_CFDC_SUBTABLE_LUN:
+ ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
+ zfcp_act_subtable_type[subtable], rule);
+ break;
+ }
+ }
+ debug_text_event(adapter->erp_dbf, 1, "fsf_s_access");
+ zfcp_erp_port_access_denied(port);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_GENERIC_COMMAND_REJECTED:
+ ZFCP_LOG_FLAGS(2, "FSF_GENERIC_COMMAND_REJECTED\n");
+ ZFCP_LOG_INFO("generic service command rejected "
+ "(adapter %s, port d_id=0x%08x)\n",
+ zfcp_get_busid_by_port(port), port->d_id);
+ ZFCP_LOG_INFO("status qualifier:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
+ (char *) &header->fsf_status_qual,
+ sizeof (union fsf_status_qual));
+ debug_text_event(adapter->erp_dbf, 1, "fsf_s_gcom_rej");
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_PORT_HANDLE_NOT_VALID:
+ ZFCP_LOG_FLAGS(2, "FSF_PORT_HANDLE_NOT_VALID\n");
+ ZFCP_LOG_DEBUG("Temporary port identifier 0x%x for port "
+ "0x%016Lx on adapter %s invalid. 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 *) &header->fsf_status_qual,
+ sizeof (union fsf_status_qual));
+ debug_text_event(adapter->erp_dbf, 1, "fsf_s_phandle_nv");
+ zfcp_erp_adapter_reopen(adapter, 0);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_PORT_BOXED:
+ ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
+ ZFCP_LOG_INFO("port needs to be reopened "
+ "(adapter %s, port d_id=0x%08x)\n",
+ zfcp_get_busid_by_port(port), port->d_id);
+ debug_text_event(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;
+
+ /* following states should never occure, all cases avoided
+ in zfcp_fsf_send_ct - but who knows ... */
+ case FSF_PAYLOAD_SIZE_MISMATCH:
+ ZFCP_LOG_FLAGS(2, "FSF_PAYLOAD_SIZE_MISMATCH\n");
+ ZFCP_LOG_INFO("payload size mismatch (adapter: %s, "
+ "req_buf_length=%d, resp_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->req_buf_length, bottom->resp_buf_length);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+ case FSF_REQUEST_SIZE_TOO_LARGE:
+ ZFCP_LOG_FLAGS(2, "FSF_REQUEST_SIZE_TOO_LARGE\n");
+ ZFCP_LOG_INFO("request size too large (adapter: %s, "
+ "req_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->req_buf_length);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+ case FSF_RESPONSE_SIZE_TOO_LARGE:
+ ZFCP_LOG_FLAGS(2, "FSF_RESPONSE_SIZE_TOO_LARGE\n");
+ ZFCP_LOG_INFO("response size too large (adapter: %s, "
+ "resp_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->resp_buf_length);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+ case FSF_SBAL_MISMATCH:
+ ZFCP_LOG_FLAGS(2, "FSF_SBAL_MISMATCH\n");
+ ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, "
+ "resp_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->req_buf_length, bottom->resp_buf_length);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ default:
+ ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+ "(debug info 0x%x)\n", header->fsf_status);
+ debug_text_event(adapter->erp_dbf, 0, "fsf_sq_inval:");
+ debug_exception(adapter->erp_dbf, 0,
+ &header->fsf_status_qual.word[0], sizeof (u32));
+ break;
+ }
+
+skip_fsfstatus:
+ send_ct->status = retval;
+
+ if (send_ct->handler != NULL)
+ send_ct->handler(send_ct->handler_data);
+
+ return retval;
+}
+
+/**
+ * zfcp_fsf_send_els - initiate an ELS command (FC-FS)
+ * @els: pointer to struct zfcp_send_els which contains all needed data for
+ * the command.
+ */
+int
+zfcp_fsf_send_els(struct zfcp_send_els *els)
+{
+ volatile struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *fsf_req;
+ fc_id_t d_id;
+ struct zfcp_adapter *adapter;
+ unsigned long lock_flags;
+ int bytes;
+ int ret = 0;
+
+ d_id = els->d_id;
+ adapter = els->adapter;
+
+ ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
+ ZFCP_REQ_AUTO_CLEANUP,
+ NULL, &lock_flags, &fsf_req);
+ if (ret < 0) {
+ ZFCP_LOG_INFO("error: creation of ELS request failed "
+ "(adapter %s, port d_id: 0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ goto failed_req;
+ }
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ if (zfcp_use_one_sbal(els->req, els->req_count,
+ els->resp, els->resp_count)){
+ /* both request buffer and response buffer
+ fit into one sbale each */
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+ sbale[2].addr = zfcp_sg_to_address(&els->req[0]);
+ sbale[2].length = els->req[0].length;
+ sbale[3].addr = zfcp_sg_to_address(&els->resp[0]);
+ sbale[3].length = els->resp[0].length;
+ sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+ } else if (adapter->supported_features &
+ FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
+ /* try to use chained SBALs */
+ bytes = zfcp_qdio_sbals_from_sg(fsf_req,
+ SBAL_FLAGS0_TYPE_WRITE_READ,
+ els->req, els->req_count,
+ ZFCP_MAX_SBALS_PER_ELS_REQ);
+ if (bytes <= 0) {
+ ZFCP_LOG_INFO("error: creation of ELS request failed "
+ "(adapter %s, port d_id: 0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ if (bytes == 0) {
+ ret = -ENOMEM;
+ } else {
+ ret = bytes;
+ }
+ goto failed_send;
+ }
+ fsf_req->qtcb->bottom.support.req_buf_length = bytes;
+ fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
+ bytes = zfcp_qdio_sbals_from_sg(fsf_req,
+ SBAL_FLAGS0_TYPE_WRITE_READ,
+ els->resp, els->resp_count,
+ ZFCP_MAX_SBALS_PER_ELS_REQ);
+ if (bytes <= 0) {
+ ZFCP_LOG_INFO("error: creation of ELS request failed "
+ "(adapter %s, port d_id: 0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ if (bytes == 0) {
+ ret = -ENOMEM;
+ } else {
+ ret = bytes;
+ }
+ goto failed_send;
+ }
+ fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
+ } else {
+ /* reject request */
+ ZFCP_LOG_INFO("error: microcode does not support chained SBALs"
+ ", ELS request too big (adapter %s, "
+ "port d_id: 0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ ret = -EOPNOTSUPP;
+ goto failed_send;
+ }
+
+ /* settings in QTCB */
+ fsf_req->qtcb->bottom.support.d_id = d_id;
+ fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class;
+ fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT;
+ fsf_req->data.send_els = els;
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+
+ /* start QDIO request for this FSF request */
+ ret = zfcp_fsf_req_send(fsf_req, els->timer);
+ if (ret) {
+ ZFCP_LOG_DEBUG("error: initiation of ELS request failed "
+ "(adapter %s, port d_id: 0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ goto failed_send;
+ }
+
+ ZFCP_LOG_DEBUG("ELS request initiated (adapter %s, port d_id: "
+ "0x%08x)\n", zfcp_get_busid_by_adapter(adapter), d_id);
+ goto out;
+
+ failed_send:
+ zfcp_fsf_req_free(fsf_req);
+
+ failed_req:
+ out:
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock,
+ lock_flags);
+
+ return ret;
+}
+
+/**
+ * zfcp_fsf_send_els_handler - handler for ELS commands
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ *
+ * Data specific for the ELS command is passed by
+ * fsf_req->data.send_els
+ * Usually a specific handler for the command is called via
+ * fsf_req->data.send_els->handler at end of this function.
+ */
+static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req)
+{
+ struct zfcp_adapter *adapter;
+ fc_id_t d_id;
+ struct zfcp_port *port;
+ struct fsf_qtcb_header *header;
+ struct fsf_qtcb_bottom_support *bottom;
+ struct zfcp_send_els *send_els;
+ int retval = -EINVAL;
+ u16 subtable, rule, counter;
+
+ send_els = fsf_req->data.send_els;
+ adapter = send_els->adapter;
+ d_id = send_els->d_id;
+ header = &fsf_req->qtcb->header;
+ bottom = &fsf_req->qtcb->bottom.support;
+
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ goto skip_fsfstatus;
+
+ switch (header->fsf_status) {
+
+ case FSF_GOOD:
+ ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
+ retval = 0;
+ break;
+
+ case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+ ZFCP_LOG_FLAGS(2, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
+ if (adapter->fc_service_class <= 3) {
+ ZFCP_LOG_INFO("error: adapter %s does "
+ "not support fibrechannel class %d.\n",
+ zfcp_get_busid_by_adapter(adapter),
+ adapter->fc_service_class);
+ } else {
+ ZFCP_LOG_INFO("bug: The fibrechannel class at "
+ "adapter %s is invalid. "
+ "(debug info %d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ adapter->fc_service_class);
+ }
+ /* stop operation for this adapter */
+ debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_ADAPTER_STATUS_AVAILABLE:
+ ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
+ switch (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(adapter->erp_dbf, 1, "fsf_sq_ltest");
+ if (send_els->ls_code != ZFCP_LS_ADISC) {
+ read_lock(&zfcp_data.config_lock);
+ port = zfcp_get_port_by_did(adapter, d_id);
+ if (port)
+ zfcp_test_link(port);
+ read_unlock(&zfcp_data.config_lock);
+ }
+ 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");
+ debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp");
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval =
+ zfcp_handle_els_rjt(header->fsf_status_qual.word[1],
+ (struct zfcp_ls_rjt_par *)
+ &header->fsf_status_qual.word[2]);
+ break;
+ case FSF_SQ_RETRY_IF_POSSIBLE:
+ ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n");
+ debug_text_event(adapter->erp_dbf, 1, "fsf_sq_retry");
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+ default:
+ ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x\n",
+ header->fsf_status_qual.word[0]);
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
+ (char*)header->fsf_status_qual.word, 16);
+ }
+ break;
+
+ case FSF_ELS_COMMAND_REJECTED:
+ ZFCP_LOG_FLAGS(2, "FSF_ELS_COMMAND_REJECTED\n");
+ ZFCP_LOG_INFO("ELS has been rejected because command filter "
+ "prohibited sending "
+ "(adapter: %s, port d_id: 0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+
+ break;
+
+ case FSF_PAYLOAD_SIZE_MISMATCH:
+ ZFCP_LOG_FLAGS(2, "FSF_PAYLOAD_SIZE_MISMATCH\n");
+ ZFCP_LOG_INFO(
+ "ELS request size and ELS response size must be either "
+ "both 0, or both greater than 0 "
+ "(adapter: %s, req_buf_length=%d resp_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->req_buf_length,
+ bottom->resp_buf_length);
+ break;
+
+ case FSF_REQUEST_SIZE_TOO_LARGE:
+ ZFCP_LOG_FLAGS(2, "FSF_REQUEST_SIZE_TOO_LARGE\n");
+ ZFCP_LOG_INFO(
+ "Length of the ELS request buffer, "
+ "specified in QTCB bottom, "
+ "exceeds the size of the buffers "
+ "that have been allocated for ELS request data "
+ "(adapter: %s, req_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->req_buf_length);
+ break;
+
+ case FSF_RESPONSE_SIZE_TOO_LARGE:
+ ZFCP_LOG_FLAGS(2, "FSF_RESPONSE_SIZE_TOO_LARGE\n");
+ ZFCP_LOG_INFO(
+ "Length of the ELS response buffer, "
+ "specified in QTCB bottom, "
+ "exceeds the size of the buffers "
+ "that have been allocated for ELS response data "
+ "(adapter: %s, resp_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->resp_buf_length);
+ break;
+
+ case FSF_SBAL_MISMATCH:
+ /* should never occure, avoided in zfcp_fsf_send_els */
+ ZFCP_LOG_FLAGS(2, "FSF_SBAL_MISMATCH\n");
+ ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, "
+ "resp_buf_length=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->req_buf_length, bottom->resp_buf_length);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_ACCESS_DENIED:
+ ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
+ ZFCP_LOG_NORMAL("access denied, cannot send ELS command "
+ "(adapter %s, port d_id=0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter), d_id);
+ for (counter = 0; counter < 2; counter++) {
+ subtable = header->fsf_status_qual.halfword[counter * 2];
+ rule = header->fsf_status_qual.halfword[counter * 2 + 1];
+ switch (subtable) {
+ case FSF_SQ_CFDC_SUBTABLE_OS:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
+ case FSF_SQ_CFDC_SUBTABLE_LUN:
+ ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
+ zfcp_act_subtable_type[subtable], rule);
+ break;
+ }
+ }
+ debug_text_event(adapter->erp_dbf, 1, "fsf_s_access");
+ read_lock(&zfcp_data.config_lock);
+ port = zfcp_get_port_by_did(adapter, d_id);
+ if (port != NULL)
+ zfcp_erp_port_access_denied(port);
+ read_unlock(&zfcp_data.config_lock);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ default:
+ ZFCP_LOG_NORMAL(
+ "bug: An unknown FSF Status was presented "
+ "(adapter: %s, fsf_status=0x%08x)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ header->fsf_status);
+ debug_text_event(adapter->erp_dbf, 0, "fsf_sq_inval");
+ debug_exception(adapter->erp_dbf, 0,
+ &header->fsf_status_qual.word[0], sizeof(u32));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+ }
+
+skip_fsfstatus:
+ send_els->status = retval;
+
+ if (send_els->handler != 0)
+ send_els->handler(send_els->handler_data);
+
+ 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)
+{
+ volatile struct qdio_buffer_element *sbale;
+ unsigned long lock_flags;
+ int retval = 0;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(erp_action->adapter,
+ FSF_QTCB_EXCHANGE_CONFIG_DATA,
+ ZFCP_REQ_AUTO_CLEANUP,
+ erp_action->adapter->pool.fsf_req_erp,
+ &lock_flags, &(erp_action->fsf_req));
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create exchange configuration "
+ "data request for adapter %s.\n",
+ zfcp_get_busid_by_adapter(erp_action->adapter));
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
+ erp_action->fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ erp_action->fsf_req->erp_action = erp_action;
+ erp_action->fsf_req->qtcb->bottom.config.feature_selection =
+ (FSF_FEATURE_CFDC | FSF_FEATURE_LUN_SHARING);
+
+ /* 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 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 %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;
+}
+
+/**
+ * zfcp_fsf_exchange_config_evaluate
+ * @fsf_req: fsf_req which belongs to xchg config data request
+ * @xchg_ok: specifies if xchg config data was incomplete or complete (0/1)
+ *
+ * returns: -EIO on error, 0 otherwise
+ */
+static int
+zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
+{
+ struct fsf_qtcb_bottom_config *bottom;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+
+ bottom = &fsf_req->qtcb->bottom.config;
+ ZFCP_LOG_DEBUG("low/high QTCB version 0x%x/0x%x of FSF\n",
+ bottom->low_qtcb_version, bottom->high_qtcb_version);
+ adapter->fsf_lic_version = bottom->lic_version;
+ adapter->supported_features = bottom->supported_features;
+
+ if (xchg_ok) {
+ adapter->wwnn = bottom->nport_serv_param.wwnn;
+ adapter->wwpn = bottom->nport_serv_param.wwpn;
+ adapter->s_id = bottom->s_id & ZFCP_DID_MASK;
+ adapter->fc_topology = bottom->fc_topology;
+ adapter->fc_link_speed = bottom->fc_link_speed;
+ adapter->hydra_version = bottom->adapter_type;
+ } else {
+ adapter->wwnn = 0;
+ adapter->wwpn = 0;
+ adapter->s_id = 0;
+ adapter->fc_topology = 0;
+ adapter->fc_link_speed = 0;
+ adapter->hydra_version = 0;
+ }
+
+ if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){
+ adapter->hardware_version = bottom->hardware_version;
+ memcpy(adapter->serial_number, bottom->serial_number, 17);
+ EBCASC(adapter->serial_number, sizeof(adapter->serial_number));
+ }
+
+ ZFCP_LOG_INFO("The adapter %s reported the following characteristics:\n"
+ "WWNN 0x%016Lx, "
+ "WWPN 0x%016Lx, "
+ "S_ID 0x%08x,\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(adapter->erp_dbf, 0, "low_qtcb_ver");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ return -EIO;
+ }
+ 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(adapter->erp_dbf, 0, "high_qtcb_ver");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * 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)
+{
+ struct fsf_qtcb_bottom_config *bottom;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return -EIO;
+
+ switch (fsf_req->qtcb->header.fsf_status) {
+
+ case FSF_GOOD:
+ ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
+
+ if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1))
+ return -EIO;
+
+ switch (adapter->fc_topology) {
+ case FSF_TOPO_P2P:
+ ZFCP_LOG_FLAGS(1, "FSF_TOPO_P2P\n");
+ ZFCP_LOG_NORMAL("error: Point-to-point fibrechannel "
+ "configuration detected at adapter %s "
+ "unsupported, 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);
+ return -EIO;
+ case FSF_TOPO_AL:
+ ZFCP_LOG_FLAGS(1, "FSF_TOPO_AL\n");
+ ZFCP_LOG_NORMAL("error: Arbitrated loop fibrechannel "
+ "topology detected at adapter %s "
+ "unsupported, 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);
+ return -EIO;
+ case FSF_TOPO_FABRIC:
+ ZFCP_LOG_FLAGS(1, "FSF_TOPO_FABRIC\n");
+ ZFCP_LOG_INFO("Switched fabric fibrechannel "
+ "network detected at adapter %s.\n",
+ zfcp_get_busid_by_adapter(adapter));
+ break;
+ default:
+ ZFCP_LOG_NORMAL("bug: The fibrechannel 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);
+ return -EIO;
+ }
+ bottom = &fsf_req->qtcb->bottom.config;
+ if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) {
+ 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),
+ sizeof(struct fsf_qtcb));
+ 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);
+ return -EIO;
+ }
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+ &adapter->status);
+ break;
+ case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
+ debug_text_event(adapter->erp_dbf, 0, "xchg-inco");
+
+ if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0))
+ return -EIO;
+
+ ZFCP_LOG_INFO("Local link to adapter %s is down\n",
+ zfcp_get_busid_by_adapter(adapter));
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+ &adapter->status);
+ zfcp_erp_adapter_failed(adapter);
+ break;
+ 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);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * zfcp_fsf_exchange_port_data - request information about local port
+ * @adapter: for which port data is requested
+ * @data: response to exchange port data request
+ */
+int
+zfcp_fsf_exchange_port_data(struct zfcp_adapter *adapter,
+ struct fsf_qtcb_bottom_port *data)
+{
+ volatile struct qdio_buffer_element *sbale;
+ int retval = 0;
+ unsigned long lock_flags;
+ struct zfcp_fsf_req *fsf_req;
+ struct timer_list *timer;
+
+ if(!(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT)){
+ ZFCP_LOG_INFO("error: exchange port data "
+ "command not supported by adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ return -EOPNOTSUPP;
+ }
+
+ timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
+ if (!timer)
+ return -ENOMEM;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
+ 0, 0, &lock_flags, &fsf_req);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+ "exchange port data request for"
+ "the adapter %s.\n",
+ zfcp_get_busid_by_adapter(adapter));
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock,
+ lock_flags);
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ fsf_req->data.port_data = data;
+
+ init_timer(timer);
+ timer->function = zfcp_fsf_request_timeout_handler;
+ timer->data = (unsigned long) adapter;
+ timer->expires = ZFCP_FSF_REQUEST_TIMEOUT;
+
+ retval = zfcp_fsf_req_send(fsf_req, timer);
+ if (retval) {
+ ZFCP_LOG_INFO("error: Could not send an exchange port data "
+ "command on the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ zfcp_fsf_req_free(fsf_req);
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock,
+ lock_flags);
+ goto out;
+ }
+
+ ZFCP_LOG_DEBUG("Exchange Port Data request initiated (adapter %s)\n",
+ zfcp_get_busid_by_adapter(adapter));
+
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock,
+ lock_flags);
+
+ wait_event(fsf_req->completion_wq,
+ fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ del_timer_sync(timer);
+ zfcp_fsf_req_cleanup(fsf_req);
+ out:
+ kfree(timer);
+ return retval;
+}
+
+
+/**
+ * zfcp_fsf_exchange_port_data_handler - handler for exchange_port_data request
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ */
+static void
+zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *fsf_req)
+{
+ struct fsf_qtcb_bottom_port *bottom;
+ struct fsf_qtcb_bottom_port *data = fsf_req->data.port_data;
+
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
+
+ switch (fsf_req->qtcb->header.fsf_status) {
+ case FSF_GOOD:
+ ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
+ bottom = &fsf_req->qtcb->bottom.port;
+ memcpy(data, bottom, sizeof(*data));
+ break;
+
+ default:
+ debug_text_event(fsf_req->adapter->erp_dbf, 0, "xchg-port-ng");
+ debug_event(fsf_req->adapter->erp_dbf, 0,
+ &fsf_req->qtcb->header.fsf_status, sizeof(u32));
+ }
+}
+
+
+/*
+ * 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)
+{
+ volatile struct qdio_buffer_element *sbale;
+ unsigned long lock_flags;
+ int retval = 0;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(erp_action->adapter,
+ FSF_QTCB_OPEN_PORT_WITH_DID,
+ ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+ erp_action->adapter->pool.fsf_req_erp,
+ &lock_flags, &(erp_action->fsf_req));
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create open port request "
+ "for port 0x%016Lx on adapter %s.\n",
+ erp_action->port->wwpn,
+ zfcp_get_busid_by_adapter(erp_action->adapter));
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
+ erp_action->fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ 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 open port request for "
+ "port 0x%016Lx on 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 %s, port 0x%016Lx)\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;
+ struct fsf_qtcb_header *header;
+ u16 subtable, rule, counter;
+
+ port = fsf_req->data.open_port.port;
+ header = &fsf_req->qtcb->header;
+
+ 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 (header->fsf_status) {
+
+ case FSF_PORT_ALREADY_OPEN:
+ ZFCP_LOG_FLAGS(0, "FSF_PORT_ALREADY_OPEN\n");
+ ZFCP_LOG_NORMAL("bug: remote port 0x%016Lx on 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_ACCESS_DENIED:
+ ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
+ ZFCP_LOG_NORMAL("Access denied, cannot open port 0x%016Lx "
+ "on adapter %s\n",
+ port->wwpn, zfcp_get_busid_by_port(port));
+ for (counter = 0; counter < 2; counter++) {
+ subtable = header->fsf_status_qual.halfword[counter * 2];
+ rule = header->fsf_status_qual.halfword[counter * 2 + 1];
+ switch (subtable) {
+ case FSF_SQ_CFDC_SUBTABLE_OS:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
+ case FSF_SQ_CFDC_SUBTABLE_LUN:
+ ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
+ zfcp_act_subtable_type[subtable], rule);
+ break;
+ }
+ }
+ debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
+ zfcp_erp_port_access_denied(port);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ 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 0x%016Lx on 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 (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 0x%016Lx on "
+ "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",
+ 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,
+ &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 = header->port_handle;
+ ZFCP_LOG_INFO("The remote port 0x%016Lx via adapter %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 */
+ /*
+ * FIXME: This check is not airtight, as the FCP channel does
+ * not monitor closures of target port connections caused on
+ * the remote side. Thus, they might miss out on invalidating
+ * locally cached WWPNs (and other N_Port parameters) of gone
+ * target ports. So, our heroic attempt to make things safe
+ * could be undermined by 'open port' response data tagged with
+ * obsolete WWPNs. Another reason to monitor potential
+ * connection closures ourself at least (by interpreting
+ * incoming ELS' and unsolicited status). It just crosses my
+ * mind that one should be able to cross-check by means of
+ * another GID_PN straight after a port has been opened.
+ * Alternately, an ADISC/PDISC ELS should suffice, as well.
+ */
+ 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 "
+ "0x%016Lx 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;
+
+ case FSF_UNKNOWN_OP_SUBTYPE:
+ /* should never occure, subtype not set in zfcp_fsf_open_port */
+ ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_OP_SUBTYPE\n");
+ ZFCP_LOG_INFO("unknown operation subtype (adapter: %s, "
+ "op_subtype=0x%x)\n",
+ zfcp_get_busid_by_port(port),
+ fsf_req->qtcb->bottom.support.operation_subtype);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ default:
+ ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+ "(debug info 0x%x)\n",
+ header->fsf_status);
+ debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+ debug_exception(fsf_req->adapter->erp_dbf, 0,
+ &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)
+{
+ volatile struct qdio_buffer_element *sbale;
+ unsigned long lock_flags;
+ int retval = 0;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(erp_action->adapter,
+ FSF_QTCB_CLOSE_PORT,
+ ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+ erp_action->adapter->pool.fsf_req_erp,
+ &lock_flags, &(erp_action->fsf_req));
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create a close port request "
+ "for port 0x%016Lx on adapter %s.\n",
+ erp_action->port->wwpn,
+ zfcp_get_busid_by_adapter(erp_action->adapter));
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
+ erp_action->fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ 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 "
+ "port 0x%016Lx on 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 %s, port 0x%016Lx)\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 0x%x for port "
+ "0x%016Lx on adapter %s invalid. 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 0x016%Lx on adapter %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;
+ volatile struct qdio_buffer_element *sbale;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(erp_action->adapter,
+ FSF_QTCB_CLOSE_PHYSICAL_PORT,
+ ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+ erp_action->adapter->pool.fsf_req_erp,
+ &lock_flags, &erp_action->fsf_req);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create close physical port "
+ "request (adapter %s, port 0x%016Lx)\n",
+ zfcp_get_busid_by_adapter(erp_action->adapter),
+ erp_action->port->wwpn);
+
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
+ erp_action->fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ /* 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 close physical port "
+ "request (adapter %s, port 0x%016Lx)\n",
+ zfcp_get_busid_by_adapter(erp_action->adapter),
+ erp_action->port->wwpn);
+ zfcp_fsf_req_free(erp_action->fsf_req);
+ erp_action->fsf_req = NULL;
+ goto out;
+ }
+
+ ZFCP_LOG_TRACE("close physical port request initiated "
+ "(adapter %s, port 0x%016Lx)\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;
+ struct fsf_qtcb_header *header;
+ u16 subtable, rule, counter;
+
+ port = fsf_req->data.close_physical_port.port;
+ header = &fsf_req->qtcb->header;
+
+ 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 (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 0x%x invalid"
+ "(adapter %s, port 0x%016Lx). "
+ "This may happen occasionally.\n",
+ port->handle,
+ zfcp_get_busid_by_port(port),
+ port->wwpn);
+ ZFCP_LOG_DEBUG("status qualifier:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+ (char *) &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_ACCESS_DENIED:
+ ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
+ ZFCP_LOG_NORMAL("Access denied, cannot close "
+ "physical port 0x%016Lx on adapter %s\n",
+ port->wwpn, zfcp_get_busid_by_port(port));
+ for (counter = 0; counter < 2; counter++) {
+ subtable = header->fsf_status_qual.halfword[counter * 2];
+ rule = header->fsf_status_qual.halfword[counter * 2 + 1];
+ switch (subtable) {
+ case FSF_SQ_CFDC_SUBTABLE_OS:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
+ case FSF_SQ_CFDC_SUBTABLE_LUN:
+ ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
+ zfcp_act_subtable_type[subtable], rule);
+ break;
+ }
+ }
+ debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
+ zfcp_erp_port_access_denied(port);
+ 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 0x%016Lx on 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 (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",
+ 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,
+ &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 0x%016Lx via adapter %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",
+ header->fsf_status);
+ debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:");
+ debug_exception(fsf_req->adapter->erp_dbf, 0,
+ &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)
+{
+ volatile struct qdio_buffer_element *sbale;
+ unsigned long lock_flags;
+ int retval = 0;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(erp_action->adapter,
+ FSF_QTCB_OPEN_LUN,
+ ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+ erp_action->adapter->pool.fsf_req_erp,
+ &lock_flags, &(erp_action->fsf_req));
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create open unit request for "
+ "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n",
+ erp_action->unit->fcp_lun,
+ erp_action->unit->port->wwpn,
+ zfcp_get_busid_by_adapter(erp_action->adapter));
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
+ erp_action->fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ erp_action->fsf_req->qtcb->header.port_handle =
+ erp_action->port->handle;
+ erp_action->fsf_req->qtcb->bottom.support.fcp_lun =
+ erp_action->unit->fcp_lun;
+ erp_action->fsf_req->qtcb->bottom.support.option =
+ FSF_OPEN_LUN_SUPPRESS_BOXING;
+ 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 0x%016Lx for "
+ "unit 0x%016Lx\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 %s, "
+ "port 0x%016Lx, unit 0x%016Lx)\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_adapter *adapter;
+ struct zfcp_unit *unit;
+ struct fsf_qtcb_header *header;
+ struct fsf_qtcb_bottom_support *bottom;
+ struct fsf_queue_designator *queue_designator;
+ u16 subtable, rule, counter;
+ u32 allowed, exclusive, readwrite;
+
+ 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;
+ }
+
+ adapter = fsf_req->adapter;
+ header = &fsf_req->qtcb->header;
+ bottom = &fsf_req->qtcb->bottom.support;
+ queue_designator = &header->fsf_status_qual.fsf_queue_designator;
+
+ allowed = bottom->lun_access_info & FSF_UNIT_ACCESS_OPEN_LUN_ALLOWED;
+ exclusive = bottom->lun_access_info & FSF_UNIT_ACCESS_EXCLUSIVE;
+ readwrite = bottom->lun_access_info & FSF_UNIT_ACCESS_OUTBOUND_TRANSFER;
+
+ atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ ZFCP_STATUS_UNIT_SHARED |
+ ZFCP_STATUS_UNIT_READONLY,
+ &unit->status);
+
+ /* evaluate FSF status in QTCB */
+ switch (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 0x%x "
+ "for port 0x%016Lx on adapter %s invalid "
+ "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 *) &header->fsf_status_qual,
+ sizeof (union fsf_status_qual));
+ debug_text_event(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 unit 0x%016Lx on "
+ "remote port 0x%016Lx on adapter %s twice.\n",
+ unit->fcp_lun,
+ unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+ debug_text_exception(adapter->erp_dbf, 0,
+ "fsf_s_uopen");
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_ACCESS_DENIED:
+ ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
+ ZFCP_LOG_NORMAL("Access denied, cannot open unit 0x%016Lx on "
+ "remote port 0x%016Lx on adapter %s\n",
+ unit->fcp_lun, unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit));
+ for (counter = 0; counter < 2; counter++) {
+ subtable = header->fsf_status_qual.halfword[counter * 2];
+ rule = header->fsf_status_qual.halfword[counter * 2 + 1];
+ switch (subtable) {
+ case FSF_SQ_CFDC_SUBTABLE_OS:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
+ case FSF_SQ_CFDC_SUBTABLE_LUN:
+ ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
+ zfcp_act_subtable_type[subtable], rule);
+ break;
+ }
+ }
+ debug_text_event(adapter->erp_dbf, 1, "fsf_s_access");
+ zfcp_erp_unit_access_denied(unit);
+ atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
+ atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
+ 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 0x%016Lx on adapter %s "
+ "needs to be reopened\n",
+ unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+ debug_text_event(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_SHARING_VIOLATION:
+ ZFCP_LOG_FLAGS(2, "FSF_LUN_SHARING_VIOLATION\n");
+ if (header->fsf_status_qual.word[0] != 0) {
+ ZFCP_LOG_NORMAL("FCP-LUN 0x%Lx at the remote port "
+ "with WWPN 0x%Lx "
+ "connected to the adapter %s "
+ "is already in use in LPAR%d, CSS%d\n",
+ unit->fcp_lun,
+ unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit),
+ queue_designator->hla,
+ queue_designator->cssid);
+ } else {
+ subtable = header->fsf_status_qual.halfword[4];
+ rule = header->fsf_status_qual.halfword[5];
+ switch (subtable) {
+ case FSF_SQ_CFDC_SUBTABLE_OS:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
+ case FSF_SQ_CFDC_SUBTABLE_LUN:
+ ZFCP_LOG_NORMAL("Access to FCP-LUN 0x%Lx at the "
+ "remote port with WWPN 0x%Lx "
+ "connected to the adapter %s "
+ "is denied (%s rule %d)\n",
+ unit->fcp_lun,
+ unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit),
+ zfcp_act_subtable_type[subtable],
+ rule);
+ break;
+ }
+ }
+ ZFCP_LOG_DEBUG("status qualifier:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+ (char *) &header->fsf_status_qual,
+ sizeof (union fsf_status_qual));
+ debug_text_event(adapter->erp_dbf, 2,
+ "fsf_s_l_sh_vio");
+ zfcp_erp_unit_access_denied(unit);
+ atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
+ atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
+ 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 unit 0x%016Lx on port 0x%016Lx "
+ "on adapter %s\n",
+ unit->fcp_lun,
+ unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit));
+ debug_text_event(adapter->erp_dbf, 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 (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(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(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",
+ header->fsf_status_qual.word[0]);
+ debug_text_event(adapter->erp_dbf, 0,
+ "fsf_sq_inval:");
+ debug_exception(adapter->erp_dbf, 0,
+ &header->fsf_status_qual.word[0],
+ sizeof (u32));
+ }
+ break;
+
+ case FSF_INVALID_COMMAND_OPTION:
+ ZFCP_LOG_FLAGS(2, "FSF_INVALID_COMMAND_OPTION\n");
+ ZFCP_LOG_NORMAL(
+ "Invalid option 0x%x has been specified "
+ "in QTCB bottom sent to the adapter %s\n",
+ bottom->option,
+ zfcp_get_busid_by_adapter(adapter));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EINVAL;
+ break;
+
+ case FSF_GOOD:
+ ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
+ /* save LUN handle assigned by FSF */
+ unit->handle = header->lun_handle;
+ ZFCP_LOG_TRACE("unit 0x%016Lx on remote port 0x%016Lx on "
+ "adapter %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);
+
+ if (adapter->supported_features & FSF_FEATURE_LUN_SHARING){
+ if (!exclusive)
+ atomic_set_mask(ZFCP_STATUS_UNIT_SHARED,
+ &unit->status);
+
+ if (!readwrite) {
+ atomic_set_mask(ZFCP_STATUS_UNIT_READONLY,
+ &unit->status);
+ ZFCP_LOG_NORMAL("read-only access for unit "
+ "(adapter %s, wwpn=0x%016Lx, "
+ "fcp_lun=0x%016Lx)\n",
+ zfcp_get_busid_by_unit(unit),
+ unit->port->wwpn,
+ unit->fcp_lun);
+ }
+
+ if (exclusive && !readwrite) {
+ ZFCP_LOG_NORMAL("exclusive access of read-only "
+ "unit not supported\n");
+ zfcp_erp_unit_failed(unit);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_unit_shutdown(unit, 0);
+ } else if (!exclusive && readwrite) {
+ ZFCP_LOG_NORMAL("shared access of read-write "
+ "unit not supported\n");
+ zfcp_erp_unit_failed(unit);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_unit_shutdown(unit, 0);
+ }
+ }
+
+ retval = 0;
+ break;
+
+ default:
+ ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
+ "(debug info 0x%x)\n",
+ header->fsf_status);
+ debug_text_event(adapter->erp_dbf, 0, "fsf_s_inval:");
+ debug_exception(adapter->erp_dbf, 0,
+ &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)
+{
+ volatile struct qdio_buffer_element *sbale;
+ unsigned long lock_flags;
+ int retval = 0;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(erp_action->adapter,
+ FSF_QTCB_CLOSE_LUN,
+ ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
+ erp_action->adapter->pool.fsf_req_erp,
+ &lock_flags, &(erp_action->fsf_req));
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create close unit request for "
+ "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n",
+ erp_action->unit->fcp_lun,
+ erp_action->port->wwpn,
+ zfcp_get_busid_by_adapter(erp_action->adapter));
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
+ erp_action->fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ 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 "
+ "unit 0x%016Lx on port 0x%016Lx onadapter %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 %s, "
+ "port 0x%016Lx, unit 0x%016Lx)\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 0x%x for port "
+ "0x%016Lx on adapter %s invalid. 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 0x%x of unit "
+ "0x%016Lx on port 0x%016Lx on adapter %s is "
+ "invalid. 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 0x%016Lx on 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 0x%016Lx on port 0x%016Lx on adapter %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;
+}
+
+/**
+ * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
+ * @adapter: adapter where scsi command is issued
+ * @unit: unit where command is sent to
+ * @scsi_cmnd: scsi command to be sent
+ * @timer: timer to be started when request is initiated
+ * @req_flags: flags for fsf_request
+ */
+int
+zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
+ struct zfcp_unit *unit,
+ struct scsi_cmnd * scsi_cmnd,
+ struct timer_list *timer, int req_flags)
+{
+ struct zfcp_fsf_req *fsf_req = NULL;
+ struct fcp_cmnd_iu *fcp_cmnd_iu;
+ unsigned int sbtype;
+ unsigned long lock_flags;
+ int real_bytes = 0;
+ int retval = 0;
+ int mask;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ adapter->pool.fsf_req_scsi,
+ &lock_flags, &fsf_req);
+ if (unlikely(retval < 0)) {
+ ZFCP_LOG_DEBUG("error: Could not create FCP command request "
+ "for unit 0x%016Lx on port 0x%016Lx on "
+ "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;
+ fsf_req->data.send_fcp_command_task.start_jiffies = jiffies;
+ fsf_req->data.send_fcp_command_task.unit = unit;
+ ZFCP_LOG_DEBUG("unit=%p, fcp_lun=0x%016Lx\n", 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 DMA_NONE:
+ ZFCP_LOG_FLAGS(3, "DMA_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 DMA_FROM_DEVICE:
+ ZFCP_LOG_FLAGS(3, "DMA_FROM_DEVICE\n");
+ fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
+ sbtype = SBAL_FLAGS0_TYPE_READ;
+ fcp_cmnd_iu->rddata = 1;
+ break;
+ case DMA_TO_DEVICE:
+ ZFCP_LOG_FLAGS(3, "DMA_TO_DEVICE\n");
+ fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
+ sbtype = SBAL_FLAGS0_TYPE_WRITE;
+ fcp_cmnd_iu->wddata = 1;
+ break;
+ case DMA_BIDIRECTIONAL:
+ ZFCP_LOG_FLAGS(0, "DMA_BIDIRECTIONAL not supported\n");
+ default:
+ /*
+ * dummy, catch this condition earlier
+ * in zfcp_scsi_queuecommand
+ */
+ goto failed_scsi_cmnd;
+ }
+
+ /* 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;
+
+ mask = ZFCP_STATUS_UNIT_READONLY | ZFCP_STATUS_UNIT_SHARED;
+
+ /* set task attributes in FCP_CMND IU in QTCB */
+ if (likely((scsi_cmnd->device->simple_tags) ||
+ (atomic_test_mask(mask, &unit->status))))
+ fcp_cmnd_iu->task_attribute = SIMPLE_Q;
+ else
+ fcp_cmnd_iu->task_attribute = UNTAGGED;
+
+ /* set additional length of FCP_CDB in FCP_CMND IU in QTCB, if needed */
+ if (unlikely(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_qdio_sbals_from_scsicmnd(fsf_req, sbtype, scsi_cmnd);
+ if (unlikely(real_bytes < 0)) {
+ if (fsf_req->sbal_number < ZFCP_MAX_SBALS_PER_REQ) {
+ ZFCP_LOG_DEBUG(
+ "Data did not fit into available buffer(s), "
+ "waiting for more...\n");
+ retval = -EIO;
+ } else {
+ ZFCP_LOG_NORMAL("error: No truncation implemented but "
+ "required. Shutting down unit "
+ "(adapter %s, port 0x%016Lx, "
+ "unit 0x%016Lx)\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, timer);
+ if (unlikely(retval < 0)) {
+ ZFCP_LOG_INFO("error: Could not send FCP command request "
+ "on adapter %s, port 0x%016Lx, unit 0x%016Lx\n",
+ zfcp_get_busid_by_adapter(adapter),
+ unit->port->wwpn,
+ unit->fcp_lun);
+ goto send_failed;
+ }
+
+ ZFCP_LOG_TRACE("Send FCP Command initiated (adapter %s, "
+ "port 0x%016Lx, unit 0x%016Lx)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ unit->port->wwpn,
+ unit->fcp_lun);
+ goto success;
+
+ send_failed:
+ no_fit:
+ failed_scsi_cmnd:
+ zfcp_fsf_req_free(fsf_req);
+ fsf_req = NULL;
+ scsi_cmnd->host_scribble = 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 *sbale;
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ adapter->pool.fsf_req_scsi,
+ &lock_flags, &fsf_req);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create FCP command (task "
+ "management) request for adapter %s, port "
+ " 0x%016Lx, unit 0x%016Lx.\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);
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
+ sbale[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 adapter %s, port 0x%016Lx for "
+ "unit LUN 0x%016Lx\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 %s, port 0x%016Lx, unit 0x%016Lx, "
+ "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;
+ struct fsf_qtcb_header *header;
+ u16 subtable, rule, counter;
+
+ header = &fsf_req->qtcb->header;
+
+ if (unlikely(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 (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
+ /* go directly to calls of special handlers */
+ goto skip_fsfstatus;
+ }
+
+ /* evaluate FSF status in QTCB */
+ switch (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 0x%x for port "
+ "0x%016Lx on adapter %s invalid\n",
+ unit->port->handle,
+ unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+ (char *) &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 0x%x for unit "
+ "0x%016Lx on port 0x%016Lx on adapter %s is "
+ "invalid. 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 *) &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 0x%x has changed "
+ "unexpectedly. (adapter %s, port 0x%016Lx, "
+ "unit 0x%016Lx)\n",
+ unit->port->handle,
+ zfcp_get_busid_by_unit(unit),
+ unit->port->wwpn,
+ unit->fcp_lun);
+ ZFCP_LOG_NORMAL("status qualifier:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+ (char *) &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,
+ &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 fibrechannel class %d.\n",
+ zfcp_get_busid_by_unit(unit),
+ fsf_req->adapter->fc_service_class);
+ } else {
+ ZFCP_LOG_NORMAL("bug: The fibrechannel class at "
+ "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,
+ &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: unit 0x%016Lx on port 0x%016Lx on "
+ "adapter %s does not have correct unit "
+ "handle 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 *) &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,
+ &header->fsf_status_qual,
+ sizeof (union fsf_status_qual));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ break;
+
+ case FSF_ACCESS_DENIED:
+ ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
+ ZFCP_LOG_NORMAL("Access denied, cannot send FCP command to "
+ "unit 0x%016Lx on port 0x%016Lx on "
+ "adapter %s\n", unit->fcp_lun, unit->port->wwpn,
+ zfcp_get_busid_by_unit(unit));
+ for (counter = 0; counter < 2; counter++) {
+ subtable = header->fsf_status_qual.halfword[counter * 2];
+ rule = header->fsf_status_qual.halfword[counter * 2 + 1];
+ switch (subtable) {
+ case FSF_SQ_CFDC_SUBTABLE_OS:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
+ case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
+ case FSF_SQ_CFDC_SUBTABLE_LUN:
+ ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
+ zfcp_act_subtable_type[subtable], rule);
+ break;
+ }
+ }
+ debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
+ zfcp_erp_unit_access_denied(unit);
+ 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 unit "
+ "0x%016Lx on port 0x%016Lx on 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,
+ &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 unit 0x%016Lx on port "
+ "0x%016Lx on 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,
+ &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 0x%016Lx on 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,
+ &header->fsf_status_qual,
+ sizeof (union fsf_status_qual));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
+ break;
+
+ case FSF_LUN_BOXED:
+ ZFCP_LOG_FLAGS(0, "FSF_LUN_BOXED\n");
+ ZFCP_LOG_NORMAL("unit needs to be reopened (adapter %s, "
+ "wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n",
+ zfcp_get_busid_by_unit(unit),
+ unit->port->wwpn, unit->fcp_lun);
+ debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_lboxed");
+ zfcp_erp_unit_reopen(unit, 0);
+ zfcp_cmd_dbf_event_fsf("unitbox", fsf_req,
+ &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 (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,
+ &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,
+ &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",
+ 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,
+ &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,
+ &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 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 (unlikely(!scpnt)) {
+ ZFCP_LOG_DEBUG
+ ("Command with fsf_req %p is not associated to "
+ "a scsi command anymore. Aborted?\n", fsf_req);
+ goto out;
+ }
+ if (unlikely(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 (unlikely(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 (unlikely(fcp_rsp_iu->scsi_status)) {
+ /* DEBUG */
+ ZFCP_LOG_DEBUG("status for SCSI Command:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+ scpnt->cmnd, scpnt->cmd_len);
+ ZFCP_LOG_DEBUG("SCSI status code 0x%x\n",
+ fcp_rsp_iu->scsi_status);
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+ (void *) fcp_rsp_iu, sizeof (struct fcp_rsp_iu));
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
+ zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
+ fcp_rsp_iu->fcp_sns_len);
+ }
+
+ /* check FCP_RSP_INFO */
+ if (unlikely(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 fibrechannel protocol data "
+ "length differs from the burst length. "
+ "The problem occured on unit 0x%016Lx "
+ "on port 0x%016Lx on 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 fibrechannel protocol data "
+ "fields were incorrectly set up. "
+ "The problem occured on the unit "
+ "0x%016Lx on port 0x%016Lx on "
+ "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 "
+ "fibrechannel payload offset from the "
+ "header were found. "
+ "The problem occured on unit 0x%016Lx "
+ "on port 0x%016Lx on 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 "
+ "0x%016Lx on port 0x%016Lx on "
+ "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 (unlikely(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 (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_over)) {
+ ZFCP_LOG_INFO("A data overrun was detected for a command. "
+ "unit 0x%016Lx, port 0x%016Lx, 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 (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) {
+ ZFCP_LOG_INFO("A data underrun was detected for a command. "
+ "unit 0x%016Lx, port 0x%016Lx, 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));
+
+ scpnt->resid = fcp_rsp_iu->fcp_resid;
+ if (scpnt->request_bufflen - scpnt->resid < scpnt->underflow)
+ scpnt->result |= DID_ERROR << 16;
+ }
+
+ skip_fsfstatus:
+ 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);
+
+ /*
+ * 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 "
+ "unit 0x%016Lx, port 0x%016Lx, 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. "
+ "unit 0x%016Lx, port 0x%016Lx, 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. "
+ "unit 0x%016Lx, port 0x%016Lx, 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_control_file
+ *
+ * purpose: Initiator of the control file upload/download FSF requests
+ *
+ * returns: 0 - FSF request is successfuly created and queued
+ * -EOPNOTSUPP - The FCP adapter does not have Control File support
+ * -EINVAL - Invalid direction specified
+ * -ENOMEM - Insufficient memory
+ * -EPERM - Cannot create FSF request or place it in QDIO queue
+ */
+int
+zfcp_fsf_control_file(struct zfcp_adapter *adapter,
+ struct zfcp_fsf_req **fsf_req_ptr,
+ u32 fsf_command,
+ u32 option,
+ struct zfcp_sg_list *sg_list)
+{
+ struct zfcp_fsf_req *fsf_req;
+ struct fsf_qtcb_bottom_support *bottom;
+ volatile struct qdio_buffer_element *sbale;
+ struct timer_list *timer;
+ unsigned long lock_flags;
+ int req_flags = 0;
+ int direction;
+ int retval = 0;
+
+ if (!(adapter->supported_features & FSF_FEATURE_CFDC)) {
+ ZFCP_LOG_INFO("cfdc not supported (adapter %s)\n",
+ zfcp_get_busid_by_adapter(adapter));
+ retval = -EOPNOTSUPP;
+ goto out;
+ }
+
+ switch (fsf_command) {
+
+ case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
+ direction = SBAL_FLAGS0_TYPE_WRITE;
+ if ((option != FSF_CFDC_OPTION_FULL_ACCESS) &&
+ (option != FSF_CFDC_OPTION_RESTRICTED_ACCESS))
+ req_flags = ZFCP_WAIT_FOR_SBAL;
+ break;
+
+ case FSF_QTCB_UPLOAD_CONTROL_FILE:
+ direction = SBAL_FLAGS0_TYPE_READ;
+ break;
+
+ default:
+ ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
+ if (!timer) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ retval = zfcp_fsf_req_create(adapter, fsf_command, req_flags,
+ NULL, &lock_flags, &fsf_req);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Could not create FSF request for the "
+ "adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ retval = -EPERM;
+ goto unlock_queue_lock;
+ }
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale[0].flags |= direction;
+
+ bottom = &fsf_req->qtcb->bottom.support;
+ bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE;
+ bottom->option = option;
+
+ if (sg_list->count > 0) {
+ int bytes;
+
+ bytes = zfcp_qdio_sbals_from_sg(fsf_req, direction,
+ sg_list->sg, sg_list->count,
+ ZFCP_MAX_SBALS_PER_REQ);
+ if (bytes != ZFCP_CFDC_MAX_CONTROL_FILE_SIZE) {
+ ZFCP_LOG_INFO(
+ "error: Could not create sufficient number of "
+ "SBALS for an FSF request to the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ retval = -ENOMEM;
+ goto free_fsf_req;
+ }
+ } else
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ init_timer(timer);
+ timer->function = zfcp_fsf_request_timeout_handler;
+ timer->data = (unsigned long) adapter;
+ timer->expires = ZFCP_FSF_REQUEST_TIMEOUT;
+
+ retval = zfcp_fsf_req_send(fsf_req, timer);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("initiation of cfdc up/download failed"
+ "(adapter %s)\n",
+ zfcp_get_busid_by_adapter(adapter));
+ retval = -EPERM;
+ goto free_fsf_req;
+ }
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+
+ ZFCP_LOG_NORMAL("Control file %s FSF request has been sent to the "
+ "adapter %s\n",
+ fsf_command == FSF_QTCB_DOWNLOAD_CONTROL_FILE ?
+ "download" : "upload",
+ zfcp_get_busid_by_adapter(adapter));
+
+ wait_event(fsf_req->completion_wq,
+ fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+
+ *fsf_req_ptr = fsf_req;
+ del_timer_sync(timer);
+ goto free_timer;
+
+ free_fsf_req:
+ zfcp_fsf_req_free(fsf_req);
+ unlock_queue_lock:
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+ free_timer:
+ kfree(timer);
+ out:
+ return retval;
+}
+
+
+/*
+ * function: zfcp_fsf_control_file_handler
+ *
+ * purpose: Handler of the control file upload/download FSF requests
+ *
+ * returns: 0 - FSF request successfuly processed
+ * -EAGAIN - Operation has to be repeated because of a temporary problem
+ * -EACCES - There is no permission to execute an operation
+ * -EPERM - The control file is not in a right format
+ * -EIO - There is a problem with the FCP adapter
+ * -EINVAL - Invalid operation
+ * -EFAULT - User space memory I/O operation fault
+ */
+static int
+zfcp_fsf_control_file_handler(struct zfcp_fsf_req *fsf_req)
+{
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+ struct fsf_qtcb_header *header = &fsf_req->qtcb->header;
+ struct fsf_qtcb_bottom_support *bottom = &fsf_req->qtcb->bottom.support;
+ int retval = 0;
+
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+ retval = -EINVAL;
+ goto skip_fsfstatus;
+ }
+
+ switch (header->fsf_status) {
+
+ case FSF_GOOD:
+ ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
+ ZFCP_LOG_NORMAL(
+ "The FSF request has been successfully completed "
+ "on the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ break;
+
+ case FSF_OPERATION_PARTIALLY_SUCCESSFUL:
+ ZFCP_LOG_FLAGS(2, "FSF_OPERATION_PARTIALLY_SUCCESSFUL\n");
+ if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) {
+ switch (header->fsf_status_qual.word[0]) {
+
+ case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE:
+ ZFCP_LOG_NORMAL(
+ "CFDC of the adapter %s could not "
+ "be saved on the SE\n",
+ zfcp_get_busid_by_adapter(adapter));
+ break;
+
+ case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2:
+ ZFCP_LOG_NORMAL(
+ "CFDC of the adapter %s could not "
+ "be copied to the secondary SE\n",
+ zfcp_get_busid_by_adapter(adapter));
+ break;
+
+ default:
+ ZFCP_LOG_NORMAL(
+ "CFDC could not be hardened "
+ "on the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ }
+ }
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EAGAIN;
+ break;
+
+ case FSF_AUTHORIZATION_FAILURE:
+ ZFCP_LOG_FLAGS(2, "FSF_AUTHORIZATION_FAILURE\n");
+ ZFCP_LOG_NORMAL(
+ "Adapter %s does not accept privileged commands\n",
+ zfcp_get_busid_by_adapter(adapter));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EACCES;
+ break;
+
+ case FSF_CFDC_ERROR_DETECTED:
+ ZFCP_LOG_FLAGS(2, "FSF_CFDC_ERROR_DETECTED\n");
+ ZFCP_LOG_NORMAL(
+ "Error at position %d in the CFDC, "
+ "CFDC is discarded by the adapter %s\n",
+ header->fsf_status_qual.word[0],
+ zfcp_get_busid_by_adapter(adapter));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EPERM;
+ break;
+
+ case FSF_CONTROL_FILE_UPDATE_ERROR:
+ ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_UPDATE_ERROR\n");
+ ZFCP_LOG_NORMAL(
+ "Adapter %s cannot harden the control file, "
+ "file is discarded\n",
+ zfcp_get_busid_by_adapter(adapter));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EIO;
+ break;
+
+ case FSF_CONTROL_FILE_TOO_LARGE:
+ ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_TOO_LARGE\n");
+ ZFCP_LOG_NORMAL(
+ "Control file is too large, file is discarded "
+ "by the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EIO;
+ break;
+
+ case FSF_ACCESS_CONFLICT_DETECTED:
+ ZFCP_LOG_FLAGS(2, "FSF_ACCESS_CONFLICT_DETECTED\n");
+ if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE)
+ ZFCP_LOG_NORMAL(
+ "CFDC has been discarded by the adapter %s, "
+ "because activation would impact "
+ "%d active connection(s)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ header->fsf_status_qual.word[0]);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EIO;
+ break;
+
+ case FSF_CONFLICTS_OVERRULED:
+ ZFCP_LOG_FLAGS(2, "FSF_CONFLICTS_OVERRULED\n");
+ if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE)
+ ZFCP_LOG_NORMAL(
+ "CFDC has been activated on the adapter %s, "
+ "but activation has impacted "
+ "%d active connection(s)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ header->fsf_status_qual.word[0]);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EIO;
+ break;
+
+ case FSF_UNKNOWN_OP_SUBTYPE:
+ ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_OP_SUBTYPE\n");
+ ZFCP_LOG_NORMAL("unknown operation subtype (adapter: %s, "
+ "op_subtype=0x%x)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ bottom->operation_subtype);
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EINVAL;
+ break;
+
+ case FSF_INVALID_COMMAND_OPTION:
+ ZFCP_LOG_FLAGS(2, "FSF_INVALID_COMMAND_OPTION\n");
+ ZFCP_LOG_NORMAL(
+ "Invalid option 0x%x has been specified "
+ "in QTCB bottom sent to the adapter %s\n",
+ bottom->option,
+ zfcp_get_busid_by_adapter(adapter));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EINVAL;
+ break;
+
+ default:
+ ZFCP_LOG_NORMAL(
+ "bug: An unknown/unexpected FSF status 0x%08x "
+ "was presented on the adapter %s\n",
+ header->fsf_status,
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval");
+ debug_exception(fsf_req->adapter->erp_dbf, 0,
+ &header->fsf_status_qual.word[0], sizeof(u32));
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ retval = -EINVAL;
+ break;
+ }
+
+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 %p\n",
+ signal, 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_sbal_check(unsigned long *flags,
+ struct zfcp_qdio_queue *queue, int needed)
+{
+ write_lock_irqsave(&queue->queue_lock, *flags);
+ if (likely(atomic_read(&queue->free_count) >= needed))
+ return 1;
+ write_unlock_irqrestore(&queue->queue_lock, *flags);
+ return 0;
+}
+
+/*
+ * set qtcb pointer in fsf_req and initialize QTCB
+ */
+static inline void
+zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req, u32 fsf_cmd)
+{
+ if (likely(fsf_req->qtcb != NULL)) {
+ 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;
+ }
+}
+
+/**
+ * zfcp_fsf_req_sbal_get - try to get one SBAL in the request queue
+ * @adapter: adapter for which request queue is examined
+ * @req_flags: flags indicating whether to wait for needed SBAL or not
+ * @lock_flags: lock_flags if queue_lock is taken
+ * Return: 0 on success, otherwise -EIO, or -ERESTARTSYS
+ * Locks: lock adapter->request_queue->queue_lock on success
+ */
+static int
+zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter, int req_flags,
+ unsigned long *lock_flags)
+{
+ long ret;
+ struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
+
+ if (unlikely(req_flags & ZFCP_WAIT_FOR_SBAL)) {
+ ret = wait_event_interruptible_timeout(adapter->request_wq,
+ zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1),
+ ZFCP_SBAL_TIMEOUT);
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return -EIO;
+ } else if (!zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1))
+ return -EIO;
+
+ 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, int req_flags,
+ mempool_t *pool, unsigned long *lock_flags,
+ struct zfcp_fsf_req **fsf_req_p)
+{
+ volatile struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *fsf_req = NULL;
+ int ret = 0;
+ struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
+
+ /* allocate new FSF request */
+ fsf_req = zfcp_fsf_req_alloc(pool, req_flags);
+ if (unlikely(NULL == fsf_req)) {
+ ZFCP_LOG_DEBUG("error: Could not put an FSF request into"
+ "the outbound (send) queue.\n");
+ ret = -ENOMEM;
+ goto failed_fsf_req;
+ }
+
+ zfcp_fsf_req_qtcb_init(fsf_req, fsf_cmd);
+
+ /* initialize waitqueue which may be used to wait on
+ this request completion */
+ init_waitqueue_head(&fsf_req->completion_wq);
+
+ ret = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags);
+ if(ret < 0) {
+ goto failed_sbals;
+ }
+
+ /*
+ * We hold queue_lock here. Check if QDIOUP is set and let request fail
+ * if it is not set (see also *_open_qdio and *_close_qdio).
+ */
+
+ if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
+ write_unlock_irqrestore(&req_queue->queue_lock, *lock_flags);
+ ret = -EIO;
+ goto failed_sbals;
+ }
+
+ fsf_req->adapter = adapter; /* pointer to "parent" adapter */
+ fsf_req->fsf_command = fsf_cmd;
+ fsf_req->sbal_number = 1;
+ fsf_req->sbal_first = req_queue->free_index;
+ fsf_req->sbal_curr = req_queue->free_index;
+ fsf_req->sbale_curr = 1;
+
+ if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) {
+ fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ }
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+
+ /* setup common SBALE fields */
+ sbale[0].addr = fsf_req;
+ sbale[0].flags |= SBAL_FLAGS0_COMMAND;
+ if (likely(fsf_req->qtcb != NULL)) {
+ sbale[1].addr = (void *) fsf_req->qtcb;
+ sbale[1].length = sizeof(struct fsf_qtcb);
+ }
+
+ ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n",
+ fsf_req->sbal_number, fsf_req->sbal_first);
+
+ goto success;
+
+ failed_sbals:
+/* 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);
+ success:
+ *fsf_req_p = fsf_req;
+ return ret;
+}
+
+/*
+ * 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)
+{
+ struct zfcp_adapter *adapter;
+ struct zfcp_qdio_queue *req_queue;
+ volatile struct qdio_buffer_element *sbale;
+ int new_distance_from_int;
+ unsigned long flags;
+ int inc_seq_no = 1;
+ int retval = 0;
+
+ adapter = fsf_req->adapter;
+ req_queue = &adapter->request_queue,
+
+
+ /* FIXME(debug): remove it later */
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_first, 0);
+ ZFCP_LOG_DEBUG("SBALE0 flags=0x%x\n", sbale[0].flags);
+ ZFCP_LOG_TRACE("HEX DUMP OF SBALE1 PAYLOAD:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) sbale[1].addr,
+ sbale[1].length);
+
+ /* set sequence counter in QTCB */
+ if (likely(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 %p of adapter %s gets "
+ "FSF sequence counter value of %i\n",
+ fsf_req,
+ zfcp_get_busid_by_adapter(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 (unlikely(timer)) {
+ timer->expires += jiffies;
+ add_timer(timer);
+ }
+
+ ZFCP_LOG_TRACE("request queue of adapter %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 adapter %s, flags=0x%x, queue_no=%i, "
+ "index_in_queue=%i, count=%i, buffers=%p\n",
+ zfcp_get_busid_by_adapter(adapter),
+ QDIO_FLAG_SYNC_OUTPUT,
+ 0, fsf_req->sbal_first, fsf_req->sbal_number,
+ &req_queue->buffer[fsf_req->sbal_first]);
+
+ /*
+ * adjust the number of free SBALs in request queue as well as
+ * position of first one
+ */
+ atomic_sub(fsf_req->sbal_number, &req_queue->free_count);
+ ZFCP_LOG_TRACE("free_count=%d\n", atomic_read(&req_queue->free_count));
+ req_queue->free_index += fsf_req->sbal_number; /* 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_first, fsf_req->sbal_number, NULL);
+
+ if (unlikely(retval)) {
+ /* Queues are down..... */
+ retval = -EIO;
+ /*
+ * FIXME(potential race):
+ * timer might be expired (absolutely unlikely)
+ */
+ if (timer)
+ del_timer(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_first, fsf_req->sbal_number);
+ atomic_add(fsf_req->sbal_number, &req_queue->free_count);
+ req_queue->free_index -= fsf_req->sbal_number; /* 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;
+ /*
+ * 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 (likely(inc_seq_no)) {
+ adapter->fsf_req_seq_no++;
+ ZFCP_LOG_TRACE
+ ("FSF sequence counter value of adapter %s "
+ "increased to %i\n",
+ zfcp_get_busid_by_adapter(adapter),
+ adapter->fsf_req_seq_no);
+ }
+ /* count FSF requests pending */
+ atomic_inc(&adapter->fsf_reqs_active);
+ }
+ 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);
+}
+
+#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
new file mode 100644
index 000000000000..5889956bbf08
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -0,0 +1,472 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_fsf.h
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ * Volker Sameske <sameske@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
+
+#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
+#define FSF_QTCB_EXCHANGE_PORT_DATA 0x0000000E
+#define FSF_QTCB_DOWNLOAD_CONTROL_FILE 0x00000012
+#define FSF_QTCB_UPLOAD_CONTROL_FILE 0x00000013
+
+/* FSF QTCB types */
+#define FSF_IO_COMMAND 0x00000001
+#define FSF_SUPPORT_COMMAND 0x00000002
+#define FSF_CONFIG_COMMAND 0x00000003
+#define FSF_PORT_COMMAND 0x00000004
+
+/* FSF control file upload/download operations' subtype and options */
+#define FSF_CFDC_OPERATION_SUBTYPE 0x00020001
+#define FSF_CFDC_OPTION_NORMAL_MODE 0x00000000
+#define FSF_CFDC_OPTION_FORCE 0x00000001
+#define FSF_CFDC_OPTION_FULL_ACCESS 0x00000002
+#define FSF_CFDC_OPTION_RESTRICTED_ACCESS 0x00000004
+
+/* 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_LUN_SHARING_VIOLATION 0x00000012
+#define FSF_FCP_COMMAND_DOES_NOT_EXIST 0x00000022
+#define FSF_DIRECTION_INDICATOR_NOT_VALID 0x00000030
+#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_ELS_COMMAND_REJECTED 0x00000050
+#define FSF_GENERIC_COMMAND_REJECTED 0x00000051
+#define FSF_OPERATION_PARTIALLY_SUCCESSFUL 0x00000052
+#define FSF_AUTHORIZATION_FAILURE 0x00000053
+#define FSF_CFDC_ERROR_DETECTED 0x00000054
+#define FSF_CONTROL_FILE_UPDATE_ERROR 0x00000055
+#define FSF_CONTROL_FILE_TOO_LARGE 0x00000056
+#define FSF_ACCESS_CONFLICT_DETECTED 0x00000057
+#define FSF_CONFLICTS_OVERRULED 0x00000058
+#define FSF_PORT_BOXED 0x00000059
+#define FSF_LUN_BOXED 0x0000005A
+#define FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE 0x0000005B
+#define FSF_PAYLOAD_SIZE_MISMATCH 0x00000060
+#define FSF_REQUEST_SIZE_TOO_LARGE 0x00000061
+#define FSF_RESPONSE_SIZE_TOO_LARGE 0x00000062
+#define FSF_SBAL_MISMATCH 0x00000063
+#define FSF_OPEN_PORT_WITHOUT_PRLI 0x00000064
+#define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD
+#define FSF_FCP_RSP_AVAILABLE 0x000000AF
+#define FSF_UNKNOWN_COMMAND 0x000000E2
+#define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3
+#define FSF_INVALID_COMMAND_OPTION 0x000000E5
+/* #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 for CFDC commands */
+#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE 0x00000001
+#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2 0x00000002
+/* CFDC subtable codes */
+#define FSF_SQ_CFDC_SUBTABLE_OS 0x0001
+#define FSF_SQ_CFDC_SUBTABLE_PORT_WWPN 0x0002
+#define FSF_SQ_CFDC_SUBTABLE_PORT_DID 0x0003
+#define FSF_SQ_CFDC_SUBTABLE_LUN 0x0004
+
+/* 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_SENSE_DATA_AVAIL 0x00000003
+#define FSF_STATUS_READ_BIT_ERROR_THRESHOLD 0x00000004
+#define FSF_STATUS_READ_LINK_DOWN 0x00000005 /* FIXME: really? */
+#define FSF_STATUS_READ_LINK_UP 0x00000006
+#define FSF_STATUS_READ_CFDC_UPDATED 0x0000000A
+#define FSF_STATUS_READ_CFDC_HARDENED 0x0000000B
+
+/* status subtypes in status read buffer */
+#define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT 0x00000001
+#define FSF_STATUS_READ_SUB_ERROR_PORT 0x00000002
+
+/* status subtypes for CFDC */
+#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE 0x00000002
+#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2 0x0000000F
+
+/* 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
+#define FSF_MAX_SBALS_PER_ELS_REQ 2
+
+/* logging space behind QTCB */
+#define FSF_QTCB_LOG_SIZE 1024
+
+/* channel features */
+#define FSF_FEATURE_QTCB_SUPPRESSION 0x00000001
+#define FSF_FEATURE_CFDC 0x00000002
+#define FSF_FEATURE_LUN_SHARING 0x00000004
+#define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010
+#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020
+
+/* option */
+#define FSF_OPEN_LUN_SUPPRESS_BOXING 0x00000001
+#define FSF_OPEN_LUN_REPLICATE_SENSE 0x00000002
+
+/* adapter types */
+#define FSF_ADAPTER_TYPE_FICON 0x00000001
+#define FSF_ADAPTER_TYPE_FICON_EXPRESS 0x00000002
+
+/* port types */
+#define FSF_HBA_PORTTYPE_UNKNOWN 0x00000001
+#define FSF_HBA_PORTTYPE_NOTPRESENT 0x00000003
+#define FSF_HBA_PORTTYPE_NPORT 0x00000005
+#define FSF_HBA_PORTTYPE_PTP 0x00000021
+/* following are not defined and used by FSF Spec
+ but are additionally defined by FC-HBA */
+#define FSF_HBA_PORTTYPE_OTHER 0x00000002
+#define FSF_HBA_PORTTYPE_NOTPRESENT 0x00000003
+#define FSF_HBA_PORTTYPE_NLPORT 0x00000006
+#define FSF_HBA_PORTTYPE_FLPORT 0x00000007
+#define FSF_HBA_PORTTYPE_FPORT 0x00000008
+#define FSF_HBA_PORTTYPE_LPORT 0x00000020
+
+/* port states */
+#define FSF_HBA_PORTSTATE_UNKNOWN 0x00000001
+#define FSF_HBA_PORTSTATE_ONLINE 0x00000002
+#define FSF_HBA_PORTSTATE_OFFLINE 0x00000003
+#define FSF_HBA_PORTSTATE_LINKDOWN 0x00000006
+#define FSF_HBA_PORTSTATE_ERROR 0x00000007
+
+/* IO states of adapter */
+#define FSF_IOSTAT_NPORT_RJT 0x00000004
+#define FSF_IOSTAT_FABRIC_RJT 0x00000005
+#define FSF_IOSTAT_LS_RJT 0x00000009
+
+/* open LUN access flags*/
+#define FSF_UNIT_ACCESS_OPEN_LUN_ALLOWED 0x01000000
+#define FSF_UNIT_ACCESS_EXCLUSIVE 0x02000000
+#define FSF_UNIT_ACCESS_OUTBOUND_TRANSFER 0x10000000
+
+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 fsf_queue_designator fsf_queue_designator;
+} __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 {
+ u32 operation_subtype;
+ u8 res1[12];
+ u32 d_id;
+ u32 option;
+ u64 fcp_lun;
+ u64 res2;
+ u64 req_handle;
+ u32 service_class;
+ u8 res3[3];
+ u8 timeout;
+ u32 lun_access_info;
+ u8 res4[180];
+ u32 els1_length;
+ u32 els2_length;
+ u32 req_buf_length;
+ u32 resp_buf_length;
+ u8 els[256];
+} __attribute__ ((packed));
+
+struct fsf_qtcb_bottom_config {
+ u32 lic_version;
+ u32 feature_selection;
+ u32 high_qtcb_version;
+ u32 low_qtcb_version;
+ u32 max_qtcb_size;
+ u32 max_data_transfer_size;
+ u32 supported_features;
+ u8 res1[4];
+ u32 fc_topology;
+ u32 fc_link_speed;
+ u32 adapter_type;
+ u32 peer_d_id;
+ u8 res2[12];
+ u32 s_id;
+ struct fsf_nport_serv_param nport_serv_param;
+ u8 res3[8];
+ u32 adapter_ports;
+ u32 hardware_version;
+ u8 serial_number[32];
+ u8 res4[272];
+} __attribute__ ((packed));
+
+struct fsf_qtcb_bottom_port {
+ u8 res1[8];
+ u32 fc_port_id;
+ u32 port_type;
+ u32 port_state;
+ u32 class_of_service; /* should be 0x00000006 for class 2 and 3 */
+ u8 supported_fc4_types[32]; /* should be 0x00000100 for scsi fcp */
+ u8 active_fc4_types[32];
+ u32 supported_speed; /* 0x0001 for 1 GBit/s or 0x0002 for 2 GBit/s */
+ u32 maximum_frame_size; /* fixed value of 2112 */
+ u64 seconds_since_last_reset;
+ u64 tx_frames;
+ u64 tx_words;
+ u64 rx_frames;
+ u64 rx_words;
+ u64 lip; /* 0 */
+ u64 nos; /* currently 0 */
+ u64 error_frames; /* currently 0 */
+ u64 dumped_frames; /* currently 0 */
+ u64 link_failure;
+ u64 loss_of_sync;
+ u64 loss_of_signal;
+ u64 psp_error_counts;
+ u64 invalid_tx_words;
+ u64 invalid_crcs;
+ u64 input_requests;
+ u64 output_requests;
+ u64 control_requests;
+ u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */
+ u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */
+ u8 res2[256];
+} __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_bottom_port port;
+};
+
+struct fsf_qtcb {
+ struct fsf_qtcb_prefix prefix;
+ struct fsf_qtcb_header header;
+ union fsf_qtcb_bottom bottom;
+ u8 log[FSF_QTCB_LOG_SIZE];
+} __attribute__ ((packed));
+
+#endif /* FSF_H */
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
new file mode 100644
index 000000000000..06e862d7bc90
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -0,0 +1,868 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_qdio.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * QDIO related routines
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Authors:
+ * Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Wolfgang Taphorn
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_QDIO_C_REVISION "$Revision: 1.20 $"
+
+#include "zfcp_ext.h"
+
+static inline void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *, int);
+static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_get
+ (struct zfcp_qdio_queue *, int, int);
+static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_resp
+ (struct zfcp_fsf_req *, int, int);
+static inline volatile struct qdio_buffer_element *zfcp_qdio_sbal_chain
+ (struct zfcp_fsf_req *, unsigned long);
+static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_next
+ (struct zfcp_fsf_req *, unsigned long);
+static inline int zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *, int, int);
+static inline int zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *);
+static inline void zfcp_qdio_sbale_fill
+ (struct zfcp_fsf_req *, unsigned long, void *, int);
+static inline int zfcp_qdio_sbals_from_segment
+ (struct zfcp_fsf_req *, unsigned long, void *, unsigned long);
+static inline int zfcp_qdio_sbals_from_buffer
+ (struct zfcp_fsf_req *, unsigned long, void *, unsigned long, int);
+
+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
+
+/*
+ * 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: allocation of "
+ "QDIO buffer failed \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("only %d QDIO buffers allocated for request "
+ "queue\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("only %d QDIO buffers allocated for response "
+ "queue", buffer_count);
+ zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]),
+ buffer_count);
+ ZFCP_LOG_TRACE("freeing 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("freeing request_queue buffers\n");
+ zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]),
+ QDIO_MAX_BUFFERS_PER_Q);
+
+ ZFCP_LOG_TRACE("freeing 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 (unlikely(status & QDIO_STATUS_LOOK_FOR_ERROR)) {
+ retval = -EIO;
+
+ ZFCP_LOG_FLAGS(1, "QDIO_STATUS_LOOK_FOR_ERROR \n");
+
+ ZFCP_LOG_INFO("QDIO problem occurred (status=0x%x, "
+ "qdio_error=0x%x, siga_error=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 0x%x\n",
+ qdio_error);
+ break;
+ }
+ /* Restarting IO on the failed adapter from scratch */
+ debug_text_event(adapter->erp_dbf, 1, "qdio_err");
+ /*
+ * Since we have been using this adapter, it is save to assume
+ * that it is not failed but recoverable. The card seems to
+ * report link-up events by self-initiated queue shutdown.
+ * That is why we need to clear the the link-down flag
+ * which is set again in case we have missed by a mile.
+ */
+ zfcp_erp_adapter_reopen(
+ adapter,
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+ ZFCP_STATUS_COMMON_ERP_FAILED);
+ }
+ 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("adapter %s, first=%d, elements_processed=%d\n",
+ zfcp_get_busid_by_adapter(adapter),
+ first_element, elements_processed);
+
+ if (unlikely(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 (unlikely(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: unexpected inbound "
+ "packet on adapter %s "
+ "(reqid=0x%lx, "
+ "first_element=%d, "
+ "elements_processed=%d)\n",
+ zfcp_get_busid_by_adapter(adapter),
+ (unsigned long) buffere->addr,
+ first_element,
+ elements_processed);
+ ZFCP_LOG_NORMAL("hex dump of inbound buffer "
+ "at address %p "
+ "(buffer_index=%d, "
+ "buffere_index=%d)\n", buffer,
+ buffer_index, buffere_index);
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+ (char *) buffer, SBAL_SIZE);
+ }
+ /*
+ * A single used SBALE per inbound SBALE has been
+ * implemented by QDIO so far. Hope they will
+ * do some optimisation. Will need to change to
+ * unlikely() then.
+ */
+ if (likely(buffere->flags & SBAL_FLAGS_LAST_ENTRY))
+ break;
+ };
+
+ if (unlikely(!(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 on adapter %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 (unlikely(retval)) {
+ atomic_set(&queue->free_count, count);
+ ZFCP_LOG_DEBUG("clearing of inbound data regions failed, "
+ "queues may be down "
+ "(count=%d, start=%d, retval=%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 enqueued to response "
+ "queue 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;
+
+ /* invalid (per convention used in this driver) */
+ if (unlikely(!sbale_addr)) {
+ ZFCP_LOG_NORMAL("bug: invalid reqid\n");
+ retval = -EINVAL;
+ goto out;
+ }
+
+ /* valid request id and thus (hopefully :) valid fsf_req address */
+ fsf_req = (struct zfcp_fsf_req *) sbale_addr;
+
+ if (unlikely(adapter != fsf_req->adapter)) {
+ ZFCP_LOG_NORMAL("bug: invalid reqid (fsf_req=%p, "
+ "fsf_req->adapter=%p, adapter=%p)\n",
+ fsf_req, fsf_req->adapter, adapter);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ ZFCP_LOG_TRACE("fsf_req at %p, QTCB at %p\n", fsf_req, fsf_req->qtcb);
+ if (likely(fsf_req->qtcb)) {
+ ZFCP_LOG_TRACE("hex dump of QTCB:\n");
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) fsf_req->qtcb,
+ sizeof(struct fsf_qtcb));
+ }
+
+ /* finish the FSF request */
+ zfcp_fsf_req_complete(fsf_req);
+ out:
+ return retval;
+}
+
+/**
+ * zfcp_qdio_sbale_get - return pointer to SBALE of qdio_queue
+ * @queue: queue from which SBALE should be returned
+ * @sbal: specifies number of SBAL in queue
+ * @sbale: specifes number of SBALE in SBAL
+ */
+static inline volatile struct qdio_buffer_element *
+zfcp_qdio_sbale_get(struct zfcp_qdio_queue *queue, int sbal, int sbale)
+{
+ return &queue->buffer[sbal]->element[sbale];
+}
+
+/**
+ * zfcp_qdio_sbale_req - return pointer to SBALE of request_queue for
+ * a struct zfcp_fsf_req
+ */
+inline volatile struct qdio_buffer_element *
+zfcp_qdio_sbale_req(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
+{
+ return zfcp_qdio_sbale_get(&fsf_req->adapter->request_queue,
+ sbal, sbale);
+}
+
+/**
+ * zfcp_qdio_sbale_resp - return pointer to SBALE of response_queue for
+ * a struct zfcp_fsf_req
+ */
+static inline volatile struct qdio_buffer_element *
+zfcp_qdio_sbale_resp(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
+{
+ return zfcp_qdio_sbale_get(&fsf_req->adapter->response_queue,
+ sbal, sbale);
+}
+
+/**
+ * zfcp_qdio_sbale_curr - return current SBALE on request_queue for
+ * a struct zfcp_fsf_req
+ */
+inline volatile struct qdio_buffer_element *
+zfcp_qdio_sbale_curr(struct zfcp_fsf_req *fsf_req)
+{
+ return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr,
+ fsf_req->sbale_curr);
+}
+
+/**
+ * zfcp_qdio_sbal_limit - determine maximum number of SBALs that can be used
+ * on the request_queue for a struct zfcp_fsf_req
+ * @fsf_req: the number of the last SBAL that can be used is stored herein
+ * @max_sbals: used to pass an upper limit for the number of SBALs
+ *
+ * Note: We can assume at least one free SBAL in the request_queue when called.
+ */
+static inline void
+zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
+{
+ int count = atomic_read(&fsf_req->adapter->request_queue.free_count);
+ count = min(count, max_sbals);
+ fsf_req->sbal_last = fsf_req->sbal_first;
+ fsf_req->sbal_last += (count - 1);
+ fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
+}
+
+/**
+ * zfcp_qdio_sbal_chain - chain SBALs if more than one SBAL is needed for a
+ * request
+ * @fsf_req: zfcp_fsf_req to be processed
+ * @sbtype: SBAL flags which have to be set in first SBALE of new SBAL
+ *
+ * This function changes sbal_curr, sbale_curr, sbal_number of fsf_req.
+ */
+static inline volatile struct qdio_buffer_element *
+zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
+{
+ volatile struct qdio_buffer_element *sbale;
+
+ /* set last entry flag in current SBALE of current SBAL */
+ sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ /* don't exceed last allowed SBAL */
+ if (fsf_req->sbal_curr == fsf_req->sbal_last)
+ return NULL;
+
+ /* set chaining flag in first SBALE of current SBAL */
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
+
+ /* calculate index of next SBAL */
+ fsf_req->sbal_curr++;
+ fsf_req->sbal_curr %= QDIO_MAX_BUFFERS_PER_Q;
+
+ /* keep this requests number of SBALs up-to-date */
+ fsf_req->sbal_number++;
+
+ /* start at first SBALE of new SBAL */
+ fsf_req->sbale_curr = 0;
+
+ /* set storage-block type for new SBAL */
+ sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale->flags |= sbtype;
+
+ return sbale;
+}
+
+/**
+ * zfcp_qdio_sbale_next - switch to next SBALE, chain SBALs if needed
+ */
+static inline volatile struct qdio_buffer_element *
+zfcp_qdio_sbale_next(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
+{
+ if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
+ return zfcp_qdio_sbal_chain(fsf_req, sbtype);
+
+ fsf_req->sbale_curr++;
+
+ return zfcp_qdio_sbale_curr(fsf_req);
+}
+
+/**
+ * zfcp_qdio_sbals_zero - initialize SBALs between first and last in queue
+ * with zero from
+ */
+static inline int
+zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *queue, int first, int last)
+{
+ struct qdio_buffer **buf = queue->buffer;
+ int curr = first;
+ int count = 0;
+
+ for(;;) {
+ curr %= QDIO_MAX_BUFFERS_PER_Q;
+ count++;
+ memset(buf[curr], 0, sizeof(struct qdio_buffer));
+ if (curr == last)
+ break;
+ curr++;
+ }
+ return count;
+}
+
+
+/**
+ * zfcp_qdio_sbals_wipe - reset all changes in SBALs for an fsf_req
+ */
+static inline int
+zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req)
+{
+ return zfcp_qdio_sbals_zero(&fsf_req->adapter->request_queue,
+ fsf_req->sbal_first, fsf_req->sbal_curr);
+}
+
+
+/**
+ * zfcp_qdio_sbale_fill - set address and lenght in current SBALE
+ * on request_queue
+ */
+static inline void
+zfcp_qdio_sbale_fill(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
+ void *addr, int length)
+{
+ volatile struct qdio_buffer_element *sbale;
+
+ sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale->addr = addr;
+ sbale->length = length;
+}
+
+/**
+ * zfcp_qdio_sbals_from_segment - map memory segment to SBALE(s)
+ * @fsf_req: request to be processed
+ * @sbtype: SBALE flags
+ * @start_addr: address of memory segment
+ * @total_length: length of memory segment
+ *
+ * Alignment and length of the segment determine how many SBALEs are needed
+ * for the memory segment.
+ */
+static inline int
+zfcp_qdio_sbals_from_segment(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
+ void *start_addr, unsigned long total_length)
+{
+ unsigned long remaining, length;
+ void *addr;
+
+ /* split segment up heeding page boundaries */
+ for (addr = start_addr, remaining = total_length; remaining > 0;
+ addr += length, remaining -= length) {
+ /* get next free SBALE for new piece */
+ if (NULL == zfcp_qdio_sbale_next(fsf_req, sbtype)) {
+ /* no SBALE left, clean up and leave */
+ zfcp_qdio_sbals_wipe(fsf_req);
+ return -EINVAL;
+ }
+ /* calculate length of new piece */
+ length = min(remaining,
+ (PAGE_SIZE - ((unsigned long) addr &
+ (PAGE_SIZE - 1))));
+ /* fill current SBALE with calculated piece */
+ zfcp_qdio_sbale_fill(fsf_req, sbtype, addr, length);
+ }
+ return total_length;
+}
+
+
+/**
+ * zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list
+ * @fsf_req: request to be processed
+ * @sbtype: SBALE flags
+ * @sg: scatter-gather list
+ * @sg_count: number of elements in scatter-gather list
+ * @max_sbals: upper bound for number of SBALs to be used
+ */
+inline int
+zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
+ struct scatterlist *sg, int sg_count, int max_sbals)
+{
+ int sg_index;
+ struct scatterlist *sg_segment;
+ int retval;
+ volatile struct qdio_buffer_element *sbale;
+ int bytes = 0;
+
+ /* figure out last allowed SBAL */
+ zfcp_qdio_sbal_limit(fsf_req, max_sbals);
+
+ /* set storage-block type for current SBAL */
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale->flags |= sbtype;
+
+ /* process all segements of scatter-gather list */
+ for (sg_index = 0, sg_segment = sg, bytes = 0;
+ sg_index < sg_count;
+ sg_index++, sg_segment++) {
+ retval = zfcp_qdio_sbals_from_segment(
+ fsf_req,
+ sbtype,
+ zfcp_sg_to_address(sg_segment),
+ sg_segment->length);
+ if (retval < 0) {
+ bytes = retval;
+ goto out;
+ } else
+ bytes += retval;
+ }
+ /* assume that no other SBALEs are to follow in the same SBAL */
+ sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
+out:
+ return bytes;
+}
+
+
+/**
+ * zfcp_qdio_sbals_from_buffer - fill SBALs from buffer
+ * @fsf_req: request to be processed
+ * @sbtype: SBALE flags
+ * @buffer: data buffer
+ * @length: length of buffer
+ * @max_sbals: upper bound for number of SBALs to be used
+ */
+static inline int
+zfcp_qdio_sbals_from_buffer(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
+ void *buffer, unsigned long length, int max_sbals)
+{
+ struct scatterlist sg_segment;
+
+ zfcp_address_to_sg(buffer, &sg_segment);
+ sg_segment.length = length;
+
+ return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, &sg_segment, 1,
+ max_sbals);
+}
+
+
+/**
+ * zfcp_qdio_sbals_from_scsicmnd - fill SBALs from scsi command
+ * @fsf_req: request to be processed
+ * @sbtype: SBALE flags
+ * @scsi_cmnd: either scatter-gather list or buffer contained herein is used
+ * to fill SBALs
+ */
+inline int
+zfcp_qdio_sbals_from_scsicmnd(struct zfcp_fsf_req *fsf_req,
+ unsigned long sbtype, struct scsi_cmnd *scsi_cmnd)
+{
+ if (scsi_cmnd->use_sg) {
+ return zfcp_qdio_sbals_from_sg(fsf_req, sbtype,
+ (struct scatterlist *)
+ scsi_cmnd->request_buffer,
+ scsi_cmnd->use_sg,
+ ZFCP_MAX_SBALS_PER_REQ);
+ } else {
+ return zfcp_qdio_sbals_from_buffer(fsf_req, sbtype,
+ scsi_cmnd->request_buffer,
+ scsi_cmnd->request_bufflen,
+ ZFCP_MAX_SBALS_PER_REQ);
+ }
+}
+
+/**
+ * zfcp_qdio_determine_pci - set PCI flag in first SBALE on qdio queue if needed
+ */
+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;
+ volatile struct qdio_buffer_element *sbale;
+
+ new_distance_from_int = req_queue->distance_from_int +
+ fsf_req->sbal_number;
+
+ if (unlikely(new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL)) {
+ new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL;
+ pci_pos = fsf_req->sbal_first;
+ pci_pos += fsf_req->sbal_number;
+ pci_pos -= new_distance_from_int;
+ pci_pos -= 1;
+ pci_pos %= QDIO_MAX_BUFFERS_PER_Q;
+ sbale = zfcp_qdio_sbale_req(fsf_req, pci_pos, 0);
+ sbale->flags |= SBAL_FLAGS0_PCI;
+ }
+ 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 %p\n",
+ index, buf[index]);
+ }
+}
+
+#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
new file mode 100644
index 000000000000..e21b547fd427
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -0,0 +1,949 @@
+/*
+ *
+ * linux/drivers/s390/scsi/zfcp_scsi.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * (C) Copyright IBM Corp. 2002, 2004
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Raimund Schroeder <raimund.schroeder@de.ibm.com>
+ * Aron Zeh
+ * Wolfgang Taphorn
+ * Stefan Bader <stefan.bader@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI
+
+#define ZFCP_SCSI_REVISION "$Revision: 1.74 $"
+
+#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(struct scsi_cmnd *,
+ void (*done) (struct scsi_cmnd *));
+static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *);
+static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *);
+static int zfcp_scsi_eh_bus_reset_handler(struct scsi_cmnd *);
+static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *);
+static int zfcp_task_management_function(struct zfcp_unit *, u8);
+
+static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, scsi_id_t,
+ scsi_lun_t);
+static struct zfcp_port *zfcp_port_lookup(struct zfcp_adapter *, int,
+ scsi_id_t);
+
+static struct device_attribute *zfcp_sysfs_sdev_attrs[];
+
+struct scsi_transport_template *zfcp_transport_template;
+
+struct zfcp_data zfcp_data = {
+ .scsi_host_template = {
+ name: ZFCP_NAME,
+ proc_name: "zfcp",
+ 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,
+ },
+ .driver_version = ZFCP_VERSION,
+ /* 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_NORMAL("bug: no unit associated with SCSI device at "
+ "address %p\n", sdpnt);
+ }
+}
+
+/*
+ * 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;
+}
+
+/**
+ * zfcp_scsi_command_fail - set result in scsi_cmnd and call scsi_done function
+ * @scpnt: pointer to struct scsi_cmnd where result is set
+ * @result: result to be set in scpnt (e.g. DID_ERROR)
+ */
+static void
+zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
+{
+ set_host_byte(&scpnt->result, result);
+ zfcp_cmd_dbf_event_scsi("failing", scpnt);
+ /* return directly */
+ scpnt->scsi_done(scpnt);
+}
+
+/**
+ * zfcp_scsi_command_async - worker for zfcp_scsi_queuecommand and
+ * zfcp_scsi_command_sync
+ * @adapter: adapter where scsi command is issued
+ * @unit: unit to which scsi command is sent
+ * @scpnt: scsi command to be sent
+ * @timer: timer to be started if request is successfully initiated
+ *
+ * Note: In scsi_done function must be set in scpnt.
+ */
+int
+zfcp_scsi_command_async(struct zfcp_adapter *adapter, struct zfcp_unit *unit,
+ struct scsi_cmnd *scpnt, struct timer_list *timer)
+{
+ int tmp;
+ int retval;
+
+ retval = 0;
+
+ BUG_ON((adapter == NULL) || (adapter != unit->port->adapter));
+ BUG_ON(scpnt->scsi_done == NULL);
+
+ if (unlikely(NULL == unit)) {
+ zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
+ goto out;
+ }
+
+ if (unlikely(
+ atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status) ||
+ !atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status))) {
+ ZFCP_LOG_DEBUG("stopping SCSI I/O on unit 0x%016Lx on port "
+ "0x%016Lx on adapter %s\n",
+ unit->fcp_lun, unit->port->wwpn,
+ zfcp_get_busid_by_adapter(adapter));
+ zfcp_scsi_command_fail(scpnt, DID_ERROR);
+ goto out;
+ }
+
+ if (unlikely(
+ !atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status))) {
+ ZFCP_LOG_DEBUG("adapter %s not ready or unit 0x%016Lx "
+ "on port 0x%016Lx in recovery\n",
+ zfcp_get_busid_by_unit(unit),
+ unit->fcp_lun, unit->port->wwpn);
+ retval = SCSI_MLQUEUE_DEVICE_BUSY;
+ goto out;
+ }
+
+ tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, timer,
+ ZFCP_REQ_AUTO_CLEANUP);
+
+ if (unlikely(tmp < 0)) {
+ ZFCP_LOG_DEBUG("error: initiation of Send FCP Cmnd failed\n");
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+out:
+ return retval;
+}
+
+void
+zfcp_scsi_command_sync_handler(struct scsi_cmnd *scpnt)
+{
+ struct completion *wait = (struct completion *) scpnt->SCp.ptr;
+ complete(wait);
+}
+
+
+/**
+ * zfcp_scsi_command_sync - send a SCSI command and wait for completion
+ * @unit: unit where command is sent to
+ * @scpnt: scsi command to be sent
+ * @timer: timer to be started if request is successfully initiated
+ * Return: 0
+ *
+ * Errors are indicated in scpnt->result
+ */
+int
+zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt,
+ struct timer_list *timer)
+{
+ int ret;
+ DECLARE_COMPLETION(wait);
+
+ scpnt->SCp.ptr = (void *) &wait; /* silent re-use */
+ scpnt->scsi_done = zfcp_scsi_command_sync_handler;
+ ret = zfcp_scsi_command_async(unit->port->adapter, unit, scpnt, timer);
+ if (ret == 0)
+ wait_for_completion(&wait);
+
+ scpnt->SCp.ptr = NULL;
+
+ return 0;
+}
+
+/*
+ * function: zfcp_scsi_queuecommand
+ *
+ * purpose: enqueues a SCSI command to the specified target device
+ *
+ * returns: 0 - success, SCSI command enqueued
+ * !0 - failure
+ */
+int
+zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct zfcp_unit *unit;
+ struct zfcp_adapter *adapter;
+
+ /* reset the status for this request */
+ scpnt->result = 0;
+ scpnt->host_scribble = NULL;
+ scpnt->scsi_done = done;
+
+ /*
+ * figure out adapter and target device
+ * (stored there by zfcp_scsi_slave_alloc)
+ */
+ adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
+ unit = (struct zfcp_unit *) scpnt->device->hostdata;
+
+ return zfcp_scsi_command_async(adapter, unit, scpnt, NULL);
+}
+
+/*
+ * function: zfcp_unit_lookup
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * context:
+ */
+static struct zfcp_unit *
+zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, scsi_id_t id,
+ scsi_lun_t 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;
+}
+
+static struct zfcp_port *
+zfcp_port_lookup(struct zfcp_adapter *adapter, int channel, scsi_id_t id)
+{
+ struct zfcp_port *port;
+
+ list_for_each_entry(port, &adapter->port_list_head, list) {
+ if (id == port->scsi_id)
+ return port;
+ }
+ return (struct zfcp_port *) NULL;
+}
+
+/*
+ * 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(struct scsi_cmnd *scpnt)
+{
+ int retval = SUCCESS;
+ struct zfcp_fsf_req *new_fsf_req, *old_fsf_req;
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
+ struct zfcp_unit *unit = (struct zfcp_unit *) scpnt->device->hostdata;
+ struct zfcp_port *port = unit->port;
+ struct Scsi_Host *scsi_host = scpnt->device->host;
+ union zfcp_req_data *req_data = NULL;
+ unsigned long flags;
+ u32 status = 0;
+
+ /* 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));
+
+ ZFCP_LOG_INFO("aborting scsi_cmnd=%p on adapter %s\n",
+ scpnt, zfcp_get_busid_by_adapter(adapter));
+
+ 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 and thus we block the
+ * release of fsf_req as long as we need to access scsi_req.
+ */
+ write_lock_irqsave(&adapter->abort_lock, flags);
+
+ /*
+ * 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=%p\n", 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;
+
+ dbf_fsf_req = (unsigned long) old_fsf_req;
+ dbf_timeout =
+ (jiffies - req_data->send_fcp_command_task.start_jiffies) / HZ;
+
+ ZFCP_LOG_DEBUG("old_fsf_req=%p\n", 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%016Lx (%p)\n", unit->fcp_lun, unit);
+
+ /*
+ * We block (call schedule)
+ * 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, 0);
+ ZFCP_LOG_DEBUG("new_fsf_req=%p\n", new_fsf_req);
+ if (!new_fsf_req) {
+ retval = FAILED;
+ ZFCP_LOG_NORMAL("error: initiation of Abort FCP Cmnd "
+ "failed\n");
+ strncpy(dbf_result, "##nores", ZFCP_ABORT_DBF_LENGTH);
+ goto out;
+ }
+
+ /* wait for completion of abort */
+ ZFCP_LOG_DEBUG("waiting for cleanup...\n");
+#if 1
+ /*
+ * 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:
+ 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);
+
+ 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(struct 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);
+
+ if (!unit) {
+ ZFCP_LOG_NORMAL("bug: Tried reset for nonexistent unit\n");
+ retval = SUCCESS;
+ goto out;
+ }
+ ZFCP_LOG_NORMAL("resetting unit 0x%016Lx\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, FCP_LOGICAL_UNIT_RESET);
+ if (retval) {
+ ZFCP_LOG_DEBUG("unit reset failed (unit=%p)\n", unit);
+ if (retval == -ENOTSUPP)
+ atomic_set_mask
+ (ZFCP_STATUS_UNIT_NOTSUPPUNITRESET,
+ &unit->status);
+ /* fall through and try 'target reset' next */
+ } else {
+ ZFCP_LOG_DEBUG("unit reset succeeded (unit=%p)\n",
+ unit);
+ /* avoid 'target reset' */
+ retval = SUCCESS;
+ goto out;
+ }
+ }
+ retval = zfcp_task_management_function(unit, FCP_TARGET_RESET);
+ if (retval) {
+ ZFCP_LOG_DEBUG("target reset failed (unit=%p)\n", unit);
+ retval = FAILED;
+ } else {
+ ZFCP_LOG_DEBUG("target reset succeeded (unit=%p)\n", 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, 0);
+ if (!fsf_req) {
+ ZFCP_LOG_INFO("error: creation of task management request "
+ "failed for unit 0x%016Lx on port 0x%016Lx on "
+ "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(struct 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;
+ ZFCP_LOG_NORMAL("bus reset because of problems with "
+ "unit 0x%016Lx\n", 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(struct 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;
+ ZFCP_LOG_NORMAL("host reset because of problems with "
+ "unit 0x%016Lx\n", 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: registration with SCSI stack failed "
+ "for adapter %s ",
+ zfcp_get_busid_by_adapter(adapter));
+ retval = -EIO;
+ goto out;
+ }
+ ZFCP_LOG_DEBUG("host registered, scsi_host=%p\n", adapter->scsi_host);
+
+ /* tell the SCSI stack some characteristics of this adapter */
+ adapter->scsi_host->max_id = 1;
+ adapter->scsi_host->max_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;
+ adapter->scsi_host->transportt = zfcp_transport_template;
+ /*
+ * Reverse mapping of the host number to avoid race condition
+ */
+ adapter->scsi_host_no = adapter->scsi_host->host_no;
+
+ /*
+ * 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;
+
+ if (scsi_add_host(adapter->scsi_host, &adapter->ccw_device->dev)) {
+ scsi_host_put(adapter->scsi_host);
+ retval = -EIO;
+ goto out;
+ }
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
+ 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;
+ adapter->scsi_host_no = 0;
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
+
+ return;
+}
+
+
+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);
+}
+
+/*
+ * Support functions for FC transport class
+ */
+static void
+zfcp_get_port_id(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0];
+ struct zfcp_port *port;
+ unsigned long flags;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ port = zfcp_port_lookup(adapter, starget->channel, starget->id);
+ if (port)
+ fc_starget_port_id(starget) = port->d_id;
+ else
+ fc_starget_port_id(starget) = -1;
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+static void
+zfcp_get_port_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0];
+ struct zfcp_port *port;
+ unsigned long flags;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ port = zfcp_port_lookup(adapter, starget->channel, starget->id);
+ if (port)
+ fc_starget_port_name(starget) = port->wwpn;
+ else
+ fc_starget_port_name(starget) = -1;
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+static void
+zfcp_get_node_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0];
+ struct zfcp_port *port;
+ unsigned long flags;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ port = zfcp_port_lookup(adapter, starget->channel, starget->id);
+ if (port)
+ fc_starget_node_name(starget) = port->wwnn;
+ else
+ fc_starget_node_name(starget) = -1;
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+struct fc_function_template zfcp_transport_functions = {
+ .get_starget_port_id = zfcp_get_port_id,
+ .get_starget_port_name = zfcp_get_port_name,
+ .get_starget_node_name = zfcp_get_node_name,
+ .show_starget_port_id = 1,
+ .show_starget_port_name = 1,
+ .show_starget_node_name = 1,
+};
+
+/**
+ * ZFCP_DEFINE_SCSI_ATTR
+ * @_name: name of show attribute
+ * @_format: format string
+ * @_value: value to print
+ *
+ * Generates attribute for a unit.
+ */
+#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value) \
+static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, \
+ char *buf) \
+{ \
+ struct scsi_device *sdev; \
+ struct zfcp_unit *unit; \
+ \
+ sdev = to_scsi_device(dev); \
+ unit = sdev->hostdata; \
+ return sprintf(buf, _format, _value); \
+} \
+ \
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL);
+
+ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n", zfcp_get_busid_by_unit(unit));
+ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", unit->port->wwpn);
+ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n", unit->fcp_lun);
+
+static struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
+ &dev_attr_fcp_lun,
+ &dev_attr_wwpn,
+ &dev_attr_hba_id,
+ NULL
+};
+
+#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c
new file mode 100644
index 000000000000..ff28ade1dfc7
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_sysfs_adapter.c
@@ -0,0 +1,298 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_adapter.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs adapter related routines
+ *
+ * (C) Copyright IBM Corp. 2003, 2004
+ *
+ * Authors:
+ * Martin Peschke <mpeschke@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_SYSFS_ADAPTER_C_REVISION "$Revision: 1.38 $"
+
+#include "zfcp_ext.h"
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_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(card_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_DEFINE_ADAPTER_ATTR(hardware_version, "0x%08x\n",
+ adapter->hardware_version);
+ZFCP_DEFINE_ADAPTER_ATTR(serial_number, "%17s\n", adapter->serial_number);
+ZFCP_DEFINE_ADAPTER_ATTR(scsi_host_no, "0x%x\n", adapter->scsi_host_no);
+ZFCP_DEFINE_ADAPTER_ATTR(in_recovery, "%d\n", atomic_test_mask
+ (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status));
+
+/**
+ * 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, 0);
+ if (!port)
+ goto out;
+
+ retval = 0;
+
+ zfcp_erp_port_reopen(port, 0);
+ zfcp_erp_wait(port->adapter);
+ zfcp_port_put(port);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) 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);
+ zfcp_port_dequeue(port);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) 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;
+ }
+
+ zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING,
+ ZFCP_SET);
+ zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
+ zfcp_erp_wait(adapter);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) 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_card_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,
+ &dev_attr_hardware_version.attr,
+ &dev_attr_serial_number.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
diff --git a/drivers/s390/scsi/zfcp_sysfs_driver.c b/drivers/s390/scsi/zfcp_sysfs_driver.c
new file mode 100644
index 000000000000..77a5e2dcc0ff
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_sysfs_driver.c
@@ -0,0 +1,135 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_driver.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs driver related routines
+ *
+ * (C) Copyright IBM Corp. 2003, 2004
+ *
+ * Authors:
+ * Martin Peschke <mpeschke@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.17 $"
+
+#include "zfcp_ext.h"
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_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", (unsigned int) \
+ ZFCP_GET_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 ssize_t zfcp_sysfs_version_show(struct device_driver *dev,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", zfcp_data.driver_version);
+}
+
+static DRIVER_ATTR(version, S_IRUGO, zfcp_sysfs_version_show, NULL);
+
+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,
+ &driver_attr_version.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
diff --git a/drivers/s390/scsi/zfcp_sysfs_port.c b/drivers/s390/scsi/zfcp_sysfs_port.c
new file mode 100644
index 000000000000..6aafb2abb4b5
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_sysfs_port.c
@@ -0,0 +1,311 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_port.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs port related routines
+ *
+ * (C) Copyright IBM Corp. 2003, 2004
+ *
+ * Authors:
+ * Martin Peschke <mpeschke@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ * Volker Sameske <sameske@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.47 $"
+
+#include "zfcp_ext.h"
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_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)
+{
+ kfree(dev);
+}
+
+/**
+ * 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_DEFINE_PORT_ATTR(in_recovery, "%d\n", atomic_test_mask
+ (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status));
+ZFCP_DEFINE_PORT_ATTR(access_denied, "%d\n", atomic_test_mask
+ (ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status));
+
+/**
+ * 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_erp_unit_reopen(unit, 0);
+ zfcp_erp_wait(unit->port->adapter);
+ zfcp_unit_put(unit);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) 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 = 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;
+ }
+
+ fcp_lun = simple_strtoull(buf, &endp, 0);
+ if ((endp + 1) < (buf + count)) {
+ retval = -EINVAL;
+ 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);
+ zfcp_unit_dequeue(unit);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) 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;
+ }
+
+ zfcp_erp_modify_port_status(port, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
+ zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED);
+ zfcp_erp_wait(port->adapter);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) 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_port_common_attrs
+ * sysfs attributes that are common for all kind of fc ports.
+ */
+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,
+ &dev_attr_access_denied.attr,
+ NULL
+};
+
+static struct attribute_group zfcp_port_common_attr_group = {
+ .attrs = zfcp_port_common_attrs,
+};
+
+/**
+ * zfcp_port_no_ns_attrs
+ * sysfs attributes not to be used for nameserver ports.
+ */
+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_port_create_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_WKA) || 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;
+}
+
+/**
+ * zfcp_sysfs_port_remove_files - remove sysfs port files
+ * @dev: pointer to belonging device
+ *
+ * Remove all attributes of the sysfs representation of a port.
+ */
+void
+zfcp_sysfs_port_remove_files(struct device *dev, u32 flags)
+{
+ sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
+ if (!(flags & ZFCP_STATUS_PORT_WKA))
+ sysfs_remove_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
+}
+
+#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_unit.c b/drivers/s390/scsi/zfcp_sysfs_unit.c
new file mode 100644
index 000000000000..87c0b461831f
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_sysfs_unit.c
@@ -0,0 +1,179 @@
+/*
+ * linux/drivers/s390/scsi/zfcp_sysfs_unit.c
+ *
+ * FCP adapter driver for IBM eServer zSeries
+ *
+ * sysfs unit related routines
+ *
+ * (C) Copyright IBM Corp. 2003, 2004
+ *
+ * Authors:
+ * Martin Peschke <mpeschke@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Andreas Herrmann <aherrman@de.ibm.com>
+ * Volker Sameske <sameske@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.30 $"
+
+#include "zfcp_ext.h"
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_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)
+{
+ kfree(dev);
+}
+
+/**
+ * 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_DEFINE_UNIT_ATTR(in_recovery, "%d\n", atomic_test_mask
+ (ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status));
+ZFCP_DEFINE_UNIT_ATTR(access_denied, "%d\n", atomic_test_mask
+ (ZFCP_STATUS_COMMON_ACCESS_DENIED, &unit->status));
+ZFCP_DEFINE_UNIT_ATTR(access_shared, "%d\n", atomic_test_mask
+ (ZFCP_STATUS_UNIT_SHARED, &unit->status));
+ZFCP_DEFINE_UNIT_ATTR(access_readonly, "%d\n", atomic_test_mask
+ (ZFCP_STATUS_UNIT_READONLY, &unit->status));
+
+/**
+ * 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;
+ }
+
+ zfcp_erp_modify_unit_status(unit, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
+ zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
+ zfcp_erp_wait(unit->port->adapter);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) 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);
+
+static struct attribute *zfcp_unit_attrs[] = {
+ &dev_attr_scsi_lun.attr,
+ &dev_attr_failed.attr,
+ &dev_attr_in_recovery.attr,
+ &dev_attr_status.attr,
+ &dev_attr_access_denied.attr,
+ &dev_attr_access_shared.attr,
+ &dev_attr_access_readonly.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);
+}
+
+/**
+ * zfcp_sysfs_remove_unit_files - remove sysfs unit files
+ * @dev: pointer to belonging device
+ *
+ * Remove all attributes of the sysfs representation of a unit.
+ */
+void
+zfcp_sysfs_unit_remove_files(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &zfcp_unit_attr_group);
+}
+
+#undef ZFCP_LOG_AREA
OpenPOWER on IntegriCloud