diff options
Diffstat (limited to 'drivers/s390/scsi')
| -rw-r--r-- | drivers/s390/scsi/Makefile | 6 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_aux.c | 1998 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_ccw.c | 416 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_dbf.c | 1256 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_dbf.h | 383 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_def.h | 1193 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_erp.c | 4033 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_ext.h | 317 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_fc.c | 991 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_fc.h | 297 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_fsf.c | 5967 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_fsf.h | 268 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_qdio.c | 958 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_qdio.h | 271 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_reqlist.h | 183 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_scsi.c | 1072 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_sysfs.c | 577 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_sysfs_adapter.c | 269 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_sysfs_driver.c | 106 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_sysfs_port.c | 294 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_sysfs_unit.c | 166 | ||||
| -rw-r--r-- | drivers/s390/scsi/zfcp_unit.c | 255 |
22 files changed, 8089 insertions, 13187 deletions
diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile index d6a78f1a2f1..9259039e886 100644 --- a/drivers/s390/scsi/Makefile +++ b/drivers/s390/scsi/Makefile @@ -2,8 +2,8 @@ # 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_dbf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \ - zfcp_sysfs_unit.o zfcp_sysfs_driver.o +zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_dbf.o zfcp_erp.o \ + zfcp_fc.o zfcp_fsf.o zfcp_qdio.o zfcp_scsi.o zfcp_sysfs.o \ + zfcp_unit.o obj-$(CONFIG_ZFCP) += zfcp.o diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 874b55ed00a..8004b071a9f 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -1,22 +1,9 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Module interface and handling of zfcp data structures. * - * 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. + * Copyright IBM Corp. 2002, 2013 */ /* @@ -31,1137 +18,451 @@ * Maxim Shchetynin * Volker Sameske * Ralph Wuerthner + * Michael Loehr + * Swen Schillig + * Christof Schmitt + * Martin Petermann + * Sven Schuetz + * Steffen Maier */ -#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 int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t); -static void zfcp_sg_list_free(struct zfcp_sg_list *); -static int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *, - void __user *, size_t); -static int zfcp_sg_list_copy_to_user(void __user *, - struct zfcp_sg_list *, size_t); -static long zfcp_cfdc_dev_ioctl(struct file *, unsigned int, unsigned long); +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt -#define ZFCP_CFDC_IOC_MAGIC 0xDD -#define ZFCP_CFDC_IOC \ - _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_sense_data) - - -static const struct file_operations zfcp_cfdc_fops = { - .unlocked_ioctl = zfcp_cfdc_dev_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = zfcp_cfdc_dev_ioctl -#endif -}; - -static struct miscdevice zfcp_cfdc_misc = { - .minor = ZFCP_CFDC_DEV_MINOR, - .name = ZFCP_CFDC_DEV_NAME, - .fops = &zfcp_cfdc_fops -}; - -/*********************** KERNEL/MODULE PARAMETERS ***************************/ +#include <linux/miscdevice.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/module.h> +#include "zfcp_ext.h" +#include "zfcp_fc.h" +#include "zfcp_reqlist.h" -/* declare driver module init/cleanup functions */ -module_init(zfcp_module_init); +#define ZFCP_BUS_ID_SIZE 20 MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com"); -MODULE_DESCRIPTION - ("FCP (SCSI over Fibre Channel) HBA driver for IBM System z9 and zSeries"); +MODULE_DESCRIPTION("FCP HBA driver"); MODULE_LICENSE("GPL"); -module_param(device, charp, 0400); +static char *init_device; +module_param_named(device, init_device, charp, 0400); MODULE_PARM_DESC(device, "specify initial device"); -module_param(loglevel, uint, 0400); -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"); - -/****************************************************************/ -/************** Functions without logging ***********************/ -/****************************************************************/ - -void -_zfcp_hex_dump(char *addr, int count) +static struct kmem_cache * __init zfcp_cache_hw_align(const char *name, + unsigned long size) { - 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"); + return kmem_cache_create(name, size, roundup_pow_of_two(size), 0, NULL); } - -/****************************************************************/ -/****** Functions to handle the request ID hash table ********/ -/****************************************************************/ - -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF - -static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter) +static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) { - int idx; - - adapter->req_list = kcalloc(REQUEST_LIST_SIZE, sizeof(struct list_head), - GFP_KERNEL); - if (!adapter->req_list) - return -ENOMEM; - - for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) - INIT_LIST_HEAD(&adapter->req_list[idx]); - return 0; -} + struct ccw_device *cdev; + struct zfcp_adapter *adapter; + struct zfcp_port *port; -static void zfcp_reqlist_free(struct zfcp_adapter *adapter) -{ - kfree(adapter->req_list); -} + cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!cdev) + return; -int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) -{ - unsigned int idx; + if (ccw_device_set_online(cdev)) + goto out_ccw_device; - for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) - if (!list_empty(&adapter->req_list[idx])) - return 0; - return 1; -} + adapter = zfcp_ccw_adapter_by_cdev(cdev); + if (!adapter) + goto out_ccw_device; -#undef ZFCP_LOG_AREA + port = zfcp_get_port_by_wwpn(adapter, wwpn); + if (!port) + goto out_port; + flush_work(&port->rport_work); -/****************************************************************/ -/************** Uncategorised Functions *************************/ -/****************************************************************/ + zfcp_unit_add(port, lun); + put_device(&port->dev); -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER +out_port: + zfcp_ccw_adapter_put(adapter); +out_ccw_device: + put_device(&cdev->dev); + return; +} -/** - * zfcp_device_setup - setup function - * @str: pointer to parameter string - * - * Parse "device=..." parameter string. - */ -static int __init -zfcp_device_setup(char *devstr) +static void __init zfcp_init_device_setup(char *devstr) { - char *tmp, *str; - size_t len; + char *token; + char *str, *str_saved; + char busid[ZFCP_BUS_ID_SIZE]; + u64 wwpn, lun; - if (!devstr) - return 0; - - len = strlen(devstr) + 1; - str = kmalloc(len, GFP_KERNEL); + /* duplicate devstr and keep the original for sysfs presentation*/ + str_saved = kstrdup(devstr, GFP_KERNEL); + str = str_saved; if (!str) - goto err_out; - memcpy(str, devstr, len); + return; - tmp = strchr(str, ','); - if (!tmp) + token = strsep(&str, ","); + if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE) goto err_out; - *tmp++ = '\0'; - strncpy(zfcp_data.init_busid, str, BUS_ID_SIZE); - zfcp_data.init_busid[BUS_ID_SIZE-1] = '\0'; + strncpy(busid, token, ZFCP_BUS_ID_SIZE); - zfcp_data.init_wwpn = simple_strtoull(tmp, &tmp, 0); - if (*tmp++ != ',') - goto err_out; - if (*tmp == '\0') + token = strsep(&str, ","); + if (!token || kstrtoull(token, 0, (unsigned long long *) &wwpn)) goto err_out; - zfcp_data.init_fcp_lun = simple_strtoull(tmp, &tmp, 0); - if (*tmp != '\0') + token = strsep(&str, ","); + if (!token || kstrtoull(token, 0, (unsigned long long *) &lun)) goto err_out; - kfree(str); - return 1; - - err_out: - ZFCP_LOG_NORMAL("Parse error for device parameter string %s\n", str); - kfree(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); + kfree(str_saved); + zfcp_init_device_configure(busid, wwpn, lun); return; -} -static int calc_alignment(int size) -{ - int align = 1; - - if (!size) - return 0; - - while ((size - align) > 0) - align <<= 1; - - return align; +err_out: + kfree(str_saved); + pr_err("%s is not a valid SCSI device\n", devstr); } -static int __init -zfcp_module_init(void) +static int __init zfcp_module_init(void) { int retval = -ENOMEM; - int size, align; - - size = sizeof(struct zfcp_fsf_req_qtcb); - align = calc_alignment(size); - zfcp_data.fsf_req_qtcb_cache = - kmem_cache_create("zfcp_fsf", size, align, 0, NULL); - if (!zfcp_data.fsf_req_qtcb_cache) - goto out; - - size = sizeof(struct fsf_status_read_buffer); - align = calc_alignment(size); - zfcp_data.sr_buffer_cache = - kmem_cache_create("zfcp_sr", size, align, 0, NULL); - if (!zfcp_data.sr_buffer_cache) - goto out_sr_cache; - - size = sizeof(struct zfcp_gid_pn_data); - align = calc_alignment(size); - zfcp_data.gid_pn_cache = - kmem_cache_create("zfcp_gid", size, align, 0, NULL); - if (!zfcp_data.gid_pn_cache) - goto out_gid_cache; - - 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_data.scsi_transport_template = - fc_attach_transport(&zfcp_transport_functions); - if (!zfcp_data.scsi_transport_template) - goto out_transport; - retval = misc_register(&zfcp_cfdc_misc); - if (retval != 0) { - ZFCP_LOG_INFO("registration of misc device " - "zfcp_cfdc failed\n"); - goto out_misc; - } - - ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n", - ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor); + zfcp_fsf_qtcb_cache = zfcp_cache_hw_align("zfcp_fsf_qtcb", + sizeof(struct fsf_qtcb)); + if (!zfcp_fsf_qtcb_cache) + goto out_qtcb_cache; - /* Initialise proc semaphores */ - sema_init(&zfcp_data.config_sema, 1); + zfcp_fc_req_cache = zfcp_cache_hw_align("zfcp_fc_req", + sizeof(struct zfcp_fc_req)); + if (!zfcp_fc_req_cache) + goto out_fc_cache; - /* initialise configuration rw lock */ - rwlock_init(&zfcp_data.config_lock); + zfcp_scsi_transport_template = + fc_attach_transport(&zfcp_transport_functions); + if (!zfcp_scsi_transport_template) + goto out_transport; + scsi_transport_reserve_device(zfcp_scsi_transport_template, + sizeof(struct zfcp_scsi_dev)); - /* setup dynamic I/O */ - retval = zfcp_ccw_register(); + retval = ccw_driver_register(&zfcp_ccw_driver); if (retval) { - ZFCP_LOG_NORMAL("registration with common I/O layer failed\n"); + pr_err("The zfcp device driver could not register with " + "the common I/O layer\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: - fc_release_transport(zfcp_data.scsi_transport_template); - out_transport: - kmem_cache_destroy(zfcp_data.gid_pn_cache); - out_gid_cache: - kmem_cache_destroy(zfcp_data.sr_buffer_cache); - out_sr_cache: - kmem_cache_destroy(zfcp_data.fsf_req_qtcb_cache); - 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 long -zfcp_cfdc_dev_ioctl(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 = kzalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL); - if (sg_list == NULL) { - retval = -ENOMEM; - goto out; - } - - 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_free(fsf_req); - - if ((adapter != NULL) && (retval != -ENXIO)) - zfcp_adapter_put(adapter); - - if (sg_list != NULL) { - zfcp_sg_list_free(sg_list); - kfree(sg_list); - } - - kfree(sense_data); + if (init_device) + zfcp_init_device_setup(init_device); + return 0; +out_ccw_register: + fc_release_transport(zfcp_scsi_transport_template); +out_transport: + kmem_cache_destroy(zfcp_fc_req_cache); +out_fc_cache: + kmem_cache_destroy(zfcp_fsf_qtcb_cache); +out_qtcb_cache: return retval; } +module_init(zfcp_module_init); -/** - * 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 int -zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) +static void __exit zfcp_module_exit(void) { - 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 = kcalloc(sg_list->count, sizeof(struct scatterlist), - GFP_KERNEL); - if (sg_list->sg == NULL) { - sg_list->count = 0; - retval = -ENOMEM; - goto out; - } - sg_init_table(sg_list->sg, sg_list->count); - - for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) { - 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, min(size, PAGE_SIZE)); - size -= sg->length; - } - - out: - return retval; + ccw_driver_unregister(&zfcp_ccw_driver); + fc_release_transport(zfcp_scsi_transport_template); + kmem_cache_destroy(zfcp_fc_req_cache); + kmem_cache_destroy(zfcp_fsf_qtcb_cache); } +module_exit(zfcp_module_exit); /** - * zfcp_sg_list_free - free memory of a scatter-gather list - * @sg_list: structure describing a scatter-gather list + * 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 * - * Memory for each element in the scatter-gather list is freed. - * Finally sg_list->sg is freed itself and sg_list->count is reset. + * Returns: pointer to zfcp_port or NULL */ -static void -zfcp_sg_list_free(struct zfcp_sg_list *sg_list) +struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, + u64 wwpn) { - 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 - */ -static 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; -} - + unsigned long flags; + struct zfcp_port *port; -/** - * 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 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; + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) + if (port->wwpn == wwpn) { + if (!get_device(&port->dev)) + port = NULL; + read_unlock_irqrestore(&adapter->port_list_lock, flags); + return port; } - user_buffer += length; - size -= length; - } - - out: - return retval; + read_unlock_irqrestore(&adapter->port_list_lock, flags); + return NULL; } - -/** - * 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 int -zfcp_sg_list_copy_to_user(void __user *user_buffer, - struct zfcp_sg_list *sg_list, - size_t size) +static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) { - 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; - } + adapter->pool.erp_req = + mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); + if (!adapter->pool.erp_req) + return -ENOMEM; - out: - return retval; -} + adapter->pool.gid_pn_req = + mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); + if (!adapter->pool.gid_pn_req) + return -ENOMEM; + adapter->pool.scsi_req = + mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); + if (!adapter->pool.scsi_req) + return -ENOMEM; -#undef ZFCP_LOG_AREA + adapter->pool.scsi_abort = + mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); + if (!adapter->pool.scsi_abort) + return -ENOMEM; -/****************************************************************/ -/****** Functions for configuration/set-up of structures ********/ -/****************************************************************/ + adapter->pool.status_read_req = + mempool_create_kmalloc_pool(FSF_STATUS_READS_RECOM, + sizeof(struct zfcp_fsf_req)); + if (!adapter->pool.status_read_req) + return -ENOMEM; -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG + adapter->pool.qtcb_pool = + mempool_create_slab_pool(4, zfcp_fsf_qtcb_cache); + if (!adapter->pool.qtcb_pool) + return -ENOMEM; -/** - * 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; -} + BUILD_BUG_ON(sizeof(struct fsf_status_read_buffer) > PAGE_SIZE); + adapter->pool.sr_data = + mempool_create_page_pool(FSF_STATUS_READS_RECOM, 0); + if (!adapter->pool.sr_data) + return -ENOMEM; -/** - * 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; + adapter->pool.gid_pn = + mempool_create_slab_pool(1, zfcp_fc_req_cache); + if (!adapter->pool.gid_pn) + return -ENOMEM; - 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; + return 0; } -/** - * 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) +static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { - 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; + if (adapter->pool.erp_req) + mempool_destroy(adapter->pool.erp_req); + if (adapter->pool.scsi_req) + mempool_destroy(adapter->pool.scsi_req); + if (adapter->pool.scsi_abort) + mempool_destroy(adapter->pool.scsi_abort); + if (adapter->pool.qtcb_pool) + mempool_destroy(adapter->pool.qtcb_pool); + if (adapter->pool.status_read_req) + mempool_destroy(adapter->pool.status_read_req); + if (adapter->pool.sr_data) + mempool_destroy(adapter->pool.sr_data); + if (adapter->pool.gid_pn) + mempool_destroy(adapter->pool.gid_pn); } /** - * 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. + * zfcp_status_read_refill - refill the long running status_read_requests + * @adapter: ptr to struct zfcp_adapter for which the buffers should be refilled + * + * Returns: 0 on success, 1 otherwise + * + * if there are 16 or more status_read requests missing an adapter_reopen + * is triggered */ -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; +int zfcp_status_read_refill(struct zfcp_adapter *adapter) +{ + while (atomic_read(&adapter->stat_miss) > 0) + if (zfcp_fsf_status_read(adapter->qdio)) { + if (atomic_read(&adapter->stat_miss) >= + adapter->stat_read_buf_num) { + zfcp_erp_adapter_reopen(adapter, 0, "axsref1"); + return 1; + } break; - } - } - return found ? adapter : NULL; + } else + atomic_dec(&adapter->stat_miss); + return 0; } -/** - * 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) +static void _zfcp_status_read_scheduler(struct work_struct *work) { - struct zfcp_unit *unit; - - /* - * check that there is no unit with this FCP_LUN already in list - * and enqueue it. - * Note: Unlike for the adapter and the port, this is an error - */ - read_lock_irq(&zfcp_data.config_lock); - unit = zfcp_get_unit_by_lun(port, fcp_lun); - read_unlock_irq(&zfcp_data.config_lock); - if (unit) - return NULL; - - unit = kzalloc(sizeof (struct zfcp_unit), GFP_KERNEL); - if (!unit) - return NULL; - - /* 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); - unit->scsi_lun = scsilun_to_int((struct scsi_lun *)&unit->fcp_lun); - - write_lock_irq(&zfcp_data.config_lock); - 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; + zfcp_status_read_refill(container_of(work, struct zfcp_adapter, + stat_work)); } -void -zfcp_unit_dequeue(struct zfcp_unit *unit) +static void zfcp_print_sl(struct seq_file *m, struct service_level *sl) { - 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); + struct zfcp_adapter *adapter = + container_of(sl, struct zfcp_adapter, service_level); + + seq_printf(m, "zfcp: %s microcode level %x\n", + dev_name(&adapter->ccw_device->dev), + adapter->fsf_lic_version); } -/* - * 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) +static int zfcp_setup_adapter_work_queue(struct zfcp_adapter *adapter) { - adapter->pool.fsf_req_erp = - mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ERP_NR, - zfcp_data.fsf_req_qtcb_cache); - if (!adapter->pool.fsf_req_erp) - return -ENOMEM; + char name[TASK_COMM_LEN]; - adapter->pool.fsf_req_scsi = - mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_SCSI_NR, - zfcp_data.fsf_req_qtcb_cache); - if (!adapter->pool.fsf_req_scsi) - return -ENOMEM; - - adapter->pool.fsf_req_abort = - mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ABORT_NR, - zfcp_data.fsf_req_qtcb_cache); - if (!adapter->pool.fsf_req_abort) - return -ENOMEM; + snprintf(name, sizeof(name), "zfcp_q_%s", + dev_name(&adapter->ccw_device->dev)); + adapter->work_queue = create_singlethread_workqueue(name); - adapter->pool.fsf_req_status_read = - mempool_create_kmalloc_pool(ZFCP_POOL_STATUS_READ_NR, - sizeof(struct zfcp_fsf_req)); - if (!adapter->pool.fsf_req_status_read) - return -ENOMEM; - - adapter->pool.data_status_read = - mempool_create_slab_pool(ZFCP_POOL_STATUS_READ_NR, - zfcp_data.sr_buffer_cache); - if (!adapter->pool.data_status_read) - return -ENOMEM; - - adapter->pool.data_gid_pn = - mempool_create_slab_pool(ZFCP_POOL_DATA_GID_PN_NR, - zfcp_data.gid_pn_cache); - if (!adapter->pool.data_gid_pn) - return -ENOMEM; - - return 0; + if (adapter->work_queue) + return 0; + return -ENOMEM; } -/** - * 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) +static void zfcp_destroy_adapter_work_queue(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); -} + if (adapter->work_queue) + destroy_workqueue(adapter->work_queue); + adapter->work_queue = NULL; -static void zfcp_dummy_release(struct device *dev) -{ - return; } -/* +/** + * zfcp_adapter_enqueue - enqueue a new adapter to the list + * @ccw_device: pointer to the struct cc_device + * + * Returns: struct zfcp_adapter* * 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) +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 - */ + if (!get_device(&ccw_device->dev)) + return ERR_PTR(-ENODEV); - /* try to allocate new adapter data structure (zeroed) */ - adapter = kzalloc(sizeof (struct zfcp_adapter), GFP_KERNEL); + adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL); if (!adapter) { - ZFCP_LOG_INFO("error: allocation of base adapter " - "structure failed\n"); - goto out; + put_device(&ccw_device->dev); + return ERR_PTR(-ENOMEM); } - ccw_device->handler = NULL; + kref_init(&adapter->ref); - /* save ccw_device pointer */ + ccw_device->handler = NULL; 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; + INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); + INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); + INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update); - retval = zfcp_allocate_low_mem_buffers(adapter); - if (retval) { - ZFCP_LOG_INFO("error: pool allocation failed\n"); - goto failed_low_mem_buffers; - } + if (zfcp_qdio_setup(adapter)) + goto failed; - /* initialise reference count stuff */ - atomic_set(&adapter->refcount, 0); - init_waitqueue_head(&adapter->remove_wq); + if (zfcp_allocate_low_mem_buffers(adapter)) + goto failed; - /* initialise list of ports */ - INIT_LIST_HEAD(&adapter->port_list_head); + adapter->req_list = zfcp_reqlist_alloc(); + if (!adapter->req_list) + goto failed; - /* initialise list of ports to be removed */ - INIT_LIST_HEAD(&adapter->port_remove_lh); + if (zfcp_dbf_adapter_register(adapter)) + goto failed; - /* initialize list of fsf requests */ - spin_lock_init(&adapter->req_list_lock); - retval = zfcp_reqlist_alloc(adapter); - if (retval) { - ZFCP_LOG_INFO("request list initialization failed\n"); - goto failed_low_mem_buffers; - } + if (zfcp_setup_adapter_work_queue(adapter)) + goto failed; - /* initialize debug locks */ + if (zfcp_fc_gs_setup(adapter)) + goto failed; - spin_lock_init(&adapter->erp_dbf_lock); - spin_lock_init(&adapter->hba_dbf_lock); - spin_lock_init(&adapter->san_dbf_lock); - spin_lock_init(&adapter->scsi_dbf_lock); + rwlock_init(&adapter->port_list_lock); + INIT_LIST_HEAD(&adapter->port_list); - retval = zfcp_adapter_debug_register(adapter); - if (retval) - goto debug_register_failed; + INIT_LIST_HEAD(&adapter->events.list); + INIT_WORK(&adapter->events.work, zfcp_fc_post_event); + spin_lock_init(&adapter->events.list_lock); - /* initialize error recovery stuff */ + init_waitqueue_head(&adapter->erp_ready_wq); + init_waitqueue_head(&adapter->erp_done_wqh); - rwlock_init(&adapter->erp_lock); - sema_init(&adapter->erp_ready_sem, 0); INIT_LIST_HEAD(&adapter->erp_ready_head); INIT_LIST_HEAD(&adapter->erp_running_head); - /* initialize abort lock */ + rwlock_init(&adapter->erp_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); + if (zfcp_erp_thread_setup(adapter)) + goto failed; - /* mark adapter unusable as long as sysfs registration is not complete */ - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); + adapter->service_level.seq_print = zfcp_print_sl; 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; + if (sysfs_create_group(&ccw_device->dev.kobj, + &zfcp_sysfs_adapter_attrs)) + goto 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); + /* report size limit per scatter-gather segment */ + adapter->dma_parms.max_segment_size = ZFCP_QDIO_SBALE_LEN; + adapter->ccw_device->dev.dma_parms = &adapter->dma_parms; - zfcp_data.adapters++; + adapter->stat_read_buf_num = FSF_STATUS_READS_RECOM; - goto out; + if (!zfcp_scsi_adapter_register(adapter)) + return adapter; - generic_services_failed: - zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); - sysfs_failed: - zfcp_adapter_debug_unregister(adapter); - debug_register_failed: - dev_set_drvdata(&ccw_device->dev, NULL); - zfcp_reqlist_free(adapter); - 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; +failed: + zfcp_adapter_unregister(adapter); + return ERR_PTR(-ENOMEM); } -/* - * 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 - */ -void -zfcp_adapter_dequeue(struct zfcp_adapter *adapter) +void zfcp_adapter_unregister(struct zfcp_adapter *adapter) { - int retval = 0; - unsigned long flags; + struct ccw_device *cdev = adapter->ccw_device; - zfcp_adapter_scsi_unregister(adapter); - 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 */ - spin_lock_irqsave(&adapter->req_list_lock, flags); - retval = zfcp_reqlist_isempty(adapter); - spin_unlock_irqrestore(&adapter->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->reqs_active)); - retval = -EBUSY; - goto out; - } - - zfcp_adapter_debug_unregister(adapter); + cancel_work_sync(&adapter->scan_work); + cancel_work_sync(&adapter->stat_work); + cancel_work_sync(&adapter->ns_up_work); + zfcp_destroy_adapter_work_queue(adapter); - /* 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); + zfcp_fc_wka_ports_force_offline(adapter->gs); + zfcp_scsi_adapter_unregister(adapter); + sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs); - /* decrease number of adapters in list */ - zfcp_data.adapters--; + zfcp_erp_thread_kill(adapter); + zfcp_dbf_adapter_unregister(adapter); + zfcp_qdio_destroy(adapter->qdio); - 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); + zfcp_ccw_adapter_put(adapter); /* final put to release */ +} - 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_adapter_release - remove the adapter from the resource list + * @ref: pointer to struct kref + * locks: adapter list write lock is assumed to be held by caller + */ +void zfcp_adapter_release(struct kref *ref) +{ + struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter, + ref); + struct ccw_device *cdev = adapter->ccw_device; + dev_set_drvdata(&adapter->ccw_device->dev, NULL); + zfcp_fc_gs_destroy(adapter); zfcp_free_low_mem_buffers(adapter); - /* free memory of adapter data structure and queues */ - zfcp_qdio_free_queues(adapter); - zfcp_reqlist_free(adapter); + kfree(adapter->req_list); kfree(adapter->fc_stats); kfree(adapter->stats_reset_data); - ZFCP_LOG_TRACE("freeing adapter structure\n"); kfree(adapter); - out: - return; + put_device(&cdev->dev); +} + +static void zfcp_port_release(struct device *dev) +{ + struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); + + zfcp_ccw_adapter_put(port->adapter); + kfree(port); } /** @@ -1170,668 +471,109 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter) * @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 + * Returns: pointer to enqueued port on success, ERR_PTR on error * * 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 *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, + u32 status, u32 d_id) { struct zfcp_port *port; - int check_wwpn; - - 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; + int retval = -ENOMEM; + + kref_get(&adapter->ref); + + port = zfcp_get_port_by_wwpn(adapter, wwpn); + if (port) { + put_device(&port->dev); + retval = -EEXIST; + goto err_out; } - port = kzalloc(sizeof (struct zfcp_port), GFP_KERNEL); + port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); if (!port) - return NULL; + goto err_out; - /* initialise reference count stuff */ - atomic_set(&port->refcount, 0); - init_waitqueue_head(&port->remove_wq); + rwlock_init(&port->unit_list_lock); + INIT_LIST_HEAD(&port->unit_list); + atomic_set(&port->units, 0); - INIT_LIST_HEAD(&port->unit_list_head); - INIT_LIST_HEAD(&port->unit_remove_lh); + INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); + INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); + INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); 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)) { + port->d_id = d_id; + port->wwpn = wwpn; + port->rport_task = RPORT_NONE; + port->dev.parent = &adapter->ccw_device->dev; + port->dev.groups = zfcp_port_attr_groups; + port->dev.release = zfcp_port_release; + + if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) { kfree(port); - return NULL; + goto err_out; } + retval = -EINVAL; - if (zfcp_sysfs_port_create_files(&port->sysfs_device, status)) { - device_unregister(&port->sysfs_device); - return NULL; + if (device_register(&port->dev)) { + put_device(&port->dev); + goto err_out; } - zfcp_port_get(port); + write_lock_irq(&adapter->port_list_lock); + list_add_tail(&port->list, &adapter->port_list); + write_unlock_irq(&adapter->port_list_lock); - write_lock_irq(&zfcp_data.config_lock); - 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); + atomic_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status); 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); - if (port->rport) - fc_remote_port_delete(port->rport); - port->rport = NULL; - 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 - -static 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); - - 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: - range_mask = ZFCP_PORTS_RANGE_PORT; - break; - case ZFCP_AREA_ADDRESS: - range_mask = ZFCP_PORTS_RANGE_AREA; - break; - case ZFCP_DOMAIN_ADDRESS: - range_mask = ZFCP_PORTS_RANGE_DOMAIN; - break; - case ZFCP_FABRIC_ADDRESS: - 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); - 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); - 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) -{ - struct fsf_plogi *els_plogi; - struct zfcp_port *port; - unsigned long flags; - els_plogi = (struct fsf_plogi *) status_buffer->payload; - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &adapter->port_list_head, list) { - if (port->wwpn == (*(wwn_t *) &els_plogi->serv_param.wwpn)) - break; - } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); - - if (!port || (port->wwpn != (*(wwn_t *) &els_plogi->serv_param.wwpn))) { - ZFCP_LOG_DEBUG("ignored incoming PLOGI for nonexisting port " - "with d_id 0x%06x on adapter %s\n", - status_buffer->d_id, - zfcp_get_busid_by_adapter(adapter)); - } else { - 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; - - 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%06x on adapter %s\n", - status_buffer->d_id, - zfcp_get_busid_by_adapter(adapter)); - } else { - 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_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 = (struct fsf_status_read_buffer *) fsf_req->data; - els_type = *(u32 *) (status_buffer->payload); - adapter = fsf_req->adapter; - - zfcp_san_dbf_event_incoming_els(fsf_req); - 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 = kmem_cache_alloc(zfcp_data.gid_pn_cache, GFP_ATOMIC); - } - - if (NULL == data) - return -ENOMEM; - - memset(data, 0, sizeof(*data)); - sg_init_table(&data->req , 1); - sg_init_table(&data->resp , 1); - 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, sizeof(struct ct_iu_gid_pn_req)); - zfcp_address_to_sg(&data->ct_iu_resp, &data->resp, 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) - mempool_free(gid_pn, gid_pn->ct.pool); - else - kmem_cache_free(zfcp_data.gid_pn_cache, gid_pn); +err_out: + zfcp_ccw_adapter_put(adapter); + return ERR_PTR(retval); } /** - * zfcp_ns_gid_pn_request - initiate GID_PN nameserver request - * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed + * zfcp_sg_free_table - free memory used by scatterlists + * @sg: pointer to scatterlist + * @count: number of scatterlist which are to be free'ed + * the scatterlist are expected to reference pages always */ -int -zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action) +void zfcp_sg_free_table(struct scatterlist *sg, int count) { - 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->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) - goto failed; - - if (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%06x\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 const char * -zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table) -{ - const char *descr = "unknown reason code"; + int i; - do { - if (code == rc_table->code) { - descr = rc_table->description; + for (i = 0; i < count; i++, sg++) + if (sg) + free_page((unsigned long) sg_virt(sg)); + else 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 + * zfcp_sg_setup_table - init scatterlist and allocate, assign buffers + * @sg: pointer to struct scatterlist + * @count: number of scatterlists which should be assigned with buffers + * of size page + * + * Returns: 0 on success, -ENOMEM otherwise */ -int -zfcp_check_ct_response(struct ct_hdr *rjt) +int zfcp_sg_setup_table(struct scatterlist *sg, int count) { - if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT) - return 0; + void *addr; + int i; - 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; + sg_init_table(sg, count); + for (i = 0; i < count; i++, sg++) { + addr = (void *) get_zeroed_page(GFP_KERNEL); + if (!addr) { + zfcp_sg_free_table(sg, i); + return -ENOMEM; + } + sg_set_buf(sg, addr, PAGE_SIZE); } - - 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 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; -} - -/** - * zfcp_plogi_evaluate - evaluate PLOGI playload and copy important fields - * into zfcp_port structure - * @port: zfcp_port structure - * @plogi: plogi payload - */ -void -zfcp_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi) -{ - port->maxframe_size = plogi->serv_param.common_serv_param[7] | - ((plogi->serv_param.common_serv_param[6] & 0x0F) << 8); - if (plogi->serv_param.class1_serv_param[0] & 0x80) - port->supported_classes |= FC_COS_CLASS1; - if (plogi->serv_param.class2_serv_param[0] & 0x80) - port->supported_classes |= FC_COS_CLASS2; - if (plogi->serv_param.class3_serv_param[0] & 0x80) - port->supported_classes |= FC_COS_CLASS3; - if (plogi->serv_param.class4_serv_param[0] & 0x80) - port->supported_classes |= FC_COS_CLASS4; + return 0; } - -#undef ZFCP_LOG_AREA diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index edc5015e920..f9879d400d0 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -1,280 +1,318 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Registration and callback for the s390 common I/O layer. * - * 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. + * Copyright IBM Corp. 2002, 2010 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/module.h> #include "zfcp_ext.h" +#include "zfcp_reqlist.h" + +#define ZFCP_MODEL_PRIV 0x4 + +static DEFINE_SPINLOCK(zfcp_ccw_adapter_ref_lock); + +struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *cdev) +{ + struct zfcp_adapter *adapter; + unsigned long flags; + + spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags); + adapter = dev_get_drvdata(&cdev->dev); + if (adapter) + kref_get(&adapter->ref); + spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags); + return adapter; +} + +void zfcp_ccw_adapter_put(struct zfcp_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags); + kref_put(&adapter->ref, zfcp_adapter_release); + spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags); +} + +/** + * zfcp_ccw_activate - activate adapter and wait for it to finish + * @cdev: pointer to belonging ccw device + * @clear: Status flags to clear. + * @tag: s390dbf trace record tag + */ +static int zfcp_ccw_activate(struct ccw_device *cdev, int clear, char *tag) +{ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return 0; -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG + zfcp_erp_clear_adapter_status(adapter, clear); + zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + tag); + zfcp_erp_wait(adapter); + flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + + zfcp_ccw_adapter_put(adapter); -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 ccw_device *); + return 0; +} 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)}, + { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) }, + { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, ZFCP_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, - .shutdown = zfcp_ccw_shutdown, - .driver = { - .groups = zfcp_driver_attr_groups, - }, -}; - MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id); /** * zfcp_ccw_probe - probe function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: 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. + * This function gets called by the common i/o layer for each FCP + * device found on the current system. This is only a stub to make cio + * work: To only allocate adapter resources for devices actually used, + * the allocation is deferred to the first call to ccw_set_online. */ -static int -zfcp_ccw_probe(struct ccw_device *ccw_device) +static int zfcp_ccw_probe(struct ccw_device *cdev) { - 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; + return 0; } /** * zfcp_ccw_remove - remove function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: 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) +static void zfcp_ccw_remove(struct ccw_device *cdev) { struct zfcp_adapter *adapter; struct zfcp_port *port, *p; struct zfcp_unit *unit, *u; + LIST_HEAD(unit_remove_lh); + LIST_HEAD(port_remove_lh); - 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) { - if (atomic_test_mask(ZFCP_STATUS_UNIT_REGISTERED, - &unit->status)) - scsi_remove_device(unit->device); - zfcp_unit_dequeue(unit); - } - zfcp_port_dequeue(port); + ccw_device_set_offline(cdev); + + adapter = zfcp_ccw_adapter_by_cdev(cdev); + if (!adapter) + return; + + write_lock_irq(&adapter->port_list_lock); + list_for_each_entry_safe(port, p, &adapter->port_list, list) { + write_lock(&port->unit_list_lock); + list_for_each_entry_safe(unit, u, &port->unit_list, list) + list_move(&unit->list, &unit_remove_lh); + write_unlock(&port->unit_list_lock); + list_move(&port->list, &port_remove_lh); } - zfcp_adapter_wait(adapter); - zfcp_adapter_dequeue(adapter); + write_unlock_irq(&adapter->port_list_lock); + zfcp_ccw_adapter_put(adapter); /* put from zfcp_ccw_adapter_by_cdev */ + + list_for_each_entry_safe(unit, u, &unit_remove_lh, list) + device_unregister(&unit->dev); + + list_for_each_entry_safe(port, p, &port_remove_lh, list) + device_unregister(&port->dev); - up(&zfcp_data.config_sema); + zfcp_adapter_unregister(adapter); } /** * zfcp_ccw_set_online - set_online function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: 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). + * This function gets called by the common i/o layer and sets an + * adapter into state online. The first call will allocate all + * adapter resources that will be retained until the device is removed + * via zfcp_ccw_remove. + * + * 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. */ -static int -zfcp_ccw_set_online(struct ccw_device *ccw_device) +static int zfcp_ccw_set_online(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; - int retval; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(&ccw_device->dev); + if (!adapter) { + adapter = zfcp_adapter_enqueue(cdev); - 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; + if (IS_ERR(adapter)) { + dev_err(&cdev->dev, + "Setting up data structures for the " + "FCP adapter failed\n"); + return PTR_ERR(adapter); + } + kref_get(&adapter->ref); } - retval = zfcp_adapter_scsi_register(adapter); - if (retval) - goto out_scsi_register; - /* initialize request counter */ - BUG_ON(!zfcp_reqlist_isempty(adapter)); + BUG_ON(!zfcp_reqlist_isempty(adapter->req_list)); adapter->req_no = 0; - 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: - up(&zfcp_data.config_sema); - return retval; + zfcp_ccw_activate(cdev, 0, "ccsonl1"); + /* scan for remote ports + either at the end of any successful adapter recovery + or only after the adapter recovery for setting a device online */ + zfcp_fc_inverse_conditional_port_scan(adapter); + flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + zfcp_ccw_adapter_put(adapter); + return 0; } /** - * zfcp_ccw_set_offline - set_offline function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * zfcp_ccw_offline_sync - shut down adapter and wait for it to finish + * @cdev: pointer to belonging ccw device + * @set: Status flags to set. + * @tag: s390dbf trace record tag * * This function gets called by the common i/o layer and sets an adapter * into state offline. */ -static int -zfcp_ccw_set_offline(struct ccw_device *ccw_device) +static int zfcp_ccw_offline_sync(struct ccw_device *cdev, int set, char *tag) { - struct zfcp_adapter *adapter; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(&ccw_device->dev); - zfcp_erp_adapter_shutdown(adapter, 0); + if (!adapter) + return 0; + + zfcp_erp_set_adapter_status(adapter, set); + zfcp_erp_adapter_shutdown(adapter, 0, tag); zfcp_erp_wait(adapter); - zfcp_erp_thread_kill(adapter); - up(&zfcp_data.config_sema); + + zfcp_ccw_adapter_put(adapter); return 0; } /** - * zfcp_ccw_notify - * @ccw_device: pointer to belonging ccw device + * zfcp_ccw_set_offline - set_offline function of zfcp driver + * @cdev: pointer to belonging ccw device + * + * This function gets called by the common i/o layer and sets an adapter + * into state offline. + */ +static int zfcp_ccw_set_offline(struct ccw_device *cdev) +{ + return zfcp_ccw_offline_sync(cdev, 0, "ccsoff1"); +} + +/** + * zfcp_ccw_notify - ccw notify function + * @cdev: 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) +static int zfcp_ccw_notify(struct ccw_device *cdev, int event) { - struct zfcp_adapter *adapter; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return 1; - 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); + if (atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_SUSPENDED) { /* notification ignore */ + zfcp_dbf_hba_basic("ccnigo1", adapter); + break; + } + dev_warn(&cdev->dev, "The FCP device has been detached\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1"); 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); + dev_warn(&cdev->dev, + "The CHPID for the FCP device is offline\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2"); 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); + if (atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_SUSPENDED) { /* notification ignore */ + zfcp_dbf_hba_basic("ccniop1", adapter); + break; + } + dev_info(&cdev->dev, "The FCP device is operational again\n"); + zfcp_erp_set_adapter_status(adapter, + ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + "ccnoti4"); + break; + case CIO_BOXED: + dev_warn(&cdev->dev, "The FCP device did not respond within " + "the specified time\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5"); break; } - zfcp_erp_wait(adapter); - up(&zfcp_data.config_sema); + + zfcp_ccw_adapter_put(adapter); 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. + * zfcp_ccw_shutdown - handle shutdown from cio + * @cdev: device for adapter to shutdown. */ -int __init -zfcp_ccw_register(void) +static void zfcp_ccw_shutdown(struct ccw_device *cdev) +{ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return; + + zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1"); + zfcp_erp_wait(adapter); + zfcp_erp_thread_kill(adapter); + + zfcp_ccw_adapter_put(adapter); +} + +static int zfcp_ccw_suspend(struct ccw_device *cdev) { - return ccw_driver_register(&zfcp_ccw_driver); + zfcp_ccw_offline_sync(cdev, ZFCP_STATUS_ADAPTER_SUSPENDED, "ccsusp1"); + return 0; } -/** - * 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 ccw_device *cdev) +static int zfcp_ccw_thaw(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; + /* trace records for thaw and final shutdown during suspend + can only be found in system dump until the end of suspend + but not after resume because it's based on the memory image + right after the very first suspend (freeze) callback */ + zfcp_ccw_activate(cdev, 0, "ccthaw1"); + return 0; +} - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(&cdev->dev); - zfcp_erp_adapter_shutdown(adapter, 0); - zfcp_erp_wait(adapter); - up(&zfcp_data.config_sema); +static int zfcp_ccw_resume(struct ccw_device *cdev) +{ + zfcp_ccw_activate(cdev, ZFCP_STATUS_ADAPTER_SUSPENDED, "ccresu1"); + return 0; } -#undef ZFCP_LOG_AREA +struct ccw_driver zfcp_ccw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "zfcp", + }, + .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, + .shutdown = zfcp_ccw_shutdown, + .freeze = zfcp_ccw_suspend, + .thaw = zfcp_ccw_thaw, + .restore = zfcp_ccw_resume, +}; diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 701046c9bb3..0ca64484cfa 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -1,27 +1,21 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Debug traces for zfcp. * - * 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. + * Copyright IBM Corp. 2002, 2013 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/module.h> #include <linux/ctype.h> +#include <linux/slab.h> #include <asm/debug.h> +#include "zfcp_dbf.h" #include "zfcp_ext.h" +#include "zfcp_fc.h" static u32 dbfsize = 4; @@ -29,917 +23,510 @@ module_param(dbfsize, uint, 0400); MODULE_PARM_DESC(dbfsize, "number of pages for each debug feature area (default 4)"); -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER +static u32 dbflevel = 3; -static int -zfcp_dbf_stck(char *out_buf, const char *label, unsigned long long stck) -{ - unsigned long long sec; - struct timespec dbftime; - int len = 0; - - stck -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); - sec = stck >> 12; - do_div(sec, 1000000); - dbftime.tv_sec = sec; - stck -= (sec * 1000000) << 12; - dbftime.tv_nsec = ((stck * 1000) >> 12); - len += sprintf(out_buf + len, "%-24s%011lu:%06lu\n", - label, dbftime.tv_sec, dbftime.tv_nsec); - - return len; -} +module_param(dbflevel, uint, 0400); +MODULE_PARM_DESC(dbflevel, + "log level for each debug feature area " + "(default 3, range 0..6)"); -static int zfcp_dbf_tag(char *out_buf, const char *label, const char *tag) +static inline unsigned int zfcp_dbf_plen(unsigned int offset) { - int len = 0, i; - - len += sprintf(out_buf + len, "%-24s", label); - for (i = 0; i < ZFCP_DBF_TAG_SIZE; i++) - len += sprintf(out_buf + len, "%c", tag[i]); - len += sprintf(out_buf + len, "\n"); - - return len; + return sizeof(struct zfcp_dbf_pay) + offset - ZFCP_DBF_PAY_MAX_REC; } -static int -zfcp_dbf_view(char *out_buf, const char *label, const char *format, ...) +static inline +void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area, + u64 req_id) { - va_list arg; - int len = 0; - - len += sprintf(out_buf + len, "%-24s", label); - va_start(arg, format); - len += vsprintf(out_buf + len, format, arg); - va_end(arg); - len += sprintf(out_buf + len, "\n"); + struct zfcp_dbf_pay *pl = &dbf->pay_buf; + u16 offset = 0, rec_length; + + spin_lock(&dbf->pay_lock); + memset(pl, 0, sizeof(*pl)); + pl->fsf_req_id = req_id; + memcpy(pl->area, area, ZFCP_DBF_TAG_LEN); + + while (offset < length) { + rec_length = min((u16) ZFCP_DBF_PAY_MAX_REC, + (u16) (length - offset)); + memcpy(pl->data, data + offset, rec_length); + debug_event(dbf->pay, 1, pl, zfcp_dbf_plen(rec_length)); + + offset += rec_length; + pl->counter++; + } - return len; + spin_unlock(&dbf->pay_lock); } -static int -zfcp_dbf_view_dump(char *out_buf, const char *label, - char *buffer, int buflen, int offset, int total_size) +/** + * zfcp_dbf_hba_fsf_res - trace event for fsf responses + * @tag: tag indicating which kind of unsolicited status has been received + * @req: request for which a response was received + */ +void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req) { - int len = 0; - - if (offset == 0) - len += sprintf(out_buf + len, "%-24s ", label); + struct zfcp_dbf *dbf = req->adapter->dbf; + struct fsf_qtcb_prefix *q_pref = &req->qtcb->prefix; + struct fsf_qtcb_header *q_head = &req->qtcb->header; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; + unsigned long flags; - while (buflen--) { - if (offset > 0) { - if ((offset % 32) == 0) - len += sprintf(out_buf + len, "\n%-24c ", ' '); - else if ((offset % 4) == 0) - len += sprintf(out_buf + len, " "); - } - len += sprintf(out_buf + len, "%02x", *buffer++); - if (++offset == total_size) { - len += sprintf(out_buf + len, "\n"); - break; - } + spin_lock_irqsave(&dbf->hba_lock, flags); + memset(rec, 0, sizeof(*rec)); + + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_RES; + rec->fsf_req_id = req->req_id; + rec->fsf_req_status = req->status; + rec->fsf_cmd = req->fsf_command; + rec->fsf_seq_no = req->seq_no; + rec->u.res.req_issued = req->issued; + rec->u.res.prot_status = q_pref->prot_status; + rec->u.res.fsf_status = q_head->fsf_status; + + memcpy(rec->u.res.prot_status_qual, &q_pref->prot_status_qual, + FSF_PROT_STATUS_QUAL_SIZE); + memcpy(rec->u.res.fsf_status_qual, &q_head->fsf_status_qual, + FSF_STATUS_QUALIFIER_SIZE); + + if (req->fsf_command != FSF_QTCB_FCP_CMND) { + rec->pl_len = q_head->log_length; + zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start, + rec->pl_len, "fsf_res", req->req_id); } - if (total_size == 0) - len += sprintf(out_buf + len, "\n"); - - return len; + debug_event(dbf->hba, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->hba_lock, flags); } -static int -zfcp_dbf_view_header(debug_info_t * id, struct debug_view *view, int area, - debug_entry_t * entry, char *out_buf) +/** + * zfcp_dbf_hba_fsf_uss - trace event for an unsolicited status buffer + * @tag: tag indicating which kind of unsolicited status has been received + * @req: request providing the unsolicited status + */ +void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req) { - struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)DEBUG_DATA(entry); - int len = 0; - - if (strncmp(dump->tag, "dump", ZFCP_DBF_TAG_SIZE) != 0) { - len += zfcp_dbf_stck(out_buf + len, "timestamp", - entry->id.stck); - len += zfcp_dbf_view(out_buf + len, "cpu", "%02i", - entry->id.fields.cpuid); - } else { - len += zfcp_dbf_view_dump(out_buf + len, NULL, - dump->data, - dump->size, - dump->offset, dump->total_size); - if ((dump->offset + dump->size) == dump->total_size) - len += sprintf(out_buf + len, "\n"); - } + struct zfcp_dbf *dbf = req->adapter->dbf; + struct fsf_status_read_buffer *srb = req->data; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; + unsigned long flags; - return len; + spin_lock_irqsave(&dbf->hba_lock, flags); + memset(rec, 0, sizeof(*rec)); + + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_USS; + rec->fsf_req_id = req->req_id; + rec->fsf_req_status = req->status; + rec->fsf_cmd = req->fsf_command; + + if (!srb) + goto log; + + rec->u.uss.status_type = srb->status_type; + rec->u.uss.status_subtype = srb->status_subtype; + rec->u.uss.d_id = ntoh24(srb->d_id); + rec->u.uss.lun = srb->fcp_lun; + memcpy(&rec->u.uss.queue_designator, &srb->queue_designator, + sizeof(rec->u.uss.queue_designator)); + + /* status read buffer payload length */ + rec->pl_len = (!srb->length) ? 0 : srb->length - + offsetof(struct fsf_status_read_buffer, payload); + + if (rec->pl_len) + zfcp_dbf_pl_write(dbf, srb->payload.data, rec->pl_len, + "fsf_uss", req->req_id); +log: + debug_event(dbf->hba, 2, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->hba_lock, flags); } -void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req) +/** + * zfcp_dbf_hba_bit_err - trace event for bit error conditions + * @tag: tag indicating which kind of unsolicited status has been received + * @req: request which caused the bit_error condition + */ +void zfcp_dbf_hba_bit_err(char *tag, struct zfcp_fsf_req *req) { - struct zfcp_adapter *adapter = fsf_req->adapter; - struct fsf_qtcb *qtcb = fsf_req->qtcb; - union fsf_prot_status_qual *prot_status_qual = - &qtcb->prefix.prot_status_qual; - union fsf_status_qual *fsf_status_qual = &qtcb->header.fsf_status_qual; - struct scsi_cmnd *scsi_cmnd; - struct zfcp_port *port; - struct zfcp_unit *unit; - struct zfcp_send_els *send_els; - struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf; - struct zfcp_hba_dbf_record_response *response = &rec->type.response; - int level; + struct zfcp_dbf *dbf = req->adapter->dbf; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; + struct fsf_status_read_buffer *sr_buf = req->data; unsigned long flags; - spin_lock_irqsave(&adapter->hba_dbf_lock, flags); - memset(rec, 0, sizeof(struct zfcp_hba_dbf_record)); - strncpy(rec->tag, "resp", ZFCP_DBF_TAG_SIZE); - - if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) && - (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) { - strncpy(rec->tag2, "perr", ZFCP_DBF_TAG_SIZE); - level = 1; - } else if (qtcb->header.fsf_status != FSF_GOOD) { - strncpy(rec->tag2, "ferr", ZFCP_DBF_TAG_SIZE); - level = 1; - } else if ((fsf_req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) || - (fsf_req->fsf_command == FSF_QTCB_OPEN_LUN)) { - strncpy(rec->tag2, "open", ZFCP_DBF_TAG_SIZE); - level = 4; - } else { - strncpy(rec->tag2, "norm", ZFCP_DBF_TAG_SIZE); - level = 6; - } + spin_lock_irqsave(&dbf->hba_lock, flags); + memset(rec, 0, sizeof(*rec)); - response->fsf_command = fsf_req->fsf_command; - response->fsf_reqid = (unsigned long)fsf_req; - response->fsf_seqno = fsf_req->seq_no; - response->fsf_issued = fsf_req->issued; - response->fsf_prot_status = qtcb->prefix.prot_status; - response->fsf_status = qtcb->header.fsf_status; - memcpy(response->fsf_prot_status_qual, - prot_status_qual, FSF_PROT_STATUS_QUAL_SIZE); - memcpy(response->fsf_status_qual, - fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE); - response->fsf_req_status = fsf_req->status; - response->sbal_first = fsf_req->sbal_first; - response->sbal_curr = fsf_req->sbal_curr; - response->sbal_last = fsf_req->sbal_last; - response->pool = fsf_req->pool != NULL; - response->erp_action = (unsigned long)fsf_req->erp_action; - - switch (fsf_req->fsf_command) { - case FSF_QTCB_FCP_CMND: - if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) - break; - scsi_cmnd = (struct scsi_cmnd *)fsf_req->data; - if (scsi_cmnd != NULL) { - response->data.send_fcp.scsi_cmnd - = (unsigned long)scsi_cmnd; - response->data.send_fcp.scsi_serial - = scsi_cmnd->serial_number; - } - break; - - case FSF_QTCB_OPEN_PORT_WITH_DID: - case FSF_QTCB_CLOSE_PORT: - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - port = (struct zfcp_port *)fsf_req->data; - response->data.port.wwpn = port->wwpn; - response->data.port.d_id = port->d_id; - response->data.port.port_handle = qtcb->header.port_handle; - break; - - case FSF_QTCB_OPEN_LUN: - case FSF_QTCB_CLOSE_LUN: - unit = (struct zfcp_unit *)fsf_req->data; - port = unit->port; - response->data.unit.wwpn = port->wwpn; - response->data.unit.fcp_lun = unit->fcp_lun; - response->data.unit.port_handle = qtcb->header.port_handle; - response->data.unit.lun_handle = qtcb->header.lun_handle; - break; - - case FSF_QTCB_SEND_ELS: - send_els = (struct zfcp_send_els *)fsf_req->data; - response->data.send_els.d_id = qtcb->bottom.support.d_id; - response->data.send_els.ls_code = send_els->ls_code >> 24; - break; - - case FSF_QTCB_ABORT_FCP_CMND: - case FSF_QTCB_SEND_GENERIC: - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - case FSF_QTCB_EXCHANGE_PORT_DATA: - case FSF_QTCB_DOWNLOAD_CONTROL_FILE: - case FSF_QTCB_UPLOAD_CONTROL_FILE: - break; - } + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_BIT; + rec->fsf_req_id = req->req_id; + rec->fsf_req_status = req->status; + rec->fsf_cmd = req->fsf_command; + memcpy(&rec->u.be, &sr_buf->payload.bit_error, + sizeof(struct fsf_bit_error_payload)); - debug_event(adapter->hba_dbf, level, - rec, sizeof(struct zfcp_hba_dbf_record)); - spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags); + debug_event(dbf->hba, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->hba_lock, flags); } -void -zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter, - struct fsf_status_read_buffer *status_buffer) +/** + * zfcp_dbf_hba_def_err - trace event for deferred error messages + * @adapter: pointer to struct zfcp_adapter + * @req_id: request id which caused the deferred error message + * @scount: number of sbals incl. the signaling sbal + * @pl: array of all involved sbals + */ +void zfcp_dbf_hba_def_err(struct zfcp_adapter *adapter, u64 req_id, u16 scount, + void **pl) { - struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf; + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_pay *payload = &dbf->pay_buf; unsigned long flags; + u16 length; - spin_lock_irqsave(&adapter->hba_dbf_lock, flags); - memset(rec, 0, sizeof(struct zfcp_hba_dbf_record)); - strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE); - strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE); - - rec->type.status.failed = adapter->status_read_failed; - if (status_buffer != NULL) { - rec->type.status.status_type = status_buffer->status_type; - rec->type.status.status_subtype = status_buffer->status_subtype; - memcpy(&rec->type.status.queue_designator, - &status_buffer->queue_designator, - sizeof(struct fsf_queue_designator)); - - switch (status_buffer->status_type) { - case FSF_STATUS_READ_SENSE_DATA_AVAIL: - rec->type.status.payload_size = - ZFCP_DBF_UNSOL_PAYLOAD_SENSE_DATA_AVAIL; - break; - - case FSF_STATUS_READ_BIT_ERROR_THRESHOLD: - rec->type.status.payload_size = - ZFCP_DBF_UNSOL_PAYLOAD_BIT_ERROR_THRESHOLD; - break; - - case FSF_STATUS_READ_LINK_DOWN: - switch (status_buffer->status_subtype) { - case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK: - case FSF_STATUS_READ_SUB_FDISC_FAILED: - rec->type.status.payload_size = - sizeof(struct fsf_link_down_info); - } - break; - - case FSF_STATUS_READ_FEATURE_UPDATE_ALERT: - rec->type.status.payload_size = - ZFCP_DBF_UNSOL_PAYLOAD_FEATURE_UPDATE_ALERT; - break; - } - memcpy(&rec->type.status.payload, - &status_buffer->payload, rec->type.status.payload_size); + if (!pl) + return; + + spin_lock_irqsave(&dbf->pay_lock, flags); + memset(payload, 0, sizeof(*payload)); + + memcpy(payload->area, "def_err", 7); + payload->fsf_req_id = req_id; + payload->counter = 0; + length = min((u16)sizeof(struct qdio_buffer), + (u16)ZFCP_DBF_PAY_MAX_REC); + + while (payload->counter < scount && (char *)pl[payload->counter]) { + memcpy(payload->data, (char *)pl[payload->counter], length); + debug_event(dbf->pay, 1, payload, zfcp_dbf_plen(length)); + payload->counter++; } - debug_event(adapter->hba_dbf, 2, - rec, sizeof(struct zfcp_hba_dbf_record)); - spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags); + spin_unlock_irqrestore(&dbf->pay_lock, flags); } -void -zfcp_hba_dbf_event_qdio(struct zfcp_adapter *adapter, unsigned int status, - unsigned int qdio_error, unsigned int siga_error, - int sbal_index, int sbal_count) +/** + * zfcp_dbf_hba_basic - trace event for basic adapter events + * @adapter: pointer to struct zfcp_adapter + */ +void zfcp_dbf_hba_basic(char *tag, struct zfcp_adapter *adapter) { - struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf; + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; unsigned long flags; - spin_lock_irqsave(&adapter->hba_dbf_lock, flags); - memset(rec, 0, sizeof(struct zfcp_hba_dbf_record)); - strncpy(rec->tag, "qdio", ZFCP_DBF_TAG_SIZE); - rec->type.qdio.status = status; - rec->type.qdio.qdio_error = qdio_error; - rec->type.qdio.siga_error = siga_error; - rec->type.qdio.sbal_index = sbal_index; - rec->type.qdio.sbal_count = sbal_count; - debug_event(adapter->hba_dbf, 0, - rec, sizeof(struct zfcp_hba_dbf_record)); - spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags); -} + spin_lock_irqsave(&dbf->hba_lock, flags); + memset(rec, 0, sizeof(*rec)); -static int -zfcp_hba_dbf_view_response(char *out_buf, - struct zfcp_hba_dbf_record_response *rec) -{ - int len = 0; - - len += zfcp_dbf_view(out_buf + len, "fsf_command", "0x%08x", - rec->fsf_command); - len += zfcp_dbf_view(out_buf + len, "fsf_reqid", "0x%0Lx", - rec->fsf_reqid); - len += zfcp_dbf_view(out_buf + len, "fsf_seqno", "0x%08x", - rec->fsf_seqno); - len += zfcp_dbf_stck(out_buf + len, "fsf_issued", rec->fsf_issued); - len += zfcp_dbf_view(out_buf + len, "fsf_prot_status", "0x%08x", - rec->fsf_prot_status); - len += zfcp_dbf_view(out_buf + len, "fsf_status", "0x%08x", - rec->fsf_status); - len += zfcp_dbf_view_dump(out_buf + len, "fsf_prot_status_qual", - rec->fsf_prot_status_qual, - FSF_PROT_STATUS_QUAL_SIZE, - 0, FSF_PROT_STATUS_QUAL_SIZE); - len += zfcp_dbf_view_dump(out_buf + len, "fsf_status_qual", - rec->fsf_status_qual, - FSF_STATUS_QUALIFIER_SIZE, - 0, FSF_STATUS_QUALIFIER_SIZE); - len += zfcp_dbf_view(out_buf + len, "fsf_req_status", "0x%08x", - rec->fsf_req_status); - len += zfcp_dbf_view(out_buf + len, "sbal_first", "0x%02x", - rec->sbal_first); - len += zfcp_dbf_view(out_buf + len, "sbal_curr", "0x%02x", - rec->sbal_curr); - len += zfcp_dbf_view(out_buf + len, "sbal_last", "0x%02x", - rec->sbal_last); - len += zfcp_dbf_view(out_buf + len, "pool", "0x%02x", rec->pool); - - switch (rec->fsf_command) { - case FSF_QTCB_FCP_CMND: - if (rec->fsf_req_status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) - break; - len += zfcp_dbf_view(out_buf + len, "scsi_cmnd", "0x%0Lx", - rec->data.send_fcp.scsi_cmnd); - len += zfcp_dbf_view(out_buf + len, "scsi_serial", "0x%016Lx", - rec->data.send_fcp.scsi_serial); - break; - - case FSF_QTCB_OPEN_PORT_WITH_DID: - case FSF_QTCB_CLOSE_PORT: - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - len += zfcp_dbf_view(out_buf + len, "wwpn", "0x%016Lx", - rec->data.port.wwpn); - len += zfcp_dbf_view(out_buf + len, "d_id", "0x%06x", - rec->data.port.d_id); - len += zfcp_dbf_view(out_buf + len, "port_handle", "0x%08x", - rec->data.port.port_handle); - break; - - case FSF_QTCB_OPEN_LUN: - case FSF_QTCB_CLOSE_LUN: - len += zfcp_dbf_view(out_buf + len, "wwpn", "0x%016Lx", - rec->data.unit.wwpn); - len += zfcp_dbf_view(out_buf + len, "fcp_lun", "0x%016Lx", - rec->data.unit.fcp_lun); - len += zfcp_dbf_view(out_buf + len, "port_handle", "0x%08x", - rec->data.unit.port_handle); - len += zfcp_dbf_view(out_buf + len, "lun_handle", "0x%08x", - rec->data.unit.lun_handle); - break; - - case FSF_QTCB_SEND_ELS: - len += zfcp_dbf_view(out_buf + len, "d_id", "0x%06x", - rec->data.send_els.d_id); - len += zfcp_dbf_view(out_buf + len, "ls_code", "0x%02x", - rec->data.send_els.ls_code); - break; - - case FSF_QTCB_ABORT_FCP_CMND: - case FSF_QTCB_SEND_GENERIC: - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - case FSF_QTCB_EXCHANGE_PORT_DATA: - case FSF_QTCB_DOWNLOAD_CONTROL_FILE: - case FSF_QTCB_UPLOAD_CONTROL_FILE: - break; - } + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_BASIC; - return len; + debug_event(dbf->hba, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->hba_lock, flags); } -static int -zfcp_hba_dbf_view_status(char *out_buf, struct zfcp_hba_dbf_record_status *rec) +static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec, + struct zfcp_adapter *adapter, + struct zfcp_port *port, + struct scsi_device *sdev) { - int len = 0; - - len += zfcp_dbf_view(out_buf + len, "failed", "0x%02x", rec->failed); - len += zfcp_dbf_view(out_buf + len, "status_type", "0x%08x", - rec->status_type); - len += zfcp_dbf_view(out_buf + len, "status_subtype", "0x%08x", - rec->status_subtype); - len += zfcp_dbf_view_dump(out_buf + len, "queue_designator", - (char *)&rec->queue_designator, - sizeof(struct fsf_queue_designator), - 0, sizeof(struct fsf_queue_designator)); - len += zfcp_dbf_view_dump(out_buf + len, "payload", - (char *)&rec->payload, - rec->payload_size, 0, rec->payload_size); - - return len; + rec->adapter_status = atomic_read(&adapter->status); + if (port) { + rec->port_status = atomic_read(&port->status); + rec->wwpn = port->wwpn; + rec->d_id = port->d_id; + } + if (sdev) { + rec->lun_status = atomic_read(&sdev_to_zfcp(sdev)->status); + rec->lun = zfcp_scsi_dev_lun(sdev); + } } -static int -zfcp_hba_dbf_view_qdio(char *out_buf, struct zfcp_hba_dbf_record_qdio *rec) +/** + * zfcp_dbf_rec_trig - trace event related to triggered recovery + * @tag: identifier for event + * @adapter: adapter on which the erp_action should run + * @port: remote port involved in the erp_action + * @sdev: scsi device involved in the erp_action + * @want: wanted erp_action + * @need: required erp_action + * + * The adapter->erp_lock has to be held. + */ +void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter, + struct zfcp_port *port, struct scsi_device *sdev, + u8 want, u8 need) { - int len = 0; - - len += zfcp_dbf_view(out_buf + len, "status", "0x%08x", rec->status); - len += zfcp_dbf_view(out_buf + len, "qdio_error", "0x%08x", - rec->qdio_error); - len += zfcp_dbf_view(out_buf + len, "siga_error", "0x%08x", - rec->siga_error); - len += zfcp_dbf_view(out_buf + len, "sbal_index", "0x%02x", - rec->sbal_index); - len += zfcp_dbf_view(out_buf + len, "sbal_count", "0x%02x", - rec->sbal_count); - - return len; -} + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_rec *rec = &dbf->rec_buf; + struct list_head *entry; + unsigned long flags; -static int -zfcp_hba_dbf_view_format(debug_info_t * id, struct debug_view *view, - char *out_buf, const char *in_buf) -{ - struct zfcp_hba_dbf_record *rec = (struct zfcp_hba_dbf_record *)in_buf; - int len = 0; - - if (strncmp(rec->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) - return 0; - - len += zfcp_dbf_tag(out_buf + len, "tag", rec->tag); - if (isalpha(rec->tag2[0])) - len += zfcp_dbf_tag(out_buf + len, "tag2", rec->tag2); - if (strncmp(rec->tag, "resp", ZFCP_DBF_TAG_SIZE) == 0) - len += zfcp_hba_dbf_view_response(out_buf + len, - &rec->type.response); - else if (strncmp(rec->tag, "stat", ZFCP_DBF_TAG_SIZE) == 0) - len += zfcp_hba_dbf_view_status(out_buf + len, - &rec->type.status); - else if (strncmp(rec->tag, "qdio", ZFCP_DBF_TAG_SIZE) == 0) - len += zfcp_hba_dbf_view_qdio(out_buf + len, &rec->type.qdio); - - len += sprintf(out_buf + len, "\n"); - - return len; -} + spin_lock_irqsave(&dbf->rec_lock, flags); + memset(rec, 0, sizeof(*rec)); -static struct debug_view zfcp_hba_dbf_view = { - "structured", - NULL, - &zfcp_dbf_view_header, - &zfcp_hba_dbf_view_format, - NULL, - NULL -}; - -static void -_zfcp_san_dbf_event_common_ct(const char *tag, struct zfcp_fsf_req *fsf_req, - u32 s_id, u32 d_id, void *buffer, int buflen) -{ - struct zfcp_send_ct *send_ct = (struct zfcp_send_ct *)fsf_req->data; - struct zfcp_port *port = send_ct->port; - struct zfcp_adapter *adapter = port->adapter; - struct ct_hdr *header = (struct ct_hdr *)buffer; - struct zfcp_san_dbf_record *rec = &adapter->san_dbf_buf; - struct zfcp_san_dbf_record_ct *ct = &rec->type.ct; - unsigned long flags; + rec->id = ZFCP_DBF_REC_TRIG; + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + zfcp_dbf_set_common(rec, adapter, port, sdev); - spin_lock_irqsave(&adapter->san_dbf_lock, flags); - memset(rec, 0, sizeof(struct zfcp_san_dbf_record)); - strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); - rec->fsf_reqid = (unsigned long)fsf_req; - rec->fsf_seqno = fsf_req->seq_no; - rec->s_id = s_id; - rec->d_id = d_id; - if (strncmp(tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) { - ct->type.request.cmd_req_code = header->cmd_rsp_code; - ct->type.request.revision = header->revision; - ct->type.request.gs_type = header->gs_type; - ct->type.request.gs_subtype = header->gs_subtype; - ct->type.request.options = header->options; - ct->type.request.max_res_size = header->max_res_size; - } else if (strncmp(tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) { - ct->type.response.cmd_rsp_code = header->cmd_rsp_code; - ct->type.response.revision = header->revision; - ct->type.response.reason_code = header->reason_code; - ct->type.response.reason_code_expl = header->reason_code_expl; - ct->type.response.vendor_unique = header->vendor_unique; - } - ct->payload_size = - min(buflen - (int)sizeof(struct ct_hdr), ZFCP_DBF_CT_PAYLOAD); - memcpy(ct->payload, buffer + sizeof(struct ct_hdr), ct->payload_size); - debug_event(adapter->san_dbf, 3, - rec, sizeof(struct zfcp_san_dbf_record)); - spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); -} + list_for_each(entry, &adapter->erp_ready_head) + rec->u.trig.ready++; -void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req) -{ - struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data; - struct zfcp_port *port = ct->port; - struct zfcp_adapter *adapter = port->adapter; - - _zfcp_san_dbf_event_common_ct("octc", fsf_req, - fc_host_port_id(adapter->scsi_host), - port->d_id, zfcp_sg_to_address(ct->req), - ct->req->length); -} + list_for_each(entry, &adapter->erp_running_head) + rec->u.trig.running++; -void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req) -{ - struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data; - struct zfcp_port *port = ct->port; - struct zfcp_adapter *adapter = port->adapter; - - _zfcp_san_dbf_event_common_ct("rctc", fsf_req, port->d_id, - fc_host_port_id(adapter->scsi_host), - zfcp_sg_to_address(ct->resp), - ct->resp->length); + rec->u.trig.want = want; + rec->u.trig.need = need; + + debug_event(dbf->rec, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->rec_lock, flags); } -static void -_zfcp_san_dbf_event_common_els(const char *tag, int level, - struct zfcp_fsf_req *fsf_req, u32 s_id, - u32 d_id, u8 ls_code, void *buffer, int buflen) + +/** + * zfcp_dbf_rec_run - trace event related to running recovery + * @tag: identifier for event + * @erp: erp_action running + */ +void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) { - struct zfcp_adapter *adapter = fsf_req->adapter; - struct zfcp_san_dbf_record *rec = &adapter->san_dbf_buf; - struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec; + struct zfcp_dbf *dbf = erp->adapter->dbf; + struct zfcp_dbf_rec *rec = &dbf->rec_buf; unsigned long flags; - int offset = 0; - - spin_lock_irqsave(&adapter->san_dbf_lock, flags); - do { - memset(rec, 0, sizeof(struct zfcp_san_dbf_record)); - if (offset == 0) { - strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); - rec->fsf_reqid = (unsigned long)fsf_req; - rec->fsf_seqno = fsf_req->seq_no; - rec->s_id = s_id; - rec->d_id = d_id; - rec->type.els.ls_code = ls_code; - buflen = min(buflen, ZFCP_DBF_ELS_MAX_PAYLOAD); - rec->type.els.payload_size = buflen; - memcpy(rec->type.els.payload, - buffer, min(buflen, ZFCP_DBF_ELS_PAYLOAD)); - offset += min(buflen, ZFCP_DBF_ELS_PAYLOAD); - } else { - strncpy(dump->tag, "dump", ZFCP_DBF_TAG_SIZE); - dump->total_size = buflen; - dump->offset = offset; - dump->size = min(buflen - offset, - (int)sizeof(struct zfcp_san_dbf_record) - - (int)sizeof(struct zfcp_dbf_dump)); - memcpy(dump->data, buffer + offset, dump->size); - offset += dump->size; - } - debug_event(adapter->san_dbf, level, - rec, sizeof(struct zfcp_san_dbf_record)); - } while (offset < buflen); - spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); -} -void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *fsf_req) -{ - struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data; - - _zfcp_san_dbf_event_common_els("oels", 2, fsf_req, - fc_host_port_id(els->adapter->scsi_host), - els->d_id, - *(u8 *) zfcp_sg_to_address(els->req), - zfcp_sg_to_address(els->req), - els->req->length); -} + spin_lock_irqsave(&dbf->rec_lock, flags); + memset(rec, 0, sizeof(*rec)); -void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *fsf_req) -{ - struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data; + rec->id = ZFCP_DBF_REC_RUN; + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + zfcp_dbf_set_common(rec, erp->adapter, erp->port, erp->sdev); - _zfcp_san_dbf_event_common_els("rels", 2, fsf_req, els->d_id, - fc_host_port_id(els->adapter->scsi_host), - *(u8 *) zfcp_sg_to_address(els->req), - zfcp_sg_to_address(els->resp), - els->resp->length); -} + rec->u.run.fsf_req_id = erp->fsf_req_id; + rec->u.run.rec_status = erp->status; + rec->u.run.rec_step = erp->step; + rec->u.run.rec_action = erp->action; -void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *fsf_req) -{ - struct zfcp_adapter *adapter = fsf_req->adapter; - struct fsf_status_read_buffer *status_buffer = - (struct fsf_status_read_buffer *)fsf_req->data; - int length = (int)status_buffer->length - - (int)((void *)&status_buffer->payload - (void *)status_buffer); - - _zfcp_san_dbf_event_common_els("iels", 1, fsf_req, status_buffer->d_id, - fc_host_port_id(adapter->scsi_host), - *(u8 *) status_buffer->payload, - (void *)status_buffer->payload, length); + if (erp->sdev) + rec->u.run.rec_count = + atomic_read(&sdev_to_zfcp(erp->sdev)->erp_counter); + else if (erp->port) + rec->u.run.rec_count = atomic_read(&erp->port->erp_counter); + else + rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter); + + debug_event(dbf->rec, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->rec_lock, flags); } -static int -zfcp_san_dbf_view_format(debug_info_t * id, struct debug_view *view, - char *out_buf, const char *in_buf) +static inline +void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len, + u64 req_id, u32 d_id) { - struct zfcp_san_dbf_record *rec = (struct zfcp_san_dbf_record *)in_buf; - char *buffer = NULL; - int buflen = 0, total = 0; - int len = 0; - - if (strncmp(rec->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) - return 0; - - len += zfcp_dbf_tag(out_buf + len, "tag", rec->tag); - len += zfcp_dbf_view(out_buf + len, "fsf_reqid", "0x%0Lx", - rec->fsf_reqid); - len += zfcp_dbf_view(out_buf + len, "fsf_seqno", "0x%08x", - rec->fsf_seqno); - len += zfcp_dbf_view(out_buf + len, "s_id", "0x%06x", rec->s_id); - len += zfcp_dbf_view(out_buf + len, "d_id", "0x%06x", rec->d_id); - - if (strncmp(rec->tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) { - len += zfcp_dbf_view(out_buf + len, "cmd_req_code", "0x%04x", - rec->type.ct.type.request.cmd_req_code); - len += zfcp_dbf_view(out_buf + len, "revision", "0x%02x", - rec->type.ct.type.request.revision); - len += zfcp_dbf_view(out_buf + len, "gs_type", "0x%02x", - rec->type.ct.type.request.gs_type); - len += zfcp_dbf_view(out_buf + len, "gs_subtype", "0x%02x", - rec->type.ct.type.request.gs_subtype); - len += zfcp_dbf_view(out_buf + len, "options", "0x%02x", - rec->type.ct.type.request.options); - len += zfcp_dbf_view(out_buf + len, "max_res_size", "0x%04x", - rec->type.ct.type.request.max_res_size); - total = rec->type.ct.payload_size; - buffer = rec->type.ct.payload; - buflen = min(total, ZFCP_DBF_CT_PAYLOAD); - } else if (strncmp(rec->tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) { - len += zfcp_dbf_view(out_buf + len, "cmd_rsp_code", "0x%04x", - rec->type.ct.type.response.cmd_rsp_code); - len += zfcp_dbf_view(out_buf + len, "revision", "0x%02x", - rec->type.ct.type.response.revision); - len += zfcp_dbf_view(out_buf + len, "reason_code", "0x%02x", - rec->type.ct.type.response.reason_code); - len += - zfcp_dbf_view(out_buf + len, "reason_code_expl", "0x%02x", - rec->type.ct.type.response.reason_code_expl); - len += - zfcp_dbf_view(out_buf + len, "vendor_unique", "0x%02x", - rec->type.ct.type.response.vendor_unique); - total = rec->type.ct.payload_size; - buffer = rec->type.ct.payload; - buflen = min(total, ZFCP_DBF_CT_PAYLOAD); - } else if (strncmp(rec->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 || - strncmp(rec->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 || - strncmp(rec->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) { - len += zfcp_dbf_view(out_buf + len, "ls_code", "0x%02x", - rec->type.els.ls_code); - total = rec->type.els.payload_size; - buffer = rec->type.els.payload; - buflen = min(total, ZFCP_DBF_ELS_PAYLOAD); - } + struct zfcp_dbf_san *rec = &dbf->san_buf; + u16 rec_len; + unsigned long flags; - len += zfcp_dbf_view_dump(out_buf + len, "payload", - buffer, buflen, 0, total); + spin_lock_irqsave(&dbf->san_lock, flags); + memset(rec, 0, sizeof(*rec)); - if (buflen == total) - len += sprintf(out_buf + len, "\n"); + rec->id = id; + rec->fsf_req_id = req_id; + rec->d_id = d_id; + rec_len = min(len, (u16)ZFCP_DBF_SAN_MAX_PAYLOAD); + memcpy(rec->payload, data, rec_len); + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); - return len; + debug_event(dbf->san, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->san_lock, flags); } -static struct debug_view zfcp_san_dbf_view = { - "structured", - NULL, - &zfcp_dbf_view_header, - &zfcp_san_dbf_view_format, - NULL, - NULL -}; - -static void -_zfcp_scsi_dbf_event_common(const char *tag, const char *tag2, int level, - struct zfcp_adapter *adapter, - struct scsi_cmnd *scsi_cmnd, - struct zfcp_fsf_req *fsf_req, - unsigned long old_req_id) +/** + * zfcp_dbf_san_req - trace event for issued SAN request + * @tag: identifier for event + * @fsf_req: request containing issued CT data + * d_id: destination ID + */ +void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id) { - struct zfcp_scsi_dbf_record *rec = &adapter->scsi_dbf_buf; - struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec; - unsigned long flags; - struct fcp_rsp_iu *fcp_rsp; - char *fcp_rsp_info = NULL, *fcp_sns_info = NULL; - int offset = 0, buflen = 0; - - spin_lock_irqsave(&adapter->scsi_dbf_lock, flags); - do { - memset(rec, 0, sizeof(struct zfcp_scsi_dbf_record)); - if (offset == 0) { - strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); - strncpy(rec->tag2, tag2, ZFCP_DBF_TAG_SIZE); - if (scsi_cmnd != NULL) { - if (scsi_cmnd->device) { - rec->scsi_id = scsi_cmnd->device->id; - rec->scsi_lun = scsi_cmnd->device->lun; - } - rec->scsi_result = scsi_cmnd->result; - rec->scsi_cmnd = (unsigned long)scsi_cmnd; - rec->scsi_serial = scsi_cmnd->serial_number; - memcpy(rec->scsi_opcode, &scsi_cmnd->cmnd, - min((int)scsi_cmnd->cmd_len, - ZFCP_DBF_SCSI_OPCODE)); - rec->scsi_retries = scsi_cmnd->retries; - rec->scsi_allowed = scsi_cmnd->allowed; - } - if (fsf_req != NULL) { - fcp_rsp = (struct fcp_rsp_iu *) - &(fsf_req->qtcb->bottom.io.fcp_rsp); - fcp_rsp_info = - zfcp_get_fcp_rsp_info_ptr(fcp_rsp); - fcp_sns_info = - zfcp_get_fcp_sns_info_ptr(fcp_rsp); - - rec->type.fcp.rsp_validity = - fcp_rsp->validity.value; - rec->type.fcp.rsp_scsi_status = - fcp_rsp->scsi_status; - rec->type.fcp.rsp_resid = fcp_rsp->fcp_resid; - if (fcp_rsp->validity.bits.fcp_rsp_len_valid) - rec->type.fcp.rsp_code = - *(fcp_rsp_info + 3); - if (fcp_rsp->validity.bits.fcp_sns_len_valid) { - buflen = min((int)fcp_rsp->fcp_sns_len, - ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO); - rec->type.fcp.sns_info_len = buflen; - memcpy(rec->type.fcp.sns_info, - fcp_sns_info, - min(buflen, - ZFCP_DBF_SCSI_FCP_SNS_INFO)); - offset += min(buflen, - ZFCP_DBF_SCSI_FCP_SNS_INFO); - } - - rec->fsf_reqid = (unsigned long)fsf_req; - rec->fsf_seqno = fsf_req->seq_no; - rec->fsf_issued = fsf_req->issued; - } - rec->type.old_fsf_reqid = old_req_id; - } else { - strncpy(dump->tag, "dump", ZFCP_DBF_TAG_SIZE); - dump->total_size = buflen; - dump->offset = offset; - dump->size = min(buflen - offset, - (int)sizeof(struct - zfcp_scsi_dbf_record) - - (int)sizeof(struct zfcp_dbf_dump)); - memcpy(dump->data, fcp_sns_info + offset, dump->size); - offset += dump->size; - } - debug_event(adapter->scsi_dbf, level, - rec, sizeof(struct zfcp_scsi_dbf_record)); - } while (offset < buflen); - spin_unlock_irqrestore(&adapter->scsi_dbf_lock, flags); + struct zfcp_dbf *dbf = fsf->adapter->dbf; + struct zfcp_fsf_ct_els *ct_els = fsf->data; + u16 length; + + length = (u16)(ct_els->req->length + FC_CT_HDR_LEN); + zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length, + fsf->req_id, d_id); } -void -zfcp_scsi_dbf_event_result(const char *tag, int level, - struct zfcp_adapter *adapter, - struct scsi_cmnd *scsi_cmnd, - struct zfcp_fsf_req *fsf_req) +/** + * zfcp_dbf_san_res - trace event for received SAN request + * @tag: identifier for event + * @fsf_req: request containing issued CT data + */ +void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf) { - _zfcp_scsi_dbf_event_common("rslt", tag, level, - adapter, scsi_cmnd, fsf_req, 0); + struct zfcp_dbf *dbf = fsf->adapter->dbf; + struct zfcp_fsf_ct_els *ct_els = fsf->data; + u16 length; + + length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN); + zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length, + fsf->req_id, 0); } -void -zfcp_scsi_dbf_event_abort(const char *tag, struct zfcp_adapter *adapter, - struct scsi_cmnd *scsi_cmnd, - struct zfcp_fsf_req *new_fsf_req, - unsigned long old_req_id) +/** + * zfcp_dbf_san_in_els - trace event for incoming ELS + * @tag: identifier for event + * @fsf_req: request containing issued CT data + */ +void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf) { - _zfcp_scsi_dbf_event_common("abrt", tag, 1, - adapter, scsi_cmnd, new_fsf_req, old_req_id); + struct zfcp_dbf *dbf = fsf->adapter->dbf; + struct fsf_status_read_buffer *srb = + (struct fsf_status_read_buffer *) fsf->data; + u16 length; + + length = (u16)(srb->length - + offsetof(struct fsf_status_read_buffer, payload)); + zfcp_dbf_san(tag, dbf, srb->payload.data, ZFCP_DBF_SAN_ELS, length, + fsf->req_id, ntoh24(srb->d_id)); } -void -zfcp_scsi_dbf_event_devreset(const char *tag, u8 flag, struct zfcp_unit *unit, - struct scsi_cmnd *scsi_cmnd) +/** + * zfcp_dbf_scsi - trace event for scsi commands + * @tag: identifier for event + * @sc: pointer to struct scsi_cmnd + * @fsf: pointer to struct zfcp_fsf_req + */ +void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) { - struct zfcp_adapter *adapter = unit->port->adapter; + struct zfcp_adapter *adapter = + (struct zfcp_adapter *) sc->device->host->hostdata[0]; + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_scsi *rec = &dbf->scsi_buf; + struct fcp_resp_with_ext *fcp_rsp; + struct fcp_resp_rsp_info *fcp_rsp_info; + unsigned long flags; + + spin_lock_irqsave(&dbf->scsi_lock, flags); + memset(rec, 0, sizeof(*rec)); + + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_SCSI_CMND; + rec->scsi_result = sc->result; + rec->scsi_retries = sc->retries; + rec->scsi_allowed = sc->allowed; + rec->scsi_id = sc->device->id; + rec->scsi_lun = sc->device->lun; + rec->host_scribble = (unsigned long)sc->host_scribble; + + memcpy(rec->scsi_opcode, sc->cmnd, + min((int)sc->cmd_len, ZFCP_DBF_SCSI_OPCODE)); + + if (fsf) { + rec->fsf_req_id = fsf->req_id; + fcp_rsp = (struct fcp_resp_with_ext *) + &(fsf->qtcb->bottom.io.fcp_rsp); + memcpy(&rec->fcp_rsp, fcp_rsp, FCP_RESP_WITH_EXT); + if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) { + fcp_rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; + rec->fcp_rsp_info = fcp_rsp_info->rsp_code; + } + if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) { + rec->pl_len = min((u16)SCSI_SENSE_BUFFERSIZE, + (u16)ZFCP_DBF_PAY_MAX_REC); + zfcp_dbf_pl_write(dbf, sc->sense_buffer, rec->pl_len, + "fcp_sns", fsf->req_id); + } + } - _zfcp_scsi_dbf_event_common(flag == FCP_TARGET_RESET ? "trst" : "lrst", - tag, 1, adapter, scsi_cmnd, NULL, 0); + debug_event(dbf->scsi, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->scsi_lock, flags); } -static int -zfcp_scsi_dbf_view_format(debug_info_t * id, struct debug_view *view, - char *out_buf, const char *in_buf) +static debug_info_t *zfcp_dbf_reg(const char *name, int size, int rec_size) { - struct zfcp_scsi_dbf_record *rec = - (struct zfcp_scsi_dbf_record *)in_buf; - int len = 0; - - if (strncmp(rec->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) - return 0; - - len += zfcp_dbf_tag(out_buf + len, "tag", rec->tag); - len += zfcp_dbf_tag(out_buf + len, "tag2", rec->tag2); - len += zfcp_dbf_view(out_buf + len, "scsi_id", "0x%08x", rec->scsi_id); - len += zfcp_dbf_view(out_buf + len, "scsi_lun", "0x%08x", - rec->scsi_lun); - len += zfcp_dbf_view(out_buf + len, "scsi_result", "0x%08x", - rec->scsi_result); - len += zfcp_dbf_view(out_buf + len, "scsi_cmnd", "0x%0Lx", - rec->scsi_cmnd); - len += zfcp_dbf_view(out_buf + len, "scsi_serial", "0x%016Lx", - rec->scsi_serial); - len += zfcp_dbf_view_dump(out_buf + len, "scsi_opcode", - rec->scsi_opcode, - ZFCP_DBF_SCSI_OPCODE, - 0, ZFCP_DBF_SCSI_OPCODE); - len += zfcp_dbf_view(out_buf + len, "scsi_retries", "0x%02x", - rec->scsi_retries); - len += zfcp_dbf_view(out_buf + len, "scsi_allowed", "0x%02x", - rec->scsi_allowed); - if (strncmp(rec->tag, "abrt", ZFCP_DBF_TAG_SIZE) == 0) { - len += zfcp_dbf_view(out_buf + len, "old_fsf_reqid", "0x%0Lx", - rec->type.old_fsf_reqid); - } - len += zfcp_dbf_view(out_buf + len, "fsf_reqid", "0x%0Lx", - rec->fsf_reqid); - len += zfcp_dbf_view(out_buf + len, "fsf_seqno", "0x%08x", - rec->fsf_seqno); - len += zfcp_dbf_stck(out_buf + len, "fsf_issued", rec->fsf_issued); - if (strncmp(rec->tag, "rslt", ZFCP_DBF_TAG_SIZE) == 0) { - len += - zfcp_dbf_view(out_buf + len, "fcp_rsp_validity", "0x%02x", - rec->type.fcp.rsp_validity); - len += - zfcp_dbf_view(out_buf + len, "fcp_rsp_scsi_status", - "0x%02x", rec->type.fcp.rsp_scsi_status); - len += - zfcp_dbf_view(out_buf + len, "fcp_rsp_resid", "0x%08x", - rec->type.fcp.rsp_resid); - len += - zfcp_dbf_view(out_buf + len, "fcp_rsp_code", "0x%08x", - rec->type.fcp.rsp_code); - len += - zfcp_dbf_view(out_buf + len, "fcp_sns_info_len", "0x%08x", - rec->type.fcp.sns_info_len); - len += - zfcp_dbf_view_dump(out_buf + len, "fcp_sns_info", - rec->type.fcp.sns_info, - min((int)rec->type.fcp.sns_info_len, - ZFCP_DBF_SCSI_FCP_SNS_INFO), 0, - rec->type.fcp.sns_info_len); - } + struct debug_info *d; + + d = debug_register(name, size, 1, rec_size); + if (!d) + return NULL; - len += sprintf(out_buf + len, "\n"); + debug_register_view(d, &debug_hex_ascii_view); + debug_set_level(d, dbflevel); - return len; + return d; } -static struct debug_view zfcp_scsi_dbf_view = { - "structured", - NULL, - &zfcp_dbf_view_header, - &zfcp_scsi_dbf_view_format, - NULL, - NULL -}; +static void zfcp_dbf_unregister(struct zfcp_dbf *dbf) +{ + if (!dbf) + return; + + debug_unregister(dbf->scsi); + debug_unregister(dbf->san); + debug_unregister(dbf->hba); + debug_unregister(dbf->pay); + debug_unregister(dbf->rec); + kfree(dbf); +} /** * 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) +int zfcp_dbf_adapter_register(struct zfcp_adapter *adapter) { - char dbf_name[DEBUG_MAX_NAME_LEN]; + char name[DEBUG_MAX_NAME_LEN]; + struct zfcp_dbf *dbf; + + dbf = kzalloc(sizeof(struct zfcp_dbf), GFP_KERNEL); + if (!dbf) + return -ENOMEM; + + spin_lock_init(&dbf->pay_lock); + spin_lock_init(&dbf->hba_lock); + spin_lock_init(&dbf->san_lock); + spin_lock_init(&dbf->scsi_lock); + spin_lock_init(&dbf->rec_lock); /* debug feature area which records recovery activity */ - sprintf(dbf_name, "zfcp_%s_erp", zfcp_get_busid_by_adapter(adapter)); - adapter->erp_dbf = debug_register(dbf_name, dbfsize, 2, - sizeof(struct zfcp_erp_dbf_record)); - if (!adapter->erp_dbf) - goto failed; - debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->erp_dbf, 3); + sprintf(name, "zfcp_%s_rec", dev_name(&adapter->ccw_device->dev)); + dbf->rec = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_rec)); + if (!dbf->rec) + goto err_out; /* debug feature area which records HBA (FSF and QDIO) conditions */ - sprintf(dbf_name, "zfcp_%s_hba", zfcp_get_busid_by_adapter(adapter)); - adapter->hba_dbf = debug_register(dbf_name, dbfsize, 1, - sizeof(struct zfcp_hba_dbf_record)); - if (!adapter->hba_dbf) - goto failed; - debug_register_view(adapter->hba_dbf, &debug_hex_ascii_view); - debug_register_view(adapter->hba_dbf, &zfcp_hba_dbf_view); - debug_set_level(adapter->hba_dbf, 3); + sprintf(name, "zfcp_%s_hba", dev_name(&adapter->ccw_device->dev)); + dbf->hba = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_hba)); + if (!dbf->hba) + goto err_out; + + /* debug feature area which records payload info */ + sprintf(name, "zfcp_%s_pay", dev_name(&adapter->ccw_device->dev)); + dbf->pay = zfcp_dbf_reg(name, dbfsize * 2, sizeof(struct zfcp_dbf_pay)); + if (!dbf->pay) + goto err_out; /* debug feature area which records SAN command failures and recovery */ - sprintf(dbf_name, "zfcp_%s_san", zfcp_get_busid_by_adapter(adapter)); - adapter->san_dbf = debug_register(dbf_name, dbfsize, 1, - sizeof(struct zfcp_san_dbf_record)); - if (!adapter->san_dbf) - goto failed; - debug_register_view(adapter->san_dbf, &debug_hex_ascii_view); - debug_register_view(adapter->san_dbf, &zfcp_san_dbf_view); - debug_set_level(adapter->san_dbf, 6); + sprintf(name, "zfcp_%s_san", dev_name(&adapter->ccw_device->dev)); + dbf->san = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_san)); + if (!dbf->san) + goto err_out; /* debug feature area which records SCSI command failures and recovery */ - sprintf(dbf_name, "zfcp_%s_scsi", zfcp_get_busid_by_adapter(adapter)); - adapter->scsi_dbf = debug_register(dbf_name, dbfsize, 1, - sizeof(struct zfcp_scsi_dbf_record)); - if (!adapter->scsi_dbf) - goto failed; - debug_register_view(adapter->scsi_dbf, &debug_hex_ascii_view); - debug_register_view(adapter->scsi_dbf, &zfcp_scsi_dbf_view); - debug_set_level(adapter->scsi_dbf, 3); - - return 0; + sprintf(name, "zfcp_%s_scsi", dev_name(&adapter->ccw_device->dev)); + dbf->scsi = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_scsi)); + if (!dbf->scsi) + goto err_out; - failed: - zfcp_adapter_debug_unregister(adapter); + adapter->dbf = dbf; + return 0; +err_out: + zfcp_dbf_unregister(dbf); return -ENOMEM; } @@ -947,16 +534,11 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter) * 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) +void zfcp_dbf_adapter_unregister(struct zfcp_adapter *adapter) { - debug_unregister(adapter->scsi_dbf); - debug_unregister(adapter->san_dbf); - debug_unregister(adapter->hba_dbf); - debug_unregister(adapter->erp_dbf); - adapter->scsi_dbf = NULL; - adapter->san_dbf = NULL; - adapter->hba_dbf = NULL; - adapter->erp_dbf = NULL; + struct zfcp_dbf *dbf = adapter->dbf; + + adapter->dbf = NULL; + zfcp_dbf_unregister(dbf); } -#undef ZFCP_LOG_AREA diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h new file mode 100644 index 00000000000..0be3d48681a --- /dev/null +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -0,0 +1,383 @@ +/* + * zfcp device driver + * debug feature declarations + * + * Copyright IBM Corp. 2008, 2010 + */ + +#ifndef ZFCP_DBF_H +#define ZFCP_DBF_H + +#include <scsi/fc/fc_fcp.h> +#include "zfcp_ext.h" +#include "zfcp_fsf.h" +#include "zfcp_def.h" + +#define ZFCP_DBF_TAG_LEN 7 + +#define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull + +/** + * struct zfcp_dbf_rec_trigger - trace record for triggered recovery action + * @ready: number of ready recovery actions + * @running: number of running recovery actions + * @want: wanted recovery action + * @need: needed recovery action + */ +struct zfcp_dbf_rec_trigger { + u32 ready; + u32 running; + u8 want; + u8 need; +} __packed; + +/** + * struct zfcp_dbf_rec_running - trace record for running recovery + * @fsf_req_id: request id for fsf requests + * @rec_status: status of the fsf request + * @rec_step: current step of the recovery action + * rec_count: recovery counter + */ +struct zfcp_dbf_rec_running { + u64 fsf_req_id; + u32 rec_status; + u16 rec_step; + u8 rec_action; + u8 rec_count; +} __packed; + +/** + * enum zfcp_dbf_rec_id - recovery trace record id + * @ZFCP_DBF_REC_TRIG: triggered recovery identifier + * @ZFCP_DBF_REC_RUN: running recovery identifier + */ +enum zfcp_dbf_rec_id { + ZFCP_DBF_REC_TRIG = 1, + ZFCP_DBF_REC_RUN = 2, +}; + +/** + * struct zfcp_dbf_rec - trace record for error recovery actions + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @lun: logical unit number + * @wwpn: word wide port number + * @d_id: destination ID + * @adapter_status: current status of the adapter + * @port_status: current status of the port + * @lun_status: current status of the lun + * @u.trig: structure zfcp_dbf_rec_trigger + * @u.run: structure zfcp_dbf_rec_running + */ +struct zfcp_dbf_rec { + u8 id; + char tag[ZFCP_DBF_TAG_LEN]; + u64 lun; + u64 wwpn; + u32 d_id; + u32 adapter_status; + u32 port_status; + u32 lun_status; + union { + struct zfcp_dbf_rec_trigger trig; + struct zfcp_dbf_rec_running run; + } u; +} __packed; + +/** + * enum zfcp_dbf_san_id - SAN trace record identifier + * @ZFCP_DBF_SAN_REQ: request trace record id + * @ZFCP_DBF_SAN_RES: response trace record id + * @ZFCP_DBF_SAN_ELS: extended link service record id + */ +enum zfcp_dbf_san_id { + ZFCP_DBF_SAN_REQ = 1, + ZFCP_DBF_SAN_RES = 2, + ZFCP_DBF_SAN_ELS = 3, +}; + +/** struct zfcp_dbf_san - trace record for SAN requests and responses + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @fsf_req_id: request id for fsf requests + * @payload: unformatted information related to request/response + * @d_id: destination id + */ +struct zfcp_dbf_san { + u8 id; + char tag[ZFCP_DBF_TAG_LEN]; + u64 fsf_req_id; + u32 d_id; +#define ZFCP_DBF_SAN_MAX_PAYLOAD (FC_CT_HDR_LEN + 32) + char payload[ZFCP_DBF_SAN_MAX_PAYLOAD]; +} __packed; + +/** + * struct zfcp_dbf_hba_res - trace record for hba responses + * @req_issued: timestamp when request was issued + * @prot_status: protocol status + * @prot_status_qual: protocol status qualifier + * @fsf_status: fsf status + * @fsf_status_qual: fsf status qualifier + */ +struct zfcp_dbf_hba_res { + u64 req_issued; + u32 prot_status; + u8 prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE]; + u32 fsf_status; + u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; +} __packed; + +/** + * struct zfcp_dbf_hba_uss - trace record for unsolicited status + * @status_type: type of unsolicited status + * @status_subtype: subtype of unsolicited status + * @d_id: destination ID + * @lun: logical unit number + * @queue_designator: queue designator + */ +struct zfcp_dbf_hba_uss { + u32 status_type; + u32 status_subtype; + u32 d_id; + u64 lun; + u64 queue_designator; +} __packed; + +/** + * enum zfcp_dbf_hba_id - HBA trace record identifier + * @ZFCP_DBF_HBA_RES: response trace record + * @ZFCP_DBF_HBA_USS: unsolicited status trace record + * @ZFCP_DBF_HBA_BIT: bit error trace record + */ +enum zfcp_dbf_hba_id { + ZFCP_DBF_HBA_RES = 1, + ZFCP_DBF_HBA_USS = 2, + ZFCP_DBF_HBA_BIT = 3, + ZFCP_DBF_HBA_BASIC = 4, +}; + +/** + * struct zfcp_dbf_hba - common trace record for HBA records + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @fsf_req_id: request id for fsf requests + * @fsf_req_status: status of fsf request + * @fsf_cmd: fsf command + * @fsf_seq_no: fsf sequence number + * @pl_len: length of payload stored as zfcp_dbf_pay + * @u: record type specific data + */ +struct zfcp_dbf_hba { + u8 id; + char tag[ZFCP_DBF_TAG_LEN]; + u64 fsf_req_id; + u32 fsf_req_status; + u32 fsf_cmd; + u32 fsf_seq_no; + u16 pl_len; + union { + struct zfcp_dbf_hba_res res; + struct zfcp_dbf_hba_uss uss; + struct fsf_bit_error_payload be; + } u; +} __packed; + +/** + * enum zfcp_dbf_scsi_id - scsi trace record identifier + * @ZFCP_DBF_SCSI_CMND: scsi command trace record + */ +enum zfcp_dbf_scsi_id { + ZFCP_DBF_SCSI_CMND = 1, +}; + +/** + * struct zfcp_dbf_scsi - common trace record for SCSI records + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @scsi_id: scsi device id + * @scsi_lun: scsi device logical unit number + * @scsi_result: scsi result + * @scsi_retries: current retry number of scsi request + * @scsi_allowed: allowed retries + * @fcp_rsp_info: FCP response info + * @scsi_opcode: scsi opcode + * @fsf_req_id: request id of fsf request + * @host_scribble: LLD specific data attached to SCSI request + * @pl_len: length of paload stored as zfcp_dbf_pay + * @fsf_rsp: response for fsf request + */ +struct zfcp_dbf_scsi { + u8 id; + char tag[ZFCP_DBF_TAG_LEN]; + u32 scsi_id; + u32 scsi_lun; + u32 scsi_result; + u8 scsi_retries; + u8 scsi_allowed; + u8 fcp_rsp_info; +#define ZFCP_DBF_SCSI_OPCODE 16 + u8 scsi_opcode[ZFCP_DBF_SCSI_OPCODE]; + u64 fsf_req_id; + u64 host_scribble; + u16 pl_len; + struct fcp_resp_with_ext fcp_rsp; +} __packed; + +/** + * struct zfcp_dbf_pay - trace record for unformatted payload information + * @area: area this record is originated from + * @counter: ascending record number + * @fsf_req_id: request id of fsf request + * @data: unformatted data + */ +struct zfcp_dbf_pay { + u8 counter; + char area[ZFCP_DBF_TAG_LEN]; + u64 fsf_req_id; +#define ZFCP_DBF_PAY_MAX_REC 0x100 + char data[ZFCP_DBF_PAY_MAX_REC]; +} __packed; + +/** + * struct zfcp_dbf - main dbf trace structure + * @pay: reference to payload trace area + * @rec: reference to recovery trace area + * @hba: reference to hba trace area + * @san: reference to san trace area + * @scsi: reference to scsi trace area + * @pay_lock: lock protecting payload trace buffer + * @rec_lock: lock protecting recovery trace buffer + * @hba_lock: lock protecting hba trace buffer + * @san_lock: lock protecting san trace buffer + * @scsi_lock: lock protecting scsi trace buffer + * @pay_buf: pre-allocated buffer for payload + * @rec_buf: pre-allocated buffer for recovery + * @hba_buf: pre-allocated buffer for hba + * @san_buf: pre-allocated buffer for san + * @scsi_buf: pre-allocated buffer for scsi + */ +struct zfcp_dbf { + debug_info_t *pay; + debug_info_t *rec; + debug_info_t *hba; + debug_info_t *san; + debug_info_t *scsi; + spinlock_t pay_lock; + spinlock_t rec_lock; + spinlock_t hba_lock; + spinlock_t san_lock; + spinlock_t scsi_lock; + struct zfcp_dbf_pay pay_buf; + struct zfcp_dbf_rec rec_buf; + struct zfcp_dbf_hba hba_buf; + struct zfcp_dbf_san san_buf; + struct zfcp_dbf_scsi scsi_buf; +}; + +static inline +void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req) +{ + if (debug_level_enabled(req->adapter->dbf->hba, level)) + zfcp_dbf_hba_fsf_res(tag, req); +} + +/** + * zfcp_dbf_hba_fsf_response - trace event for request completion + * @req: request that has been completed + */ +static inline +void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req) +{ + struct fsf_qtcb *qtcb = req->qtcb; + + if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) && + (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) { + zfcp_dbf_hba_fsf_resp("fs_perr", 1, req); + + } else if (qtcb->header.fsf_status != FSF_GOOD) { + zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req); + + } else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) || + (req->fsf_command == FSF_QTCB_OPEN_LUN)) { + zfcp_dbf_hba_fsf_resp("fs_open", 4, req); + + } else if (qtcb->header.log_length) { + zfcp_dbf_hba_fsf_resp("fs_qtcb", 5, req); + + } else { + zfcp_dbf_hba_fsf_resp("fs_norm", 6, req); + } +} + +static inline +void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd, + struct zfcp_fsf_req *req) +{ + struct zfcp_adapter *adapter = (struct zfcp_adapter *) + scmd->device->host->hostdata[0]; + + if (debug_level_enabled(adapter->dbf->scsi, level)) + zfcp_dbf_scsi(tag, scmd, req); +} + +/** + * zfcp_dbf_scsi_result - trace event for SCSI command completion + * @scmd: SCSI command pointer + * @req: FSF request used to issue SCSI command + */ +static inline +void zfcp_dbf_scsi_result(struct scsi_cmnd *scmd, struct zfcp_fsf_req *req) +{ + if (scmd->result != 0) + _zfcp_dbf_scsi("rsl_err", 3, scmd, req); + else if (scmd->retries > 0) + _zfcp_dbf_scsi("rsl_ret", 4, scmd, req); + else + _zfcp_dbf_scsi("rsl_nor", 6, scmd, req); +} + +/** + * zfcp_dbf_scsi_fail_send - trace event for failure to send SCSI command + * @scmd: SCSI command pointer + */ +static inline +void zfcp_dbf_scsi_fail_send(struct scsi_cmnd *scmd) +{ + _zfcp_dbf_scsi("rsl_fai", 4, scmd, NULL); +} + +/** + * zfcp_dbf_scsi_abort - trace event for SCSI command abort + * @tag: tag indicating success or failure of abort operation + * @scmd: SCSI command to be aborted + * @fsf_req: request containing abort (might be NULL) + */ +static inline +void zfcp_dbf_scsi_abort(char *tag, struct scsi_cmnd *scmd, + struct zfcp_fsf_req *fsf_req) +{ + _zfcp_dbf_scsi(tag, 1, scmd, fsf_req); +} + +/** + * zfcp_dbf_scsi_devreset - trace event for Logical Unit or Target Reset + * @tag: tag indicating success or failure of reset operation + * @scmnd: SCSI command which caused this error recovery + * @flag: indicates type of reset (Target Reset, Logical Unit Reset) + */ +static inline +void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag) +{ + char tmp_tag[ZFCP_DBF_TAG_LEN]; + + if (flag == FCP_TMF_TGT_RESET) + memcpy(tmp_tag, "tr_", 3); + else + memcpy(tmp_tag, "lr_", 3); + + memcpy(&tmp_tag[3], tag, 4); + _zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL); +} + +#endif /* ZFCP_DBF_H */ diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 9e9f6c1e4e5..d91173f326c 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -1,22 +1,9 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Global definitions for the zfcp device driver. * - * 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. + * Copyright IBM Corp. 2002, 2010 */ #ifndef ZFCP_DEF_H @@ -26,7 +13,6 @@ #include <linux/init.h> #include <linux/moduleparam.h> -#include <linux/miscdevice.h> #include <linux/major.h> #include <linux/blkdev.h> #include <linux/delay.h> @@ -36,6 +22,8 @@ #include <linux/syscalls.h> #include <linux/scatterlist.h> #include <linux/ioctl.h> +#include <scsi/fc/fc_fs.h> +#include <scsi/fc/fc_gs.h> #include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_cmnd.h> @@ -43,545 +31,28 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/scsi_bsg_fc.h> #include <asm/ccwdev.h> -#include <asm/qdio.h> #include <asm/debug.h> #include <asm/ebcdic.h> +#include <asm/sysinfo.h> #include "zfcp_fsf.h" +#include "zfcp_fc.h" +#include "zfcp_qdio.h" - -/********************* GENERAL DEFINES *********************************/ - -/* zfcp version number, it consists of major, minor, and patch-level number */ -#define ZFCP_VERSION "4.8.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 sg_virt(list); -} - -/** - * zfcp_address_to_sg - set up struct scatterlist from kernel address - * @address: kernel address - * @list: struct scatterlist - * @size: buffer size - */ -static inline void -zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size) -{ - sg_set_buf(list, address, size); -} - -#define REQUEST_LIST_SIZE 128 +struct zfcp_reqlist; /********************* SCSI SPECIFIC DEFINES *********************************/ #define ZFCP_SCSI_ER_TIMEOUT (10*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 */ - -#define ZFCP_MAX_SECTORS (ZFCP_MAX_SBALES_PER_REQ * 8) - /* max. number of (data buffer) SBALEs in largest SBAL chain - multiplied with number of sectors per 4k block */ - -/* 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 /* seconds */ - -/* 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 */ -#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer)) - /********************* 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 - -/* Do 1st retry in 1 second, then double the timeout for each following retry */ -#define ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP 1 -#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 7 /* 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 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_RSCN 0x61040000 -#define LS_LOGO 0x05000000 -#define LS_PLOGI 0x03000000 - -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 - -/* see fc-ph */ -struct fcp_logo { - u32 command; - u32 nport_did; - wwn_t nport_wwpn; -} __attribute__((packed)); - -/* - * DBF stuff - */ -#define ZFCP_DBF_TAG_SIZE 4 - -struct zfcp_dbf_dump { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u32 total_size; /* size of total dump data */ - u32 offset; /* how much data has being already dumped */ - u32 size; /* how much data comes with this record */ - u8 data[]; /* dump data */ -} __attribute__ ((packed)); - -/* FIXME: to be inflated when reworking the erp dbf */ -struct zfcp_erp_dbf_record { - u8 dummy[16]; -} __attribute__ ((packed)); - -struct zfcp_hba_dbf_record_response { - u32 fsf_command; - u64 fsf_reqid; - u32 fsf_seqno; - u64 fsf_issued; - u32 fsf_prot_status; - u32 fsf_status; - u8 fsf_prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE]; - u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; - u32 fsf_req_status; - u8 sbal_first; - u8 sbal_curr; - u8 sbal_last; - u8 pool; - u64 erp_action; - union { - struct { - u64 scsi_cmnd; - u64 scsi_serial; - } send_fcp; - struct { - u64 wwpn; - u32 d_id; - u32 port_handle; - } port; - struct { - u64 wwpn; - u64 fcp_lun; - u32 port_handle; - u32 lun_handle; - } unit; - struct { - u32 d_id; - u8 ls_code; - } send_els; - } data; -} __attribute__ ((packed)); - -struct zfcp_hba_dbf_record_status { - u8 failed; - u32 status_type; - u32 status_subtype; - struct fsf_queue_designator - queue_designator; - u32 payload_size; -#define ZFCP_DBF_UNSOL_PAYLOAD 80 -#define ZFCP_DBF_UNSOL_PAYLOAD_SENSE_DATA_AVAIL 32 -#define ZFCP_DBF_UNSOL_PAYLOAD_BIT_ERROR_THRESHOLD 56 -#define ZFCP_DBF_UNSOL_PAYLOAD_FEATURE_UPDATE_ALERT 2 * sizeof(u32) - u8 payload[ZFCP_DBF_UNSOL_PAYLOAD]; -} __attribute__ ((packed)); - -struct zfcp_hba_dbf_record_qdio { - u32 status; - u32 qdio_error; - u32 siga_error; - u8 sbal_index; - u8 sbal_count; -} __attribute__ ((packed)); - -struct zfcp_hba_dbf_record { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u8 tag2[ZFCP_DBF_TAG_SIZE]; - union { - struct zfcp_hba_dbf_record_response response; - struct zfcp_hba_dbf_record_status status; - struct zfcp_hba_dbf_record_qdio qdio; - } type; -} __attribute__ ((packed)); - -struct zfcp_san_dbf_record_ct { - union { - struct { - u16 cmd_req_code; - u8 revision; - u8 gs_type; - u8 gs_subtype; - u8 options; - u16 max_res_size; - } request; - struct { - u16 cmd_rsp_code; - u8 revision; - u8 reason_code; - u8 reason_code_expl; - u8 vendor_unique; - } response; - } type; - u32 payload_size; -#define ZFCP_DBF_CT_PAYLOAD 24 - u8 payload[ZFCP_DBF_CT_PAYLOAD]; -} __attribute__ ((packed)); - -struct zfcp_san_dbf_record_els { - u8 ls_code; - u32 payload_size; -#define ZFCP_DBF_ELS_PAYLOAD 32 -#define ZFCP_DBF_ELS_MAX_PAYLOAD 1024 - u8 payload[ZFCP_DBF_ELS_PAYLOAD]; -} __attribute__ ((packed)); - -struct zfcp_san_dbf_record { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u64 fsf_reqid; - u32 fsf_seqno; - u32 s_id; - u32 d_id; - union { - struct zfcp_san_dbf_record_ct ct; - struct zfcp_san_dbf_record_els els; - } type; -} __attribute__ ((packed)); - -struct zfcp_scsi_dbf_record { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u8 tag2[ZFCP_DBF_TAG_SIZE]; - u32 scsi_id; - u32 scsi_lun; - u32 scsi_result; - u64 scsi_cmnd; - u64 scsi_serial; -#define ZFCP_DBF_SCSI_OPCODE 16 - u8 scsi_opcode[ZFCP_DBF_SCSI_OPCODE]; - u8 scsi_retries; - u8 scsi_allowed; - u64 fsf_reqid; - u32 fsf_seqno; - u64 fsf_issued; - union { - u64 old_fsf_reqid; - struct { - u8 rsp_validity; - u8 rsp_scsi_status; - u32 rsp_resid; - u8 rsp_code; -#define ZFCP_DBF_SCSI_FCP_SNS_INFO 16 -#define ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO 256 - u32 sns_info_len; - u8 sns_info[ZFCP_DBF_SCSI_FCP_SNS_INFO]; - } fcp; - } type; -} __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) - -/******************** LOGGING MACROS AND DEFINES *****************************/ - -/* - * Logging may be applied on certain kinds of driver operations - * independently. Additionally, different log-levels are supported for - * each of these areas. - */ - -#define ZFCP_NAME "zfcp" - -/* independent log areas */ -#define ZFCP_LOG_AREA_OTHER 0 -#define ZFCP_LOG_AREA_SCSI 1 -#define ZFCP_LOG_AREA_FSF 2 -#define ZFCP_LOG_AREA_CONFIG 3 -#define ZFCP_LOG_AREA_CIO 4 -#define ZFCP_LOG_AREA_QDIO 5 -#define ZFCP_LOG_AREA_ERP 6 -#define ZFCP_LOG_AREA_FC 7 - -/* log level values*/ -#define ZFCP_LOG_LEVEL_NORMAL 0 -#define ZFCP_LOG_LEVEL_INFO 1 -#define ZFCP_LOG_LEVEL_DEBUG 2 -#define ZFCP_LOG_LEVEL_TRACE 3 - -/* - * 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...) do { } while (0) -#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...) do { } while (0) -#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...) do { } while (0) -#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...) do { } while (0) -#else -# define ZFCP_LOG_TRACE(fmt, args...) \ - ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args) -#endif - /*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/ /* @@ -591,269 +62,52 @@ do { \ #define ZFCP_COMMON_FLAGS 0xfff00000 /* 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 #define ZFCP_STATUS_COMMON_ACCESS_BOXED 0x00400000 +#define ZFCP_STATUS_COMMON_NOESC 0x00200000 /* adapter status */ +#define ZFCP_STATUS_ADAPTER_MB_ACT 0x00000001 #define ZFCP_STATUS_ADAPTER_QDIOUP 0x00000002 -#define ZFCP_STATUS_ADAPTER_REGISTERED 0x00000004 +#define ZFCP_STATUS_ADAPTER_SIOSL_ISSUED 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_SUSPENDED 0x00000040 #define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100 #define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200 -#define ZFCP_STATUS_ADAPTER_XPORT_OK 0x00000800 - -/* 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 +#define ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED 0x00000400 /* 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 - -/* 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 -#define ZFCP_STATUS_UNIT_REGISTERED 0x00000010 -#define ZFCP_STATUS_UNIT_SCSI_WORK_PENDING 0x00000020 +#define ZFCP_STATUS_PORT_LINK_TEST 0x00000002 /* 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; -}; - -/* - * 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; - u32 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 - * @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 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 - * @port: port where ELS is destinated (port reference count has to be increased) - * @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 - * @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; - struct zfcp_port *port; - u32 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 completion *completion; - int ls_code; - int status; -}; - -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 */ + mempool_t *erp_req; + mempool_t *gid_pn_req; + mempool_t *scsi_req; + mempool_t *scsi_abort; + mempool_t *status_read_req; + mempool_t *sr_data; + mempool_t *gid_pn; + mempool_t *qtcb_pool; }; struct zfcp_erp_action { @@ -861,54 +115,63 @@ struct zfcp_erp_action { 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 */ + struct scsi_device *sdev; + 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 */ + unsigned long fsf_req_id; struct timer_list timer; }; +struct fsf_latency_record { + u32 min; + u32 max; + u64 sum; +}; + +struct latency_cont { + struct fsf_latency_record channel; + struct fsf_latency_record fabric; + u64 counter; +}; + +struct zfcp_latencies { + struct latency_cont read; + struct latency_cont write; + struct latency_cont cmd; + spinlock_t lock; +}; 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 peer_wwnn; /* P2P peer WWNN */ - wwn_t peer_wwpn; /* P2P peer WWPN */ + struct kref ref; + u64 peer_wwnn; /* P2P peer WWNN */ + u64 peer_wwpn; /* P2P peer WWPN */ u32 peer_d_id; /* P2P peer D_ID */ struct ccw_device *ccw_device; /* S/390 ccw device */ + struct zfcp_qdio *qdio; u32 hydra_version; /* Hydra version */ u32 fsf_lic_version; u32 adapter_features; /* FCP channel features */ u32 connection_features; /* host connection features */ u32 hardware_version; /* of FCP channel */ + u16 timer_ticks; /* time int for a tick */ struct Scsi_Host *scsi_host; /* Pointer to mid-layer */ - 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 */ - atomic_t reqs_active; /* # active FSF reqs */ + struct list_head port_list; /* remote port list */ + rwlock_t port_list_lock; /* port list lock */ unsigned long req_no; /* unique FSF req number */ - struct list_head *req_list; /* list of pending reqs */ - spinlock_t req_list_lock; /* request list lock */ - struct zfcp_qdio_queue request_queue; /* request queue */ + struct zfcp_reqlist *req_list; 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 stat_miss; /* # missing status reads*/ + unsigned int stat_read_buf_num; + struct work_struct stat_work; atomic_t status; /* status of this adapter */ struct list_head erp_ready_head; /* error recovery for this adapter/devices */ + wait_queue_head_t erp_ready_wq; 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; @@ -916,286 +179,144 @@ struct zfcp_adapter { 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; - debug_info_t *hba_dbf; - debug_info_t *san_dbf; /* debug feature areas */ - debug_info_t *scsi_dbf; - spinlock_t erp_dbf_lock; - spinlock_t hba_dbf_lock; - spinlock_t san_dbf_lock; - spinlock_t scsi_dbf_lock; - struct zfcp_erp_dbf_record erp_dbf_buf; - struct zfcp_hba_dbf_record hba_dbf_buf; - struct zfcp_san_dbf_record san_dbf_buf; - struct zfcp_scsi_dbf_record scsi_dbf_buf; + struct task_struct *erp_thread; + struct zfcp_fc_wka_ports *gs; /* generic services */ + struct zfcp_dbf *dbf; /* debug traces */ 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 */ struct fc_host_statistics *fc_stats; struct fsf_qtcb_bottom_port *stats_reset_data; unsigned long stats_reset; + struct work_struct scan_work; + struct work_struct ns_up_work; + struct service_level service_level; + struct workqueue_struct *work_queue; + struct device_dma_parameters dma_parms; + struct zfcp_fc_events events; }; -/* - * 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 device dev; struct fc_rport *rport; /* rport of fc transport class */ 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 */ + struct list_head unit_list; /* head of logical unit list */ + rwlock_t unit_list_lock; /* unit list lock */ + atomic_t units; /* zfcp_unit count */ atomic_t status; /* status of this remote port */ - wwn_t wwnn; /* WWNN if known */ - wwn_t wwpn; /* WWPN */ + u64 wwnn; /* WWNN if known */ + u64 wwpn; /* WWPN */ u32 d_id; /* D_ID */ u32 handle; /* handle assigned by FSF */ struct zfcp_erp_action erp_action; /* pending error recovery */ atomic_t erp_counter; u32 maxframe_size; u32 supported_classes; + struct work_struct gid_pn_work; + struct work_struct test_link_work; + struct work_struct rport_work; + enum { RPORT_NONE, RPORT_ADD, RPORT_DEL } rport_task; + unsigned int starget_id; }; -/* 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 - LUN configured via zfcp sysfs + * @dev: struct device for sysfs representation and reference counting + * @list: entry in LUN/unit list per zfcp_port + * @port: reference to zfcp_port where this LUN is configured + * @fcp_lun: 64 bit LUN value + * @scsi_work: for running scsi_scan_target + * + * This is the representation of a LUN that has been configured for + * usage. The main data here is the 64 bit LUN value, data for + * running I/O and recovery is in struct zfcp_scsi_dev. */ 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 */ - unsigned int 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 */ - unsigned long req_id; /* unique request ID */ - 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 */ - unsigned long data; /* private data of request */ - struct timer_list timer; /* used for erp or scsi er */ - 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 */ - unsigned long long issued; /* request sent time (STCK) */ - struct zfcp_unit *unit; -}; - -typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*); - -/* driver data */ -struct zfcp_data { - struct scsi_host_template scsi_host_template; - struct scsi_transport_template *scsi_transport_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 */ - 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 kmem_cache *fsf_req_qtcb_cache; - struct kmem_cache *sr_buffer_cache; - struct kmem_cache *gid_pn_cache; + struct device dev; + struct list_head list; + struct zfcp_port *port; + u64 fcp_lun; + struct work_struct scsi_work; }; /** - * 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_scsi_dev - zfcp data per SCSI device + * @status: zfcp internal status flags + * @lun_handle: handle from "open lun" for issuing FSF requests + * @erp_action: zfcp erp data for opening and recovering this LUN + * @erp_counter: zfcp erp counter for this LUN + * @latencies: FSF channel and fabric latencies + * @port: zfcp_port where this LUN belongs to */ -struct zfcp_sg_list { - struct scatterlist *sg; - unsigned int count; +struct zfcp_scsi_dev { + atomic_t status; + u32 lun_handle; + struct zfcp_erp_action erp_action; + atomic_t erp_counter; + struct zfcp_latencies latencies; + struct zfcp_port *port; }; -/* 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_qtcb { - struct zfcp_fsf_req fsf_req; - struct fsf_qtcb qtcb; -}; - -/********************** ZFCP SPECIFIC DEFINES ********************************/ - -#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 - -#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)) - -/* - * Helper functions for request ID management. +/** + * sdev_to_zfcp - Access zfcp LUN data for SCSI device + * @sdev: scsi_device where to get the zfcp_scsi_dev pointer */ -static inline int zfcp_reqlist_hash(unsigned long req_id) -{ - return req_id % REQUEST_LIST_SIZE; -} - -static inline void zfcp_reqlist_add(struct zfcp_adapter *adapter, - struct zfcp_fsf_req *fsf_req) +static inline struct zfcp_scsi_dev *sdev_to_zfcp(struct scsi_device *sdev) { - unsigned int idx; - - idx = zfcp_reqlist_hash(fsf_req->req_id); - list_add_tail(&fsf_req->list, &adapter->req_list[idx]); -} - -static inline void zfcp_reqlist_remove(struct zfcp_adapter *adapter, - struct zfcp_fsf_req *fsf_req) -{ - list_del(&fsf_req->list); + return scsi_transport_device_data(sdev); } -static inline struct zfcp_fsf_req * -zfcp_reqlist_find(struct zfcp_adapter *adapter, unsigned long req_id) -{ - struct zfcp_fsf_req *request; - unsigned int idx; - - idx = zfcp_reqlist_hash(req_id); - list_for_each_entry(request, &adapter->req_list[idx], list) - if (request->req_id == req_id) - return request; - return NULL; -} - -static inline struct zfcp_fsf_req * -zfcp_reqlist_find_safe(struct zfcp_adapter *adapter, struct zfcp_fsf_req *req) -{ - struct zfcp_fsf_req *request; - unsigned int idx; - - for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) { - list_for_each_entry(request, &adapter->req_list[idx], list) - if (request == req) - return request; - } - return NULL; -} - -/* - * functions needed for reference/usage counting +/** + * zfcp_scsi_dev_lun - Return SCSI device LUN as 64 bit FCP LUN + * @sdev: SCSI device where to get the LUN from */ - -static inline void -zfcp_unit_get(struct zfcp_unit *unit) +static inline u64 zfcp_scsi_dev_lun(struct scsi_device *sdev) { - atomic_inc(&unit->refcount); -} + u64 fcp_lun; -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); + int_to_scsilun(sdev->lun, (struct scsi_lun *)&fcp_lun); + return fcp_lun; } -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); -} +/** + * struct zfcp_fsf_req - basic FSF request structure + * @list: list of FSF requests + * @req_id: unique request ID + * @adapter: adapter this request belongs to + * @qdio_req: qdio queue related values + * @completion: used to signal the completion of the request + * @status: status of the request + * @fsf_command: FSF command issued + * @qtcb: associated QTCB + * @seq_no: sequence number of this request + * @data: private data + * @timer: timer data of this request + * @erp_action: reference to erp action if request issued on behalf of ERP + * @pool: reference to memory pool if used for this request + * @issued: time when request was send (STCK) + * @handler: handler which should be called to process response + */ +struct zfcp_fsf_req { + struct list_head list; + unsigned long req_id; + struct zfcp_adapter *adapter; + struct zfcp_qdio_req qdio_req; + struct completion completion; + u32 status; + u32 fsf_command; + struct fsf_qtcb *qtcb; + u32 seq_no; + void *data; + struct timer_list timer; + struct zfcp_erp_action *erp_action; + mempool_t *pool; + unsigned long long issued; + void (*handler)(struct zfcp_fsf_req *); +}; -static inline void -zfcp_adapter_wait(struct zfcp_adapter *adapter) +static inline +int zfcp_adapter_multi_buffer_active(struct zfcp_adapter *adapter) { - wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0); + return atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_MB_ACT; } #endif /* ZFCP_DEF_H */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 2dc8110ebf7..c82fe65c412 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1,3400 +1,1573 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Error Recovery Procedures (ERP). * - * 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. + * Copyright IBM Corp. 2002, 2010 */ -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kthread.h> #include "zfcp_ext.h" +#include "zfcp_reqlist.h" -static int zfcp_erp_adisc(struct zfcp_port *); -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 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_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_xport(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 void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *); -static void zfcp_erp_action_dismiss_port(struct zfcp_port *); -static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *); -static void 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 void zfcp_erp_action_to_ready(struct zfcp_erp_action *); -static void zfcp_erp_action_to_running(struct zfcp_erp_action *); - -static void zfcp_erp_memwait_handler(unsigned long); - -/** - * zfcp_close_qdio - close qdio queues for an adapter - */ -static void zfcp_close_qdio(struct zfcp_adapter *adapter) -{ - struct zfcp_qdio_queue *req_queue; - int first, count; +#define ZFCP_MAX_ERPS 3 - if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) - return; +enum zfcp_erp_act_flags { + ZFCP_STATUS_ERP_TIMEDOUT = 0x10000000, + ZFCP_STATUS_ERP_CLOSE_ONLY = 0x01000000, + ZFCP_STATUS_ERP_DISMISSING = 0x00100000, + ZFCP_STATUS_ERP_DISMISSED = 0x00200000, + ZFCP_STATUS_ERP_LOWMEM = 0x00400000, + ZFCP_STATUS_ERP_NO_REF = 0x00800000, +}; - /* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */ - req_queue = &adapter->request_queue; - write_lock_irq(&req_queue->queue_lock); - atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status); - write_unlock_irq(&req_queue->queue_lock); - - debug_text_event(adapter->erp_dbf, 3, "qdio_down2a"); - while (qdio_shutdown(adapter->ccw_device, - QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS) - ssleep(1); - debug_text_event(adapter->erp_dbf, 3, "qdio_down2b"); - - /* cleanup used outbound sbals */ - count = atomic_read(&req_queue->free_count); - if (count < QDIO_MAX_BUFFERS_PER_Q) { - first = (req_queue->free_index+count) % QDIO_MAX_BUFFERS_PER_Q; - count = QDIO_MAX_BUFFERS_PER_Q - count; - zfcp_qdio_zero_sbals(req_queue->buffer, first, count); - } - req_queue->free_index = 0; - atomic_set(&req_queue->free_count, 0); - req_queue->distance_from_int = 0; - adapter->response_queue.free_index = 0; - atomic_set(&adapter->response_queue.free_count, 0); -} +enum zfcp_erp_steps { + ZFCP_ERP_STEP_UNINITIALIZED = 0x0000, + ZFCP_ERP_STEP_FSF_XCONFIG = 0x0001, + ZFCP_ERP_STEP_PHYS_PORT_CLOSING = 0x0010, + ZFCP_ERP_STEP_PORT_CLOSING = 0x0100, + ZFCP_ERP_STEP_PORT_OPENING = 0x0800, + ZFCP_ERP_STEP_LUN_CLOSING = 0x1000, + ZFCP_ERP_STEP_LUN_OPENING = 0x2000, +}; -/** - * zfcp_close_fsf - stop FSF operations for an adapter - * - * Dismiss and cleanup all pending fsf_reqs (this wakes up all initiators of - * requests waiting for completion; especially this returns SCSI commands - * with error state). - */ -static void zfcp_close_fsf(struct zfcp_adapter *adapter) -{ - /* close queues to ensure that buffers are not accessed by adapter */ - zfcp_close_qdio(adapter); - 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); -} +enum zfcp_erp_act_type { + ZFCP_ERP_ACTION_REOPEN_LUN = 1, + ZFCP_ERP_ACTION_REOPEN_PORT = 2, + ZFCP_ERP_ACTION_REOPEN_PORT_FORCED = 3, + ZFCP_ERP_ACTION_REOPEN_ADAPTER = 4, +}; -/** - * 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. - */ -static void zfcp_fsf_request_timeout_handler(unsigned long data) -{ - struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; - zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED); -} +enum zfcp_erp_act_state { + ZFCP_ERP_ACTION_RUNNING = 1, + ZFCP_ERP_ACTION_READY = 2, +}; -void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, unsigned long timeout) -{ - fsf_req->timer.function = zfcp_fsf_request_timeout_handler; - fsf_req->timer.data = (unsigned long) fsf_req->adapter; - fsf_req->timer.expires = jiffies + timeout; - add_timer(&fsf_req->timer); -} +enum zfcp_erp_act_result { + ZFCP_ERP_SUCCEEDED = 0, + ZFCP_ERP_FAILED = 1, + ZFCP_ERP_CONTINUES = 2, + ZFCP_ERP_EXIT = 3, + ZFCP_ERP_DISMISSED = 4, + ZFCP_ERP_NOMEM = 5, +}; -/* - * function: - * - * purpose: called if an adapter failed, - * initiates adapter recovery which is done - * asynchronously - * - * returns: 0 - initiated action successfully - * <0 - failed to initiate action - */ -static int -zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter, int clear_mask) +static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int 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; + zfcp_erp_clear_adapter_status(adapter, + ZFCP_STATUS_COMMON_UNBLOCKED | mask); } -/* - * function: - * - * purpose: Wrappper for zfcp_erp_adapter_reopen_internal - * used to ensure the correct locking - * - * returns: 0 - initiated action successfully - * <0 - failed to initiate action - */ -int -zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask) +static int zfcp_erp_action_exists(struct zfcp_erp_action *act) { - 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); + struct zfcp_erp_action *curr_act; - return retval; + list_for_each_entry(curr_act, &act->adapter->erp_running_head, list) + if (act == curr_act) + return ZFCP_ERP_ACTION_RUNNING; + return 0; } -int -zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear_mask) +static void zfcp_erp_action_ready(struct zfcp_erp_action *act) { - int retval; - - retval = zfcp_erp_adapter_reopen(adapter, - ZFCP_STATUS_COMMON_RUNNING | - ZFCP_STATUS_COMMON_ERP_FAILED | - clear_mask); + struct zfcp_adapter *adapter = act->adapter; - return retval; + list_move(&act->list, &act->adapter->erp_ready_head); + zfcp_dbf_rec_run("erardy1", act); + wake_up(&adapter->erp_ready_wq); + zfcp_dbf_rec_run("erardy2", act); } -int -zfcp_erp_port_shutdown(struct zfcp_port *port, int clear_mask) +static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act) { - int retval; - - retval = zfcp_erp_port_reopen(port, - ZFCP_STATUS_COMMON_RUNNING | - ZFCP_STATUS_COMMON_ERP_FAILED | - clear_mask); - - return retval; + act->status |= ZFCP_STATUS_ERP_DISMISSED; + if (zfcp_erp_action_exists(act) == ZFCP_ERP_ACTION_RUNNING) + zfcp_erp_action_ready(act); } -int -zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask) +static void zfcp_erp_action_dismiss_lun(struct scsi_device *sdev) { - int retval; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - retval = zfcp_erp_unit_reopen(unit, - ZFCP_STATUS_COMMON_RUNNING | - ZFCP_STATUS_COMMON_ERP_FAILED | - clear_mask); - - return retval; + if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_INUSE) + zfcp_erp_action_dismiss(&zfcp_sdev->erp_action); } - -/** - * zfcp_erp_adisc - send ADISC ELS command - * @port: port structure - */ -static int -zfcp_erp_adisc(struct zfcp_port *port) +static void zfcp_erp_action_dismiss_port(struct zfcp_port *port) { - struct zfcp_adapter *adapter = port->adapter; - struct zfcp_send_els *send_els; - struct zfcp_ls_adisc *adisc; - void *address = NULL; - int retval = 0; - - send_els = kzalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC); - if (send_els == NULL) - goto nomem; - - send_els->req = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC); - if (send_els->req == NULL) - goto nomem; - sg_init_table(send_els->req, 1); - - send_els->resp = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC); - if (send_els->resp == NULL) - goto nomem; - sg_init_table(send_els->resp, 1); - - address = (void *) get_zeroed_page(GFP_ATOMIC); - if (address == NULL) - goto nomem; - - zfcp_address_to_sg(address, send_els->req, sizeof(struct zfcp_ls_adisc)); - address += PAGE_SIZE >> 1; - zfcp_address_to_sg(address, send_els->resp, sizeof(struct zfcp_ls_adisc_acc)); - send_els->req_count = send_els->resp_count = 1; - - send_els->adapter = adapter; - send_els->port = port; - send_els->d_id = port->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; - - /* 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 = fc_host_port_name(adapter->scsi_host); - adisc->wwnn = fc_host_node_name(adapter->scsi_host); - adisc->nport_id = fc_host_port_id(adapter->scsi_host); - ZFCP_LOG_INFO("ADISC request from s_id 0x%06x to d_id 0x%06x " - "(wwpn=0x%016Lx, wwnn=0x%016Lx, " - "hard_nport_id=0x%06x, nport_id=0x%06x)\n", - adisc->nport_id, send_els->d_id, (wwn_t) adisc->wwpn, - (wwn_t) adisc->wwnn, adisc->hard_nport_id, - adisc->nport_id); - - retval = zfcp_fsf_send_els(send_els); - if (retval != 0) { - ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port " - "0x%06x on adapter %s\n", send_els->d_id, - zfcp_get_busid_by_adapter(adapter)); - goto freemem; - } + struct scsi_device *sdev; - goto out; - - nomem: - retval = -ENOMEM; - freemem: - if (address != NULL) - __free_pages(sg_page(send_els->req), 0); - if (send_els != NULL) { - kfree(send_els->req); - kfree(send_els->resp); - kfree(send_els); + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) + zfcp_erp_action_dismiss(&port->erp_action); + else { + spin_lock(port->adapter->scsi_host->host_lock); + __shost_for_each_device(sdev, port->adapter->scsi_host) + if (sdev_to_zfcp(sdev)->port == port) + zfcp_erp_action_dismiss_lun(sdev); + spin_unlock(port->adapter->scsi_host->host_lock); } - 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. - */ -static void -zfcp_erp_adisc_handler(unsigned long data) +static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) { - struct zfcp_send_els *send_els; struct zfcp_port *port; - struct zfcp_adapter *adapter; - u32 d_id; - struct zfcp_ls_adisc_acc *adisc; - - send_els = (struct zfcp_send_els *) data; - adapter = send_els->adapter; - port = send_els->port; - d_id = send_els->d_id; - - /* 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%06x)\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%06x to s_id " - "0x%06x (wwpn=0x%016Lx, wwnn=0x%016Lx, " - "hard_nport_id=0x%06x, nport_id=0x%06x)\n", - d_id, fc_host_port_id(adapter->scsi_host), - (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); + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE) + zfcp_erp_action_dismiss(&adapter->erp_action); + else { + read_lock(&adapter->port_list_lock); + list_for_each_entry(port, &adapter->port_list, list) + zfcp_erp_action_dismiss_port(port); + read_unlock(&adapter->port_list_lock); + } +} + +static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter, + struct zfcp_port *port, + struct scsi_device *sdev) +{ + int need = want; + int l_status, p_status, a_status; + struct zfcp_scsi_dev *zfcp_sdev; + + switch (want) { + case ZFCP_ERP_ACTION_REOPEN_LUN: + zfcp_sdev = sdev_to_zfcp(sdev); + l_status = atomic_read(&zfcp_sdev->status); + if (l_status & ZFCP_STATUS_COMMON_ERP_INUSE) + return 0; + p_status = atomic_read(&port->status); + if (!(p_status & ZFCP_STATUS_COMMON_RUNNING) || + p_status & ZFCP_STATUS_COMMON_ERP_FAILED) + return 0; + if (!(p_status & ZFCP_STATUS_COMMON_UNBLOCKED)) + need = ZFCP_ERP_ACTION_REOPEN_PORT; + /* fall through */ + case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: + p_status = atomic_read(&port->status); + if (!(p_status & ZFCP_STATUS_COMMON_OPEN)) + need = ZFCP_ERP_ACTION_REOPEN_PORT; + /* fall through */ + case ZFCP_ERP_ACTION_REOPEN_PORT: + p_status = atomic_read(&port->status); + if (p_status & ZFCP_STATUS_COMMON_ERP_INUSE) + return 0; + a_status = atomic_read(&adapter->status); + if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) || + a_status & ZFCP_STATUS_COMMON_ERP_FAILED) + return 0; + if (p_status & ZFCP_STATUS_COMMON_NOESC) + return need; + if (!(a_status & ZFCP_STATUS_COMMON_UNBLOCKED)) + need = ZFCP_ERP_ACTION_REOPEN_ADAPTER; + /* fall through */ + case ZFCP_ERP_ACTION_REOPEN_ADAPTER: + a_status = atomic_read(&adapter->status); + if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE) + return 0; + if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) && + !(a_status & ZFCP_STATUS_COMMON_OPEN)) + return 0; /* shutdown requested for closed adapter */ } - out: - zfcp_port_put(port); - __free_pages(sg_page(send_els->req), 0); - kfree(send_els->req); - kfree(send_els->resp); - kfree(send_els); + return need; } - -/** - * 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) +static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, + struct zfcp_adapter *adapter, + struct zfcp_port *port, + struct scsi_device *sdev) { - int retval; - - zfcp_port_get(port); - retval = zfcp_erp_adisc(port); - if (retval != 0 && retval != -EBUSY) { - 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 successfully - * <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)); + struct zfcp_erp_action *erp_action; + struct zfcp_scsi_dev *zfcp_sdev; + + switch (need) { + case ZFCP_ERP_ACTION_REOPEN_LUN: + zfcp_sdev = sdev_to_zfcp(sdev); + if (!(act_status & ZFCP_STATUS_ERP_NO_REF)) + if (scsi_device_get(sdev)) + return NULL; + atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, + &zfcp_sdev->status); + erp_action = &zfcp_sdev->erp_action; + memset(erp_action, 0, sizeof(struct zfcp_erp_action)); + erp_action->port = port; + erp_action->sdev = sdev; + if (!(atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_RUNNING)) + act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; + break; - ZFCP_LOG_DEBUG("forced reopen of port 0x%016Lx on adapter %s\n", - port->wwpn, zfcp_get_busid_by_port(port)); + case ZFCP_ERP_ACTION_REOPEN_PORT: + case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: + if (!get_device(&port->dev)) + return NULL; + zfcp_erp_action_dismiss_port(port); + atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status); + erp_action = &port->erp_action; + memset(erp_action, 0, sizeof(struct zfcp_erp_action)); + erp_action->port = port; + if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING)) + act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; + break; - zfcp_erp_port_block(port, clear_mask); + case ZFCP_ERP_ACTION_REOPEN_ADAPTER: + kref_get(&adapter->ref); + zfcp_erp_action_dismiss_adapter(adapter); + atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status); + erp_action = &adapter->erp_action; + memset(erp_action, 0, sizeof(struct zfcp_erp_action)); + if (!(atomic_read(&adapter->status) & + ZFCP_STATUS_COMMON_RUNNING)) + act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; + break; - 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; + default: + return NULL; } - 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 successfully - * <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); + erp_action->adapter = adapter; + erp_action->action = need; + erp_action->status = act_status; - return retval; + return erp_action; } -/* - * function: - * - * purpose: called if a port is to be opened - * initiates Reopen recovery which is done - * asynchronously - * - * returns: 0 - initiated action successfully - * <0 - failed to initiate action - */ -static int -zfcp_erp_port_reopen_internal(struct zfcp_port *port, int clear_mask) +static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, + struct zfcp_port *port, + struct scsi_device *sdev, + char *id, u32 act_status) { - int retval; - 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)); + int retval = 1, need; + struct zfcp_erp_action *act; - 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 (!adapter->erp_thread) + return -EIO; - 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; + need = zfcp_erp_required_act(want, adapter, port, sdev); + if (!need) goto out; - } - - retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT, - port->adapter, port, NULL); + act = zfcp_erp_setup_act(need, act_status, adapter, port, sdev); + if (!act) + goto out; + atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status); + ++adapter->erp_total_count; + list_add_tail(&act->list, &adapter->erp_ready_head); + wake_up(&adapter->erp_ready_wq); + retval = 0; out: + zfcp_dbf_rec_trig(id, adapter, port, sdev, want, need); return retval; } -/** - * 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) +static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, + int clear_mask, char *id) { - 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; -} + zfcp_erp_adapter_block(adapter, clear_mask); + zfcp_scsi_schedule_rports_block(adapter); -/* - * function: - * - * purpose: called if a unit is to be opened - * initiates Reopen recovery which is done - * asynchronously - * - * returns: 0 - initiated action successfully - * <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; + /* ensure propagation of failed status to new devices */ + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { + zfcp_erp_set_adapter_status(adapter, + ZFCP_STATUS_COMMON_ERP_FAILED); + return -EIO; } - - retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT, - unit->port->adapter, unit->port, unit); - out: - return retval; + return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, + adapter, NULL, NULL, id, 0); } /** - * 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. + * zfcp_erp_adapter_reopen - Reopen adapter. + * @adapter: Adapter to reopen. + * @clear: Status flags to clear. + * @id: Id for debug trace event. */ -int -zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask) +void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, char *id) { - int retval; unsigned long flags; - struct zfcp_adapter *adapter; - struct zfcp_port *port; - port = unit->port; - adapter = port->adapter; + zfcp_erp_adapter_block(adapter, clear); + zfcp_scsi_schedule_rports_block(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; + write_lock_irqsave(&adapter->erp_lock, flags); + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + zfcp_erp_set_adapter_status(adapter, + ZFCP_STATUS_COMMON_ERP_FAILED); + else + zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter, + NULL, NULL, id, 0); + write_unlock_irqrestore(&adapter->erp_lock, flags); } /** - * zfcp_erp_adapter_block - mark adapter as blocked, block scsi requests + * zfcp_erp_adapter_shutdown - Shutdown adapter. + * @adapter: Adapter to shut down. + * @clear: Status flags to clear. + * @id: Id for debug trace event. */ -static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask) +void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear, + char *id) { - debug_text_event(adapter->erp_dbf, 6, "a_bl"); - zfcp_erp_modify_adapter_status(adapter, - ZFCP_STATUS_COMMON_UNBLOCKED | - clear_mask, ZFCP_CLEAR); + int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; + zfcp_erp_adapter_reopen(adapter, clear | flags, id); } /** - * zfcp_erp_adapter_unblock - mark adapter as unblocked, allow scsi requests + * zfcp_erp_port_shutdown - Shutdown port + * @port: Port to shut down. + * @clear: Status flags to clear. + * @id: Id for debug trace event. */ -static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter) +void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, char *id) { - debug_text_event(adapter->erp_dbf, 6, "a_ubl"); - atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status); + int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; + zfcp_erp_port_reopen(port, clear | flags, id); } -/* - * 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) +static void zfcp_erp_port_block(struct zfcp_port *port, int clear) { - 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); + zfcp_erp_clear_port_status(port, + ZFCP_STATUS_COMMON_UNBLOCKED | clear); } -/* - * function: - * - * purpose: enable I/O - * - * returns: - */ -static void -zfcp_erp_port_unblock(struct zfcp_port *port) +static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, + char *id) { - 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); -} + zfcp_erp_port_block(port, clear); + zfcp_scsi_schedule_rport_block(port); -/* - * 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; + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + return; - 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); + zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, + port->adapter, port, NULL, id, 0); } -/* - * function: - * - * purpose: enable I/O - * - * returns: +/** + * zfcp_erp_port_forced_reopen - Forced close of port and open again + * @port: Port to force close and to reopen. + * @clear: Status flags to clear. + * @id: Id for debug trace event. */ -static void -zfcp_erp_unit_unblock(struct zfcp_unit *unit) +void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id) { - struct zfcp_adapter *adapter = unit->port->adapter; + unsigned long flags; + struct zfcp_adapter *adapter = 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); + write_lock_irqsave(&adapter->erp_lock, flags); + _zfcp_erp_port_forced_reopen(port, clear, id); + write_unlock_irqrestore(&adapter->erp_lock, flags); } -static void -zfcp_erp_action_ready(struct zfcp_erp_action *erp_action) +static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) { - struct zfcp_adapter *adapter = erp_action->adapter; + zfcp_erp_port_block(port, clear); + zfcp_scsi_schedule_rport_block(port); - debug_text_event(adapter->erp_dbf, 4, "a_ar"); - debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof (int)); + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { + /* ensure propagation of failed status to new devices */ + zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED); + return -EIO; + } - zfcp_erp_action_to_ready(erp_action); - up(&adapter->erp_ready_sem); + return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT, + port->adapter, port, NULL, id, 0); } -/* - * 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 +/** + * zfcp_erp_port_reopen - trigger remote port recovery + * @port: port to recover + * @clear_mask: flags in port status to be cleared + * @id: Id for debug trace event. * - * locks: erp_lock must be held + * Returns 0 if recovery has been triggered, < 0 if not. */ -static int -zfcp_erp_action_exists(struct zfcp_erp_action *erp_action) +int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) { - int retval = -EINVAL; - struct list_head *entry; - struct zfcp_erp_action *entry_erp_action; - struct zfcp_adapter *adapter = erp_action->adapter; + int retval; + unsigned long flags; + struct zfcp_adapter *adapter = port->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; - } - } + write_lock_irqsave(&adapter->erp_lock, flags); + retval = _zfcp_erp_port_reopen(port, clear, id); + write_unlock_irqrestore(&adapter->erp_lock, flags); - 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) - */ -static void -zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action) +static void zfcp_erp_lun_block(struct scsi_device *sdev, int clear_mask) { - struct zfcp_adapter *adapter = erp_action->adapter; - - if (erp_action->fsf_req) { - /* take lock to ensure that request is not deleted meanwhile */ - spin_lock(&adapter->req_list_lock); - if (zfcp_reqlist_find_safe(adapter, erp_action->fsf_req) && - erp_action->fsf_req->erp_action == erp_action) { - /* fsf_req still exists */ - debug_text_event(adapter->erp_dbf, 3, "a_ca_req"); - debug_event(adapter->erp_dbf, 3, &erp_action->fsf_req, - sizeof (unsigned long)); - /* dismiss fsf_req of timed out/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"); - erp_action->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 (erp_action->fsf_req->status & - (ZFCP_STATUS_FSFREQ_COMPLETED | - ZFCP_STATUS_FSFREQ_DISMISSED)) { - /* forget about association between fsf_req - and erp_action */ - 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; - } - spin_unlock(&adapter->req_list_lock); - } else - debug_text_event(adapter->erp_dbf, 3, "a_ca_noreq"); + zfcp_erp_clear_lun_status(sdev, + ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask); } -/** - * zfcp_erp_async_handler_nolock - complete erp_action - * - * Used for normal completion, time-out, dismissal and failure after - * low memory condition. - */ -static void zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action, - unsigned long set_mask) +static void _zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id, + u32 act_status) { - struct zfcp_adapter *adapter = erp_action->adapter; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); + struct zfcp_adapter *adapter = zfcp_sdev->port->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)); - erp_action->status |= set_mask; - zfcp_erp_action_ready(erp_action); - } 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)); - } + zfcp_erp_lun_block(sdev, clear); + + if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + return; + + zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_LUN, adapter, + zfcp_sdev->port, sdev, id, act_status); } /** - * zfcp_erp_async_handler - wrapper for erp_async_handler_nolock w/ locking + * zfcp_erp_lun_reopen - initiate reopen of a LUN + * @sdev: SCSI device / LUN to be reopened + * @clear_mask: specifies flags in LUN status to be cleared + * @id: Id for debug trace event. + * + * Return: 0 on success, < 0 on error */ -void zfcp_erp_async_handler(struct zfcp_erp_action *erp_action, - unsigned long set_mask) +void zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id) { - struct zfcp_adapter *adapter = erp_action->adapter; unsigned long flags; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); + struct zfcp_port *port = zfcp_sdev->port; + struct zfcp_adapter *adapter = port->adapter; write_lock_irqsave(&adapter->erp_lock, flags); - zfcp_erp_async_handler_nolock(erp_action, set_mask); + _zfcp_erp_lun_reopen(sdev, clear, id, 0); write_unlock_irqrestore(&adapter->erp_lock, flags); } -/* - * purpose: is called for erp_action which was slept waiting for - * memory becoming avaliable, - * will trigger that this action will be continued +/** + * zfcp_erp_lun_shutdown - Shutdown LUN + * @sdev: SCSI device / LUN to shut down. + * @clear: Status flags to clear. + * @id: Id for debug trace event. */ -static void -zfcp_erp_memwait_handler(unsigned long data) +void zfcp_erp_lun_shutdown(struct scsi_device *sdev, int clear, char *id) { - 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); + int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; + zfcp_erp_lun_reopen(sdev, clear | flags, id); } -/* - * purpose: is called if an asynchronous erp step timed out, - * action gets an appropriate flag and will be processed - * accordingly +/** + * zfcp_erp_lun_shutdown_wait - Shutdown LUN and wait for erp completion + * @sdev: SCSI device / LUN to shut down. + * @id: Id for debug trace event. + * + * Do not acquire a reference for the LUN when creating the ERP + * action. It is safe, because this function waits for the ERP to + * complete first. This allows to shutdown the LUN, even when the SCSI + * device is in the state SDEV_DEL when scsi_device_get will fail. */ -static void zfcp_erp_timeout_handler(unsigned long data) +void zfcp_erp_lun_shutdown_wait(struct scsi_device *sdev, char *id) { - struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data; - struct zfcp_adapter *adapter = erp_action->adapter; + unsigned long flags; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); + struct zfcp_port *port = zfcp_sdev->port; + struct zfcp_adapter *adapter = port->adapter; + int clear = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; - debug_text_event(adapter->erp_dbf, 2, "a_th"); - debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int)); + write_lock_irqsave(&adapter->erp_lock, flags); + _zfcp_erp_lun_reopen(sdev, clear, id, ZFCP_STATUS_ERP_NO_REF); + write_unlock_irqrestore(&adapter->erp_lock, flags); - zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT); + zfcp_erp_wait(adapter); } -/** - * zfcp_erp_action_dismiss - dismiss an erp_action - * - * adapter->erp_lock must be held - * - * Dismissal of an erp_action is usually required if an erp_action of - * higher priority is generated. - */ -static void zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action) +static int status_change_set(unsigned long mask, atomic_t *status) { - 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)); - - erp_action->status |= ZFCP_STATUS_ERP_DISMISSED; - if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) - zfcp_erp_action_ready(erp_action); + return (atomic_read(status) ^ mask) & mask; } -int -zfcp_erp_thread_setup(struct zfcp_adapter *adapter) +static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter) { - int retval = 0; - - atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status); - - 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); + if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status)) + zfcp_dbf_rec_run("eraubl1", &adapter->erp_action); + atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status); } -/* - * 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) +static void zfcp_erp_port_unblock(struct zfcp_port *port) { - 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); + if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status)) + zfcp_dbf_rec_run("erpubl1", &port->erp_action); + atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status); +} - debug_text_event(adapter->erp_dbf, 5, "a_thki_ok"); +static void zfcp_erp_lun_unblock(struct scsi_device *sdev) +{ + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - return retval; + if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status)) + zfcp_dbf_rec_run("erlubl1", &sdev_to_zfcp(sdev)->erp_action); + atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status); } -/* - * 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) +static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action) { - struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; - struct list_head *next; - struct zfcp_erp_action *erp_action; - unsigned long flags; - - daemonize("zfcperp%s", zfcp_get_busid_by_adapter(adapter)); - /* Block all signals */ - siginitsetinv(¤t->blocked, 0); - atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status); - debug_text_event(adapter->erp_dbf, 5, "a_th_run"); - wake_up(&adapter->erp_thread_wqh); + list_move(&erp_action->list, &erp_action->adapter->erp_running_head); + zfcp_dbf_rec_run("erator1", erp_action); +} - while (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, - &adapter->status)) { +static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act) +{ + struct zfcp_adapter *adapter = act->adapter; + struct zfcp_fsf_req *req; - write_lock_irqsave(&adapter->erp_lock, flags); - next = adapter->erp_ready_head.next; - write_unlock_irqrestore(&adapter->erp_lock, flags); + if (!act->fsf_req_id) + return; - 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); + spin_lock(&adapter->req_list->lock); + req = _zfcp_reqlist_find(adapter->req_list, act->fsf_req_id); + if (req && req->erp_action == act) { + if (act->status & (ZFCP_STATUS_ERP_DISMISSED | + ZFCP_STATUS_ERP_TIMEDOUT)) { + req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; + zfcp_dbf_rec_run("erscf_1", act); + req->erp_action = NULL; } - - /* - * 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; + if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) + zfcp_dbf_rec_run("erscf_2", act); + if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) + act->fsf_req_id = 0; + } else + act->fsf_req_id = 0; + spin_unlock(&adapter->req_list->lock); } -/* - * 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) +/** + * zfcp_erp_notify - Trigger ERP action. + * @erp_action: ERP action to continue. + * @set_mask: ERP action status flags to set. */ -static int -zfcp_erp_strategy(struct zfcp_erp_action *erp_action) +void zfcp_erp_notify(struct zfcp_erp_action *erp_action, unsigned long set_mask) { - 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, - * we go through down() here because there has been an up() - */ - if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) - retval = ZFCP_ERP_CONTINUES; - - switch (retval) { - 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; + write_lock_irqsave(&adapter->erp_lock, flags); + if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) { + erp_action->status |= set_mask; + zfcp_erp_action_ready(erp_action); } - - /* 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; + write_unlock_irqrestore(&adapter->erp_lock, flags); } -/* - * function: - * - * purpose: - * - * returns: ZFCP_ERP_DISMISSED - if action has been dismissed - * retval - otherwise +/** + * zfcp_erp_timeout_handler - Trigger ERP action from timed out ERP request + * @data: ERP action (from timer data) */ -static int -zfcp_erp_strategy_check_action(struct zfcp_erp_action *erp_action, int retval) +void zfcp_erp_timeout_handler(unsigned long data) { - 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; + struct zfcp_erp_action *act = (struct zfcp_erp_action *) data; + zfcp_erp_notify(act, ZFCP_STATUS_ERP_TIMEDOUT); } -static int -zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action) +static void zfcp_erp_memwait_handler(unsigned long data) { - 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 separate 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; + zfcp_erp_notify((struct zfcp_erp_action *)data, 0); } -/* - * 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) +static void 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; + erp_action->timer.expires = jiffies + HZ; 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%06x)\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) +static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, + int clear, char *id) { - zfcp_erp_modify_unit_status(unit, - ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET); + struct zfcp_port *port; - 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)); + read_lock(&adapter->port_list_lock); + list_for_each_entry(port, &adapter->port_list, list) + _zfcp_erp_port_reopen(port, clear, id); + read_unlock(&adapter->port_list_lock); } -/* - * 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) +static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear, + char *id) { - 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; + struct scsi_device *sdev; - 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; + spin_lock(port->adapter->scsi_host->host_lock); + __shost_for_each_device(sdev, port->adapter->scsi_host) + if (sdev_to_zfcp(sdev)->port == port) + _zfcp_erp_lun_reopen(sdev, clear, id, 0); + spin_unlock(port->adapter->scsi_host->host_lock); } -static int -zfcp_erp_strategy_statechange(int action, - u32 status, - struct zfcp_adapter *adapter, - struct zfcp_port *port, - struct zfcp_unit *unit, int retval) +static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) { - debug_text_event(adapter->erp_dbf, 3, "a_stsc"); - debug_event(adapter->erp_dbf, 3, &action, sizeof (int)); - - switch (action) { - + switch (act->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; - } + _zfcp_erp_adapter_reopen(act->adapter, 0, "ersff_1"); 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; - } + _zfcp_erp_port_forced_reopen(act->port, 0, "ersff_2"); 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; -} - -static 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)); -} - -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; -} - -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; -} - -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); + case ZFCP_ERP_ACTION_REOPEN_PORT: + _zfcp_erp_port_reopen(act->port, 0, "ersff_3"); break; - case ZFCP_ERP_EXIT : - /* nothing */ + case ZFCP_ERP_ACTION_REOPEN_LUN: + _zfcp_erp_lun_reopen(act->sdev, 0, "ersff_4", 0); 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; } -struct zfcp_erp_add_work { - struct zfcp_unit *unit; - struct work_struct work; -}; - -/** - * zfcp_erp_scsi_scan - * @data: pointer to a struct zfcp_erp_add_work - * - * Registers a logical unit with the SCSI stack. - */ -static void zfcp_erp_scsi_scan(struct work_struct *work) +static void zfcp_erp_strategy_followup_success(struct zfcp_erp_action *act) { - struct zfcp_erp_add_work *p = - container_of(work, struct zfcp_erp_add_work, work); - struct zfcp_unit *unit = p->unit; - struct fc_rport *rport = unit->port->rport; - scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, - unit->scsi_lun, 0); - atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); - zfcp_unit_put(unit); - kfree(p); -} - -/** - * zfcp_erp_schedule_work - * @unit: pointer to unit which should be registered with SCSI stack - * - * Schedules work which registers a unit with the SCSI stack - */ -static void -zfcp_erp_schedule_work(struct zfcp_unit *unit) -{ - struct zfcp_erp_add_work *p; - - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) { - ZFCP_LOG_NORMAL("error: Out of resources. Could not register " - "the FCP-LUN 0x%Lx connected to " - "the port with WWPN 0x%Lx connected to " - "the adapter %s with the SCSI stack.\n", - unit->fcp_lun, - unit->port->wwpn, - zfcp_get_busid_by_unit(unit)); - return; - } - - zfcp_unit_get(unit); - atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); - INIT_WORK(&p->work, zfcp_erp_scsi_scan); - p->unit = unit; - schedule_work(&p->work); -} - -/* - * 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) { - + switch (act->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); + _zfcp_erp_port_reopen_all(act->adapter, 0, "ersfs_1"); 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); + _zfcp_erp_port_reopen(act->port, 0, "ersfs_2"); 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: - /* Nothing to do if status == ZFCP_ERP_SUCCEEDED */ - if (status != ZFCP_ERP_SUCCEEDED) - zfcp_erp_port_reopen_internal(unit->port, 0); + _zfcp_erp_lun_reopen_all(act->port, 0, "ersfs_3"); break; } - - return 0; } -static int -zfcp_erp_strategy_check_queues(struct zfcp_adapter *adapter) +static void zfcp_erp_wakeup(struct zfcp_adapter *adapter) { unsigned long flags; - read_lock_irqsave(&zfcp_data.config_lock, flags); - read_lock(&adapter->erp_lock); + read_lock_irqsave(&adapter->erp_lock, flags); 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; + } + read_unlock_irqrestore(&adapter->erp_lock, flags); } -void -zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, - u32 mask, int set_or_clear) +static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter) { 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); + port = zfcp_port_enqueue(adapter, adapter->peer_wwpn, 0, + adapter->peer_d_id); + if (IS_ERR(port)) /* error or port already attached */ + return; + _zfcp_erp_port_reopen(port, 0, "ereptp1"); } -/* - * 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) +static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action) { - 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)); + int retries; + int sleep = 1; + struct zfcp_adapter *adapter = erp_action->adapter; - /* 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); -} + atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status); -/* - * 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); + for (retries = 7; retries; retries--) { + atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, + &adapter->status); + write_lock_irq(&adapter->erp_lock); + zfcp_erp_action_to_running(erp_action); + write_unlock_irq(&adapter->erp_lock); + if (zfcp_fsf_exchange_config_data(erp_action)) { + atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, + &adapter->status); + return ZFCP_ERP_FAILED; } - 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 successfully - * <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; -} + wait_event(adapter->erp_ready_wq, + !list_empty(&adapter->erp_ready_head)); + if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) + break; -static int -zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *adapter, int clear_mask) -{ - int retval = 0; - struct zfcp_port *port; + if (!(atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_HOST_CON_INIT)) + break; - 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); + ssleep(sleep); + sleep *= 2; + } - return retval; -} + atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, + &adapter->status); -/* - * 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; + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK)) + return ZFCP_ERP_FAILED; - list_for_each_entry(unit, &port->unit_list_head, list) - zfcp_erp_unit_reopen_internal(unit, clear_mask); + if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) + zfcp_erp_enqueue_ptp_port(adapter); - return retval; + return ZFCP_ERP_SUCCEEDED; } -/* - * 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) +static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *act) { - int retval; - struct zfcp_adapter *adapter = erp_action->adapter; + int ret; + struct zfcp_adapter *adapter = act->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); + write_lock_irq(&adapter->erp_lock); + zfcp_erp_action_to_running(act); + write_unlock_irq(&adapter->erp_lock); - 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)); + ret = zfcp_fsf_exchange_port_data(act); + if (ret == -EOPNOTSUPP) + return ZFCP_ERP_SUCCEEDED; + if (ret) + return ZFCP_ERP_FAILED; - if (retval == ZFCP_ERP_FAILED) { - ZFCP_LOG_INFO("Waiting to allow the adapter %s " - "to recover itself\n", - zfcp_get_busid_by_adapter(adapter)); - ssleep(ZFCP_TYPE2_RECOVERY_TIME); - } + zfcp_dbf_rec_run("erasox1", act); + wait_event(adapter->erp_ready_wq, + !list_empty(&adapter->erp_ready_head)); + zfcp_dbf_rec_run("erasox2", act); + if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) + return ZFCP_ERP_FAILED; - return retval; + return ZFCP_ERP_SUCCEEDED; } -/* - * 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) +static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *act) { - int retval; + if (zfcp_erp_adapter_strat_fsf_xconf(act) == ZFCP_ERP_FAILED) + return ZFCP_ERP_FAILED; - 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); + if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED) + return ZFCP_ERP_FAILED; - return retval; -} + if (mempool_resize(act->adapter->pool.sr_data, + act->adapter->stat_read_buf_num, GFP_KERNEL)) + return ZFCP_ERP_FAILED; -/* - * 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; + if (mempool_resize(act->adapter->pool.status_read_req, + act->adapter->stat_read_buf_num, GFP_KERNEL)) + return ZFCP_ERP_FAILED; - 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); + atomic_set(&act->adapter->stat_miss, act->adapter->stat_read_buf_num); + if (zfcp_status_read_refill(act->adapter)) + return ZFCP_ERP_FAILED; - return retval; + return ZFCP_ERP_SUCCEEDED; } -/* - * 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) +static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act) { - int retval = ZFCP_ERP_SUCCEEDED; + struct zfcp_adapter *adapter = act->adapter; - 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); + /* close queues to ensure that buffers are not accessed by adapter */ + zfcp_qdio_close(adapter->qdio); + zfcp_fsf_req_dismiss_all(adapter); + adapter->fsf_req_seq_no = 0; + zfcp_fc_wka_ports_force_offline(adapter->gs); + /* all ports and LUNs are closed */ + zfcp_erp_clear_adapter_status(adapter, ZFCP_STATUS_COMMON_OPEN); - failed_openfcp: - zfcp_close_fsf(erp_action->adapter); - failed_qdio: atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK | - ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | - ZFCP_STATUS_ADAPTER_XPORT_OK, - &erp_action->adapter->status); - out: - return retval; + ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); } -/* - * function: zfcp_qdio_init - * - * purpose: setup QDIO operation for specified adapter - * - * returns: 0 - successful setup - * !0 - failed setup - */ -static int -zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action) +static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *act) { - int retval; - int i; - volatile struct qdio_buffer_element *sbale; - struct zfcp_adapter *adapter = erp_action->adapter; + struct zfcp_adapter *adapter = act->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 = NULL; - } - - 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); + if (zfcp_qdio_open(adapter->qdio)) { + atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK | + ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, + &adapter->status); + return ZFCP_ERP_FAILED; } - /* 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) - ssleep(1); - debug_text_event(adapter->erp_dbf, 3, "qdio_down1b"); - - failed_qdio_establish: - failed_sanity: - retval = ZFCP_ERP_FAILED; - out: - return retval; -} - - -static int -zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action) -{ - int retval; - - retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action); - if (retval == ZFCP_ERP_FAILED) + if (zfcp_erp_adapter_strategy_open_fsf(act)) { + zfcp_erp_adapter_strategy_close(act); return ZFCP_ERP_FAILED; + } - retval = zfcp_erp_adapter_strategy_open_fsf_xport(erp_action); - if (retval == ZFCP_ERP_FAILED) - return ZFCP_ERP_FAILED; + atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &adapter->status); - return zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action); + return ZFCP_ERP_SUCCEEDED; } -static int -zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action) +static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *act) { - int retval = ZFCP_ERP_SUCCEEDED; - int retries; - int sleep = ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP; - struct zfcp_adapter *adapter = erp_action->adapter; - - atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status); + struct zfcp_adapter *adapter = act->adapter; - for (retries = ZFCP_EXCHANGE_CONFIG_DATA_RETRIES; retries; retries--) { - atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, - &adapter->status); - ZFCP_LOG_DEBUG("Doing exchange config data\n"); - write_lock_irq(&adapter->erp_lock); - zfcp_erp_action_to_running(erp_action); - write_unlock_irq(&adapter->erp_lock); - if (zfcp_fsf_exchange_config_data(erp_action)) { - 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)) - break; - - ZFCP_LOG_DEBUG("host connection still initialising... " - "waiting and retrying...\n"); - /* sleep a little bit before retry */ - ssleep(sleep); - sleep *= 2; + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN) { + zfcp_erp_adapter_strategy_close(act); + if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY) + return ZFCP_ERP_EXIT; } - atomic_clear_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; + if (zfcp_erp_adapter_strategy_open(act)) { + ssleep(8); + return ZFCP_ERP_FAILED; } - return retval; + return ZFCP_ERP_SUCCEEDED; } -static int -zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action) +static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act) { - int ret; - struct zfcp_adapter *adapter; - - adapter = erp_action->adapter; - atomic_clear_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); - - write_lock_irq(&adapter->erp_lock); - zfcp_erp_action_to_running(erp_action); - write_unlock_irq(&adapter->erp_lock); + int retval; - ret = zfcp_fsf_exchange_port_data(erp_action); - if (ret == -EOPNOTSUPP) { - debug_text_event(adapter->erp_dbf, 3, "a_xport_notsupp"); - return ZFCP_ERP_SUCCEEDED; - } else if (ret) { - debug_text_event(adapter->erp_dbf, 3, "a_xport_failed"); + retval = zfcp_fsf_close_physical_port(act); + if (retval == -ENOMEM) + return ZFCP_ERP_NOMEM; + act->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING; + if (retval) return ZFCP_ERP_FAILED; - } - debug_text_event(adapter->erp_dbf, 6, "a_xport_ok"); - - ret = ZFCP_ERP_SUCCEEDED; - down(&adapter->erp_ready_sem); - if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) { - ZFCP_LOG_INFO("error: exchange port data timed out (adapter " - "%s)\n", zfcp_get_busid_by_adapter(adapter)); - ret = ZFCP_ERP_FAILED; - } - /* don't treat as error for the sake of compatibility */ - if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status)) - ZFCP_LOG_INFO("warning: exchange port data failed (adapter " - "%s\n", zfcp_get_busid_by_adapter(adapter)); - - return ret; + return ZFCP_ERP_CONTINUES; } -static int -zfcp_erp_adapter_strategy_open_fsf_statusread(struct zfcp_erp_action - *erp_action) +static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port) { - 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; + atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status); } -/* - * 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) +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; + int status = atomic_read(&port->status); 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; + if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) && + (status & ZFCP_STATUS_COMMON_OPEN)) + return zfcp_erp_port_forced_strategy_close(erp_action); + else + return ZFCP_ERP_FAILED; case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: - if (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; + if (!(status & ZFCP_STATUS_PORT_PHYS_OPEN)) + return ZFCP_ERP_SUCCEEDED; } - - 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; + return ZFCP_ERP_FAILED; } -/* - * 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) +static int zfcp_erp_port_strategy_close(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)); + int retval; - return retval; + retval = zfcp_fsf_close_port(erp_action); + if (retval == -ENOMEM) + return ZFCP_ERP_NOMEM; + erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING; + if (retval) + return ZFCP_ERP_FAILED; + return ZFCP_ERP_CONTINUES; } -static int -zfcp_erp_port_strategy_open(struct zfcp_erp_action *erp_action) +static int zfcp_erp_port_strategy_open_port(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; + retval = zfcp_fsf_open_port(erp_action); + if (retval == -ENOMEM) + return ZFCP_ERP_NOMEM; + erp_action->step = ZFCP_ERP_STEP_PORT_OPENING; + if (retval) + return ZFCP_ERP_FAILED; + return ZFCP_ERP_CONTINUES; } -static int -zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action) +static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act) { - int retval = 0; - struct zfcp_adapter *adapter = erp_action->adapter; - struct zfcp_port *port = erp_action->port; + struct zfcp_adapter *adapter = act->adapter; + struct zfcp_port *port = act->port; - switch (erp_action->step) { + if (port->wwpn != adapter->peer_wwpn) { + zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED); + return ZFCP_ERP_FAILED; + } + port->d_id = adapter->peer_d_id; + return zfcp_erp_port_strategy_open_port(act); +} +static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act) +{ + struct zfcp_adapter *adapter = act->adapter; + struct zfcp_port *port = act->port; + int p_status = atomic_read(&port->status); + + switch (act->step) { case ZFCP_ERP_STEP_UNINITIALIZED: case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: case ZFCP_ERP_STEP_PORT_CLOSING: - if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) { - if (port->wwpn != adapter->peer_wwpn) { - ZFCP_LOG_NORMAL("Failed to open port 0x%016Lx " - "on adapter %s.\nPeer WWPN " - "0x%016Lx does not match\n", - port->wwpn, - zfcp_get_busid_by_adapter(adapter), - adapter->peer_wwpn); - zfcp_erp_port_failed(port); - retval = ZFCP_ERP_FAILED; - break; - } - port->d_id = adapter->peer_d_id; - atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status); - retval = zfcp_erp_port_strategy_open_port(erp_action); - break; - } - 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); + if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) + return zfcp_erp_open_ptp_port(act); + if (!port->d_id) { + zfcp_fc_trigger_did_lookup(port); + return ZFCP_ERP_EXIT; } - 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%06x -> " - "trying open\n", port->wwpn, port->d_id); - retval = zfcp_erp_port_strategy_open_port(erp_action); - } - break; + return zfcp_erp_port_strategy_open_port(act); 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; + if (p_status & ZFCP_STATUS_COMMON_OPEN) { + if (!port->d_id) { + zfcp_fc_trigger_did_lookup(port); + return ZFCP_ERP_EXIT; + } + return ZFCP_ERP_SUCCEEDED; } - break; - - default: - ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\n", - erp_action->step); - retval = ZFCP_ERP_FAILED; + if (port->d_id && !(p_status & ZFCP_STATUS_COMMON_NOESC)) { + port->d_id = 0; + return ZFCP_ERP_FAILED; + } + /* fall through otherwise */ } - - return retval; + return ZFCP_ERP_FAILED; } -static int -zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *erp_action) +static int zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action) { - int retval; struct zfcp_port *port = erp_action->port; + int p_status = atomic_read(&port->status); - switch (erp_action->step) { + if ((p_status & ZFCP_STATUS_COMMON_NOESC) && + !(p_status & ZFCP_STATUS_COMMON_OPEN)) + goto close_init_done; + switch (erp_action->step) { case ZFCP_ERP_STEP_UNINITIALIZED: - case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: - case ZFCP_ERP_STEP_PORT_CLOSING: - ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%06x -> trying open\n", - port->wwpn, port->d_id); - retval = zfcp_erp_port_strategy_open_port(erp_action); + zfcp_erp_port_strategy_clearstati(port); + if (p_status & ZFCP_STATUS_COMMON_OPEN) + return zfcp_erp_port_strategy_close(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); + case ZFCP_ERP_STEP_PORT_CLOSING: + if (p_status & ZFCP_STATUS_COMMON_OPEN) + return ZFCP_ERP_FAILED; break; - - default: - ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\n", - erp_action->step); - retval = ZFCP_ERP_FAILED; } - return retval; +close_init_done: + if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY) + return ZFCP_ERP_EXIT; + + return zfcp_erp_port_strategy_open_common(erp_action); } -/* - * 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) +static void zfcp_erp_lun_strategy_clearstati(struct scsi_device *sdev) { - int retval = 0; - unsigned long flags; - struct zfcp_adapter *adapter = ns_erp_action->adapter; - struct zfcp_erp_action *erp_action, *tmp; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - 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; + atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, + &zfcp_sdev->status); } -/* - * 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) +static int zfcp_erp_lun_strategy_close(struct zfcp_erp_action *erp_action) { - int retval; - struct zfcp_adapter *adapter = erp_action->adapter; - struct zfcp_port *port = erp_action->port; - - 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; + int retval = zfcp_fsf_close_lun(erp_action); + if (retval == -ENOMEM) + return ZFCP_ERP_NOMEM; + erp_action->step = ZFCP_ERP_STEP_LUN_CLOSING; + if (retval) + return ZFCP_ERP_FAILED; + return ZFCP_ERP_CONTINUES; } -static int -zfcp_erp_port_strategy_clearstati(struct zfcp_port *port) +static int zfcp_erp_lun_strategy_open(struct zfcp_erp_action *erp_action) { - 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; + int retval = zfcp_fsf_open_lun(erp_action); + if (retval == -ENOMEM) + return ZFCP_ERP_NOMEM; + erp_action->step = ZFCP_ERP_STEP_LUN_OPENING; + if (retval) + return ZFCP_ERP_FAILED; + return ZFCP_ERP_CONTINUES; } -/* - * 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) +static int zfcp_erp_lun_strategy(struct zfcp_erp_action *erp_action) { - int retval; - struct zfcp_adapter *adapter = erp_action->adapter; - struct zfcp_port *port = erp_action->port; + struct scsi_device *sdev = erp_action->sdev; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - 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; + switch (erp_action->step) { + case ZFCP_ERP_STEP_UNINITIALIZED: + zfcp_erp_lun_strategy_clearstati(sdev); + if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) + return zfcp_erp_lun_strategy_close(erp_action); + /* already closed, fall through */ + case ZFCP_ERP_STEP_LUN_CLOSING: + if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) + return ZFCP_ERP_FAILED; + if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY) + return ZFCP_ERP_EXIT; + return zfcp_erp_lun_strategy_open(erp_action); + + case ZFCP_ERP_STEP_LUN_OPENING: + if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_OPEN) + return ZFCP_ERP_SUCCEEDED; } - 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; + return ZFCP_ERP_FAILED; } -/* - * 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) +static int zfcp_erp_strategy_check_lun(struct scsi_device *sdev, int result) { - int retval; - struct zfcp_adapter *adapter = erp_action->adapter; - struct zfcp_port *port = erp_action->port; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - 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; + switch (result) { + case ZFCP_ERP_SUCCEEDED : + atomic_set(&zfcp_sdev->erp_counter, 0); + zfcp_erp_lun_unblock(sdev); + break; + case ZFCP_ERP_FAILED : + atomic_inc(&zfcp_sdev->erp_counter); + if (atomic_read(&zfcp_sdev->erp_counter) > ZFCP_MAX_ERPS) { + dev_err(&zfcp_sdev->port->adapter->ccw_device->dev, + "ERP failed for LUN 0x%016Lx on " + "port 0x%016Lx\n", + (unsigned long long)zfcp_scsi_dev_lun(sdev), + (unsigned long long)zfcp_sdev->port->wwpn); + zfcp_erp_set_lun_status(sdev, + ZFCP_STATUS_COMMON_ERP_FAILED); + } + break; } - 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; + + if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { + zfcp_erp_lun_block(sdev, 0); + result = ZFCP_ERP_EXIT; } - 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; + return result; } -/* - * 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) +static int zfcp_erp_strategy_check_port(struct zfcp_port *port, int result) { - int retval; - struct zfcp_adapter *adapter = erp_action->adapter; - struct zfcp_port *port = erp_action->port; + switch (result) { + case ZFCP_ERP_SUCCEEDED : + atomic_set(&port->erp_counter, 0); + zfcp_erp_port_unblock(port); + break; - 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; + case ZFCP_ERP_FAILED : + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC) { + zfcp_erp_port_block(port, 0); + result = ZFCP_ERP_EXIT; + } + atomic_inc(&port->erp_counter); + if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) { + dev_err(&port->adapter->ccw_device->dev, + "ERP failed for remote port 0x%016Lx\n", + (unsigned long long)port->wwpn); + zfcp_erp_set_port_status(port, + ZFCP_STATUS_COMMON_ERP_FAILED); + } + break; } - 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; + + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { + zfcp_erp_port_block(port, 0); + result = ZFCP_ERP_EXIT; } - 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; + return result; } -/* - * 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) +static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter, + int result) { - 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); - } - } + switch (result) { + case ZFCP_ERP_SUCCEEDED : + atomic_set(&adapter->erp_counter, 0); + zfcp_erp_adapter_unblock(adapter); 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; + case ZFCP_ERP_FAILED : + atomic_inc(&adapter->erp_counter); + if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) { + dev_err(&adapter->ccw_device->dev, + "ERP cannot recover an error " + "on the FCP device\n"); + zfcp_erp_set_adapter_status(adapter, + ZFCP_STATUS_COMMON_ERP_FAILED); } break; } - 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; + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { + zfcp_erp_adapter_block(adapter, 0); + result = ZFCP_ERP_EXIT; + } + return result; } -static int -zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit) +static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *erp_action, + int result) { - 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); + struct zfcp_adapter *adapter = erp_action->adapter; + struct zfcp_port *port = erp_action->port; + struct scsi_device *sdev = erp_action->sdev; - return retval; -} + switch (erp_action->action) { -/* - * 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; - - 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; + case ZFCP_ERP_ACTION_REOPEN_LUN: + result = zfcp_erp_strategy_check_lun(sdev, result); + break; - out: - return retval; -} + case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: + case ZFCP_ERP_ACTION_REOPEN_PORT: + result = zfcp_erp_strategy_check_port(port, result); + break; -/* - * 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; - - 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; + case ZFCP_ERP_ACTION_REOPEN_ADAPTER: + result = zfcp_erp_strategy_check_adapter(adapter, result); + break; } - 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; + return result; } -void zfcp_erp_start_timer(struct zfcp_fsf_req *fsf_req) +static int zfcp_erp_strat_change_det(atomic_t *target_status, u32 erp_status) { - BUG_ON(!fsf_req->erp_action); - fsf_req->timer.function = zfcp_erp_timeout_handler; - fsf_req->timer.data = (unsigned long) fsf_req->erp_action; - fsf_req->timer.expires = jiffies + ZFCP_ERP_FSFREQ_TIMEOUT; - add_timer(&fsf_req->timer); -} + int status = atomic_read(target_status); -/* - * 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; + if ((status & ZFCP_STATUS_COMMON_RUNNING) && + (erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY)) + return 1; /* take it online */ - 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 !!! */ + if (!(status & ZFCP_STATUS_COMMON_RUNNING) && + !(erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY)) + return 1; /* take it offline */ - 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 !!! */ + return 0; +} - case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: - if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, - &port->status)) { - if (port->erp_action.action != - ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) { - ZFCP_LOG_INFO("dropped erp action %i (port " - "0x%016Lx, action in use: %i)\n", - action, port->wwpn, - port->erp_action.action); - debug_text_event(adapter->erp_dbf, 4, - "pf_actenq_drp"); - } else - debug_text_event(adapter->erp_dbf, 4, - "pf_actenq_drpcp"); - 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 !!! */ +static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret) +{ + int action = act->action; + struct zfcp_adapter *adapter = act->adapter; + struct zfcp_port *port = act->port; + struct scsi_device *sdev = act->sdev; + struct zfcp_scsi_dev *zfcp_sdev; + u32 erp_status = act->status; + switch (action) { case ZFCP_ERP_ACTION_REOPEN_ADAPTER: - if (atomic_test_mask - (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) { - debug_text_event(adapter->erp_dbf, 4, "a_actenq_drp"); - goto out; + if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) { + _zfcp_erp_adapter_reopen(adapter, + ZFCP_STATUS_COMMON_ERP_FAILED, + "ersscg1"); + return ZFCP_ERP_EXIT; } break; - 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; + case ZFCP_ERP_ACTION_REOPEN_PORT: + if (zfcp_erp_strat_change_det(&port->status, erp_status)) { + _zfcp_erp_port_reopen(port, + ZFCP_STATUS_COMMON_ERP_FAILED, + "ersscg2"); + return ZFCP_ERP_EXIT; + } break; - case ZFCP_ERP_ACTION_REOPEN_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; + case ZFCP_ERP_ACTION_REOPEN_LUN: + zfcp_sdev = sdev_to_zfcp(sdev); + if (zfcp_erp_strat_change_det(&zfcp_sdev->status, erp_status)) { + _zfcp_erp_lun_reopen(sdev, + ZFCP_STATUS_COMMON_ERP_FAILED, + "ersscg3", 0); + return ZFCP_ERP_EXIT; + } break; } - - 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_tail(&erp_action->list, &adapter->erp_ready_head); - up(&adapter->erp_ready_sem); - retval = 0; - out: - return retval; + return ret; } -static int -zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) +static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) { - int retval = 0; struct zfcp_adapter *adapter = erp_action->adapter; + struct zfcp_scsi_dev *zfcp_sdev; - --adapter->erp_total_count; + adapter->erp_total_count--; if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { - --adapter->erp_low_mem_count; + 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); + zfcp_dbf_rec_run("eractd1", erp_action); + switch (erp_action->action) { - case ZFCP_ERP_ACTION_REOPEN_UNIT: + case ZFCP_ERP_ACTION_REOPEN_LUN: + zfcp_sdev = sdev_to_zfcp(erp_action->sdev); atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, - &erp_action->unit->status); + &zfcp_sdev->status); break; + case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: case ZFCP_ERP_ACTION_REOPEN_PORT: atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->port->status); break; + case ZFCP_ERP_ACTION_REOPEN_ADAPTER: atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->adapter->status); break; - default: - /* bug */ - break; } - return retval; } -/** - * zfcp_erp_action_cleanup - * - * Register unit with scsi stack if appropriate 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) +static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, 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 - && port->rport) { - atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED, - &unit->status); - if (atomic_test_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, - &unit->status) == 0) - zfcp_erp_schedule_work(unit); - } - zfcp_unit_put(unit); + struct zfcp_adapter *adapter = act->adapter; + struct zfcp_port *port = act->port; + struct scsi_device *sdev = act->sdev; + + switch (act->action) { + case ZFCP_ERP_ACTION_REOPEN_LUN: + if (!(act->status & ZFCP_STATUS_ERP_NO_REF)) + scsi_device_put(sdev); break; - case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: - case ZFCP_ERP_ACTION_REOPEN_PORT: - if (atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN, - &port->status)) { - zfcp_port_put(port); - break; - } - if ((result == ZFCP_ERP_SUCCEEDED) - && !port->rport) { - struct fc_rport_identifiers ids; - ids.node_name = port->wwnn; - ids.port_name = port->wwpn; - ids.port_id = port->d_id; - ids.roles = FC_RPORT_ROLE_FCP_TARGET; - port->rport = - fc_remote_port_add(adapter->scsi_host, 0, &ids); - if (!port->rport) - ZFCP_LOG_NORMAL("failed registration of rport" - "(adapter %s, wwpn=0x%016Lx)\n", - zfcp_get_busid_by_port(port), - port->wwpn); - else { - scsi_target_unblock(&port->rport->dev); - port->rport->maxframe_size = port->maxframe_size; - port->rport->supported_classes = - port->supported_classes; - } - } - if ((result != ZFCP_ERP_SUCCEEDED) && port->rport) { - fc_remote_port_delete(port->rport); - port->rport = NULL; - } - zfcp_port_put(port); + case ZFCP_ERP_ACTION_REOPEN_PORT: + if (result == ZFCP_ERP_SUCCEEDED) + zfcp_scsi_schedule_rport_register(port); + /* fall through */ + case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: + put_device(&port->dev); break; + case ZFCP_ERP_ACTION_REOPEN_ADAPTER: - if (result != ZFCP_ERP_SUCCEEDED) { - list_for_each_entry(port, &adapter->port_list_head, list) - if (port->rport && - !atomic_test_mask(ZFCP_STATUS_PORT_WKA, - &port->status)) { - fc_remote_port_delete(port->rport); - port->rport = NULL; - } - } - zfcp_adapter_put(adapter); - break; - default: + if (result == ZFCP_ERP_SUCCEEDED) { + register_service_level(&adapter->service_level); + zfcp_fc_conditional_port_scan(adapter); + queue_work(adapter->work_queue, &adapter->ns_up_work); + } else + unregister_service_level(&adapter->service_level); + + kref_put(&adapter->ref, zfcp_adapter_release); break; } } - -static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) +static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action) { - 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); + switch (erp_action->action) { + case ZFCP_ERP_ACTION_REOPEN_ADAPTER: + return zfcp_erp_adapter_strategy(erp_action); + case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: + return zfcp_erp_port_forced_strategy(erp_action); + case ZFCP_ERP_ACTION_REOPEN_PORT: + return zfcp_erp_port_strategy(erp_action); + case ZFCP_ERP_ACTION_REOPEN_LUN: + return zfcp_erp_lun_strategy(erp_action); + } + return ZFCP_ERP_FAILED; } -static void zfcp_erp_action_dismiss_port(struct zfcp_port *port) +static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action) { - struct zfcp_unit *unit; - struct zfcp_adapter *adapter = port->adapter; + int retval; + unsigned long flags; + struct zfcp_adapter *adapter = erp_action->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); + kref_get(&adapter->ref); + + write_lock_irqsave(&adapter->erp_lock, flags); + zfcp_erp_strategy_check_fsfreq(erp_action); + + if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) { + zfcp_erp_action_dequeue(erp_action); + retval = ZFCP_ERP_DISMISSED; + goto unlock; + } + + if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) { + retval = ZFCP_ERP_FAILED; + goto check_target; + } + + zfcp_erp_action_to_running(erp_action); + + /* no lock to allow for blocking operations */ + write_unlock_irqrestore(&adapter->erp_lock, flags); + retval = zfcp_erp_strategy_do_action(erp_action); + write_lock_irqsave(&adapter->erp_lock, flags); + + if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) + retval = ZFCP_ERP_CONTINUES; + + switch (retval) { + case ZFCP_ERP_NOMEM: + if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) { + ++adapter->erp_low_mem_count; + erp_action->status |= ZFCP_STATUS_ERP_LOWMEM; + } + if (adapter->erp_total_count == adapter->erp_low_mem_count) + _zfcp_erp_adapter_reopen(adapter, 0, "erstgy1"); + else { + zfcp_erp_strategy_memwait(erp_action); + retval = ZFCP_ERP_CONTINUES; + } + goto unlock; + + case ZFCP_ERP_CONTINUES: + if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { + --adapter->erp_low_mem_count; + erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM; + } + goto unlock; + } + +check_target: + retval = zfcp_erp_strategy_check_target(erp_action, retval); + zfcp_erp_action_dequeue(erp_action); + retval = zfcp_erp_strategy_statechange(erp_action, retval); + if (retval == ZFCP_ERP_EXIT) + goto unlock; + if (retval == ZFCP_ERP_SUCCEEDED) + zfcp_erp_strategy_followup_success(erp_action); + if (retval == ZFCP_ERP_FAILED) + zfcp_erp_strategy_followup_failed(erp_action); + + unlock: + write_unlock_irqrestore(&adapter->erp_lock, flags); + + if (retval != ZFCP_ERP_CONTINUES) + zfcp_erp_action_cleanup(erp_action, retval); + + kref_put(&adapter->ref, zfcp_adapter_release); + return retval; } -static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit) +static int zfcp_erp_thread(void *data) { - struct zfcp_adapter *adapter = unit->port->adapter; + struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; + struct list_head *next; + struct zfcp_erp_action *act; + unsigned long flags; - 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); + for (;;) { + wait_event_interruptible(adapter->erp_ready_wq, + !list_empty(&adapter->erp_ready_head) || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + write_lock_irqsave(&adapter->erp_lock, flags); + next = adapter->erp_ready_head.next; + write_unlock_irqrestore(&adapter->erp_lock, flags); + + if (next != &adapter->erp_ready_head) { + act = list_entry(next, struct zfcp_erp_action, list); + + /* there is more to come after dismission, no notify */ + if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED) + zfcp_erp_wakeup(adapter); + } + } + + return 0; } -static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action) +/** + * zfcp_erp_thread_setup - Start ERP thread for adapter + * @adapter: Adapter to start the ERP thread for + * + * Returns 0 on success or error code from kernel_thread() + */ +int zfcp_erp_thread_setup(struct zfcp_adapter *adapter) { - struct zfcp_adapter *adapter = erp_action->adapter; + struct task_struct *thread; - 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); + thread = kthread_run(zfcp_erp_thread, adapter, "zfcperp%s", + dev_name(&adapter->ccw_device->dev)); + if (IS_ERR(thread)) { + dev_err(&adapter->ccw_device->dev, + "Creating an ERP thread for the FCP device failed.\n"); + return PTR_ERR(thread); + } + + adapter->erp_thread = thread; + return 0; } -static void zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action) +/** + * zfcp_erp_thread_kill - Stop ERP thread. + * @adapter: Adapter where the ERP thread should be stopped. + * + * The caller of this routine ensures that the specified adapter has + * been shut down and that this operation has been completed. Thus, + * there are no pending erp_actions which would need to be handled + * here. + */ +void zfcp_erp_thread_kill(struct zfcp_adapter *adapter) { - struct zfcp_adapter *adapter = erp_action->adapter; + kthread_stop(adapter->erp_thread); + adapter->erp_thread = NULL; + WARN_ON(!list_empty(&adapter->erp_ready_head)); + WARN_ON(!list_empty(&adapter->erp_running_head)); +} - 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); +/** + * zfcp_erp_wait - wait for completion of error recovery on an adapter + * @adapter: adapter for which to wait for completion of its error recovery + */ +void zfcp_erp_wait(struct zfcp_adapter *adapter) +{ + wait_event(adapter->erp_done_wqh, + !(atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_ERP_PENDING)); } -void -zfcp_erp_port_boxed(struct zfcp_port *port) +/** + * zfcp_erp_set_adapter_status - set adapter status bits + * @adapter: adapter to change the status + * @mask: status bits to change + * + * Changes in common status bits are propagated to attached ports and LUNs. + */ +void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask) { - struct zfcp_adapter *adapter = port->adapter; + struct zfcp_port *port; + struct scsi_device *sdev; unsigned long flags; + u32 common_mask = mask & ZFCP_COMMON_FLAGS; - debug_text_event(adapter->erp_dbf, 3, "p_access_boxed"); - 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_ACCESS_BOXED, - ZFCP_SET); - read_unlock_irqrestore(&zfcp_data.config_lock, flags); - zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED); -} + atomic_set_mask(mask, &adapter->status); -void -zfcp_erp_unit_boxed(struct zfcp_unit *unit) -{ - struct zfcp_adapter *adapter = unit->port->adapter; + if (!common_mask) + return; + + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) + atomic_set_mask(common_mask, &port->status); + read_unlock_irqrestore(&adapter->port_list_lock, flags); - debug_text_event(adapter->erp_dbf, 3, "u_access_boxed"); - debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t)); - zfcp_erp_modify_unit_status(unit, - ZFCP_STATUS_COMMON_ACCESS_BOXED, - ZFCP_SET); - zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED); + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, adapter->scsi_host) + atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status); + spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } -void -zfcp_erp_port_access_denied(struct zfcp_port *port) +/** + * zfcp_erp_clear_adapter_status - clear adapter status bits + * @adapter: adapter to change the status + * @mask: status bits to change + * + * Changes in common status bits are propagated to attached ports and LUNs. + */ +void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask) { - struct zfcp_adapter *adapter = port->adapter; + struct zfcp_port *port; + struct scsi_device *sdev; unsigned long flags; + u32 common_mask = mask & ZFCP_COMMON_FLAGS; + u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; + + atomic_clear_mask(mask, &adapter->status); - debug_text_event(adapter->erp_dbf, 3, "p_access_denied"); - 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); + if (!common_mask) + return; + + if (clear_counter) + atomic_set(&adapter->erp_counter, 0); + + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) { + atomic_clear_mask(common_mask, &port->status); + if (clear_counter) + atomic_set(&port->erp_counter, 0); + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); + + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, adapter->scsi_host) { + atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status); + if (clear_counter) + atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); + } + spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } -void -zfcp_erp_unit_access_denied(struct zfcp_unit *unit) +/** + * zfcp_erp_set_port_status - set port status bits + * @port: port to change the status + * @mask: status bits to change + * + * Changes in common status bits are propagated to attached LUNs. + */ +void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask) { - struct zfcp_adapter *adapter = unit->port->adapter; + struct scsi_device *sdev; + u32 common_mask = mask & ZFCP_COMMON_FLAGS; + unsigned long flags; + + atomic_set_mask(mask, &port->status); - debug_text_event(adapter->erp_dbf, 3, "u_access_denied"); - 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); + if (!common_mask) + return; + + spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, port->adapter->scsi_host) + if (sdev_to_zfcp(sdev)->port == port) + atomic_set_mask(common_mask, + &sdev_to_zfcp(sdev)->status); + spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } -void -zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter) +/** + * zfcp_erp_clear_port_status - clear port status bits + * @port: adapter to change the status + * @mask: status bits to change + * + * Changes in common status bits are propagated to attached LUNs. + */ +void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) { - struct zfcp_port *port; + struct scsi_device *sdev; + u32 common_mask = mask & ZFCP_COMMON_FLAGS; + u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; unsigned long flags; - if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) + atomic_clear_mask(mask, &port->status); + + if (!common_mask) return; - debug_text_event(adapter->erp_dbf, 3, "a_access_recover"); - debug_event(adapter->erp_dbf, 3, zfcp_get_busid_by_adapter(adapter), 8); + if (clear_counter) + atomic_set(&port->erp_counter, 0); - read_lock_irqsave(&zfcp_data.config_lock, flags); - if (adapter->nameserver_port) - zfcp_erp_port_access_changed(adapter->nameserver_port); - 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); + spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, port->adapter->scsi_host) + if (sdev_to_zfcp(sdev)->port == port) { + atomic_clear_mask(common_mask, + &sdev_to_zfcp(sdev)->status); + if (clear_counter) + atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); + } + spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } -void -zfcp_erp_port_access_changed(struct zfcp_port *port) +/** + * zfcp_erp_set_lun_status - set lun status bits + * @sdev: SCSI device / lun to set the status bits + * @mask: status bits to change + */ +void zfcp_erp_set_lun_status(struct scsi_device *sdev, u32 mask) { - struct zfcp_adapter *adapter = port->adapter; - struct zfcp_unit *unit; - - debug_text_event(adapter->erp_dbf, 3, "p_access_recover"); - debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t)); - - if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, - &port->status) && - !atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED, - &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; - } + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - 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); + atomic_set_mask(mask, &zfcp_sdev->status); } -void -zfcp_erp_unit_access_changed(struct zfcp_unit *unit) +/** + * zfcp_erp_clear_lun_status - clear lun status bits + * @sdev: SCSi device / lun to clear the status bits + * @mask: status bits to change + */ +void zfcp_erp_clear_lun_status(struct scsi_device *sdev, u32 mask) { - struct zfcp_adapter *adapter = unit->port->adapter; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - debug_text_event(adapter->erp_dbf, 3, "u_access_recover"); - debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t)); - - if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, - &unit->status) && - !atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED, - &unit->status)) - return; + atomic_clear_mask(mask, &zfcp_sdev->status); - 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); + if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) + atomic_set(&zfcp_sdev->erp_counter, 0); } -#undef ZFCP_LOG_AREA diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 06b1079b7f3..a9c570a09b8 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -1,190 +1,159 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * External function declarations. * - * 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. + * Copyright IBM Corp. 2002, 2010 */ #ifndef ZFCP_EXT_H #define ZFCP_EXT_H +#include <linux/types.h> +#include <scsi/fc/fc_els.h> #include "zfcp_def.h" +#include "zfcp_fc.h" -extern struct zfcp_data zfcp_data; - -/******************************** SYSFS *************************************/ -extern struct attribute_group *zfcp_driver_attr_groups[]; -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 *); +/* zfcp_aux.c */ +extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, u64); 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_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 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_config_data_sync(struct zfcp_adapter *, - struct fsf_qtcb_bottom_config *); -extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *); -extern int zfcp_fsf_exchange_port_data_sync(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_start_timer(struct zfcp_fsf_req *, unsigned long); -extern void zfcp_erp_start_timer(struct zfcp_fsf_req *); +extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32, + u32); +extern void zfcp_sg_free_table(struct scatterlist *, int); +extern int zfcp_sg_setup_table(struct scatterlist *, int); +extern void zfcp_adapter_release(struct kref *); +extern void zfcp_adapter_unregister(struct zfcp_adapter *); + +/* zfcp_ccw.c */ +extern struct ccw_driver zfcp_ccw_driver; +extern struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *); +extern void zfcp_ccw_adapter_put(struct zfcp_adapter *); + +/* zfcp_dbf.c */ +extern int zfcp_dbf_adapter_register(struct zfcp_adapter *); +extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *); +extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *, + struct zfcp_port *, struct scsi_device *, u8, u8); +extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *); +extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_fsf_res(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **); +extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *); +extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32); +extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_scsi(char *, struct scsi_cmnd *, struct zfcp_fsf_req *); + +/* zfcp_erp.c */ +extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32); +extern void zfcp_erp_clear_adapter_status(struct zfcp_adapter *, u32); +extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, char *); +extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, char *); +extern void zfcp_erp_set_port_status(struct zfcp_port *, u32); +extern void zfcp_erp_clear_port_status(struct zfcp_port *, u32); +extern int zfcp_erp_port_reopen(struct zfcp_port *, int, char *); +extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, char *); +extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, char *); +extern void zfcp_erp_set_lun_status(struct scsi_device *, u32); +extern void zfcp_erp_clear_lun_status(struct scsi_device *, u32); +extern void zfcp_erp_lun_reopen(struct scsi_device *, int, char *); +extern void zfcp_erp_lun_shutdown(struct scsi_device *, int, char *); +extern void zfcp_erp_lun_shutdown_wait(struct scsi_device *, char *); +extern int zfcp_erp_thread_setup(struct zfcp_adapter *); +extern void zfcp_erp_thread_kill(struct zfcp_adapter *); +extern void zfcp_erp_wait(struct zfcp_adapter *); +extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long); +extern void zfcp_erp_timeout_handler(unsigned long); + +/* zfcp_fc.c */ +extern struct kmem_cache *zfcp_fc_req_cache; +extern void zfcp_fc_enqueue_event(struct zfcp_adapter *, + enum fc_host_event_code event_code, u32); +extern void zfcp_fc_post_event(struct work_struct *); +extern void zfcp_fc_scan_ports(struct work_struct *); +extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *); +extern void zfcp_fc_port_did_lookup(struct work_struct *); +extern void zfcp_fc_trigger_did_lookup(struct zfcp_port *); +extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fc_els_flogi *); +extern void zfcp_fc_test_link(struct zfcp_port *); +extern void zfcp_fc_link_test_work(struct work_struct *); +extern void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *); +extern int zfcp_fc_gs_setup(struct zfcp_adapter *); +extern void zfcp_fc_gs_destroy(struct zfcp_adapter *); +extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *); +extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *); +extern void zfcp_fc_sym_name_update(struct work_struct *); +extern void zfcp_fc_conditional_port_scan(struct zfcp_adapter *); +extern void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *); + +/* zfcp_fsf.c */ +extern struct kmem_cache *zfcp_fsf_qtcb_cache; +extern int zfcp_fsf_open_port(struct zfcp_erp_action *); +extern int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *); +extern int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *); +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_lun(struct zfcp_erp_action *); +extern int zfcp_fsf_close_lun(struct zfcp_erp_action *); +extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *); +extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *, + struct fsf_qtcb_bottom_config *); +extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *); +extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *, + struct fsf_qtcb_bottom_port *); extern void 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_send_fcp_command_task(struct zfcp_adapter *, - struct zfcp_unit *, - struct scsi_cmnd *, int, int); -extern int zfcp_fsf_req_complete(struct zfcp_fsf_req *); -extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *); +extern int zfcp_fsf_status_read(struct zfcp_qdio *); +extern int zfcp_status_read_refill(struct zfcp_adapter *adapter); +extern int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *, struct zfcp_fsf_ct_els *, + mempool_t *, unsigned int); +extern int zfcp_fsf_send_els(struct zfcp_adapter *, u32, + struct zfcp_fsf_ct_els *, unsigned int); +extern int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *); extern void zfcp_fsf_req_free(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 *); -extern void zfcp_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *); - -/******************************* 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(int *, char); -extern void set_driver_byte(int *, char); -extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *); -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 *, int); -extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *, int); +extern struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *, u8); +extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_cmnd(struct scsi_cmnd *); +extern void zfcp_fsf_reqid_check(struct zfcp_qdio *, int); + +/* zfcp_qdio.c */ +extern int zfcp_qdio_setup(struct zfcp_adapter *); +extern void zfcp_qdio_destroy(struct zfcp_qdio *); +extern int zfcp_qdio_sbal_get(struct zfcp_qdio *); +extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_qdio_req *); +extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, struct zfcp_qdio_req *, + struct scatterlist *); +extern int zfcp_qdio_open(struct zfcp_qdio *); +extern void zfcp_qdio_close(struct zfcp_qdio *); +extern void zfcp_qdio_siosl(struct zfcp_adapter *); + +/* zfcp_scsi.c */ +extern struct scsi_transport_template *zfcp_scsi_transport_template; +extern int zfcp_scsi_adapter_register(struct zfcp_adapter *); +extern void zfcp_scsi_adapter_unregister(struct zfcp_adapter *); 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 void zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long); - -extern int zfcp_test_link(struct zfcp_port *); - -extern void zfcp_erp_port_boxed(struct zfcp_port *); -extern void zfcp_erp_unit_boxed(struct zfcp_unit *); -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_hba_dbf_event_fsf_response(struct zfcp_fsf_req *); -extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *, - struct fsf_status_read_buffer *); -extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *, - unsigned int, unsigned int, unsigned int, - int, int); - -extern void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *); -extern void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *); -extern void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *); -extern void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *); -extern void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *); - -extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *, - struct scsi_cmnd *, - struct zfcp_fsf_req *); -extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *, - struct scsi_cmnd *, struct zfcp_fsf_req *, - unsigned long); -extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *, - struct scsi_cmnd *); -extern int zfcp_reqlist_isempty(struct zfcp_adapter *); +extern void zfcp_scsi_rport_work(struct work_struct *); +extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *); +extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *); +extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *); +extern void zfcp_scsi_set_prot(struct zfcp_adapter *); +extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int); + +/* zfcp_sysfs.c */ +extern const struct attribute_group *zfcp_unit_attr_groups[]; +extern struct attribute_group zfcp_sysfs_adapter_attrs; +extern const struct attribute_group *zfcp_port_attr_groups[]; +extern struct mutex zfcp_sysfs_port_units_mutex; +extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; +extern struct device_attribute *zfcp_sysfs_shost_attrs[]; + +/* zfcp_unit.c */ +extern int zfcp_unit_add(struct zfcp_port *, u64); +extern int zfcp_unit_remove(struct zfcp_port *, u64); +extern struct zfcp_unit *zfcp_unit_find(struct zfcp_port *, u64); +extern struct scsi_device *zfcp_unit_sdev(struct zfcp_unit *unit); +extern void zfcp_unit_scsi_scan(struct zfcp_unit *); +extern void zfcp_unit_queue_scsi_scan(struct zfcp_port *); +extern unsigned int zfcp_unit_sdev_status(struct zfcp_unit *); #endif /* ZFCP_EXT_H */ diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c new file mode 100644 index 00000000000..ca28e1c6611 --- /dev/null +++ b/drivers/s390/scsi/zfcp_fc.c @@ -0,0 +1,991 @@ +/* + * zfcp device driver + * + * Fibre Channel related functions for the zfcp device driver. + * + * Copyright IBM Corp. 2008, 2010 + */ + +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/utsname.h> +#include <scsi/fc/fc_els.h> +#include <scsi/libfc.h> +#include "zfcp_ext.h" +#include "zfcp_fc.h" + +struct kmem_cache *zfcp_fc_req_cache; + +static u32 zfcp_fc_rscn_range_mask[] = { + [ELS_ADDR_FMT_PORT] = 0xFFFFFF, + [ELS_ADDR_FMT_AREA] = 0xFFFF00, + [ELS_ADDR_FMT_DOM] = 0xFF0000, + [ELS_ADDR_FMT_FAB] = 0x000000, +}; + +static bool no_auto_port_rescan; +module_param_named(no_auto_port_rescan, no_auto_port_rescan, bool, 0600); +MODULE_PARM_DESC(no_auto_port_rescan, + "no automatic port_rescan (default off)"); + +void zfcp_fc_conditional_port_scan(struct zfcp_adapter *adapter) +{ + if (no_auto_port_rescan) + return; + + queue_work(adapter->work_queue, &adapter->scan_work); +} + +void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter) +{ + if (!no_auto_port_rescan) + return; + + queue_work(adapter->work_queue, &adapter->scan_work); +} + +/** + * zfcp_fc_post_event - post event to userspace via fc_transport + * @work: work struct with enqueued events + */ +void zfcp_fc_post_event(struct work_struct *work) +{ + struct zfcp_fc_event *event = NULL, *tmp = NULL; + LIST_HEAD(tmp_lh); + struct zfcp_fc_events *events = container_of(work, + struct zfcp_fc_events, work); + struct zfcp_adapter *adapter = container_of(events, struct zfcp_adapter, + events); + + spin_lock_bh(&events->list_lock); + list_splice_init(&events->list, &tmp_lh); + spin_unlock_bh(&events->list_lock); + + list_for_each_entry_safe(event, tmp, &tmp_lh, list) { + fc_host_post_event(adapter->scsi_host, fc_get_event_number(), + event->code, event->data); + list_del(&event->list); + kfree(event); + } + +} + +/** + * zfcp_fc_enqueue_event - safely enqueue FC HBA API event from irq context + * @adapter: The adapter where to enqueue the event + * @event_code: The event code (as defined in fc_host_event_code in + * scsi_transport_fc.h) + * @event_data: The event data (e.g. n_port page in case of els) + */ +void zfcp_fc_enqueue_event(struct zfcp_adapter *adapter, + enum fc_host_event_code event_code, u32 event_data) +{ + struct zfcp_fc_event *event; + + event = kmalloc(sizeof(struct zfcp_fc_event), GFP_ATOMIC); + if (!event) + return; + + event->code = event_code; + event->data = event_data; + + spin_lock(&adapter->events.list_lock); + list_add_tail(&event->list, &adapter->events.list); + spin_unlock(&adapter->events.list_lock); + + queue_work(adapter->work_queue, &adapter->events.work); +} + +static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port *wka_port) +{ + if (mutex_lock_interruptible(&wka_port->mutex)) + return -ERESTARTSYS; + + if (wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE || + wka_port->status == ZFCP_FC_WKA_PORT_CLOSING) { + wka_port->status = ZFCP_FC_WKA_PORT_OPENING; + if (zfcp_fsf_open_wka_port(wka_port)) + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; + } + + mutex_unlock(&wka_port->mutex); + + wait_event(wka_port->completion_wq, + wka_port->status == ZFCP_FC_WKA_PORT_ONLINE || + wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE); + + if (wka_port->status == ZFCP_FC_WKA_PORT_ONLINE) { + atomic_inc(&wka_port->refcount); + return 0; + } + return -EIO; +} + +static void zfcp_fc_wka_port_offline(struct work_struct *work) +{ + struct delayed_work *dw = to_delayed_work(work); + struct zfcp_fc_wka_port *wka_port = + container_of(dw, struct zfcp_fc_wka_port, work); + + mutex_lock(&wka_port->mutex); + if ((atomic_read(&wka_port->refcount) != 0) || + (wka_port->status != ZFCP_FC_WKA_PORT_ONLINE)) + goto out; + + wka_port->status = ZFCP_FC_WKA_PORT_CLOSING; + if (zfcp_fsf_close_wka_port(wka_port)) { + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; + wake_up(&wka_port->completion_wq); + } +out: + mutex_unlock(&wka_port->mutex); +} + +static void zfcp_fc_wka_port_put(struct zfcp_fc_wka_port *wka_port) +{ + if (atomic_dec_return(&wka_port->refcount) != 0) + return; + /* wait 10 milliseconds, other reqs might pop in */ + schedule_delayed_work(&wka_port->work, HZ / 100); +} + +static void zfcp_fc_wka_port_init(struct zfcp_fc_wka_port *wka_port, u32 d_id, + struct zfcp_adapter *adapter) +{ + init_waitqueue_head(&wka_port->completion_wq); + + wka_port->adapter = adapter; + wka_port->d_id = d_id; + + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; + atomic_set(&wka_port->refcount, 0); + mutex_init(&wka_port->mutex); + INIT_DELAYED_WORK(&wka_port->work, zfcp_fc_wka_port_offline); +} + +static void zfcp_fc_wka_port_force_offline(struct zfcp_fc_wka_port *wka) +{ + cancel_delayed_work_sync(&wka->work); + mutex_lock(&wka->mutex); + wka->status = ZFCP_FC_WKA_PORT_OFFLINE; + mutex_unlock(&wka->mutex); +} + +void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *gs) +{ + if (!gs) + return; + zfcp_fc_wka_port_force_offline(&gs->ms); + zfcp_fc_wka_port_force_offline(&gs->ts); + zfcp_fc_wka_port_force_offline(&gs->ds); + zfcp_fc_wka_port_force_offline(&gs->as); +} + +static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, + struct fc_els_rscn_page *page) +{ + unsigned long flags; + struct zfcp_adapter *adapter = fsf_req->adapter; + struct zfcp_port *port; + + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) { + if ((port->d_id & range) == (ntoh24(page->rscn_fid) & range)) + zfcp_fc_test_link(port); + if (!port->d_id) + zfcp_erp_port_reopen(port, + ZFCP_STATUS_COMMON_ERP_FAILED, + "fcrscn1"); + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); +} + +static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req) +{ + struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data; + struct fc_els_rscn *head; + struct fc_els_rscn_page *page; + u16 i; + u16 no_entries; + unsigned int afmt; + + head = (struct fc_els_rscn *) status_buffer->payload.data; + page = (struct fc_els_rscn_page *) head; + + /* see FC-FS */ + no_entries = head->rscn_plen / sizeof(struct fc_els_rscn_page); + + for (i = 1; i < no_entries; i++) { + /* skip head and start with 1st element */ + page++; + afmt = page->rscn_page_flags & ELS_RSCN_ADDR_FMT_MASK; + _zfcp_fc_incoming_rscn(fsf_req, zfcp_fc_rscn_range_mask[afmt], + page); + zfcp_fc_enqueue_event(fsf_req->adapter, FCH_EVT_RSCN, + *(u32 *)page); + } + zfcp_fc_conditional_port_scan(fsf_req->adapter); +} + +static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn) +{ + unsigned long flags; + struct zfcp_adapter *adapter = req->adapter; + struct zfcp_port *port; + + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) + if (port->wwpn == wwpn) { + zfcp_erp_port_forced_reopen(port, 0, "fciwwp1"); + break; + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); +} + +static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req) +{ + struct fsf_status_read_buffer *status_buffer; + struct fc_els_flogi *plogi; + + status_buffer = (struct fsf_status_read_buffer *) req->data; + plogi = (struct fc_els_flogi *) status_buffer->payload.data; + zfcp_fc_incoming_wwpn(req, plogi->fl_wwpn); +} + +static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req) +{ + struct fsf_status_read_buffer *status_buffer = + (struct fsf_status_read_buffer *)req->data; + struct fc_els_logo *logo = + (struct fc_els_logo *) status_buffer->payload.data; + + zfcp_fc_incoming_wwpn(req, logo->fl_n_port_wwn); +} + +/** + * zfcp_fc_incoming_els - handle incoming ELS + * @fsf_req - request which contains incoming ELS + */ +void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req) +{ + struct fsf_status_read_buffer *status_buffer = + (struct fsf_status_read_buffer *) fsf_req->data; + unsigned int els_type = status_buffer->payload.data[0]; + + zfcp_dbf_san_in_els("fciels1", fsf_req); + if (els_type == ELS_PLOGI) + zfcp_fc_incoming_plogi(fsf_req); + else if (els_type == ELS_LOGO) + zfcp_fc_incoming_logo(fsf_req); + else if (els_type == ELS_RSCN) + zfcp_fc_incoming_rscn(fsf_req); +} + +static void zfcp_fc_ns_gid_pn_eval(struct zfcp_fc_req *fc_req) +{ + struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els; + struct zfcp_fc_gid_pn_rsp *gid_pn_rsp = &fc_req->u.gid_pn.rsp; + + if (ct_els->status) + return; + if (gid_pn_rsp->ct_hdr.ct_cmd != FC_FS_ACC) + return; + + /* looks like a valid d_id */ + ct_els->port->d_id = ntoh24(gid_pn_rsp->gid_pn.fp_fid); +} + +static void zfcp_fc_complete(void *data) +{ + complete(data); +} + +static void zfcp_fc_ct_ns_init(struct fc_ct_hdr *ct_hdr, u16 cmd, u16 mr_size) +{ + ct_hdr->ct_rev = FC_CT_REV; + ct_hdr->ct_fs_type = FC_FST_DIR; + ct_hdr->ct_fs_subtype = FC_NS_SUBTYPE; + ct_hdr->ct_cmd = cmd; + ct_hdr->ct_mr_size = mr_size / 4; +} + +static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port, + struct zfcp_fc_req *fc_req) +{ + struct zfcp_adapter *adapter = port->adapter; + DECLARE_COMPLETION_ONSTACK(completion); + struct zfcp_fc_gid_pn_req *gid_pn_req = &fc_req->u.gid_pn.req; + struct zfcp_fc_gid_pn_rsp *gid_pn_rsp = &fc_req->u.gid_pn.rsp; + int ret; + + /* setup parameters for send generic command */ + fc_req->ct_els.port = port; + fc_req->ct_els.handler = zfcp_fc_complete; + fc_req->ct_els.handler_data = &completion; + fc_req->ct_els.req = &fc_req->sg_req; + fc_req->ct_els.resp = &fc_req->sg_rsp; + sg_init_one(&fc_req->sg_req, gid_pn_req, sizeof(*gid_pn_req)); + sg_init_one(&fc_req->sg_rsp, gid_pn_rsp, sizeof(*gid_pn_rsp)); + + zfcp_fc_ct_ns_init(&gid_pn_req->ct_hdr, + FC_NS_GID_PN, ZFCP_FC_CT_SIZE_PAGE); + gid_pn_req->gid_pn.fn_wwpn = port->wwpn; + + ret = zfcp_fsf_send_ct(&adapter->gs->ds, &fc_req->ct_els, + adapter->pool.gid_pn_req, + ZFCP_FC_CTELS_TMO); + if (!ret) { + wait_for_completion(&completion); + zfcp_fc_ns_gid_pn_eval(fc_req); + } + return ret; +} + +/** + * zfcp_fc_ns_gid_pn - initiate GID_PN nameserver request + * @port: port where GID_PN request is needed + * return: -ENOMEM on error, 0 otherwise + */ +static int zfcp_fc_ns_gid_pn(struct zfcp_port *port) +{ + int ret; + struct zfcp_fc_req *fc_req; + struct zfcp_adapter *adapter = port->adapter; + + fc_req = mempool_alloc(adapter->pool.gid_pn, GFP_ATOMIC); + if (!fc_req) + return -ENOMEM; + + memset(fc_req, 0, sizeof(*fc_req)); + + ret = zfcp_fc_wka_port_get(&adapter->gs->ds); + if (ret) + goto out; + + ret = zfcp_fc_ns_gid_pn_request(port, fc_req); + + zfcp_fc_wka_port_put(&adapter->gs->ds); +out: + mempool_free(fc_req, adapter->pool.gid_pn); + return ret; +} + +void zfcp_fc_port_did_lookup(struct work_struct *work) +{ + int ret; + struct zfcp_port *port = container_of(work, struct zfcp_port, + gid_pn_work); + + ret = zfcp_fc_ns_gid_pn(port); + if (ret) { + /* could not issue gid_pn for some reason */ + zfcp_erp_adapter_reopen(port->adapter, 0, "fcgpn_1"); + goto out; + } + + if (!port->d_id) { + zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED); + goto out; + } + + zfcp_erp_port_reopen(port, 0, "fcgpn_3"); +out: + put_device(&port->dev); +} + +/** + * zfcp_fc_trigger_did_lookup - trigger the d_id lookup using a GID_PN request + * @port: The zfcp_port to lookup the d_id for. + */ +void zfcp_fc_trigger_did_lookup(struct zfcp_port *port) +{ + get_device(&port->dev); + if (!queue_work(port->adapter->work_queue, &port->gid_pn_work)) + put_device(&port->dev); +} + +/** + * zfcp_fc_plogi_evaluate - evaluate PLOGI playload + * @port: zfcp_port structure + * @plogi: plogi payload + * + * Evaluate PLOGI playload and copy important fields into zfcp_port structure + */ +void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fc_els_flogi *plogi) +{ + if (plogi->fl_wwpn != port->wwpn) { + port->d_id = 0; + dev_warn(&port->adapter->ccw_device->dev, + "A port opened with WWPN 0x%016Lx returned data that " + "identifies it as WWPN 0x%016Lx\n", + (unsigned long long) port->wwpn, + (unsigned long long) plogi->fl_wwpn); + return; + } + + port->wwnn = plogi->fl_wwnn; + port->maxframe_size = plogi->fl_csp.sp_bb_data; + + if (plogi->fl_cssp[0].cp_class & FC_CPC_VALID) + port->supported_classes |= FC_COS_CLASS1; + if (plogi->fl_cssp[1].cp_class & FC_CPC_VALID) + port->supported_classes |= FC_COS_CLASS2; + if (plogi->fl_cssp[2].cp_class & FC_CPC_VALID) + port->supported_classes |= FC_COS_CLASS3; + if (plogi->fl_cssp[3].cp_class & FC_CPC_VALID) + port->supported_classes |= FC_COS_CLASS4; +} + +static void zfcp_fc_adisc_handler(void *data) +{ + struct zfcp_fc_req *fc_req = data; + struct zfcp_port *port = fc_req->ct_els.port; + struct fc_els_adisc *adisc_resp = &fc_req->u.adisc.rsp; + + if (fc_req->ct_els.status) { + /* request rejected or timed out */ + zfcp_erp_port_forced_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, + "fcadh_1"); + goto out; + } + + if (!port->wwnn) + port->wwnn = adisc_resp->adisc_wwnn; + + if ((port->wwpn != adisc_resp->adisc_wwpn) || + !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) { + zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, + "fcadh_2"); + goto out; + } + + /* port is good, unblock rport without going through erp */ + zfcp_scsi_schedule_rport_register(port); + out: + atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); + put_device(&port->dev); + kmem_cache_free(zfcp_fc_req_cache, fc_req); +} + +static int zfcp_fc_adisc(struct zfcp_port *port) +{ + struct zfcp_fc_req *fc_req; + struct zfcp_adapter *adapter = port->adapter; + struct Scsi_Host *shost = adapter->scsi_host; + int ret; + + fc_req = kmem_cache_zalloc(zfcp_fc_req_cache, GFP_ATOMIC); + if (!fc_req) + return -ENOMEM; + + fc_req->ct_els.port = port; + fc_req->ct_els.req = &fc_req->sg_req; + fc_req->ct_els.resp = &fc_req->sg_rsp; + sg_init_one(&fc_req->sg_req, &fc_req->u.adisc.req, + sizeof(struct fc_els_adisc)); + sg_init_one(&fc_req->sg_rsp, &fc_req->u.adisc.rsp, + sizeof(struct fc_els_adisc)); + + fc_req->ct_els.handler = zfcp_fc_adisc_handler; + fc_req->ct_els.handler_data = fc_req; + + /* 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 */ + fc_req->u.adisc.req.adisc_wwpn = fc_host_port_name(shost); + fc_req->u.adisc.req.adisc_wwnn = fc_host_node_name(shost); + fc_req->u.adisc.req.adisc_cmd = ELS_ADISC; + hton24(fc_req->u.adisc.req.adisc_port_id, fc_host_port_id(shost)); + + ret = zfcp_fsf_send_els(adapter, port->d_id, &fc_req->ct_els, + ZFCP_FC_CTELS_TMO); + if (ret) + kmem_cache_free(zfcp_fc_req_cache, fc_req); + + return ret; +} + +void zfcp_fc_link_test_work(struct work_struct *work) +{ + struct zfcp_port *port = + container_of(work, struct zfcp_port, test_link_work); + int retval; + + get_device(&port->dev); + port->rport_task = RPORT_DEL; + zfcp_scsi_rport_work(&port->rport_work); + + /* only issue one test command at one time per port */ + if (atomic_read(&port->status) & ZFCP_STATUS_PORT_LINK_TEST) + goto out; + + atomic_set_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); + + retval = zfcp_fc_adisc(port); + if (retval == 0) + return; + + /* send of ADISC was not possible */ + atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); + zfcp_erp_port_forced_reopen(port, 0, "fcltwk1"); + +out: + put_device(&port->dev); +} + +/** + * zfcp_fc_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. + * If there is a problem with the remote port, error recovery steps + * will be triggered. + */ +void zfcp_fc_test_link(struct zfcp_port *port) +{ + get_device(&port->dev); + if (!queue_work(port->adapter->work_queue, &port->test_link_work)) + put_device(&port->dev); +} + +static struct zfcp_fc_req *zfcp_alloc_sg_env(int buf_num) +{ + struct zfcp_fc_req *fc_req; + + fc_req = kmem_cache_zalloc(zfcp_fc_req_cache, GFP_KERNEL); + if (!fc_req) + return NULL; + + if (zfcp_sg_setup_table(&fc_req->sg_rsp, buf_num)) { + kmem_cache_free(zfcp_fc_req_cache, fc_req); + return NULL; + } + + sg_init_one(&fc_req->sg_req, &fc_req->u.gpn_ft.req, + sizeof(struct zfcp_fc_gpn_ft_req)); + + return fc_req; +} + +static int zfcp_fc_send_gpn_ft(struct zfcp_fc_req *fc_req, + struct zfcp_adapter *adapter, int max_bytes) +{ + struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els; + struct zfcp_fc_gpn_ft_req *req = &fc_req->u.gpn_ft.req; + DECLARE_COMPLETION_ONSTACK(completion); + int ret; + + zfcp_fc_ct_ns_init(&req->ct_hdr, FC_NS_GPN_FT, max_bytes); + req->gpn_ft.fn_fc4_type = FC_TYPE_FCP; + + ct_els->handler = zfcp_fc_complete; + ct_els->handler_data = &completion; + ct_els->req = &fc_req->sg_req; + ct_els->resp = &fc_req->sg_rsp; + + ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct_els, NULL, + ZFCP_FC_CTELS_TMO); + if (!ret) + wait_for_completion(&completion); + return ret; +} + +static void zfcp_fc_validate_port(struct zfcp_port *port, struct list_head *lh) +{ + if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC)) + return; + + atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status); + + if ((port->supported_classes != 0) || + !list_empty(&port->unit_list)) + return; + + list_move_tail(&port->list, lh); +} + +static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_req *fc_req, + struct zfcp_adapter *adapter, int max_entries) +{ + struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els; + struct scatterlist *sg = &fc_req->sg_rsp; + struct fc_ct_hdr *hdr = sg_virt(sg); + struct fc_gpn_ft_resp *acc = sg_virt(sg); + struct zfcp_port *port, *tmp; + unsigned long flags; + LIST_HEAD(remove_lh); + u32 d_id; + int ret = 0, x, last = 0; + + if (ct_els->status) + return -EIO; + + if (hdr->ct_cmd != FC_FS_ACC) { + if (hdr->ct_reason == FC_BA_RJT_UNABLE) + return -EAGAIN; /* might be a temporary condition */ + return -EIO; + } + + if (hdr->ct_mr_size) { + dev_warn(&adapter->ccw_device->dev, + "The name server reported %d words residual data\n", + hdr->ct_mr_size); + return -E2BIG; + } + + /* first entry is the header */ + for (x = 1; x < max_entries && !last; x++) { + if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1)) + acc++; + else + acc = sg_virt(++sg); + + last = acc->fp_flags & FC_NS_FID_LAST; + d_id = ntoh24(acc->fp_fid); + + /* don't attach ports with a well known address */ + if (d_id >= FC_FID_WELL_KNOWN_BASE) + continue; + /* skip the adapter's port and known remote ports */ + if (acc->fp_wwpn == fc_host_port_name(adapter->scsi_host)) + continue; + + port = zfcp_port_enqueue(adapter, acc->fp_wwpn, + ZFCP_STATUS_COMMON_NOESC, d_id); + if (!IS_ERR(port)) + zfcp_erp_port_reopen(port, 0, "fcegpf1"); + else if (PTR_ERR(port) != -EEXIST) + ret = PTR_ERR(port); + } + + zfcp_erp_wait(adapter); + write_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry_safe(port, tmp, &adapter->port_list, list) + zfcp_fc_validate_port(port, &remove_lh); + write_unlock_irqrestore(&adapter->port_list_lock, flags); + + list_for_each_entry_safe(port, tmp, &remove_lh, list) { + zfcp_erp_port_shutdown(port, 0, "fcegpf2"); + device_unregister(&port->dev); + } + + return ret; +} + +/** + * zfcp_fc_scan_ports - scan remote ports and attach new ports + * @work: reference to scheduled work + */ +void zfcp_fc_scan_ports(struct work_struct *work) +{ + struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, + scan_work); + int ret, i; + struct zfcp_fc_req *fc_req; + int chain, max_entries, buf_num, max_bytes; + + chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS; + buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1; + max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE; + max_bytes = chain ? ZFCP_FC_GPN_FT_MAX_SIZE : ZFCP_FC_CT_SIZE_PAGE; + + if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT && + fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV) + return; + + if (zfcp_fc_wka_port_get(&adapter->gs->ds)) + return; + + fc_req = zfcp_alloc_sg_env(buf_num); + if (!fc_req) + goto out; + + for (i = 0; i < 3; i++) { + ret = zfcp_fc_send_gpn_ft(fc_req, adapter, max_bytes); + if (!ret) { + ret = zfcp_fc_eval_gpn_ft(fc_req, adapter, max_entries); + if (ret == -EAGAIN) + ssleep(1); + else + break; + } + } + zfcp_sg_free_table(&fc_req->sg_rsp, buf_num); + kmem_cache_free(zfcp_fc_req_cache, fc_req); +out: + zfcp_fc_wka_port_put(&adapter->gs->ds); +} + +static int zfcp_fc_gspn(struct zfcp_adapter *adapter, + struct zfcp_fc_req *fc_req) +{ + DECLARE_COMPLETION_ONSTACK(completion); + char devno[] = "DEVNO:"; + struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els; + struct zfcp_fc_gspn_req *gspn_req = &fc_req->u.gspn.req; + struct zfcp_fc_gspn_rsp *gspn_rsp = &fc_req->u.gspn.rsp; + int ret; + + zfcp_fc_ct_ns_init(&gspn_req->ct_hdr, FC_NS_GSPN_ID, + FC_SYMBOLIC_NAME_SIZE); + hton24(gspn_req->gspn.fp_fid, fc_host_port_id(adapter->scsi_host)); + + sg_init_one(&fc_req->sg_req, gspn_req, sizeof(*gspn_req)); + sg_init_one(&fc_req->sg_rsp, gspn_rsp, sizeof(*gspn_rsp)); + + ct_els->handler = zfcp_fc_complete; + ct_els->handler_data = &completion; + ct_els->req = &fc_req->sg_req; + ct_els->resp = &fc_req->sg_rsp; + + ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct_els, NULL, + ZFCP_FC_CTELS_TMO); + if (ret) + return ret; + + wait_for_completion(&completion); + if (ct_els->status) + return ct_els->status; + + if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_NPIV && + !(strstr(gspn_rsp->gspn.fp_name, devno))) + snprintf(fc_host_symbolic_name(adapter->scsi_host), + FC_SYMBOLIC_NAME_SIZE, "%s%s %s NAME: %s", + gspn_rsp->gspn.fp_name, devno, + dev_name(&adapter->ccw_device->dev), + init_utsname()->nodename); + else + strlcpy(fc_host_symbolic_name(adapter->scsi_host), + gspn_rsp->gspn.fp_name, FC_SYMBOLIC_NAME_SIZE); + + return 0; +} + +static void zfcp_fc_rspn(struct zfcp_adapter *adapter, + struct zfcp_fc_req *fc_req) +{ + DECLARE_COMPLETION_ONSTACK(completion); + struct Scsi_Host *shost = adapter->scsi_host; + struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els; + struct zfcp_fc_rspn_req *rspn_req = &fc_req->u.rspn.req; + struct fc_ct_hdr *rspn_rsp = &fc_req->u.rspn.rsp; + int ret, len; + + zfcp_fc_ct_ns_init(&rspn_req->ct_hdr, FC_NS_RSPN_ID, + FC_SYMBOLIC_NAME_SIZE); + hton24(rspn_req->rspn.fr_fid.fp_fid, fc_host_port_id(shost)); + len = strlcpy(rspn_req->rspn.fr_name, fc_host_symbolic_name(shost), + FC_SYMBOLIC_NAME_SIZE); + rspn_req->rspn.fr_name_len = len; + + sg_init_one(&fc_req->sg_req, rspn_req, sizeof(*rspn_req)); + sg_init_one(&fc_req->sg_rsp, rspn_rsp, sizeof(*rspn_rsp)); + + ct_els->handler = zfcp_fc_complete; + ct_els->handler_data = &completion; + ct_els->req = &fc_req->sg_req; + ct_els->resp = &fc_req->sg_rsp; + + ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct_els, NULL, + ZFCP_FC_CTELS_TMO); + if (!ret) + wait_for_completion(&completion); +} + +/** + * zfcp_fc_sym_name_update - Retrieve and update the symbolic port name + * @work: ns_up_work of the adapter where to update the symbolic port name + * + * Retrieve the current symbolic port name that may have been set by + * the hardware using the GSPN request and update the fc_host + * symbolic_name sysfs attribute. When running in NPIV mode (and hence + * the port name is unique for this system), update the symbolic port + * name to add Linux specific information and update the FC nameserver + * using the RSPN request. + */ +void zfcp_fc_sym_name_update(struct work_struct *work) +{ + struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, + ns_up_work); + int ret; + struct zfcp_fc_req *fc_req; + + if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT && + fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV) + return; + + fc_req = kmem_cache_zalloc(zfcp_fc_req_cache, GFP_KERNEL); + if (!fc_req) + return; + + ret = zfcp_fc_wka_port_get(&adapter->gs->ds); + if (ret) + goto out_free; + + ret = zfcp_fc_gspn(adapter, fc_req); + if (ret || fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV) + goto out_ds_put; + + memset(fc_req, 0, sizeof(*fc_req)); + zfcp_fc_rspn(adapter, fc_req); + +out_ds_put: + zfcp_fc_wka_port_put(&adapter->gs->ds); +out_free: + kmem_cache_free(zfcp_fc_req_cache, fc_req); +} + +static void zfcp_fc_ct_els_job_handler(void *data) +{ + struct fc_bsg_job *job = data; + struct zfcp_fsf_ct_els *zfcp_ct_els = job->dd_data; + struct fc_bsg_reply *jr = job->reply; + + jr->reply_payload_rcv_len = job->reply_payload.payload_len; + jr->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; + jr->result = zfcp_ct_els->status ? -EIO : 0; + job->job_done(job); +} + +static struct zfcp_fc_wka_port *zfcp_fc_job_wka_port(struct fc_bsg_job *job) +{ + u32 preamble_word1; + u8 gs_type; + struct zfcp_adapter *adapter; + + preamble_word1 = job->request->rqst_data.r_ct.preamble_word1; + gs_type = (preamble_word1 & 0xff000000) >> 24; + + adapter = (struct zfcp_adapter *) job->shost->hostdata[0]; + + switch (gs_type) { + case FC_FST_ALIAS: + return &adapter->gs->as; + case FC_FST_MGMT: + return &adapter->gs->ms; + case FC_FST_TIME: + return &adapter->gs->ts; + break; + case FC_FST_DIR: + return &adapter->gs->ds; + break; + default: + return NULL; + } +} + +static void zfcp_fc_ct_job_handler(void *data) +{ + struct fc_bsg_job *job = data; + struct zfcp_fc_wka_port *wka_port; + + wka_port = zfcp_fc_job_wka_port(job); + zfcp_fc_wka_port_put(wka_port); + + zfcp_fc_ct_els_job_handler(data); +} + +static int zfcp_fc_exec_els_job(struct fc_bsg_job *job, + struct zfcp_adapter *adapter) +{ + struct zfcp_fsf_ct_els *els = job->dd_data; + struct fc_rport *rport = job->rport; + struct zfcp_port *port; + u32 d_id; + + if (rport) { + port = zfcp_get_port_by_wwpn(adapter, rport->port_name); + if (!port) + return -EINVAL; + + d_id = port->d_id; + put_device(&port->dev); + } else + d_id = ntoh24(job->request->rqst_data.h_els.port_id); + + els->handler = zfcp_fc_ct_els_job_handler; + return zfcp_fsf_send_els(adapter, d_id, els, job->req->timeout / HZ); +} + +static int zfcp_fc_exec_ct_job(struct fc_bsg_job *job, + struct zfcp_adapter *adapter) +{ + int ret; + struct zfcp_fsf_ct_els *ct = job->dd_data; + struct zfcp_fc_wka_port *wka_port; + + wka_port = zfcp_fc_job_wka_port(job); + if (!wka_port) + return -EINVAL; + + ret = zfcp_fc_wka_port_get(wka_port); + if (ret) + return ret; + + ct->handler = zfcp_fc_ct_job_handler; + ret = zfcp_fsf_send_ct(wka_port, ct, NULL, job->req->timeout / HZ); + if (ret) + zfcp_fc_wka_port_put(wka_port); + + return ret; +} + +int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job) +{ + struct Scsi_Host *shost; + struct zfcp_adapter *adapter; + struct zfcp_fsf_ct_els *ct_els = job->dd_data; + + shost = job->rport ? rport_to_shost(job->rport) : job->shost; + adapter = (struct zfcp_adapter *)shost->hostdata[0]; + + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN)) + return -EINVAL; + + ct_els->req = job->request_payload.sg_list; + ct_els->resp = job->reply_payload.sg_list; + ct_els->handler_data = job; + + switch (job->request->msgcode) { + case FC_BSG_RPT_ELS: + case FC_BSG_HST_ELS_NOLOGIN: + return zfcp_fc_exec_els_job(job, adapter); + case FC_BSG_RPT_CT: + case FC_BSG_HST_CT: + return zfcp_fc_exec_ct_job(job, adapter); + default: + return -EINVAL; + } +} + +int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *job) +{ + /* hardware tracks timeout, reset bsg timeout to not interfere */ + return -EAGAIN; +} + +int zfcp_fc_gs_setup(struct zfcp_adapter *adapter) +{ + struct zfcp_fc_wka_ports *wka_ports; + + wka_ports = kzalloc(sizeof(struct zfcp_fc_wka_ports), GFP_KERNEL); + if (!wka_ports) + return -ENOMEM; + + adapter->gs = wka_ports; + zfcp_fc_wka_port_init(&wka_ports->ms, FC_FID_MGMT_SERV, adapter); + zfcp_fc_wka_port_init(&wka_ports->ts, FC_FID_TIME_SERV, adapter); + zfcp_fc_wka_port_init(&wka_ports->ds, FC_FID_DIR_SERV, adapter); + zfcp_fc_wka_port_init(&wka_ports->as, FC_FID_ALIASES, adapter); + + return 0; +} + +void zfcp_fc_gs_destroy(struct zfcp_adapter *adapter) +{ + kfree(adapter->gs); + adapter->gs = NULL; +} + diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h new file mode 100644 index 00000000000..b1d2024ed51 --- /dev/null +++ b/drivers/s390/scsi/zfcp_fc.h @@ -0,0 +1,297 @@ +/* + * zfcp device driver + * + * Fibre Channel related definitions and inline functions for the zfcp + * device driver + * + * Copyright IBM Corp. 2009 + */ + +#ifndef ZFCP_FC_H +#define ZFCP_FC_H + +#include <scsi/fc/fc_els.h> +#include <scsi/fc/fc_fcp.h> +#include <scsi/fc/fc_ns.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_tcq.h> +#include "zfcp_fsf.h" + +#define ZFCP_FC_CT_SIZE_PAGE (PAGE_SIZE - sizeof(struct fc_ct_hdr)) +#define ZFCP_FC_GPN_FT_ENT_PAGE (ZFCP_FC_CT_SIZE_PAGE \ + / sizeof(struct fc_gpn_ft_resp)) +#define ZFCP_FC_GPN_FT_NUM_BUFS 4 /* memory pages */ + +#define ZFCP_FC_GPN_FT_MAX_SIZE (ZFCP_FC_GPN_FT_NUM_BUFS * PAGE_SIZE \ + - sizeof(struct fc_ct_hdr)) +#define ZFCP_FC_GPN_FT_MAX_ENT (ZFCP_FC_GPN_FT_NUM_BUFS * \ + (ZFCP_FC_GPN_FT_ENT_PAGE + 1)) + +#define ZFCP_FC_CTELS_TMO (2 * FC_DEF_R_A_TOV / 1000) + +/** + * struct zfcp_fc_event - FC HBAAPI event for internal queueing from irq context + * @code: Event code + * @data: Event data + * @list: list_head for zfcp_fc_events list + */ +struct zfcp_fc_event { + enum fc_host_event_code code; + u32 data; + struct list_head list; +}; + +/** + * struct zfcp_fc_events - Infrastructure for posting FC events from irq context + * @list: List for queueing of events from irq context to workqueue + * @list_lock: Lock for event list + * @work: work_struct for forwarding events in workqueue +*/ +struct zfcp_fc_events { + struct list_head list; + spinlock_t list_lock; + struct work_struct work; +}; + +/** + * struct zfcp_fc_gid_pn_req - container for ct header plus gid_pn request + * @ct_hdr: FC GS common transport header + * @gid_pn: GID_PN request + */ +struct zfcp_fc_gid_pn_req { + struct fc_ct_hdr ct_hdr; + struct fc_ns_gid_pn gid_pn; +} __packed; + +/** + * struct zfcp_fc_gid_pn_rsp - container for ct header plus gid_pn response + * @ct_hdr: FC GS common transport header + * @gid_pn: GID_PN response + */ +struct zfcp_fc_gid_pn_rsp { + struct fc_ct_hdr ct_hdr; + struct fc_gid_pn_resp gid_pn; +} __packed; + +/** + * struct zfcp_fc_gpn_ft - container for ct header plus gpn_ft request + * @ct_hdr: FC GS common transport header + * @gpn_ft: GPN_FT request + */ +struct zfcp_fc_gpn_ft_req { + struct fc_ct_hdr ct_hdr; + struct fc_ns_gid_ft gpn_ft; +} __packed; + +/** + * struct zfcp_fc_gspn_req - container for ct header plus GSPN_ID request + * @ct_hdr: FC GS common transport header + * @gspn: GSPN_ID request + */ +struct zfcp_fc_gspn_req { + struct fc_ct_hdr ct_hdr; + struct fc_gid_pn_resp gspn; +} __packed; + +/** + * struct zfcp_fc_gspn_rsp - container for ct header plus GSPN_ID response + * @ct_hdr: FC GS common transport header + * @gspn: GSPN_ID response + * @name: The name string of the GSPN_ID response + */ +struct zfcp_fc_gspn_rsp { + struct fc_ct_hdr ct_hdr; + struct fc_gspn_resp gspn; + char name[FC_SYMBOLIC_NAME_SIZE]; +} __packed; + +/** + * struct zfcp_fc_rspn_req - container for ct header plus RSPN_ID request + * @ct_hdr: FC GS common transport header + * @rspn: RSPN_ID request + * @name: The name string of the RSPN_ID request + */ +struct zfcp_fc_rspn_req { + struct fc_ct_hdr ct_hdr; + struct fc_ns_rspn rspn; + char name[FC_SYMBOLIC_NAME_SIZE]; +} __packed; + +/** + * struct zfcp_fc_req - Container for FC ELS and CT requests sent from zfcp + * @ct_els: data required for issuing fsf command + * @sg_req: scatterlist entry for request data + * @sg_rsp: scatterlist entry for response data + * @u: request specific data + */ +struct zfcp_fc_req { + struct zfcp_fsf_ct_els ct_els; + struct scatterlist sg_req; + struct scatterlist sg_rsp; + union { + struct { + struct fc_els_adisc req; + struct fc_els_adisc rsp; + } adisc; + struct { + struct zfcp_fc_gid_pn_req req; + struct zfcp_fc_gid_pn_rsp rsp; + } gid_pn; + struct { + struct scatterlist sg_rsp2[ZFCP_FC_GPN_FT_NUM_BUFS - 1]; + struct zfcp_fc_gpn_ft_req req; + } gpn_ft; + struct { + struct zfcp_fc_gspn_req req; + struct zfcp_fc_gspn_rsp rsp; + } gspn; + struct { + struct zfcp_fc_rspn_req req; + struct fc_ct_hdr rsp; + } rspn; + } u; +}; + +/** + * enum zfcp_fc_wka_status - FC WKA port status in zfcp + * @ZFCP_FC_WKA_PORT_OFFLINE: Port is closed and not in use + * @ZFCP_FC_WKA_PORT_CLOSING: The FSF "close port" request is pending + * @ZFCP_FC_WKA_PORT_OPENING: The FSF "open port" request is pending + * @ZFCP_FC_WKA_PORT_ONLINE: The port is open and the port handle is valid + */ +enum zfcp_fc_wka_status { + ZFCP_FC_WKA_PORT_OFFLINE, + ZFCP_FC_WKA_PORT_CLOSING, + ZFCP_FC_WKA_PORT_OPENING, + ZFCP_FC_WKA_PORT_ONLINE, +}; + +/** + * struct zfcp_fc_wka_port - representation of well-known-address (WKA) FC port + * @adapter: Pointer to adapter structure this WKA port belongs to + * @completion_wq: Wait for completion of open/close command + * @status: Current status of WKA port + * @refcount: Reference count to keep port open as long as it is in use + * @d_id: FC destination id or well-known-address + * @handle: FSF handle for the open WKA port + * @mutex: Mutex used during opening/closing state changes + * @work: For delaying the closing of the WKA port + */ +struct zfcp_fc_wka_port { + struct zfcp_adapter *adapter; + wait_queue_head_t completion_wq; + enum zfcp_fc_wka_status status; + atomic_t refcount; + u32 d_id; + u32 handle; + struct mutex mutex; + struct delayed_work work; +}; + +/** + * struct zfcp_fc_wka_ports - Data structures for FC generic services + * @ms: FC Management service + * @ts: FC time service + * @ds: FC directory service + * @as: FC alias service + */ +struct zfcp_fc_wka_ports { + struct zfcp_fc_wka_port ms; + struct zfcp_fc_wka_port ts; + struct zfcp_fc_wka_port ds; + struct zfcp_fc_wka_port as; +}; + +/** + * zfcp_fc_scsi_to_fcp - setup FCP command with data from scsi_cmnd + * @fcp: fcp_cmnd to setup + * @scsi: scsi_cmnd where to get LUN, task attributes/flags and CDB + * @tm: task management flags to setup task management command + */ +static inline +void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi, + u8 tm_flags) +{ + char tag[2]; + + int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun); + + if (unlikely(tm_flags)) { + fcp->fc_tm_flags = tm_flags; + return; + } + + if (scsi_populate_tag_msg(scsi, tag)) { + switch (tag[0]) { + case MSG_ORDERED_TAG: + fcp->fc_pri_ta |= FCP_PTA_ORDERED; + break; + case MSG_SIMPLE_TAG: + fcp->fc_pri_ta |= FCP_PTA_SIMPLE; + break; + }; + } else + fcp->fc_pri_ta = FCP_PTA_SIMPLE; + + if (scsi->sc_data_direction == DMA_FROM_DEVICE) + fcp->fc_flags |= FCP_CFL_RDDATA; + if (scsi->sc_data_direction == DMA_TO_DEVICE) + fcp->fc_flags |= FCP_CFL_WRDATA; + + memcpy(fcp->fc_cdb, scsi->cmnd, scsi->cmd_len); + + fcp->fc_dl = scsi_bufflen(scsi); + + if (scsi_get_prot_type(scsi) == SCSI_PROT_DIF_TYPE1) + fcp->fc_dl += fcp->fc_dl / scsi->device->sector_size * 8; +} + +/** + * zfcp_fc_evap_fcp_rsp - evaluate FCP RSP IU and update scsi_cmnd accordingly + * @fcp_rsp: FCP RSP IU to evaluate + * @scsi: SCSI command where to update status and sense buffer + */ +static inline +void zfcp_fc_eval_fcp_rsp(struct fcp_resp_with_ext *fcp_rsp, + struct scsi_cmnd *scsi) +{ + struct fcp_resp_rsp_info *rsp_info; + char *sense; + u32 sense_len, resid; + u8 rsp_flags; + + set_msg_byte(scsi, COMMAND_COMPLETE); + scsi->result |= fcp_rsp->resp.fr_status; + + rsp_flags = fcp_rsp->resp.fr_flags; + + if (unlikely(rsp_flags & FCP_RSP_LEN_VAL)) { + rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; + if (rsp_info->rsp_code == FCP_TMF_CMPL) + set_host_byte(scsi, DID_OK); + else { + set_host_byte(scsi, DID_ERROR); + return; + } + } + + if (unlikely(rsp_flags & FCP_SNS_LEN_VAL)) { + sense = (char *) &fcp_rsp[1]; + if (rsp_flags & FCP_RSP_LEN_VAL) + sense += fcp_rsp->ext.fr_rsp_len; + sense_len = min(fcp_rsp->ext.fr_sns_len, + (u32) SCSI_SENSE_BUFFERSIZE); + memcpy(scsi->sense_buffer, sense, sense_len); + } + + if (unlikely(rsp_flags & FCP_RESID_UNDER)) { + resid = fcp_rsp->ext.fr_resid; + scsi_set_resid(scsi, resid); + if (scsi_bufflen(scsi) - resid < scsi->underflow && + !(rsp_flags & FCP_SNS_LEN_VAL) && + fcp_rsp->resp.fr_status == SAM_STAT_GOOD) + set_host_byte(scsi, DID_ERROR); + } +} + +#endif diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 0dff05840ee..0fe8d5d9511 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1,54 +1,50 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Implementation of FSF commands. * - * 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. + * Copyright IBM Corp. 2002, 2013 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/blktrace_api.h> +#include <linux/slab.h> +#include <scsi/fc/fc_els.h> #include "zfcp_ext.h" +#include "zfcp_fc.h" +#include "zfcp_dbf.h" +#include "zfcp_qdio.h" +#include "zfcp_reqlist.h" + +struct kmem_cache *zfcp_fsf_qtcb_cache; + +static void zfcp_fsf_request_timeout_handler(unsigned long data) +{ + struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; + zfcp_qdio_siosl(adapter); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + "fsrth_1"); +} + +static void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, + unsigned long timeout) +{ + fsf_req->timer.function = zfcp_fsf_request_timeout_handler; + fsf_req->timer.data = (unsigned long) fsf_req->adapter; + fsf_req->timer.expires = jiffies + timeout; + add_timer(&fsf_req->timer); +} -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 *); -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 void zfcp_fsf_link_down_info_eval(struct zfcp_adapter *, - struct fsf_link_down_info *); -static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *); +static void zfcp_fsf_start_erp_timer(struct zfcp_fsf_req *fsf_req) +{ + BUG_ON(!fsf_req->erp_action); + fsf_req->timer.function = zfcp_erp_timeout_handler; + fsf_req->timer.data = (unsigned long) fsf_req->erp_action; + fsf_req->timer.expires = jiffies + 30 * HZ; + add_timer(&fsf_req->timer); +} /* association between FSF command and FSF QTCB type */ static u32 fsf_qtcb_type[] = { @@ -67,2561 +63,1342 @@ static u32 fsf_qtcb_type[] = { [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) +static void zfcp_fsf_class_not_supp(struct zfcp_fsf_req *req) { - 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_qtcb); - - if (likely(pool)) - ptr = mempool_alloc(pool, GFP_ATOMIC); - else { - if (req_flags & ZFCP_REQ_NO_QTCB) - ptr = kmalloc(size, GFP_ATOMIC); - else - ptr = kmem_cache_alloc(zfcp_data.fsf_req_qtcb_cache, - GFP_ATOMIC); - } - - if (unlikely(!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_qtcb *) ptr)->fsf_req; - fsf_req->qtcb = &((struct zfcp_fsf_req_qtcb *) ptr)->qtcb; - } - - fsf_req->pool = pool; - - out: - return fsf_req; + dev_err(&req->adapter->ccw_device->dev, "FCP device not " + "operational because of an unsupported FC class\n"); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fscns_1"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; } -/* - * 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 +/** + * zfcp_fsf_req_free - free memory used by fsf request + * @fsf_req: pointer to struct zfcp_fsf_req */ -void -zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req) +void zfcp_fsf_req_free(struct zfcp_fsf_req *req) { - if (likely(fsf_req->pool)) { - mempool_free(fsf_req, fsf_req->pool); - return; - } - - if (fsf_req->qtcb) { - kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, fsf_req); + if (likely(req->pool)) { + if (likely(req->qtcb)) + mempool_free(req->qtcb, req->adapter->pool.qtcb_pool); + mempool_free(req, req->pool); return; } - kfree(fsf_req); + if (likely(req->qtcb)) + kmem_cache_free(zfcp_fsf_qtcb_cache, req->qtcb); + kfree(req); } -/* - * Never ever call this without shutting down the adapter first. - * Otherwise the adapter would continue using and corrupting s390 storage. - * Included BUG_ON() call to ensure this is done. - * ERP is supposed to be the only user of this function. - */ -void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter) +static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req) { - struct zfcp_fsf_req *fsf_req, *tmp; unsigned long flags; - LIST_HEAD(remove_queue); - unsigned int i; - - BUG_ON(atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)); - spin_lock_irqsave(&adapter->req_list_lock, flags); - atomic_set(&adapter->reqs_active, 0); - for (i = 0; i < REQUEST_LIST_SIZE; i++) - list_splice_init(&adapter->req_list[i], &remove_queue); - spin_unlock_irqrestore(&adapter->req_list_lock, flags); - - list_for_each_entry_safe(fsf_req, tmp, &remove_queue, list) { - list_del(&fsf_req->list); - fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; - zfcp_fsf_req_complete(fsf_req); - } + struct fsf_status_read_buffer *sr_buf = req->data; + struct zfcp_adapter *adapter = req->adapter; + struct zfcp_port *port; + int d_id = ntoh24(sr_buf->d_id); + + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) + if (port->d_id == d_id) { + zfcp_erp_port_reopen(port, 0, "fssrpc1"); + break; + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); } -/* - * 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) +static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, + struct fsf_link_down_info *link_down) { - int retval = 0; - int cleanup; - - 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 { - del_timer(&fsf_req->timer); - zfcp_fsf_protstatus_eval(fsf_req); - } + struct zfcp_adapter *adapter = req->adapter; - /* - * 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_free(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; -} + if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED) + return; -/* - * 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; - struct fsf_qtcb *qtcb = fsf_req->qtcb; - union fsf_prot_status_qual *prot_status_qual = - &qtcb->prefix.prot_status_qual; - - zfcp_hba_dbf_event_fsf_response(fsf_req); - - 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. */ - goto skip_protstatus; - } + atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); - /* log additional information provided by FSF (if any) */ - if (likely(qtcb->header.log_length)) { - /* do not trust them ;-) */ - if (unlikely(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", - qtcb->header.log_start, - sizeof(struct fsf_qtcb)); - goto forget_log; - } - if (unlikely((size_t) (qtcb->header.log_start + - 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", - qtcb->header.log_start, - 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 *) qtcb + qtcb->header.log_start, - qtcb->header.log_length); - } - forget_log: + zfcp_scsi_schedule_rports_block(adapter); - /* evaluate FSF Protocol Status */ - switch (qtcb->prefix.prot_status) { + if (!link_down) + goto out; - case FSF_PROT_GOOD: - case FSF_PROT_FSF_STATUS_PRESENTED: + switch (link_down->error_code) { + case FSF_PSQ_LINK_NO_LIGHT: + dev_warn(&req->adapter->ccw_device->dev, + "There is no light signal from the local " + "fibre channel cable\n"); break; - - case FSF_PROT_QTCB_VERSION_ERROR: - 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), - prot_status_qual->version_error.fsf_version, - ZFCP_QTCB_VERSION); - zfcp_erp_adapter_shutdown(adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_PSQ_LINK_WRAP_PLUG: + dev_warn(&req->adapter->ccw_device->dev, + "There is a wrap plug instead of a fibre " + "channel cable\n"); break; - - case FSF_PROT_SEQ_NUMB_ERROR: - ZFCP_LOG_NORMAL("bug: Sequence number mismatch between " - "driver (0x%x) and adapter %s (0x%x). " - "Restarting all operations on this adapter.\n", - qtcb->prefix.req_seq_no, - zfcp_get_busid_by_adapter(adapter), - prot_status_qual->sequence_error.exp_req_seq_no); - zfcp_erp_adapter_reopen(adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY; - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_PSQ_LINK_NO_FCP: + dev_warn(&req->adapter->ccw_device->dev, + "The adjacent fibre channel node does not " + "support FCP\n"); break; - - case FSF_PROT_UNSUPP_QTCB_TYPE: - ZFCP_LOG_NORMAL("error: Packet header type used by the " - "device driver is incompatible with " - "that used on adapter %s. " - "Stopping all operations on this adapter.\n", - zfcp_get_busid_by_adapter(adapter)); - zfcp_erp_adapter_shutdown(adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_PSQ_LINK_FIRMWARE_UPDATE: + dev_warn(&req->adapter->ccw_device->dev, + "The FCP device is suspended because of a " + "firmware update\n"); break; - - case FSF_PROT_HOST_CONNECTION_INITIALIZING: - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, - &(adapter->status)); + case FSF_PSQ_LINK_INVALID_WWPN: + dev_warn(&req->adapter->ccw_device->dev, + "The FCP device detected a WWPN that is " + "duplicate or not valid\n"); break; - - case FSF_PROT_DUPLICATE_REQUEST_ID: - 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*) - (&qtcb->bottom.support.req_handle), - zfcp_get_busid_by_adapter(adapter)); - zfcp_erp_adapter_shutdown(adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_PSQ_LINK_NO_NPIV_SUPPORT: + dev_warn(&req->adapter->ccw_device->dev, + "The fibre channel fabric does not support NPIV\n"); break; - - case FSF_PROT_LINK_DOWN: - zfcp_fsf_link_down_info_eval(adapter, - &prot_status_qual->link_down_info); - zfcp_erp_adapter_reopen(adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_PSQ_LINK_NO_FCP_RESOURCES: + dev_warn(&req->adapter->ccw_device->dev, + "The FCP adapter cannot support more NPIV ports\n"); break; - - case FSF_PROT_REEST_QUEUE: - ZFCP_LOG_NORMAL("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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_PSQ_LINK_NO_FABRIC_RESOURCES: + dev_warn(&req->adapter->ccw_device->dev, + "The adjacent switch cannot support " + "more NPIV ports\n"); break; - - case FSF_PROT_ERROR_STATE: - 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)); - zfcp_erp_adapter_reopen(adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY; - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE: + dev_warn(&req->adapter->ccw_device->dev, + "The FCP adapter could not log in to the " + "fibre channel fabric\n"); + break; + case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED: + dev_warn(&req->adapter->ccw_device->dev, + "The WWPN assignment file on the FCP adapter " + "has been damaged\n"); + break; + case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED: + dev_warn(&req->adapter->ccw_device->dev, + "The mode table on the FCP adapter " + "has been damaged\n"); + break; + case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT: + dev_warn(&req->adapter->ccw_device->dev, + "All NPIV ports on the FCP adapter have " + "been assigned\n"); 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), - qtcb->prefix.prot_status); - zfcp_erp_adapter_shutdown(adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + dev_warn(&req->adapter->ccw_device->dev, + "The link between the FCP adapter and " + "the FC fabric is down\n"); } +out: + zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_ERP_FAILED); +} - 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; +static void zfcp_fsf_status_read_link_down(struct zfcp_fsf_req *req) +{ + struct fsf_status_read_buffer *sr_buf = req->data; + struct fsf_link_down_info *ldi = + (struct fsf_link_down_info *) &sr_buf->payload; + + switch (sr_buf->status_subtype) { + case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK: + zfcp_fsf_link_down_info_eval(req, ldi); + break; + case FSF_STATUS_READ_SUB_FDISC_FAILED: + zfcp_fsf_link_down_info_eval(req, ldi); + break; + case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE: + zfcp_fsf_link_down_info_eval(req, NULL); + }; } -/* - * 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) +static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req) { - int retval = 0; + struct zfcp_adapter *adapter = req->adapter; + struct fsf_status_read_buffer *sr_buf = req->data; - if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) { - goto skip_fsfstatus; + if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { + zfcp_dbf_hba_fsf_uss("fssrh_1", req); + mempool_free(virt_to_page(sr_buf), adapter->pool.sr_data); + zfcp_fsf_req_free(req); + return; } - /* evaluate FSF Status */ - switch (fsf_req->qtcb->header.fsf_status) { - case FSF_UNKNOWN_COMMAND: - 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); - zfcp_erp_adapter_shutdown(fsf_req->adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; + zfcp_dbf_hba_fsf_uss("fssrh_4", req); - case FSF_FCP_RSP_AVAILABLE: - ZFCP_LOG_DEBUG("FCP Sense data will be presented to the " - "SCSI stack.\n"); + switch (sr_buf->status_type) { + case FSF_STATUS_READ_PORT_CLOSED: + zfcp_fsf_status_read_port_closed(req); + break; + case FSF_STATUS_READ_INCOMING_ELS: + zfcp_fc_incoming_els(req); break; + case FSF_STATUS_READ_SENSE_DATA_AVAIL: + break; + case FSF_STATUS_READ_BIT_ERROR_THRESHOLD: + dev_warn(&adapter->ccw_device->dev, + "The error threshold for checksum statistics " + "has been exceeded\n"); + zfcp_dbf_hba_bit_err("fssrh_3", req); + break; + case FSF_STATUS_READ_LINK_DOWN: + zfcp_fsf_status_read_link_down(req); + zfcp_fc_enqueue_event(adapter, FCH_EVT_LINKDOWN, 0); + break; + case FSF_STATUS_READ_LINK_UP: + dev_info(&adapter->ccw_device->dev, + "The local link has been restored\n"); + /* All ports should be marked as ready to run again */ + zfcp_erp_set_adapter_status(adapter, + ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, + ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | + ZFCP_STATUS_COMMON_ERP_FAILED, + "fssrh_2"); + zfcp_fc_enqueue_event(adapter, FCH_EVT_LINKUP, 0); - case FSF_ADAPTER_STATUS_AVAILABLE: - zfcp_fsf_fsfstatus_qual_eval(fsf_req); + break; + case FSF_STATUS_READ_NOTIFICATION_LOST: + if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS) + zfcp_fc_conditional_port_scan(adapter); + break; + case FSF_STATUS_READ_FEATURE_UPDATE_ALERT: + adapter->adapter_features = sr_buf->payload.word[0]; 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); + mempool_free(virt_to_page(sr_buf), adapter->pool.sr_data); + zfcp_fsf_req_free(req); - return retval; + atomic_inc(&adapter->stat_miss); + queue_work(adapter->work_queue, &adapter->stat_work); } -/* - * 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) +static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req) { - int retval = 0; - - switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { + switch (req->qtcb->header.fsf_status_qual.word[0]) { case FSF_SQ_FCP_RSP_AVAILABLE: - break; - case FSF_SQ_RETRY_IF_POSSIBLE: - /* The SCSI-stack may now issue retries or escalate */ - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - case FSF_SQ_COMMAND_ABORTED: - /* Carry the aborted state on to upper layer */ - fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED; - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - case FSF_SQ_NO_RECOM: - 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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - case FSF_SQ_ULP_PROGRAMMING_ERROR: - ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer " - "(adapter %s)\n", - zfcp_get_busid_by_adapter(fsf_req->adapter)); - 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 */ + return; + case FSF_SQ_COMMAND_ABORTED: 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)); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_SQ_NO_RECOM: + dev_err(&req->adapter->ccw_device->dev, + "The FCP adapter reported a problem " + "that cannot be recovered\n"); + zfcp_qdio_siosl(req->adapter); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfsqe1"); break; } - - return retval; + /* all non-return stats set FSFREQ_ERROR*/ + req->status |= ZFCP_STATUS_FSFREQ_ERROR; } -/** - * zfcp_fsf_link_down_info_eval - evaluate link down information block - */ -static void -zfcp_fsf_link_down_info_eval(struct zfcp_adapter *adapter, - struct fsf_link_down_info *link_down) +static void zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *req) { - if (atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, - &adapter->status)) + if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) return; - atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); - - if (link_down == NULL) - goto out; - - switch (link_down->error_code) { - case FSF_PSQ_LINK_NO_LIGHT: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(no light detected)\n", - zfcp_get_busid_by_adapter(adapter)); - break; - case FSF_PSQ_LINK_WRAP_PLUG: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(wrap plug detected)\n", - zfcp_get_busid_by_adapter(adapter)); - break; - case FSF_PSQ_LINK_NO_FCP: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(adjacent node on link does not support FCP)\n", - zfcp_get_busid_by_adapter(adapter)); + switch (req->qtcb->header.fsf_status) { + case FSF_UNKNOWN_COMMAND: + dev_err(&req->adapter->ccw_device->dev, + "The FCP adapter does not recognize the command 0x%x\n", + req->qtcb->header.fsf_command); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfse_1"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_PSQ_LINK_FIRMWARE_UPDATE: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(firmware update in progress)\n", - zfcp_get_busid_by_adapter(adapter)); - break; - case FSF_PSQ_LINK_INVALID_WWPN: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(duplicate or invalid WWPN detected)\n", - zfcp_get_busid_by_adapter(adapter)); + case FSF_ADAPTER_STATUS_AVAILABLE: + zfcp_fsf_fsfstatus_qual_eval(req); break; - case FSF_PSQ_LINK_NO_NPIV_SUPPORT: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(no support for NPIV by Fabric)\n", - zfcp_get_busid_by_adapter(adapter)); + } +} + +static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req) +{ + struct zfcp_adapter *adapter = req->adapter; + struct fsf_qtcb *qtcb = req->qtcb; + union fsf_prot_status_qual *psq = &qtcb->prefix.prot_status_qual; + + zfcp_dbf_hba_fsf_response(req); + + if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { + req->status |= ZFCP_STATUS_FSFREQ_ERROR; + return; + } + + switch (qtcb->prefix.prot_status) { + case FSF_PROT_GOOD: + case FSF_PROT_FSF_STATUS_PRESENTED: + return; + case FSF_PROT_QTCB_VERSION_ERROR: + dev_err(&adapter->ccw_device->dev, + "QTCB version 0x%x not supported by FCP adapter " + "(0x%x to 0x%x)\n", FSF_QTCB_CURRENT_VERSION, + psq->word[0], psq->word[1]); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_1"); break; - case FSF_PSQ_LINK_NO_FCP_RESOURCES: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(out of resource in FCP daughtercard)\n", - zfcp_get_busid_by_adapter(adapter)); + case FSF_PROT_ERROR_STATE: + case FSF_PROT_SEQ_NUMB_ERROR: + zfcp_erp_adapter_reopen(adapter, 0, "fspse_2"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_PSQ_LINK_NO_FABRIC_RESOURCES: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(out of resource in Fabric)\n", - zfcp_get_busid_by_adapter(adapter)); + case FSF_PROT_UNSUPP_QTCB_TYPE: + dev_err(&adapter->ccw_device->dev, + "The QTCB type is not supported by the FCP adapter\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_3"); break; - case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(unable to Fabric login)\n", - zfcp_get_busid_by_adapter(adapter)); + case FSF_PROT_HOST_CONNECTION_INITIALIZING: + atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, + &adapter->status); break; - case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED: - ZFCP_LOG_NORMAL("WWPN assignment file corrupted on adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); + case FSF_PROT_DUPLICATE_REQUEST_ID: + dev_err(&adapter->ccw_device->dev, + "0x%Lx is an ambiguous request identifier\n", + (unsigned long long)qtcb->bottom.support.req_handle); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_4"); break; - case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED: - ZFCP_LOG_NORMAL("Mode table corrupted on adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); + case FSF_PROT_LINK_DOWN: + zfcp_fsf_link_down_info_eval(req, &psq->link_down_info); + /* go through reopen to flush pending requests */ + zfcp_erp_adapter_reopen(adapter, 0, "fspse_6"); break; - case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT: - ZFCP_LOG_NORMAL("No WWPN for assignment table on adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); + case FSF_PROT_REEST_QUEUE: + /* All ports should be marked as ready to run again */ + zfcp_erp_set_adapter_status(adapter, + ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, + ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | + ZFCP_STATUS_COMMON_ERP_FAILED, + "fspse_8"); break; default: - ZFCP_LOG_NORMAL("The local link to adapter %s is down " - "(warning: unknown reason code %d)\n", - zfcp_get_busid_by_adapter(adapter), - link_down->error_code); + dev_err(&adapter->ccw_device->dev, + "0x%x is not a valid transfer protocol status\n", + qtcb->prefix.prot_status); + zfcp_qdio_siosl(adapter); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_9"); } - - if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) - ZFCP_LOG_DEBUG("Debug information to link down: " - "primary_status=0x%02x " - "ioerr_code=0x%02x " - "action_code=0x%02x " - "reason_code=0x%02x " - "explanation_code=0x%02x " - "vendor_specific_code=0x%02x\n", - link_down->primary_status, - link_down->ioerr_code, - link_down->action_code, - link_down->reason_code, - link_down->explanation_code, - link_down->vendor_specific_code); - - out: - zfcp_erp_adapter_failed(adapter); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; } -/* - * function: zfcp_fsf_req_dispatch +/** + * zfcp_fsf_req_complete - process completion of a FSF request + * @fsf_req: The FSF request that has been completed. * - * purpose: calls the appropriate command specific handler + * When a request has been completed either from the FCP adapter, + * or it has been dismissed due to a queue shutdown, this function + * is called to process the completion status and trigger further + * events related to the FSF request. + */ +static void zfcp_fsf_req_complete(struct zfcp_fsf_req *req) +{ + if (unlikely(req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS)) { + zfcp_fsf_status_read_handler(req); + return; + } + + del_timer(&req->timer); + zfcp_fsf_protstatus_eval(req); + zfcp_fsf_fsfstatus_eval(req); + req->handler(req); + + if (req->erp_action) + zfcp_erp_notify(req->erp_action, 0); + + if (likely(req->status & ZFCP_STATUS_FSFREQ_CLEANUP)) + zfcp_fsf_req_free(req); + else + complete(&req->completion); +} + +/** + * zfcp_fsf_req_dismiss_all - dismiss all fsf requests + * @adapter: pointer to struct zfcp_adapter * - * returns: + * Never ever call this without shutting down the adapter first. + * Otherwise the adapter would continue using and corrupting s390 storage. + * Included BUG_ON() call to ensure this is done. + * ERP is supposed to be the only user of this function. */ -static int -zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) +void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter) { - struct zfcp_erp_action *erp_action = fsf_req->erp_action; - struct zfcp_adapter *adapter = fsf_req->adapter; - int retval = 0; + struct zfcp_fsf_req *req, *tmp; + LIST_HEAD(remove_queue); + BUG_ON(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP); + zfcp_reqlist_move(adapter->req_list, &remove_queue); - switch (fsf_req->fsf_command) { + list_for_each_entry_safe(req, tmp, &remove_queue, list) { + list_del(&req->list); + req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; + zfcp_fsf_req_complete(req); + } +} - case FSF_QTCB_FCP_CMND: - zfcp_fsf_send_fcp_command_handler(fsf_req); - break; +#define ZFCP_FSF_PORTSPEED_1GBIT (1 << 0) +#define ZFCP_FSF_PORTSPEED_2GBIT (1 << 1) +#define ZFCP_FSF_PORTSPEED_4GBIT (1 << 2) +#define ZFCP_FSF_PORTSPEED_10GBIT (1 << 3) +#define ZFCP_FSF_PORTSPEED_8GBIT (1 << 4) +#define ZFCP_FSF_PORTSPEED_16GBIT (1 << 5) +#define ZFCP_FSF_PORTSPEED_NOT_NEGOTIATED (1 << 15) - case FSF_QTCB_ABORT_FCP_CMND: - zfcp_fsf_abort_fcp_command_handler(fsf_req); - break; +static u32 zfcp_fsf_convert_portspeed(u32 fsf_speed) +{ + u32 fdmi_speed = 0; + if (fsf_speed & ZFCP_FSF_PORTSPEED_1GBIT) + fdmi_speed |= FC_PORTSPEED_1GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_2GBIT) + fdmi_speed |= FC_PORTSPEED_2GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_4GBIT) + fdmi_speed |= FC_PORTSPEED_4GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_10GBIT) + fdmi_speed |= FC_PORTSPEED_10GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_8GBIT) + fdmi_speed |= FC_PORTSPEED_8GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_16GBIT) + fdmi_speed |= FC_PORTSPEED_16GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_NOT_NEGOTIATED) + fdmi_speed |= FC_PORTSPEED_NOT_NEGOTIATED; + return fdmi_speed; +} - case FSF_QTCB_SEND_GENERIC: - zfcp_fsf_send_ct_handler(fsf_req); - break; +static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) +{ + struct fsf_qtcb_bottom_config *bottom = &req->qtcb->bottom.config; + struct zfcp_adapter *adapter = req->adapter; + struct Scsi_Host *shost = adapter->scsi_host; + struct fc_els_flogi *nsp, *plogi; - case FSF_QTCB_OPEN_PORT_WITH_DID: - zfcp_fsf_open_port_handler(fsf_req); - break; + /* adjust pointers for missing command code */ + nsp = (struct fc_els_flogi *) ((u8 *)&bottom->nport_serv_param + - sizeof(u32)); + plogi = (struct fc_els_flogi *) ((u8 *)&bottom->plogi_payload + - sizeof(u32)); - case FSF_QTCB_OPEN_LUN: - zfcp_fsf_open_unit_handler(fsf_req); - break; + if (req->data) + memcpy(req->data, bottom, sizeof(*bottom)); - case FSF_QTCB_CLOSE_LUN: - zfcp_fsf_close_unit_handler(fsf_req); - break; + fc_host_port_name(shost) = nsp->fl_wwpn; + fc_host_node_name(shost) = nsp->fl_wwnn; + fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3; - case FSF_QTCB_CLOSE_PORT: - zfcp_fsf_close_port_handler(fsf_req); - break; + adapter->timer_ticks = bottom->timer_interval & ZFCP_FSF_TIMER_INT_MASK; + adapter->stat_read_buf_num = max(bottom->status_read_buf_num, + (u16)FSF_STATUS_READS_RECOM); - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - zfcp_fsf_close_physical_port_handler(fsf_req); - break; + if (fc_host_permanent_port_name(shost) == -1) + fc_host_permanent_port_name(shost) = fc_host_port_name(shost); - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - zfcp_fsf_exchange_config_data_handler(fsf_req); - break; + zfcp_scsi_set_prot(adapter); - case FSF_QTCB_EXCHANGE_PORT_DATA: - zfcp_fsf_exchange_port_data_handler(fsf_req); - break; + /* no error return above here, otherwise must fix call chains */ + /* do not evaluate invalid fields */ + if (req->qtcb->header.fsf_status == FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE) + return 0; - case FSF_QTCB_SEND_ELS: - zfcp_fsf_send_els_handler(fsf_req); - break; + fc_host_port_id(shost) = ntoh24(bottom->s_id); + fc_host_speed(shost) = + zfcp_fsf_convert_portspeed(bottom->fc_link_speed); - case FSF_QTCB_DOWNLOAD_CONTROL_FILE: - zfcp_fsf_control_file_handler(fsf_req); - break; + adapter->hydra_version = bottom->adapter_type; - case FSF_QTCB_UPLOAD_CONTROL_FILE: - zfcp_fsf_control_file_handler(fsf_req); + switch (bottom->fc_topology) { + case FSF_TOPO_P2P: + adapter->peer_d_id = ntoh24(bottom->peer_d_id); + adapter->peer_wwpn = plogi->fl_wwpn; + adapter->peer_wwnn = plogi->fl_wwnn; + fc_host_port_type(shost) = FC_PORTTYPE_PTP; break; - + case FSF_TOPO_FABRIC: + fc_host_port_type(shost) = FC_PORTTYPE_NPORT; + break; + case FSF_TOPO_AL: + fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; + /* fall through */ default: - 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(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(adapter), - fsf_req->fsf_command, - fsf_req->qtcb->header.fsf_command); + dev_err(&adapter->ccw_device->dev, + "Unknown or unsupported arbitrated loop " + "fibre channel topology detected\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "fsece_1"); + return -EIO; } - if (!erp_action) - return retval; - - zfcp_erp_async_handler(erp_action, 0); - - return retval; + return 0; } -/* - * 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) +static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) { - 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; - } + struct zfcp_adapter *adapter = req->adapter; + struct fsf_qtcb *qtcb = req->qtcb; + struct fsf_qtcb_bottom_config *bottom = &qtcb->bottom.config; + struct Scsi_Host *shost = adapter->scsi_host; - 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; + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; - 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; + adapter->fsf_lic_version = bottom->lic_version; + adapter->adapter_features = bottom->adapter_features; + adapter->connection_features = bottom->connection_features; + adapter->peer_wwpn = 0; + adapter->peer_wwnn = 0; + adapter->peer_d_id = 0; + + switch (qtcb->header.fsf_status) { + case FSF_GOOD: + if (zfcp_fsf_exchange_config_evaluate(req)) + return; + + if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) { + dev_err(&adapter->ccw_device->dev, + "FCP adapter maximum QTCB size (%d bytes) " + "is too small\n", + bottom->max_qtcb_size); + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh1"); + return; + } + atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, + &adapter->status); + break; + case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: + fc_host_node_name(shost) = 0; + fc_host_port_name(shost) = 0; + fc_host_port_id(shost) = 0; + fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; + fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; + adapter->hydra_version = 0; + + /* avoids adapter shutdown to be able to recognize + * events such as LINK UP */ + atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, + &adapter->status); + zfcp_fsf_link_down_info_eval(req, + &qtcb->header.fsf_status_qual.link_down_info); + if (zfcp_fsf_exchange_config_evaluate(req)) + return; + break; + default: + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh3"); + return; } - memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer)); - fsf_req->data = (unsigned long) 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); + if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) { + adapter->hardware_version = bottom->hardware_version; + memcpy(fc_host_serial_number(shost), bottom->serial_number, + min(FC_SERIAL_NUMBER_SIZE, 17)); + EBCASC(fc_host_serial_number(shost), + min(FC_SERIAL_NUMBER_SIZE, 17)); + } - retval = zfcp_fsf_req_send(fsf_req); - if (retval) { - ZFCP_LOG_DEBUG("error: Could not set-up unsolicited status " - "environment.\n"); - goto failed_req_send; + if (FSF_QTCB_CURRENT_VERSION < bottom->low_qtcb_version) { + dev_err(&adapter->ccw_device->dev, + "The FCP adapter only supports newer " + "control block versions\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh4"); + return; } + if (FSF_QTCB_CURRENT_VERSION > bottom->high_qtcb_version) { + dev_err(&adapter->ccw_device->dev, + "The FCP adapter only supports older " + "control block versions\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh5"); + } +} - ZFCP_LOG_TRACE("Status Read request initiated (adapter%s)\n", - zfcp_get_busid_by_adapter(adapter)); - goto out; +static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req) +{ + struct zfcp_adapter *adapter = req->adapter; + struct fsf_qtcb_bottom_port *bottom = &req->qtcb->bottom.port; + struct Scsi_Host *shost = adapter->scsi_host; - failed_req_send: - mempool_free(status_buffer, adapter->pool.data_status_read); + if (req->data) + memcpy(req->data, bottom, sizeof(*bottom)); - failed_buf: - zfcp_fsf_req_free(fsf_req); - failed_req_create: - zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL); - out: - write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); - return retval; + if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) { + fc_host_permanent_port_name(shost) = bottom->wwpn; + fc_host_port_type(shost) = FC_PORTTYPE_NPIV; + } else + fc_host_permanent_port_name(shost) = fc_host_port_name(shost); + fc_host_maxframe_size(shost) = bottom->maximum_frame_size; + fc_host_supported_speeds(shost) = + zfcp_fsf_convert_portspeed(bottom->supported_speed); + memcpy(fc_host_supported_fc4s(shost), bottom->supported_fc4_types, + FC_FC4_LIST_SIZE); + memcpy(fc_host_active_fc4s(shost), bottom->active_fc4_types, + FC_FC4_LIST_SIZE); } -static int -zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req) +static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req) { - struct fsf_status_read_buffer *status_buffer; - struct zfcp_adapter *adapter; - struct zfcp_port *port; - unsigned long flags; + struct fsf_qtcb *qtcb = req->qtcb; - status_buffer = (struct fsf_status_read_buffer *) fsf_req->data; - adapter = fsf_req->adapter; + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; - 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)) + switch (qtcb->header.fsf_status) { + case FSF_GOOD: + zfcp_fsf_exchange_port_evaluate(req); + break; + case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: + zfcp_fsf_exchange_port_evaluate(req); + zfcp_fsf_link_down_info_eval(req, + &qtcb->header.fsf_status_qual.link_down_info); 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%06x 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) { +static struct zfcp_fsf_req *zfcp_fsf_alloc(mempool_t *pool) +{ + struct zfcp_fsf_req *req; - case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT: - debug_text_event(adapter->erp_dbf, 3, "unsol_pc_phys:"); - zfcp_erp_port_reopen(port, 0); - break; + if (likely(pool)) + req = mempool_alloc(pool, GFP_ATOMIC); + else + req = kmalloc(sizeof(*req), GFP_ATOMIC); - case FSF_STATUS_READ_SUB_ERROR_PORT: - debug_text_event(adapter->erp_dbf, 1, "unsol_pc_err:"); - zfcp_erp_port_shutdown(port, 0); - break; + if (unlikely(!req)) + return NULL; - 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%06x 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; + memset(req, 0, sizeof(*req)); + req->pool = pool; + return req; } -/* - * 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) +static struct fsf_qtcb *zfcp_qtcb_alloc(mempool_t *pool) { - int retval = 0; - struct zfcp_adapter *adapter = fsf_req->adapter; - struct fsf_status_read_buffer *status_buffer = - (struct fsf_status_read_buffer *) fsf_req->data; - struct fsf_bit_error_payload *fsf_bit_error; - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { - zfcp_hba_dbf_event_fsf_unsol("dism", adapter, status_buffer); - mempool_free(status_buffer, adapter->pool.data_status_read); - zfcp_fsf_req_free(fsf_req); - goto out; - } - - zfcp_hba_dbf_event_fsf_unsol("read", adapter, status_buffer); + struct fsf_qtcb *qtcb; - switch (status_buffer->status_type) { + if (likely(pool)) + qtcb = mempool_alloc(pool, GFP_ATOMIC); + else + qtcb = kmem_cache_alloc(zfcp_fsf_qtcb_cache, GFP_ATOMIC); - case FSF_STATUS_READ_PORT_CLOSED: - zfcp_fsf_status_read_port_closed(fsf_req); - break; + if (unlikely(!qtcb)) + return NULL; - case FSF_STATUS_READ_INCOMING_ELS: - zfcp_fsf_incoming_els(fsf_req); - break; + memset(qtcb, 0, sizeof(*qtcb)); + return qtcb; +} - case FSF_STATUS_READ_SENSE_DATA_AVAIL: - ZFCP_LOG_INFO("unsolicited sense data received (adapter %s)\n", - zfcp_get_busid_by_adapter(adapter)); - break; +static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio, + u32 fsf_cmd, u8 sbtype, + mempool_t *pool) +{ + struct zfcp_adapter *adapter = qdio->adapter; + struct zfcp_fsf_req *req = zfcp_fsf_alloc(pool); - case FSF_STATUS_READ_BIT_ERROR_THRESHOLD: - fsf_bit_error = (struct fsf_bit_error_payload *) - status_buffer->payload; - ZFCP_LOG_NORMAL("Warning: bit error threshold data " - "received (adapter %s, " - "link failures = %i, loss of sync errors = %i, " - "loss of signal errors = %i, " - "primitive sequence errors = %i, " - "invalid transmission word errors = %i, " - "CRC errors = %i)\n", - zfcp_get_busid_by_adapter(adapter), - fsf_bit_error->link_failure_error_count, - fsf_bit_error->loss_of_sync_error_count, - fsf_bit_error->loss_of_signal_error_count, - fsf_bit_error->primitive_sequence_error_count, - fsf_bit_error->invalid_transmission_word_error_count, - fsf_bit_error->crc_error_count); - ZFCP_LOG_INFO("Additional bit error threshold data " - "(adapter %s, " - "primitive sequence event time-outs = %i, " - "elastic buffer overrun errors = %i, " - "advertised receive buffer-to-buffer credit = %i, " - "current receice buffer-to-buffer credit = %i, " - "advertised transmit buffer-to-buffer credit = %i, " - "current transmit buffer-to-buffer credit = %i)\n", - zfcp_get_busid_by_adapter(adapter), - fsf_bit_error->primitive_sequence_event_timeout_count, - fsf_bit_error->elastic_buffer_overrun_error_count, - fsf_bit_error->advertised_receive_b2b_credit, - fsf_bit_error->current_receive_b2b_credit, - fsf_bit_error->advertised_transmit_b2b_credit, - fsf_bit_error->current_transmit_b2b_credit); - break; + if (unlikely(!req)) + return ERR_PTR(-ENOMEM); - case FSF_STATUS_READ_LINK_DOWN: - switch (status_buffer->status_subtype) { - case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK: - ZFCP_LOG_INFO("Physical link to adapter %s is down\n", - zfcp_get_busid_by_adapter(adapter)); - zfcp_fsf_link_down_info_eval(adapter, - (struct fsf_link_down_info *) - &status_buffer->payload); - break; - case FSF_STATUS_READ_SUB_FDISC_FAILED: - ZFCP_LOG_INFO("Local link to adapter %s is down " - "due to failed FDISC login\n", - zfcp_get_busid_by_adapter(adapter)); - zfcp_fsf_link_down_info_eval(adapter, - (struct fsf_link_down_info *) - &status_buffer->payload); - break; - case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE: - ZFCP_LOG_INFO("Local link to adapter %s is down " - "due to firmware update on adapter\n", - zfcp_get_busid_by_adapter(adapter)); - zfcp_fsf_link_down_info_eval(adapter, NULL); - break; - default: - ZFCP_LOG_INFO("Local link to adapter %s is down " - "due to unknown reason\n", - zfcp_get_busid_by_adapter(adapter)); - zfcp_fsf_link_down_info_eval(adapter, NULL); - }; - break; + if (adapter->req_no == 0) + adapter->req_no++; - case FSF_STATUS_READ_LINK_UP: - ZFCP_LOG_NORMAL("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; + INIT_LIST_HEAD(&req->list); + init_timer(&req->timer); + init_completion(&req->completion); - case FSF_STATUS_READ_NOTIFICATION_LOST: - ZFCP_LOG_NORMAL("Unsolicited status notification(s) lost: " - "adapter %s%s%s%s%s%s%s%s%s\n", - zfcp_get_busid_by_adapter(adapter), - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_INCOMING_ELS) ? - ", incoming ELS" : "", - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_SENSE_DATA) ? - ", sense data" : "", - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_LINK_STATUS) ? - ", link status change" : "", - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_PORT_CLOSED) ? - ", port close" : "", - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_BIT_ERROR_THRESHOLD) ? - ", bit error exception" : "", - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_ACT_UPDATED) ? - ", ACT update" : "", - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_ACT_HARDENED) ? - ", ACT hardening" : "", - (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_FEATURE_UPDATE_ALERT) ? - ", adapter feature change" : ""); - - if (status_buffer->status_subtype & - FSF_STATUS_READ_SUB_ACT_UPDATED) - zfcp_erp_adapter_access_changed(adapter); - break; + req->adapter = adapter; + req->fsf_command = fsf_cmd; + req->req_id = adapter->req_no; - case FSF_STATUS_READ_CFDC_UPDATED: - ZFCP_LOG_NORMAL("CFDC has been updated on the adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - zfcp_erp_adapter_access_changed(adapter); - break; + if (likely(fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS)) { + if (likely(pool)) + req->qtcb = zfcp_qtcb_alloc(adapter->pool.qtcb_pool); + else + req->qtcb = zfcp_qtcb_alloc(NULL); - case FSF_STATUS_READ_CFDC_HARDENED: - switch (status_buffer->status_subtype) { - case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE: - ZFCP_LOG_NORMAL("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_NORMAL("CFDC of adapter %s has been copied " - "to the secondary SE\n", - zfcp_get_busid_by_adapter(adapter)); - break; - default: - ZFCP_LOG_NORMAL("CFDC of adapter %s has been hardened\n", - zfcp_get_busid_by_adapter(adapter)); + if (unlikely(!req->qtcb)) { + zfcp_fsf_req_free(req); + return ERR_PTR(-ENOMEM); } - break; - case FSF_STATUS_READ_FEATURE_UPDATE_ALERT: - debug_text_event(adapter->erp_dbf, 2, "unsol_features:"); - ZFCP_LOG_INFO("List of supported features on adapter %s has " - "been changed from 0x%08X to 0x%08X\n", - zfcp_get_busid_by_adapter(adapter), - *(u32*) (status_buffer->payload + 4), - *(u32*) (status_buffer->payload)); - adapter->adapter_features = *(u32*) status_buffer->payload; - break; - - default: - ZFCP_LOG_NORMAL("warning: 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; + req->seq_no = adapter->fsf_req_seq_no; + req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no; + req->qtcb->prefix.req_id = req->req_id; + req->qtcb->prefix.ulp_info = 26; + req->qtcb->prefix.qtcb_type = fsf_qtcb_type[req->fsf_command]; + req->qtcb->prefix.qtcb_version = FSF_QTCB_CURRENT_VERSION; + req->qtcb->header.req_handle = req->req_id; + req->qtcb->header.fsf_command = req->fsf_command; } - mempool_free(status_buffer, adapter->pool.data_status_read); - zfcp_fsf_req_free(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; + + zfcp_qdio_req_init(adapter->qdio, &req->qdio_req, req->req_id, sbtype, + req->qtcb, sizeof(struct fsf_qtcb)); + + return req; } -/* - * 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) +static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req = NULL; - unsigned long lock_flags; - 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; + struct zfcp_adapter *adapter = req->adapter; + struct zfcp_qdio *qdio = adapter->qdio; + int with_qtcb = (req->qtcb != NULL); + int req_id = req->req_id; + + zfcp_reqlist_add(adapter->req_list, req); + + req->qdio_req.qdio_outb_usage = atomic_read(&qdio->req_q_free); + req->issued = get_tod_clock(); + if (zfcp_qdio_send(qdio, &req->qdio_req)) { + del_timer(&req->timer); + /* lookup request again, list might have changed */ + zfcp_reqlist_find_rm(adapter->req_list, req_id); + zfcp_erp_adapter_reopen(adapter, 0, "fsrs__1"); + return -EIO; } - if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, - &unit->status))) - goto unit_blocked; + /* Don't increase for unsolicited status */ + if (with_qtcb) + adapter->fsf_req_seq_no++; + adapter->req_no++; + + return 0; +} - 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; +/** + * zfcp_fsf_status_read - send status read request + * @adapter: pointer to struct zfcp_adapter + * @req_flags: request flags + * Returns: 0 on success, ERROR otherwise + */ +int zfcp_fsf_status_read(struct zfcp_qdio *qdio) +{ + struct zfcp_adapter *adapter = qdio->adapter; + struct zfcp_fsf_req *req; + struct fsf_status_read_buffer *sr_buf; + struct page *page; + int retval = -EIO; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; - fsf_req->data = (unsigned long) unit; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_UNSOLICITED_STATUS, + SBAL_SFLAGS0_TYPE_STATUS, + adapter->pool.status_read_req); + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out; + } - /* 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; + page = mempool_alloc(adapter->pool.sr_data, GFP_ATOMIC); + if (!page) { + retval = -ENOMEM; + goto failed_buf; + } + sr_buf = page_address(page); + memset(sr_buf, 0, sizeof(*sr_buf)); + req->data = sr_buf; - /* set handle of request which should be aborted */ - fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id; + zfcp_qdio_fill_next(qdio, &req->qdio_req, sr_buf, sizeof(*sr_buf)); + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT); - retval = zfcp_fsf_req_send(fsf_req); - if (!retval) - goto out; + retval = zfcp_fsf_req_send(req); + if (retval) + goto failed_req_send; - unit_blocked: - zfcp_fsf_req_free(fsf_req); - fsf_req = NULL; + goto out; - out: - write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); - return fsf_req; +failed_req_send: + req->data = NULL; + mempool_free(virt_to_page(sr_buf), adapter->pool.sr_data); +failed_buf: + zfcp_dbf_hba_fsf_uss("fssr__1", req); + zfcp_fsf_req_free(req); +out: + spin_unlock_irq(&qdio->req_q_lock); + return retval; } -/* - * 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) +static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req) { - int retval = -EINVAL; - struct zfcp_unit *unit; - union fsf_status_qual *fsf_stat_qual = - &new_fsf_req->qtcb->header.fsf_status_qual; + struct scsi_device *sdev = req->data; + struct zfcp_scsi_dev *zfcp_sdev; + union fsf_status_qual *fsq = &req->qtcb->header.fsf_status_qual; - if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { - /* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */ - goto skip_fsfstatus; - } - - unit = (struct zfcp_unit *) new_fsf_req->data; + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; - /* evaluate FSF status in QTCB */ - switch (new_fsf_req->qtcb->header.fsf_status) { + zfcp_sdev = sdev_to_zfcp(sdev); + switch (req->qtcb->header.fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) { - 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_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; + if (fsq->word[0] == fsq->word[1]) { + zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, + "fsafch1"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; } break; - case FSF_LUN_HANDLE_NOT_VALID: - if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) { - 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_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; + if (fsq->word[0] == fsq->word[1]) { + zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fsafch2"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; } break; - case FSF_FCP_COMMAND_DOES_NOT_EXIST: - retval = 0; - debug_text_event(new_fsf_req->adapter->erp_dbf, 3, - "fsf_s_no_exist"); - new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED; + req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED; break; - case FSF_PORT_BOXED: - 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_boxed(unit->port); - new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR - | ZFCP_STATUS_FSFREQ_RETRY; + zfcp_erp_set_port_status(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ACCESS_BOXED); + zfcp_erp_port_reopen(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ERP_FAILED, "fsafch3"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_LUN_BOXED: - 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_boxed(unit); - new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR - | ZFCP_STATUS_FSFREQ_RETRY; + zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ACCESS_BOXED); + zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, + "fsafch4"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_ADAPTER_STATUS_AVAILABLE: - switch (new_fsf_req->qtcb->header.fsf_status_qual.word[0]) { + switch (fsq->word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - debug_text_event(new_fsf_req->adapter->erp_dbf, 1, - "fsf_sq_ltest"); - zfcp_test_link(unit->port); - new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; + zfcp_fc_test_link(zfcp_sdev->port); + /* fall through */ case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: - /* 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)); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } break; - case FSF_GOOD: - 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)); + req->status |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED; 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. + * zfcp_fsf_abort_fcp_cmnd - abort running SCSI command + * @scmnd: The SCSI command to abort + * Returns: pointer to struct zfcp_fsf_req */ -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) +struct zfcp_fsf_req *zfcp_fsf_abort_fcp_cmnd(struct scsi_cmnd *scmnd) { - 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; + struct zfcp_fsf_req *req = NULL; + struct scsi_device *sdev = scmnd->device; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); + struct zfcp_qdio *qdio = zfcp_sdev->port->adapter->qdio; + unsigned long old_req_id = (unsigned long) scmnd->host_scribble; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_ABORT_FCP_CMND, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.scsi_abort); + if (IS_ERR(req)) { + req = NULL; + goto out; } - 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->adapter_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 = - ZFCP_FC_SERVICE_CLASS_DEFAULT; - fsf_req->qtcb->bottom.support.timeout = ct->timeout; - fsf_req->data = (unsigned long) ct; - - zfcp_san_dbf_event_ct_request(fsf_req); - - if (erp_action) { - erp_action->fsf_req = fsf_req; - fsf_req->erp_action = erp_action; - zfcp_erp_start_timer(fsf_req); - } else - zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); + if (unlikely(!(atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_UNBLOCKED))) + goto out_error_free; - ret = zfcp_fsf_req_send(fsf_req); - 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_qdio_set_sbale_last(qdio, &req->qdio_req); - ZFCP_LOG_DEBUG("CT request initiated (adapter %s, port 0x%016Lx)\n", - zfcp_get_busid_by_adapter(adapter), port->wwpn); - goto out; + req->data = sdev; + req->handler = zfcp_fsf_abort_fcp_command_handler; + req->qtcb->header.lun_handle = zfcp_sdev->lun_handle; + req->qtcb->header.port_handle = zfcp_sdev->port->handle; + req->qtcb->bottom.support.req_handle = (u64) old_req_id; - 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_start_timer(req, ZFCP_SCSI_ER_TIMEOUT); + if (!zfcp_fsf_req_send(req)) + goto out; + +out_error_free: + zfcp_fsf_req_free(req); + req = NULL; +out: + spin_unlock_irq(&qdio->req_q_lock); + return req; } -/** - * 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 using - * fsf_req->data. There we find the pointer to struct zfcp_send_ct. - * Usually a specific handler for the CT request is called which is - * found in this structure. - */ -static int -zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) +static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *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 = (struct zfcp_send_ct *) fsf_req->data; - port = send_ct->port; - header = &fsf_req->qtcb->header; - bottom = &fsf_req->qtcb->bottom.support; - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) + struct zfcp_adapter *adapter = req->adapter; + struct zfcp_fsf_ct_els *ct = req->data; + struct fsf_qtcb_header *header = &req->qtcb->header; + + ct->status = -EINVAL; + + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) goto skip_fsfstatus; - /* evaluate FSF status in QTCB */ switch (header->fsf_status) { - case FSF_GOOD: - zfcp_san_dbf_event_ct_response(fsf_req); - retval = 0; + zfcp_dbf_san_res("fsscth2", req); + ct->status = 0; break; - case FSF_SERVICE_CLASS_NOT_SUPPORTED: - ZFCP_LOG_INFO("error: adapter %s does not support fc " - "class %d.\n", - zfcp_get_busid_by_port(port), - ZFCP_FC_SERVICE_CLASS_DEFAULT); - /* 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; + zfcp_fsf_class_not_supp(req); break; - case FSF_ADAPTER_STATUS_AVAILABLE: switch (header->fsf_status_qual.word[0]){ case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - /* 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: - /* 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]); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } break; - - case FSF_ACCESS_DENIED: - ZFCP_LOG_NORMAL("access denied, cannot send generic service " - "command (adapter %s, port d_id=0x%06x)\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_INFO("generic service command rejected " - "(adapter %s, port d_id=0x%06x)\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_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_INFO("port needs to be reopened " - "(adapter %s, port d_id=0x%06x)\n", - zfcp_get_busid_by_port(port), port->d_id); - debug_text_event(adapter->erp_dbf, 2, "fsf_s_pboxed"); - zfcp_erp_port_boxed(port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR - | ZFCP_STATUS_FSFREQ_RETRY; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - - /* following states should never occure, all cases avoided - in zfcp_fsf_send_ct - but who knows ... */ + case FSF_PORT_HANDLE_NOT_VALID: + zfcp_erp_adapter_reopen(adapter, 0, "fsscth1"); + /* fall through */ + case FSF_GENERIC_COMMAND_REJECTED: case FSF_PAYLOAD_SIZE_MISMATCH: - 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_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_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_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)); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } skip_fsfstatus: - send_ct->status = retval; + if (ct->handler) + ct->handler(ct->handler_data); +} - if (send_ct->handler != NULL) - send_ct->handler(send_ct->handler_data); +static void zfcp_fsf_setup_ct_els_unchained(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req, + struct scatterlist *sg_req, + struct scatterlist *sg_resp) +{ + zfcp_qdio_fill_next(qdio, q_req, sg_virt(sg_req), sg_req->length); + zfcp_qdio_fill_next(qdio, q_req, sg_virt(sg_resp), sg_resp->length); + zfcp_qdio_set_sbale_last(qdio, q_req); +} - return retval; +static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, + struct scatterlist *sg_req, + struct scatterlist *sg_resp) +{ + struct zfcp_adapter *adapter = req->adapter; + struct zfcp_qdio *qdio = adapter->qdio; + struct fsf_qtcb *qtcb = req->qtcb; + u32 feat = adapter->adapter_features; + + if (zfcp_adapter_multi_buffer_active(adapter)) { + if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_req)) + return -EIO; + if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_resp)) + return -EIO; + + zfcp_qdio_set_data_div(qdio, &req->qdio_req, + zfcp_qdio_sbale_count(sg_req)); + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + zfcp_qdio_set_scount(qdio, &req->qdio_req); + return 0; + } + + /* use single, unchained SBAL if it can hold the request */ + if (zfcp_qdio_sg_one_sbale(sg_req) && zfcp_qdio_sg_one_sbale(sg_resp)) { + zfcp_fsf_setup_ct_els_unchained(qdio, &req->qdio_req, + sg_req, sg_resp); + return 0; + } + + if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) + return -EOPNOTSUPP; + + if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_req)) + return -EIO; + + qtcb->bottom.support.req_buf_length = zfcp_qdio_real_bytes(sg_req); + + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + zfcp_qdio_skip_to_last_sbale(qdio, &req->qdio_req); + + if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_resp)) + return -EIO; + + qtcb->bottom.support.resp_buf_length = zfcp_qdio_real_bytes(sg_resp); + + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + + return 0; +} + +static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req, + struct scatterlist *sg_req, + struct scatterlist *sg_resp, + unsigned int timeout) +{ + int ret; + + ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp); + if (ret) + return ret; + + /* common settings for ct/gs and els requests */ + if (timeout > 255) + timeout = 255; /* max value accepted by hardware */ + req->qtcb->bottom.support.service_class = FSF_CLASS_3; + req->qtcb->bottom.support.timeout = timeout; + zfcp_fsf_start_timer(req, (timeout + 10) * HZ); + + return 0; } /** - * 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. + * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS) + * @ct: pointer to struct zfcp_send_ct with data for request + * @pool: if non-null this mempool is used to allocate struct zfcp_fsf_req */ -int -zfcp_fsf_send_els(struct zfcp_send_els *els) +int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, + struct zfcp_fsf_ct_els *ct, mempool_t *pool, + unsigned int timeout) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - u32 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%06x)\n", - zfcp_get_busid_by_adapter(adapter), d_id); - goto failed_req; - } + struct zfcp_qdio *qdio = wka_port->adapter->qdio; + struct zfcp_fsf_req *req; + int ret = -EIO; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; + + req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_GENERIC, + SBAL_SFLAGS0_TYPE_WRITE_READ, pool); - if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, - &els->port->status))) { - ret = -EBUSY; - goto port_blocked; + if (IS_ERR(req)) { + ret = PTR_ERR(req); + goto out; } - 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->adapter_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%06x)\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%06x)\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%06x)\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 = - ZFCP_FC_SERVICE_CLASS_DEFAULT; - fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT; - fsf_req->data = (unsigned long) els; - - sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); - - zfcp_san_dbf_event_els_request(fsf_req); - - zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); - ret = zfcp_fsf_req_send(fsf_req); - if (ret) { - ZFCP_LOG_DEBUG("error: initiation of ELS request failed " - "(adapter %s, port d_id: 0x%06x)\n", - zfcp_get_busid_by_adapter(adapter), d_id); + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + ret = zfcp_fsf_setup_ct_els(req, ct->req, ct->resp, timeout); + if (ret) goto failed_send; - } - ZFCP_LOG_DEBUG("ELS request initiated (adapter %s, port d_id: " - "0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); - goto out; + req->handler = zfcp_fsf_send_ct_handler; + req->qtcb->header.port_handle = wka_port->handle; + req->data = ct; - port_blocked: - failed_send: - zfcp_fsf_req_free(fsf_req); + zfcp_dbf_san_req("fssct_1", req, wka_port->d_id); - failed_req: - out: - write_unlock_irqrestore(&adapter->request_queue.queue_lock, - lock_flags); + ret = zfcp_fsf_req_send(req); + if (ret) + goto failed_send; + + goto out; - return ret; +failed_send: + zfcp_fsf_req_free(req); +out: + spin_unlock_irq(&qdio->req_q_lock); + 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 using - * fsf_req->data. There we find the pointer to struct zfcp_send_els. - * Usually a specific handler for the ELS command is called which is - * found in this structure. - */ -static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) +static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req) { - struct zfcp_adapter *adapter; - struct zfcp_port *port; - u32 d_id; - 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 = (struct zfcp_send_els *) fsf_req->data; - adapter = send_els->adapter; - port = send_els->port; - 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) + struct zfcp_fsf_ct_els *send_els = req->data; + struct fsf_qtcb_header *header = &req->qtcb->header; + + send_els->status = -EINVAL; + + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) goto skip_fsfstatus; switch (header->fsf_status) { - case FSF_GOOD: - zfcp_san_dbf_event_els_response(fsf_req); - retval = 0; + zfcp_dbf_san_res("fsselh1", req); + send_els->status = 0; break; - case FSF_SERVICE_CLASS_NOT_SUPPORTED: - ZFCP_LOG_INFO("error: adapter %s does not support fc " - "class %d.\n", - zfcp_get_busid_by_adapter(adapter), - ZFCP_FC_SERVICE_CLASS_DEFAULT); - /* 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; + zfcp_fsf_class_not_supp(req); break; - case FSF_ADAPTER_STATUS_AVAILABLE: switch (header->fsf_status_qual.word[0]){ case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ltest"); - if (port && (send_els->ls_code != ZFCP_LS_ADISC)) - zfcp_test_link(port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: - 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: - debug_text_event(adapter->erp_dbf, 1, "fsf_sq_retry"); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + 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_INFO("ELS has been rejected because command filter " - "prohibited sending " - "(adapter: %s, port d_id: 0x%06x)\n", - zfcp_get_busid_by_adapter(adapter), d_id); - - break; - case FSF_PAYLOAD_SIZE_MISMATCH: - 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_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_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_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_NORMAL("access denied, cannot send ELS command " - "(adapter %s, port d_id=0x%06x)\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"); - if (port != NULL) - zfcp_erp_port_access_denied(port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - + /* should never occur, avoided in zfcp_fsf_send_els */ + /* fall through */ 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; + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } - skip_fsfstatus: - send_els->status = retval; - if (send_els->handler) send_els->handler(send_els->handler_data); - - return retval; } -int -zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) +/** + * zfcp_fsf_send_els - initiate an ELS command (FC-FS) + * @els: pointer to struct zfcp_send_els with data for the command + */ +int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id, + struct zfcp_fsf_ct_els *els, unsigned int timeout) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - struct zfcp_adapter *adapter = erp_action->adapter; - unsigned long lock_flags; - int retval; - - /* setup new FSF request */ - retval = zfcp_fsf_req_create(adapter, - FSF_QTCB_EXCHANGE_CONFIG_DATA, - ZFCP_REQ_AUTO_CLEANUP, - adapter->pool.fsf_req_erp, - &lock_flags, &fsf_req); - if (retval) { - ZFCP_LOG_INFO("error: Could not create exchange configuration " - "data request for adapter %s.\n", - zfcp_get_busid_by_adapter(adapter)); - write_unlock_irqrestore(&adapter->request_queue.queue_lock, - lock_flags); - return retval; - } + struct zfcp_fsf_req *req; + struct zfcp_qdio *qdio = adapter->qdio; + int ret = -EIO; - 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; + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; - fsf_req->qtcb->bottom.config.feature_selection = - FSF_FEATURE_CFDC | - FSF_FEATURE_LUN_SHARING | - FSF_FEATURE_NOTIFICATION_LOST | - FSF_FEATURE_UPDATE_ALERT; - fsf_req->erp_action = erp_action; - erp_action->fsf_req = fsf_req; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_ELS, + SBAL_SFLAGS0_TYPE_WRITE_READ, NULL); - zfcp_erp_start_timer(fsf_req); - retval = zfcp_fsf_req_send(fsf_req); - write_unlock_irqrestore(&adapter->request_queue.queue_lock, - lock_flags); - if (retval) { - ZFCP_LOG_INFO("error: Could not send exchange configuration " - "data command on the adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - zfcp_fsf_req_free(fsf_req); - erp_action->fsf_req = NULL; + if (IS_ERR(req)) { + ret = PTR_ERR(req); + goto out; } - else - ZFCP_LOG_DEBUG("exchange configuration data request initiated " - "(adapter %s)\n", - zfcp_get_busid_by_adapter(adapter)); - return retval; -} + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; -int -zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter, - struct fsf_qtcb_bottom_config *data) -{ - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - unsigned long lock_flags; - int retval; + if (!zfcp_adapter_multi_buffer_active(adapter)) + zfcp_qdio_sbal_limit(qdio, &req->qdio_req, 2); - /* setup new FSF request */ - retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA, - 0, NULL, &lock_flags, &fsf_req); - if (retval) { - ZFCP_LOG_INFO("error: Could not create exchange configuration " - "data request for adapter %s.\n", - zfcp_get_busid_by_adapter(adapter)); - write_unlock_irqrestore(&adapter->request_queue.queue_lock, - lock_flags); - return retval; - } + ret = zfcp_fsf_setup_ct_els(req, els->req, els->resp, timeout); - 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; + if (ret) + goto failed_send; - fsf_req->qtcb->bottom.config.feature_selection = - FSF_FEATURE_CFDC | - FSF_FEATURE_LUN_SHARING | - FSF_FEATURE_NOTIFICATION_LOST | - FSF_FEATURE_UPDATE_ALERT; + hton24(req->qtcb->bottom.support.d_id, d_id); + req->handler = zfcp_fsf_send_els_handler; + req->data = els; - if (data) - fsf_req->data = (unsigned long) data; + zfcp_dbf_san_req("fssels1", req, d_id); - zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); - retval = zfcp_fsf_req_send(fsf_req); - write_unlock_irqrestore(&adapter->request_queue.queue_lock, - lock_flags); - if (retval) - ZFCP_LOG_INFO("error: Could not send exchange configuration " - "data command on the adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - else - wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + ret = zfcp_fsf_req_send(req); + if (ret) + goto failed_send; - zfcp_fsf_req_free(fsf_req); + goto out; - return retval; +failed_send: + zfcp_fsf_req_free(req); +out: + spin_unlock_irq(&qdio->req_q_lock); + return ret; } -/** - * 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) +int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) { - struct fsf_qtcb_bottom_config *bottom; - struct zfcp_adapter *adapter = fsf_req->adapter; - struct Scsi_Host *shost = adapter->scsi_host; + struct zfcp_fsf_req *req; + struct zfcp_qdio *qdio = erp_action->adapter->qdio; + int retval = -EIO; - 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->adapter_features = bottom->adapter_features; - adapter->connection_features = bottom->connection_features; - adapter->peer_wwpn = 0; - adapter->peer_wwnn = 0; - adapter->peer_d_id = 0; + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; - if (xchg_ok) { - - if (fsf_req->data) - memcpy((struct fsf_qtcb_bottom_config *) fsf_req->data, - bottom, sizeof (struct fsf_qtcb_bottom_config)); - - fc_host_node_name(shost) = bottom->nport_serv_param.wwnn; - fc_host_port_name(shost) = bottom->nport_serv_param.wwpn; - fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK; - fc_host_speed(shost) = bottom->fc_link_speed; - fc_host_supported_classes(shost) = - FC_COS_CLASS2 | FC_COS_CLASS3; - adapter->hydra_version = bottom->adapter_type; - if (fc_host_permanent_port_name(shost) == -1) - fc_host_permanent_port_name(shost) = - fc_host_port_name(shost); - if (bottom->fc_topology == FSF_TOPO_P2P) { - adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK; - adapter->peer_wwpn = bottom->plogi_payload.wwpn; - adapter->peer_wwnn = bottom->plogi_payload.wwnn; - fc_host_port_type(shost) = FC_PORTTYPE_PTP; - } else if (bottom->fc_topology == FSF_TOPO_FABRIC) - fc_host_port_type(shost) = FC_PORTTYPE_NPORT; - else if (bottom->fc_topology == FSF_TOPO_AL) - fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; - else - fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; - } else { - fc_host_node_name(shost) = 0; - fc_host_port_name(shost) = 0; - fc_host_port_id(shost) = 0; - fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; - fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; - adapter->hydra_version = 0; - } + req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_CONFIG_DATA, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); - if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) { - adapter->hardware_version = bottom->hardware_version; - memcpy(fc_host_serial_number(shost), bottom->serial_number, - min(FC_SERIAL_NUMBER_SIZE, 17)); - EBCASC(fc_host_serial_number(shost), - min(FC_SERIAL_NUMBER_SIZE, 17)); + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out; } - ZFCP_LOG_NORMAL("The adapter %s reported the following " - "characteristics:\n" - "WWNN 0x%016Lx, " - "WWPN 0x%016Lx, " - "S_ID 0x%06x,\n" - "adapter version 0x%x, " - "LIC version 0x%x, " - "FC link speed %d Gb/s\n", - zfcp_get_busid_by_adapter(adapter), - (wwn_t) fc_host_node_name(shost), - (wwn_t) fc_host_port_name(shost), - fc_host_port_id(shost), - adapter->hydra_version, - adapter->fsf_lic_version, - fc_host_speed(shost)); - 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; + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + + req->qtcb->bottom.config.feature_selection = + FSF_FEATURE_NOTIFICATION_LOST | + FSF_FEATURE_UPDATE_ALERT; + req->erp_action = erp_action; + req->handler = zfcp_fsf_exchange_config_data_handler; + erp_action->fsf_req_id = req->req_id; + + zfcp_fsf_start_erp_timer(req); + retval = zfcp_fsf_req_send(req); + if (retval) { + zfcp_fsf_req_free(req); + erp_action->fsf_req_id = 0; } - return 0; +out: + spin_unlock_irq(&qdio->req_q_lock); + return retval; } -/** - * function: zfcp_fsf_exchange_config_data_handler - * - * purpose: is called for finished Exchange Configuration Data command - * - * returns: - */ -static int -zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) +int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *qdio, + struct fsf_qtcb_bottom_config *data) { - struct fsf_qtcb_bottom_config *bottom; - struct zfcp_adapter *adapter = fsf_req->adapter; - struct fsf_qtcb *qtcb = fsf_req->qtcb; + struct zfcp_fsf_req *req = NULL; + int retval = -EIO; - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) - return -EIO; + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out_unlock; - switch (qtcb->header.fsf_status) { + req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_CONFIG_DATA, + SBAL_SFLAGS0_TYPE_READ, NULL); - case FSF_GOOD: - if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1)) - return -EIO; + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out_unlock; + } - switch (fc_host_port_type(adapter->scsi_host)) { - case FC_PORTTYPE_PTP: - ZFCP_LOG_NORMAL("Point-to-Point fibrechannel " - "configuration detected at adapter %s\n" - "Peer WWNN 0x%016llx, " - "peer WWPN 0x%016llx, " - "peer d_id 0x%06x\n", - zfcp_get_busid_by_adapter(adapter), - adapter->peer_wwnn, - adapter->peer_wwpn, - adapter->peer_d_id); - debug_text_event(fsf_req->adapter->erp_dbf, 0, - "top-p-to-p"); - break; - case FC_PORTTYPE_NLPORT: - 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 FC_PORTTYPE_NPORT: - ZFCP_LOG_NORMAL("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 = &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"); + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + req->handler = zfcp_fsf_exchange_config_data_handler; - if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0)) - return -EIO; + req->qtcb->bottom.config.feature_selection = + FSF_FEATURE_NOTIFICATION_LOST | + FSF_FEATURE_UPDATE_ALERT; - atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, - &adapter->status); + if (data) + req->data = data; - zfcp_fsf_link_down_info_eval(adapter, - &qtcb->header.fsf_status_qual.link_down_info); - 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_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); + retval = zfcp_fsf_req_send(req); + spin_unlock_irq(&qdio->req_q_lock); + if (!retval) + wait_for_completion(&req->completion); + + zfcp_fsf_req_free(req); + return retval; + +out_unlock: + spin_unlock_irq(&qdio->req_q_lock); + return retval; } /** * zfcp_fsf_exchange_port_data - request information about local port * @erp_action: ERP action for the adapter for which port data is requested + * Returns: 0 on success, error otherwise */ -int -zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) +int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - struct zfcp_adapter *adapter = erp_action->adapter; - unsigned long lock_flags; - int retval; + struct zfcp_qdio *qdio = erp_action->adapter->qdio; + struct zfcp_fsf_req *req; + int retval = -EIO; - if (!(adapter->adapter_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)); + if (!(qdio->adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) return -EOPNOTSUPP; - } - /* setup new FSF request */ - retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, - ZFCP_REQ_AUTO_CLEANUP, - adapter->pool.fsf_req_erp, - &lock_flags, &fsf_req); - if (retval) { - 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); - return retval; - } + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; + + req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_PORT_DATA, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); - 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; + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out; + } - erp_action->fsf_req = fsf_req; - fsf_req->erp_action = erp_action; - zfcp_erp_start_timer(fsf_req); + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - retval = zfcp_fsf_req_send(fsf_req); - write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); + req->handler = zfcp_fsf_exchange_port_data_handler; + req->erp_action = erp_action; + erp_action->fsf_req_id = req->req_id; + zfcp_fsf_start_erp_timer(req); + retval = zfcp_fsf_req_send(req); 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); - erp_action->fsf_req = NULL; + zfcp_fsf_req_free(req); + erp_action->fsf_req_id = 0; } - else - ZFCP_LOG_DEBUG("exchange port data request initiated " - "(adapter %s)\n", - zfcp_get_busid_by_adapter(adapter)); +out: + spin_unlock_irq(&qdio->req_q_lock); return retval; } - /** * zfcp_fsf_exchange_port_data_sync - request information about local port - * and wait until information is ready + * @qdio: pointer to struct zfcp_qdio + * @data: pointer to struct fsf_qtcb_bottom_port + * Returns: 0 on success, error otherwise */ -int -zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter, - struct fsf_qtcb_bottom_port *data) +int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio, + struct fsf_qtcb_bottom_port *data) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - unsigned long lock_flags; - int retval; + struct zfcp_fsf_req *req = NULL; + int retval = -EIO; - if (!(adapter->adapter_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)); + if (!(qdio->adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) return -EOPNOTSUPP; - } - /* setup new FSF request */ - retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, - 0, NULL, &lock_flags, &fsf_req); - if (retval) { - 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); - return retval; + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out_unlock; + + req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_PORT_DATA, + SBAL_SFLAGS0_TYPE_READ, NULL); + + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out_unlock; } if (data) - fsf_req->data = (unsigned long) data; + req->data = data; - 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; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); - retval = zfcp_fsf_req_send(fsf_req); - write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); + req->handler = zfcp_fsf_exchange_port_data_handler; + zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); + retval = zfcp_fsf_req_send(req); + spin_unlock_irq(&qdio->req_q_lock); - 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)); - else - wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + if (!retval) + wait_for_completion(&req->completion); - zfcp_fsf_req_free(fsf_req); + zfcp_fsf_req_free(req); return retval; -} - -/** - * zfcp_fsf_exchange_port_evaluate - * @fsf_req: fsf_req which belongs to xchg port data request - * @xchg_ok: specifies if xchg port data was incomplete or complete (0/1) - */ -static void -zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) -{ - struct zfcp_adapter *adapter; - struct fsf_qtcb_bottom_port *bottom; - struct Scsi_Host *shost; - - adapter = fsf_req->adapter; - bottom = &fsf_req->qtcb->bottom.port; - shost = adapter->scsi_host; - - if (fsf_req->data) - memcpy((struct fsf_qtcb_bottom_port*) fsf_req->data, bottom, - sizeof(struct fsf_qtcb_bottom_port)); - - if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) - fc_host_permanent_port_name(shost) = bottom->wwpn; - else - fc_host_permanent_port_name(shost) = fc_host_port_name(shost); - fc_host_maxframe_size(shost) = bottom->maximum_frame_size; - fc_host_supported_speeds(shost) = bottom->supported_speed; -} - -/** - * 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 zfcp_adapter *adapter; - struct fsf_qtcb *qtcb; - - adapter = fsf_req->adapter; - qtcb = fsf_req->qtcb; - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) - return; - - switch (qtcb->header.fsf_status) { - case FSF_GOOD: - zfcp_fsf_exchange_port_evaluate(fsf_req, 1); - atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); - break; - case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: - zfcp_fsf_exchange_port_evaluate(fsf_req, 0); - atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); - zfcp_fsf_link_down_info_eval(adapter, - &qtcb->header.fsf_status_qual.link_down_info); - break; - default: - debug_text_event(adapter->erp_dbf, 0, "xchg-port-ng"); - debug_event(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; - struct zfcp_fsf_req *fsf_req; - 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, &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(fsf_req, fsf_req->sbal_curr, 0); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; - fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id; - atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status); - fsf_req->data = (unsigned long) erp_action->port; - fsf_req->erp_action = erp_action; - erp_action->fsf_req = fsf_req; - - zfcp_erp_start_timer(fsf_req); - retval = zfcp_fsf_req_send(fsf_req); - 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(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); +out_unlock: + spin_unlock_irq(&qdio->req_q_lock); 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) +static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) { - int retval = -EINVAL; - struct zfcp_port *port; - struct fsf_plogi *plogi; - struct fsf_qtcb_header *header; - u16 subtable, rule, counter; + struct zfcp_port *port = req->data; + struct fsf_qtcb_header *header = &req->qtcb->header; + struct fc_els_flogi *plogi; - port = (struct zfcp_port *) fsf_req->data; - header = &fsf_req->qtcb->header; - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { - /* don't change port status in our bookkeeping */ - goto skip_fsfstatus; - } + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + goto out; - /* evaluate FSF status in QTCB */ switch (header->fsf_status) { - case FSF_PORT_ALREADY_OPEN: - 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_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_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; + dev_warn(&req->adapter->ccw_device->dev, + "Not enough FCP adapter resources to open " + "remote port 0x%016Lx\n", + (unsigned long long)port->wwpn); + zfcp_erp_set_port_status(port, + ZFCP_STATUS_COMMON_ERP_FAILED); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_ADAPTER_STATUS_AVAILABLE: switch (header->fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - 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_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)); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } break; - case FSF_GOOD: - /* 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); atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | ZFCP_STATUS_COMMON_ACCESS_BOXED, &port->status); - retval = 0; /* check whether D_ID has changed during open */ /* * FIXME: This check is not airtight, as the FCP channel does @@ -2637,2273 +1414,977 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) * 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 < - sizeof (struct fsf_plogi)) { - 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; - zfcp_plogi_evaluate(port, plogi); - } - } - } + plogi = (struct fc_els_flogi *) req->qtcb->bottom.support.els; + if (req->qtcb->bottom.support.els1_length >= + FSF_PLOGI_MIN_LEN) + zfcp_fc_plogi_evaluate(port, plogi); break; - case FSF_UNKNOWN_OP_SUBTYPE: - /* should never occure, subtype not set in zfcp_fsf_open_port */ - 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)); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } - skip_fsfstatus: - atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &port->status); - return retval; +out: + put_device(&port->dev); } -/* - * function: zfcp_fsf_close_port - * - * purpose: submit FSF command "close port" - * - * returns: address of initiated FSF request - * NULL - request could not be initiated +/** + * zfcp_fsf_open_port - create and send open port request + * @erp_action: pointer to struct zfcp_erp_action + * Returns: 0 on success, error otherwise */ -int -zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) +int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - 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, &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)); + struct zfcp_qdio *qdio = erp_action->adapter->qdio; + struct zfcp_port *port = erp_action->port; + struct zfcp_fsf_req *req; + int retval = -EIO; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; + + req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_PORT_WITH_DID, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); + + if (IS_ERR(req)) { + retval = PTR_ERR(req); 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; + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status); - fsf_req->data = (unsigned long) erp_action->port; - fsf_req->erp_action = erp_action; - fsf_req->qtcb->header.port_handle = erp_action->port->handle; - fsf_req->erp_action = erp_action; - erp_action->fsf_req = fsf_req; + req->handler = zfcp_fsf_open_port_handler; + hton24(req->qtcb->bottom.support.d_id, port->d_id); + req->data = port; + req->erp_action = erp_action; + erp_action->fsf_req_id = req->req_id; + get_device(&port->dev); - zfcp_erp_start_timer(fsf_req); - retval = zfcp_fsf_req_send(fsf_req); + zfcp_fsf_start_erp_timer(req); + retval = zfcp_fsf_req_send(req); 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(fsf_req); - erp_action->fsf_req = NULL; - goto out; + zfcp_fsf_req_free(req); + erp_action->fsf_req_id = 0; + put_device(&port->dev); } - - 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); +out: + spin_unlock_irq(&qdio->req_q_lock); 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) +static void zfcp_fsf_close_port_handler(struct zfcp_fsf_req *req) { - int retval = -EINVAL; - struct zfcp_port *port; - - port = (struct zfcp_port *) fsf_req->data; + struct zfcp_port *port = req->data; - 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) { + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; + switch (req->qtcb->header.fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - 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; + zfcp_erp_adapter_reopen(port->adapter, 0, "fscph_1"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_ADAPTER_STATUS_AVAILABLE: - /* 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_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; + zfcp_erp_clear_port_status(port, ZFCP_STATUS_COMMON_OPEN); 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; +/** + * zfcp_fsf_close_port - create and send close port request + * @erp_action: pointer to struct zfcp_erp_action + * Returns: 0 on success, error otherwise + */ +int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) +{ + struct zfcp_qdio *qdio = erp_action->adapter->qdio; + struct zfcp_fsf_req *req; + int retval = -EIO; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; + + req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PORT, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); + + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out; } - skip_fsfstatus: - atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &port->status); + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + + req->handler = zfcp_fsf_close_port_handler; + req->data = erp_action->port; + req->erp_action = erp_action; + req->qtcb->header.port_handle = erp_action->port->handle; + erp_action->fsf_req_id = req->req_id; + + zfcp_fsf_start_erp_timer(req); + retval = zfcp_fsf_req_send(req); + if (retval) { + zfcp_fsf_req_free(req); + erp_action->fsf_req_id = 0; + } +out: + spin_unlock_irq(&qdio->req_q_lock); 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) +static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - unsigned long lock_flags; - int retval = 0; - - /* 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, &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); + struct zfcp_fc_wka_port *wka_port = req->data; + struct fsf_qtcb_header *header = &req->qtcb->header; + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) { + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; 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; - - /* mark port as being closed */ - atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, - &erp_action->port->status); - /* save a pointer to this port */ - fsf_req->data = (unsigned long) erp_action->port; - fsf_req->qtcb->header.port_handle = erp_action->port->handle; - fsf_req->erp_action = erp_action; - erp_action->fsf_req = fsf_req; - - zfcp_erp_start_timer(fsf_req); - retval = zfcp_fsf_req_send(fsf_req); - 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(fsf_req); - erp_action->fsf_req = NULL; + switch (header->fsf_status) { + case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED: + dev_warn(&req->adapter->ccw_device->dev, + "Opening WKA port 0x%x failed\n", wka_port->d_id); + /* fall through */ + case FSF_ADAPTER_STATUS_AVAILABLE: + req->status |= ZFCP_STATUS_FSFREQ_ERROR; + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; + break; + case FSF_GOOD: + wka_port->handle = header->port_handle; + /* fall through */ + case FSF_PORT_ALREADY_OPEN: + wka_port->status = ZFCP_FC_WKA_PORT_ONLINE; + } +out: + wake_up(&wka_port->completion_wq); +} + +/** + * zfcp_fsf_open_wka_port - create and send open wka-port request + * @wka_port: pointer to struct zfcp_fc_wka_port + * Returns: 0 on success, error otherwise + */ +int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) +{ + struct zfcp_qdio *qdio = wka_port->adapter->qdio; + struct zfcp_fsf_req *req; + int retval = -EIO; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; + + req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_PORT_WITH_DID, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); + + if (IS_ERR(req)) { + retval = PTR_ERR(req); 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); + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + + req->handler = zfcp_fsf_open_wka_port_handler; + hton24(req->qtcb->bottom.support.d_id, wka_port->d_id); + req->data = wka_port; + + zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); + retval = zfcp_fsf_req_send(req); + if (retval) + zfcp_fsf_req_free(req); +out: + spin_unlock_irq(&qdio->req_q_lock); return retval; } -/* - * function: zfcp_fsf_close_physical_port_handler - * - * purpose: is called for finished Close Physical Port FSF command - * - * returns: +static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) +{ + struct zfcp_fc_wka_port *wka_port = req->data; + + if (req->qtcb->header.fsf_status == FSF_PORT_HANDLE_NOT_VALID) { + req->status |= ZFCP_STATUS_FSFREQ_ERROR; + zfcp_erp_adapter_reopen(wka_port->adapter, 0, "fscwph1"); + } + + wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; + wake_up(&wka_port->completion_wq); +} + +/** + * zfcp_fsf_close_wka_port - create and send close wka port request + * @wka_port: WKA port to open + * Returns: 0 on success, error otherwise */ -static int -zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) +int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) { - int retval = -EINVAL; - struct zfcp_port *port; - struct zfcp_unit *unit; - struct fsf_qtcb_header *header; - u16 subtable, rule, counter; + struct zfcp_qdio *qdio = wka_port->adapter->qdio; + struct zfcp_fsf_req *req; + int retval = -EIO; - port = (struct zfcp_port *) fsf_req->data; - header = &fsf_req->qtcb->header; + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { - /* don't change port status in our bookkeeping */ - goto skip_fsfstatus; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PORT, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); + + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out; } - /* evaluate FSF status in QTCB */ - switch (header->fsf_status) { + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - case FSF_PORT_HANDLE_NOT_VALID: - 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; + req->handler = zfcp_fsf_close_wka_port_handler; + req->data = wka_port; + req->qtcb->header.port_handle = wka_port->handle; - case FSF_ACCESS_DENIED: - 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; + zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); + retval = zfcp_fsf_req_send(req); + if (retval) + zfcp_fsf_req_free(req); +out: + spin_unlock_irq(&qdio->req_q_lock); + return retval; +} +static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req) +{ + struct zfcp_port *port = req->data; + struct fsf_qtcb_header *header = &req->qtcb->header; + struct scsi_device *sdev; + + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; + + switch (header->fsf_status) { + case FSF_PORT_HANDLE_NOT_VALID: + zfcp_erp_adapter_reopen(port->adapter, 0, "fscpph1"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; case FSF_PORT_BOXED: - 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_boxed(port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + /* 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); + shost_for_each_device(sdev, port->adapter->scsi_host) + if (sdev_to_zfcp(sdev)->port == port) + atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, + &sdev_to_zfcp(sdev)->status); + zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ACCESS_BOXED); + zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, + "fscpph2"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_ADAPTER_STATUS_AVAILABLE: switch (header->fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - 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; + /* fall through */ case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: - /* ERP strategy will escalate */ - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_sq_ulp"); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - default: - ZFCP_LOG_NORMAL - ("bug: Wrong status qualifier 0x%x arrived.\n", - 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)); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } break; - case FSF_GOOD: - 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)); + shost_for_each_device(sdev, port->adapter->scsi_host) + if (sdev_to_zfcp(sdev)->port == port) + atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, + &sdev_to_zfcp(sdev)->status); 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 +/** + * zfcp_fsf_close_physical_port - close physical port + * @erp_action: pointer to struct zfcp_erp_action + * Returns: 0 on success */ -int -zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) +int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - 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, &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)); + struct zfcp_qdio *qdio = erp_action->adapter->qdio; + struct zfcp_fsf_req *req; + int retval = -EIO; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) 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->qtcb->header.port_handle = erp_action->port->handle; - fsf_req->qtcb->bottom.support.fcp_lun = erp_action->unit->fcp_lun; - if (!(erp_action->adapter->connection_features & FSF_FEATURE_NPIV_MODE)) - fsf_req->qtcb->bottom.support.option = - FSF_OPEN_LUN_SUPPRESS_BOXING; - atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status); - fsf_req->data = (unsigned long) erp_action->unit; - fsf_req->erp_action = erp_action; - erp_action->fsf_req = fsf_req; - - zfcp_erp_start_timer(fsf_req); - retval = zfcp_fsf_req_send(erp_action->fsf_req); - 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(fsf_req); - erp_action->fsf_req = NULL; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PHYSICAL_PORT, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); + + if (IS_ERR(req)) { + retval = PTR_ERR(req); 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); + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + + req->data = erp_action->port; + req->qtcb->header.port_handle = erp_action->port->handle; + req->erp_action = erp_action; + req->handler = zfcp_fsf_close_physical_port_handler; + erp_action->fsf_req_id = req->req_id; + + zfcp_fsf_start_erp_timer(req); + retval = zfcp_fsf_req_send(req); + if (retval) { + zfcp_fsf_req_free(req); + erp_action->fsf_req_id = 0; + } +out: + spin_unlock_irq(&qdio->req_q_lock); 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) +static void zfcp_fsf_open_lun_handler(struct zfcp_fsf_req *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; - int exclusive, readwrite; - - unit = (struct zfcp_unit *) fsf_req->data; - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { - /* don't change unit status in our bookkeeping */ - goto skip_fsfstatus; - } + struct zfcp_adapter *adapter = req->adapter; + struct scsi_device *sdev = req->data; + struct zfcp_scsi_dev *zfcp_sdev; + struct fsf_qtcb_header *header = &req->qtcb->header; + union fsf_status_qual *qual = &header->fsf_status_qual; - adapter = fsf_req->adapter; - header = &fsf_req->qtcb->header; - bottom = &fsf_req->qtcb->bottom.support; - queue_designator = &header->fsf_status_qual.fsf_queue_designator; + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; + + zfcp_sdev = sdev_to_zfcp(sdev); atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | - ZFCP_STATUS_COMMON_ACCESS_BOXED | - ZFCP_STATUS_UNIT_SHARED | - ZFCP_STATUS_UNIT_READONLY, - &unit->status); + ZFCP_STATUS_COMMON_ACCESS_BOXED, + &zfcp_sdev->status); - /* evaluate FSF status in QTCB */ switch (header->fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - 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; - + zfcp_erp_adapter_reopen(adapter, 0, "fsouh_1"); + /* fall through */ case FSF_LUN_ALREADY_OPEN: - 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_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_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_boxed(unit->port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + zfcp_erp_set_port_status(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ACCESS_BOXED); + zfcp_erp_port_reopen(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ERP_FAILED, "fsouh_2"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_LUN_SHARING_VIOLATION: - 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; + if (qual->word[0]) + dev_warn(&zfcp_sdev->port->adapter->ccw_device->dev, + "LUN 0x%Lx on port 0x%Lx is already in " + "use by CSS%d, MIF Image ID %x\n", + zfcp_scsi_dev_lun(sdev), + (unsigned long long)zfcp_sdev->port->wwpn, + qual->fsf_queue_designator.cssid, + qual->fsf_queue_designator.hla); + zfcp_erp_set_lun_status(sdev, + ZFCP_STATUS_COMMON_ERP_FAILED | + ZFCP_STATUS_COMMON_ACCESS_DENIED); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED: - 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; + dev_warn(&adapter->ccw_device->dev, + "No handle is available for LUN " + "0x%016Lx on port 0x%016Lx\n", + (unsigned long long)zfcp_scsi_dev_lun(sdev), + (unsigned long long)zfcp_sdev->port->wwpn); + zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ERP_FAILED); + /* fall through */ + case FSF_INVALID_COMMAND_OPTION: + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_ADAPTER_STATUS_AVAILABLE: switch (header->fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - /* Re-establish link to port */ - debug_text_event(adapter->erp_dbf, 1, - "fsf_sq_ltest"); - zfcp_test_link(unit->port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; + zfcp_fc_test_link(zfcp_sdev->port); + /* fall through */ case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: - /* ERP strategy will escalate */ - debug_text_event(adapter->erp_dbf, 1, - "fsf_sq_ulp"); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + 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_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: - /* 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->connection_features & FSF_FEATURE_NPIV_MODE) && - (adapter->adapter_features & FSF_FEATURE_LUN_SHARING) && - (adapter->ccw_device->id.dev_model != ZFCP_DEVICE_MODEL_PRIV)) { - exclusive = (bottom->lun_access_info & - FSF_UNIT_ACCESS_EXCLUSIVE); - readwrite = (bottom->lun_access_info & - FSF_UNIT_ACCESS_OUTBOUND_TRANSFER); - - 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)); + zfcp_sdev->lun_handle = header->lun_handle; + atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &zfcp_sdev->status); 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 +/** + * zfcp_fsf_open_lun - open LUN + * @erp_action: pointer to struct zfcp_erp_action + * Returns: 0 on success, error otherwise */ -int -zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action) +int zfcp_fsf_open_lun(struct zfcp_erp_action *erp_action) { - volatile struct qdio_buffer_element *sbale; - struct zfcp_fsf_req *fsf_req; - 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, &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)); + struct zfcp_adapter *adapter = erp_action->adapter; + struct zfcp_qdio *qdio = adapter->qdio; + struct zfcp_fsf_req *req; + int retval = -EIO; + + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; + + req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_LUN, + SBAL_SFLAGS0_TYPE_READ, + adapter->pool.erp_req); + + if (IS_ERR(req)) { + retval = PTR_ERR(req); 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; + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); + + req->qtcb->header.port_handle = erp_action->port->handle; + req->qtcb->bottom.support.fcp_lun = zfcp_scsi_dev_lun(erp_action->sdev); + req->handler = zfcp_fsf_open_lun_handler; + req->data = erp_action->sdev; + req->erp_action = erp_action; + erp_action->fsf_req_id = req->req_id; - fsf_req->qtcb->header.port_handle = erp_action->port->handle; - fsf_req->qtcb->header.lun_handle = erp_action->unit->handle; - atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status); - fsf_req->data = (unsigned long) erp_action->unit; - fsf_req->erp_action = erp_action; - erp_action->fsf_req = fsf_req; + if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE)) + req->qtcb->bottom.support.option = FSF_OPEN_LUN_SUPPRESS_BOXING; - zfcp_erp_start_timer(fsf_req); - retval = zfcp_fsf_req_send(erp_action->fsf_req); + zfcp_fsf_start_erp_timer(req); + retval = zfcp_fsf_req_send(req); 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(fsf_req); - erp_action->fsf_req = NULL; - goto out; + zfcp_fsf_req_free(req); + erp_action->fsf_req_id = 0; } - - 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); +out: + spin_unlock_irq(&qdio->req_q_lock); 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) +static void zfcp_fsf_close_lun_handler(struct zfcp_fsf_req *req) { - int retval = -EINVAL; - struct zfcp_unit *unit; + struct scsi_device *sdev = req->data; + struct zfcp_scsi_dev *zfcp_sdev; - unit = (struct zfcp_unit *) fsf_req->data; - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { - /* don't change unit status in our bookkeeping */ - goto skip_fsfstatus; - } + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; - /* evaluate FSF status in QTCB */ - switch (fsf_req->qtcb->header.fsf_status) { + zfcp_sdev = sdev_to_zfcp(sdev); + switch (req->qtcb->header.fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - 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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fscuh_1"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_LUN_HANDLE_NOT_VALID: - 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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fscuh_2"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_PORT_BOXED: - 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_boxed(unit->port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + zfcp_erp_set_port_status(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ACCESS_BOXED); + zfcp_erp_port_reopen(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ERP_FAILED, "fscuh_3"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_ADAPTER_STATUS_AVAILABLE: - switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { + switch (req->qtcb->header.fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - /* re-establish link to port */ - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_sq_ltest"); - zfcp_test_link(unit->port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; + zfcp_fc_test_link(zfcp_sdev->port); + /* fall through */ case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: - /* ERP strategy will escalate */ - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_sq_ulp"); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - default: - ZFCP_LOG_NORMAL - ("bug: Wrong status qualifier 0x%x arrived.\n", - fsf_req->qtcb->header.fsf_status_qual.word[0]); - 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)); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } break; - case FSF_GOOD: - 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)); + atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &zfcp_sdev->status); 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 + * zfcp_fsf_close_LUN - close LUN + * @erp_action: pointer to erp_action triggering the "close LUN" + * Returns: 0 on success, error otherwise */ -int -zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, - struct zfcp_unit *unit, - struct scsi_cmnd * scsi_cmnd, - int use_timer, int req_flags) +int zfcp_fsf_close_lun(struct zfcp_erp_action *erp_action) { - 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; - } - - if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, - &unit->status))) { - retval = -EBUSY; - goto unit_blocked; - } - - zfcp_unit_get(unit); - fsf_req->unit = unit; - - /* associate FSF request with SCSI request (for look up on abort) */ - scsi_cmnd->host_scribble = (unsigned char *) fsf_req->req_id; - - /* associate SCSI command with FSF request */ - fsf_req->data = (unsigned long) scsi_cmnd; - - /* 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: - 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: - 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: - fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE; - sbtype = SBAL_FLAGS0_TYPE_WRITE; - fcp_cmnd_iu->wddata = 1; - break; - case DMA_BIDIRECTIONAL: - 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 = ZFCP_FC_SERVICE_CLASS_DEFAULT; + struct zfcp_qdio *qdio = erp_action->adapter->qdio; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(erp_action->sdev); + struct zfcp_fsf_req *req; + int retval = -EIO; - /* set FCP_LUN in FCP_CMND IU in QTCB */ - fcp_cmnd_iu->fcp_lun = unit->fcp_lun; + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; - mask = ZFCP_STATUS_UNIT_READONLY | ZFCP_STATUS_UNIT_SHARED; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_LUN, + SBAL_SFLAGS0_TYPE_READ, + qdio->adapter->pool.erp_req); - /* 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; + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out; } - /* set length of FCP data length in FCP_CMND IU in QTCB */ - zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes); + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - ZFCP_LOG_DEBUG("Sending SCSI command:\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, - (char *) scsi_cmnd->cmnd, scsi_cmnd->cmd_len); + req->qtcb->header.port_handle = erp_action->port->handle; + req->qtcb->header.lun_handle = zfcp_sdev->lun_handle; + req->handler = zfcp_fsf_close_lun_handler; + req->data = erp_action->sdev; + req->erp_action = erp_action; + erp_action->fsf_req_id = req->req_id; - if (use_timer) - zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); - - retval = zfcp_fsf_req_send(fsf_req); - 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_fsf_start_erp_timer(req); + retval = zfcp_fsf_req_send(req); + if (retval) { + zfcp_fsf_req_free(req); + erp_action->fsf_req_id = 0; } - - 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: - unit_blocked: - zfcp_unit_put(unit); - 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); +out: + spin_unlock_irq(&qdio->req_q_lock); return retval; } -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) +static void zfcp_fsf_update_lat(struct fsf_latency_record *lat_rec, u32 lat) { - 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; - } - - if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, - &unit->status))) - goto unit_blocked; - - /* - * 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; + lat_rec->sum += lat; + lat_rec->min = min(lat_rec->min, lat); + lat_rec->max = max(lat_rec->max, lat); +} - /* - * hold a pointer to the unit being target of this - * task management request - */ - fsf_req->data = (unsigned long) 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 = ZFCP_FC_SERVICE_CLASS_DEFAULT; - 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; - - zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT); - retval = zfcp_fsf_req_send(fsf_req); - if (!retval) - goto out; +static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi) +{ + struct fsf_qual_latency_info *lat_in; + struct latency_cont *lat = NULL; + struct zfcp_scsi_dev *zfcp_sdev; + struct zfcp_blk_drv_data blktrc; + int ticks = req->adapter->timer_ticks; + + lat_in = &req->qtcb->prefix.prot_status_qual.latency_info; + + blktrc.flags = 0; + blktrc.magic = ZFCP_BLK_DRV_DATA_MAGIC; + if (req->status & ZFCP_STATUS_FSFREQ_ERROR) + blktrc.flags |= ZFCP_BLK_REQ_ERROR; + blktrc.inb_usage = 0; + blktrc.outb_usage = req->qdio_req.qdio_outb_usage; + + if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA && + !(req->status & ZFCP_STATUS_FSFREQ_ERROR)) { + zfcp_sdev = sdev_to_zfcp(scsi->device); + blktrc.flags |= ZFCP_BLK_LAT_VALID; + blktrc.channel_lat = lat_in->channel_lat * ticks; + blktrc.fabric_lat = lat_in->fabric_lat * ticks; + + switch (req->qtcb->bottom.io.data_direction) { + case FSF_DATADIR_DIF_READ_STRIP: + case FSF_DATADIR_DIF_READ_CONVERT: + case FSF_DATADIR_READ: + lat = &zfcp_sdev->latencies.read; + break; + case FSF_DATADIR_DIF_WRITE_INSERT: + case FSF_DATADIR_DIF_WRITE_CONVERT: + case FSF_DATADIR_WRITE: + lat = &zfcp_sdev->latencies.write; + break; + case FSF_DATADIR_CMND: + lat = &zfcp_sdev->latencies.cmd; + break; + } - unit_blocked: - zfcp_fsf_req_free(fsf_req); - fsf_req = NULL; + if (lat) { + spin_lock(&zfcp_sdev->latencies.lock); + zfcp_fsf_update_lat(&lat->channel, lat_in->channel_lat); + zfcp_fsf_update_lat(&lat->fabric, lat_in->fabric_lat); + lat->counter++; + spin_unlock(&zfcp_sdev->latencies.lock); + } + } - out: - write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); - return fsf_req; + blk_add_driver_data(scsi->request->q, scsi->request, &blktrc, + sizeof(blktrc)); } -/* - * 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) +static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) { - int retval = -EINVAL; - struct zfcp_unit *unit; - struct fsf_qtcb_header *header; - u16 subtable, rule, counter; + struct scsi_cmnd *scmnd = req->data; + struct scsi_device *sdev = scmnd->device; + struct zfcp_scsi_dev *zfcp_sdev; + struct fsf_qtcb_header *header = &req->qtcb->header; - header = &fsf_req->qtcb->header; - - if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)) - unit = (struct zfcp_unit *) fsf_req->data; - else - unit = fsf_req->unit; + if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) + return; - if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) { - /* go directly to calls of special handlers */ - goto skip_fsfstatus; - } + zfcp_sdev = sdev_to_zfcp(sdev); - /* evaluate FSF status in QTCB */ switch (header->fsf_status) { - + case FSF_HANDLE_MISMATCH: case FSF_PORT_HANDLE_NOT_VALID: - 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; + zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fssfch1"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - + case FSF_FCPLUN_NOT_VALID: case FSF_LUN_HANDLE_NOT_VALID: - 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_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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fssfch2"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_SERVICE_CLASS_NOT_SUPPORTED: - ZFCP_LOG_INFO("error: adapter %s does not support fc " - "class %d.\n", - zfcp_get_busid_by_unit(unit), - ZFCP_FC_SERVICE_CLASS_DEFAULT); - /* 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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - - case FSF_FCPLUN_NOT_VALID: - 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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + zfcp_fsf_class_not_supp(req); break; - - case FSF_ACCESS_DENIED: - 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_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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + dev_err(&req->adapter->ccw_device->dev, + "Incorrect direction %d, LUN 0x%016Lx on port " + "0x%016Lx closed\n", + req->qtcb->bottom.io.data_direction, + (unsigned long long)zfcp_scsi_dev_lun(sdev), + (unsigned long long)zfcp_sdev->port->wwpn); + zfcp_erp_adapter_shutdown(zfcp_sdev->port->adapter, 0, + "fssfch3"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_CMND_LENGTH_NOT_VALID: - 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); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + dev_err(&req->adapter->ccw_device->dev, + "Incorrect CDB length %d, LUN 0x%016Lx on " + "port 0x%016Lx closed\n", + req->qtcb->bottom.io.fcp_cmnd_length, + (unsigned long long)zfcp_scsi_dev_lun(sdev), + (unsigned long long)zfcp_sdev->port->wwpn); + zfcp_erp_adapter_shutdown(zfcp_sdev->port->adapter, 0, + "fssfch4"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_PORT_BOXED: - 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_boxed(unit->port); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | - ZFCP_STATUS_FSFREQ_RETRY; + zfcp_erp_set_port_status(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ACCESS_BOXED); + zfcp_erp_port_reopen(zfcp_sdev->port, + ZFCP_STATUS_COMMON_ERP_FAILED, "fssfch5"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_LUN_BOXED: - 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_boxed(unit); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR - | ZFCP_STATUS_FSFREQ_RETRY; + zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ACCESS_BOXED); + zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, + "fssfch6"); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_ADAPTER_STATUS_AVAILABLE: - switch (header->fsf_status_qual.word[0]) { - case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - /* re-establish link to port */ - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_sq_ltest"); - zfcp_test_link(unit->port); - break; - case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: - /* 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"); - break; - default: - ZFCP_LOG_NORMAL - ("Unknown 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; - } - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - - case FSF_GOOD: - break; - - case FSF_FCP_RSP_AVAILABLE: + if (header->fsf_status_qual.word[0] == + FSF_SQ_INVOKE_LINK_TEST_PROCEDURE) + zfcp_fc_test_link(zfcp_sdev->port); + req->status |= ZFCP_STATUS_FSFREQ_ERROR; 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); - fsf_req->unit = NULL; - zfcp_unit_put(unit); } - 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) +static void zfcp_fsf_fcp_cmnd_handler(struct zfcp_fsf_req *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); + struct fcp_resp_with_ext *fcp_rsp; unsigned long flags; - struct zfcp_unit *unit = fsf_req->unit; - read_lock_irqsave(&fsf_req->adapter->abort_lock, flags); - scpnt = (struct scsi_cmnd *) fsf_req->data; - 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; - } + read_lock_irqsave(&req->adapter->abort_lock, flags); - 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); + scpnt = req->data; + if (unlikely(!scpnt)) { + read_unlock_irqrestore(&req->adapter->abort_lock, flags); + return; } - /* 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: - /* 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: - /* 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); - set_host_byte(&scpnt->result, DID_ERROR); - goto skip_fsfstatus; - case RSP_CODE_FIELD_INVALID: - /* 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); - goto skip_fsfstatus; - case RSP_CODE_RO_MISMATCH: - /* 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); - 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); - set_host_byte(&scpnt->result, DID_ERROR); - goto skip_fsfstatus; - } - } + zfcp_fsf_fcp_handler_common(req); - /* 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)); + if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) { + set_host_byte(scpnt, DID_TRANSPORT_DISRUPTED); + goto skip_fsfstatus; } - /* 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)); - - scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid); - if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) < - scpnt->underflow) - set_host_byte(&scpnt->result, DID_ERROR); + switch (req->qtcb->header.fsf_status) { + case FSF_INCONSISTENT_PROT_DATA: + case FSF_INVALID_PROT_PARM: + set_host_byte(scpnt, DID_ERROR); + goto skip_fsfstatus; + case FSF_BLOCK_GUARD_CHECK_FAILURE: + zfcp_scsi_dif_sense_error(scpnt, 0x1); + goto skip_fsfstatus; + case FSF_APP_TAG_CHECK_FAILURE: + zfcp_scsi_dif_sense_error(scpnt, 0x2); + goto skip_fsfstatus; + case FSF_REF_TAG_CHECK_FAILURE: + zfcp_scsi_dif_sense_error(scpnt, 0x3); + goto skip_fsfstatus; } + fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp; + zfcp_fc_eval_fcp_rsp(fcp_rsp, scpnt); - skip_fsfstatus: - ZFCP_LOG_DEBUG("scpnt->result =0x%x\n", scpnt->result); - - if (scpnt->result != 0) - zfcp_scsi_dbf_event_result("erro", 3, fsf_req->adapter, scpnt, fsf_req); - else if (scpnt->retries > 0) - zfcp_scsi_dbf_event_result("retr", 4, fsf_req->adapter, scpnt, fsf_req); - else - zfcp_scsi_dbf_event_result("norm", 6, fsf_req->adapter, scpnt, fsf_req); +skip_fsfstatus: + zfcp_fsf_req_trace(req, scpnt); + zfcp_dbf_scsi_result(scpnt, req); - /* cleanup pointer (need this especially for abort) */ scpnt->host_scribble = NULL; - - /* 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; + read_unlock_irqrestore(&req->adapter->abort_lock, flags); } -/* - * 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) +static int zfcp_fsf_set_data_dir(struct scsi_cmnd *scsi_cmnd, u32 *data_dir) { - 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 = (struct zfcp_unit *) fsf_req->data; - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { - fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; - goto skip_fsfstatus; - } + switch (scsi_get_prot_op(scsi_cmnd)) { + case SCSI_PROT_NORMAL: + switch (scsi_cmnd->sc_data_direction) { + case DMA_NONE: + *data_dir = FSF_DATADIR_CMND; + break; + case DMA_FROM_DEVICE: + *data_dir = FSF_DATADIR_READ; + break; + case DMA_TO_DEVICE: + *data_dir = FSF_DATADIR_WRITE; + break; + case DMA_BIDIRECTIONAL: + return -EINVAL; + } + break; - /* check FCP_RSP_INFO */ - switch (fcp_rsp_info[3]) { - case RSP_CODE_GOOD: - /* ok, continue */ - ZFCP_LOG_DEBUG("no failure or Task Management " - "Function complete\n"); + case SCSI_PROT_READ_STRIP: + *data_dir = FSF_DATADIR_DIF_READ_STRIP; + break; + case SCSI_PROT_WRITE_INSERT: + *data_dir = FSF_DATADIR_DIF_WRITE_INSERT; break; - case RSP_CODE_TASKMAN_UNSUPP: - 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; + case SCSI_PROT_READ_PASS: + *data_dir = FSF_DATADIR_DIF_READ_CONVERT; break; - case RSP_CODE_TASKMAN_FAILED: - 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; + case SCSI_PROT_WRITE_PASS: + *data_dir = FSF_DATADIR_DIF_WRITE_CONVERT; 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; + return -EINVAL; } - skip_fsfstatus: - return retval; + return 0; } - -/* - * 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 +/** + * zfcp_fsf_fcp_cmnd - initiate an FCP command (for a SCSI command) + * @scsi_cmnd: scsi command to be sent */ -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) +int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *scsi_cmnd) { - struct zfcp_fsf_req *fsf_req; - struct fsf_qtcb_bottom_support *bottom; - volatile struct qdio_buffer_element *sbale; - unsigned long lock_flags; - int req_flags = 0; - int direction; - int retval = 0; - - if (!(adapter->adapter_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; + struct zfcp_fsf_req *req; + struct fcp_cmnd *fcp_cmnd; + u8 sbtype = SBAL_SFLAGS0_TYPE_READ; + int retval = -EIO; + struct scsi_device *sdev = scsi_cmnd->device; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); + struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; + struct zfcp_qdio *qdio = adapter->qdio; + struct fsf_qtcb_bottom_io *io; + unsigned long flags; - case FSF_QTCB_UPLOAD_CONTROL_FILE: - direction = SBAL_FLAGS0_TYPE_READ; - break; + if (unlikely(!(atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_UNBLOCKED))) + return -EBUSY; - default: - ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command); - retval = -EINVAL; + spin_lock_irqsave(&qdio->req_q_lock, flags); + if (atomic_read(&qdio->req_q_free) <= 0) { + atomic_inc(&qdio->req_q_full); 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; - - zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); - retval = zfcp_fsf_req_send(fsf_req); - 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)); + if (scsi_cmnd->sc_data_direction == DMA_TO_DEVICE) + sbtype = SBAL_SFLAGS0_TYPE_WRITE; - wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + req = zfcp_fsf_req_create(qdio, FSF_QTCB_FCP_CMND, + sbtype, adapter->pool.scsi_req); - *fsf_req_ptr = fsf_req; - goto out; - - free_fsf_req: - zfcp_fsf_req_free(fsf_req); - unlock_queue_lock: - write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); - 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; + if (IS_ERR(req)) { + retval = PTR_ERR(req); + goto out; } - switch (header->fsf_status) { - - case FSF_GOOD: - 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: - if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) { - switch (header->fsf_status_qual.word[0]) { - - case FSF_SQ_CFDC_HARDENED_ON_SE: - ZFCP_LOG_NORMAL( - "CFDC on the adapter %s has being " - "hardened on primary and secondary SE\n", - zfcp_get_busid_by_adapter(adapter)); - break; - - 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; + scsi_cmnd->host_scribble = (unsigned char *) req->req_id; - case FSF_AUTHORIZATION_FAILURE: - 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; + io = &req->qtcb->bottom.io; + req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + req->data = scsi_cmnd; + req->handler = zfcp_fsf_fcp_cmnd_handler; + req->qtcb->header.lun_handle = zfcp_sdev->lun_handle; + req->qtcb->header.port_handle = zfcp_sdev->port->handle; + io->service_class = FSF_CLASS_3; + io->fcp_cmnd_length = FCP_CMND_LEN; - case FSF_CFDC_ERROR_DETECTED: - 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; + if (scsi_get_prot_op(scsi_cmnd) != SCSI_PROT_NORMAL) { + io->data_block_length = scsi_cmnd->device->sector_size; + io->ref_tag_value = scsi_get_lba(scsi_cmnd) & 0xFFFFFFFF; + } - case FSF_CONTROL_FILE_UPDATE_ERROR: - 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; + if (zfcp_fsf_set_data_dir(scsi_cmnd, &io->data_direction)) + goto failed_scsi_cmnd; - case FSF_CONTROL_FILE_TOO_LARGE: - 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; + fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; + zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd, 0); - case FSF_ACCESS_CONFLICT_DETECTED: - 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; + if (scsi_prot_sg_count(scsi_cmnd)) { + zfcp_qdio_set_data_div(qdio, &req->qdio_req, + scsi_prot_sg_count(scsi_cmnd)); + retval = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, + scsi_prot_sglist(scsi_cmnd)); + if (retval) + goto failed_scsi_cmnd; + io->prot_data_length = zfcp_qdio_real_bytes( + scsi_prot_sglist(scsi_cmnd)); + } - case FSF_CONFLICTS_OVERRULED: - 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; + retval = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, + scsi_sglist(scsi_cmnd)); + if (unlikely(retval)) + goto failed_scsi_cmnd; - case FSF_UNKNOWN_OP_SUBTYPE: - 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; + zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req); + if (zfcp_adapter_multi_buffer_active(adapter)) + zfcp_qdio_set_scount(qdio, &req->qdio_req); - case FSF_INVALID_COMMAND_OPTION: - 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; + retval = zfcp_fsf_req_send(req); + if (unlikely(retval)) + goto failed_scsi_cmnd; - 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; - } + goto out; -skip_fsfstatus: +failed_scsi_cmnd: + zfcp_fsf_req_free(req); + scsi_cmnd->host_scribble = NULL; +out: + spin_unlock_irqrestore(&qdio->req_q_lock, flags); return retval; } -static inline int -zfcp_fsf_req_sbal_check(unsigned long *flags, - struct zfcp_qdio_queue *queue, int needed) +static void zfcp_fsf_fcp_task_mgmt_handler(struct zfcp_fsf_req *req) { - 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; -} + struct fcp_resp_with_ext *fcp_rsp; + struct fcp_resp_rsp_info *rsp_info; -/* - * set qtcb pointer in fsf_req and initialize QTCB - */ -static void -zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req) -{ - if (likely(fsf_req->qtcb != NULL)) { - fsf_req->qtcb->prefix.req_seq_no = - fsf_req->adapter->fsf_req_seq_no; - fsf_req->qtcb->prefix.req_id = fsf_req->req_id; - fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION; - fsf_req->qtcb->prefix.qtcb_type = - fsf_qtcb_type[fsf_req->fsf_command]; - fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION; - fsf_req->qtcb->header.req_handle = fsf_req->req_id; - fsf_req->qtcb->header.fsf_command = fsf_req->fsf_command; - } -} + zfcp_fsf_fcp_handler_common(req); -/** - * 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; + fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp; + rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; - return 0; + if ((rsp_info->rsp_code != FCP_TMF_CMPL) || + (req->status & ZFCP_STATUS_FSFREQ_ERROR)) + req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; } -/* - * 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) +/** + * zfcp_fsf_fcp_task_mgmt - send SCSI task management command + * @scmnd: SCSI command to send the task management command for + * @tm_flags: unsigned byte for task management flags + * Returns: on success pointer to struct fsf_req, NULL otherwise */ -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) +struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd, + u8 tm_flags) { - 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; - } - - fsf_req->adapter = adapter; - fsf_req->fsf_command = fsf_cmd; - INIT_LIST_HEAD(&fsf_req->list); - init_timer(&fsf_req->timer); - - /* initialize waitqueue which may be used to wait on - this request completion */ - init_waitqueue_head(&fsf_req->completion_wq); + struct zfcp_fsf_req *req = NULL; + struct fcp_cmnd *fcp_cmnd; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scmnd->device); + struct zfcp_qdio *qdio = zfcp_sdev->port->adapter->qdio; - ret = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags); - if (ret < 0) - goto failed_sbals; + if (unlikely(!(atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_UNBLOCKED))) + return NULL; - /* this is serialized (we are holding req_queue-lock of adapter) */ - if (adapter->req_no == 0) - adapter->req_no++; - fsf_req->req_id = adapter->req_no++; - - zfcp_fsf_req_qtcb_init(fsf_req); - - /* - * 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; - } + spin_lock_irq(&qdio->req_q_lock); + if (zfcp_qdio_sbal_get(qdio)) + goto out; - if (fsf_req->qtcb) { - fsf_req->seq_no = adapter->fsf_req_seq_no; - fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no; - } - 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; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_FCP_CMND, + SBAL_SFLAGS0_TYPE_WRITE, + qdio->adapter->pool.scsi_req); - if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) { - fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + if (IS_ERR(req)) { + req = NULL; + goto out; } - sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); - - /* setup common SBALE fields */ - sbale[0].addr = (void *) fsf_req->req_id; - 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); - } + req->data = scmnd; + req->handler = zfcp_fsf_fcp_task_mgmt_handler; + req->qtcb->header.lun_handle = zfcp_sdev->lun_handle; + req->qtcb->header.port_handle = zfcp_sdev->port->handle; + req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND; + req->qtcb->bottom.io.service_class = FSF_CLASS_3; + req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN; - ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n", - fsf_req->sbal_number, fsf_req->sbal_first); + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - goto success; + fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; + zfcp_fc_scsi_to_fcp(fcp_cmnd, scmnd, tm_flags); - failed_sbals: -/* dequeue new FSF request previously enqueued */ - zfcp_fsf_req_free(fsf_req); - fsf_req = NULL; + zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT); + if (!zfcp_fsf_req_send(req)) + goto out; - failed_fsf_req: - write_lock_irqsave(&req_queue->queue_lock, *lock_flags); - success: - *fsf_req_p = fsf_req; - return ret; + zfcp_fsf_req_free(req); + req = NULL; +out: + spin_unlock_irq(&qdio->req_q_lock); + return req; } -/* - * 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 +/** + * zfcp_fsf_reqid_check - validate req_id contained in SBAL returned by QDIO + * @adapter: pointer to struct zfcp_adapter + * @sbal_idx: response queue index of SBAL to be processed */ -static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req) +void zfcp_fsf_reqid_check(struct zfcp_qdio *qdio, int sbal_idx) { - struct zfcp_adapter *adapter; - struct zfcp_qdio_queue *req_queue; - volatile struct qdio_buffer_element *sbale; - int inc_seq_no; - int new_distance_from_int; - u64 dbg_tmp[2]; - 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); - - /* put allocated FSF request into hash table */ - spin_lock(&adapter->req_list_lock); - zfcp_reqlist_add(adapter, fsf_req); - spin_unlock(&adapter->req_list_lock); - - inc_seq_no = (fsf_req->qtcb != NULL); - - 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]); + struct zfcp_adapter *adapter = qdio->adapter; + struct qdio_buffer *sbal = qdio->res_q[sbal_idx]; + struct qdio_buffer_element *sbale; + struct zfcp_fsf_req *fsf_req; + unsigned long req_id; + int idx; - /* - * 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); - - fsf_req->issued = get_clock(); - - retval = do_QDIO(adapter->ccw_device, - QDIO_FLAG_SYNC_OUTPUT, - 0, fsf_req->sbal_first, fsf_req->sbal_number, NULL); - - dbg_tmp[0] = (unsigned long) sbale[0].addr; - dbg_tmp[1] = (u64) retval; - debug_event(adapter->erp_dbf, 4, (void *) dbg_tmp, 16); - - if (unlikely(retval)) { - /* Queues are down..... */ - retval = -EIO; - del_timer(&fsf_req->timer); - spin_lock(&adapter->req_list_lock); - zfcp_reqlist_remove(adapter, fsf_req); - spin_unlock(&adapter->req_list_lock); - /* undo changes in request queue made for this request */ - 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; - req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q; - req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */ - zfcp_erp_adapter_reopen(adapter, 0); - } 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, - */ + for (idx = 0; idx < QDIO_MAX_ELEMENTS_PER_BUFFER; idx++) { - /* Don't increase for unsolicited status */ - if (inc_seq_no) - adapter->fsf_req_seq_no++; + sbale = &sbal->element[idx]; + req_id = (unsigned long) sbale->addr; + fsf_req = zfcp_reqlist_find_rm(adapter->req_list, req_id); - /* count FSF requests pending */ - atomic_inc(&adapter->reqs_active); + if (!fsf_req) { + /* + * Unknown request means that we have potentially memory + * corruption and must stop the machine immediately. + */ + zfcp_qdio_siosl(adapter); + panic("error: unknown req_id (%lx) on adapter %s.\n", + req_id, dev_name(&adapter->ccw_device->dev)); + } + + fsf_req->qdio_req.sbal_response = sbal_idx; + zfcp_fsf_req_complete(fsf_req); + + if (likely(sbale->eflags & SBAL_EFLAGS_LAST_ENTRY)) + break; } - return retval; } - -#undef ZFCP_LOG_AREA diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 8cce5cc11d5..57ae3ae1046 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -1,27 +1,18 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Interface to the FSF support functions. * - * 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. + * Copyright IBM Corp. 2002, 2010 */ #ifndef FSF_H #define FSF_H +#include <linux/pfn.h> +#include <linux/scatterlist.h> +#include <scsi/libfc.h> + #define FSF_QTCB_CURRENT_VERSION 0x00000001 /* FSF commands */ @@ -45,13 +36,6 @@ #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 states */ #define FSF_PROT_GOOD 0x00000001 #define FSF_PROT_QTCB_VERSION_ERROR 0x00000010 @@ -73,7 +57,6 @@ #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 @@ -82,13 +65,6 @@ #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 @@ -96,13 +72,15 @@ #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_INCONSISTENT_PROT_DATA 0x00000070 +#define FSF_INVALID_PROT_PARM 0x00000071 +#define FSF_BLOCK_GUARD_CHECK_FAILURE 0x00000081 +#define FSF_APP_TAG_CHECK_FAILURE 0x00000082 +#define FSF_REF_TAG_CHECK_FAILURE 0x00000083 #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_PROT_STATUS_QUAL_SIZE 16 #define FSF_STATUS_QUALIFIER_SIZE 16 @@ -113,20 +91,9 @@ #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_HARDENED_ON_SE 0x00000000 -#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_NO_LIGHT 0x00000004 #define FSF_PSQ_LINK_WRAP_PLUG 0x00000008 @@ -155,14 +122,8 @@ #define FSF_STATUS_READ_LINK_DOWN 0x00000005 #define FSF_STATUS_READ_LINK_UP 0x00000006 #define FSF_STATUS_READ_NOTIFICATION_LOST 0x00000009 -#define FSF_STATUS_READ_CFDC_UPDATED 0x0000000A -#define FSF_STATUS_READ_CFDC_HARDENED 0x0000000B #define FSF_STATUS_READ_FEATURE_UPDATE_ALERT 0x0000000C -/* 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 link down */ #define FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK 0x00000000 #define FSF_STATUS_READ_SUB_FDISC_FAILED 0x00000001 @@ -170,20 +131,8 @@ /* status subtypes for unsolicited status notification lost */ #define FSF_STATUS_READ_SUB_INCOMING_ELS 0x00000001 -#define FSF_STATUS_READ_SUB_SENSE_DATA 0x00000002 -#define FSF_STATUS_READ_SUB_LINK_STATUS 0x00000004 -#define FSF_STATUS_READ_SUB_PORT_CLOSED 0x00000008 -#define FSF_STATUS_READ_SUB_BIT_ERROR_THRESHOLD 0x00000010 -#define FSF_STATUS_READ_SUB_ACT_UPDATED 0x00000020 -#define FSF_STATUS_READ_SUB_ACT_HARDENED 0x00000040 -#define FSF_STATUS_READ_SUB_FEATURE_UPDATE_ALERT 0x00000080 - -/* 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 @@ -191,71 +140,35 @@ /* 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 +#define FSF_DATADIR_DIF_WRITE_INSERT 0x00000009 +#define FSF_DATADIR_DIF_READ_STRIP 0x0000000a +#define FSF_DATADIR_DIF_WRITE_CONVERT 0x0000000b +#define FSF_DATADIR_DIF_READ_CONVERT 0X0000000c + +/* data protection control flags */ +#define FSF_APP_TAG_CHECK_ENABLE 0x10 /* 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_CFDC 0x00000002 -#define FSF_FEATURE_LUN_SHARING 0x00000004 #define FSF_FEATURE_NOTIFICATION_LOST 0x00000008 #define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010 -#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 +#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 #define FSF_FEATURE_UPDATE_ALERT 0x00000100 +#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200 +#define FSF_FEATURE_DIF_PROT_TYPE1 0x00010000 +#define FSF_FEATURE_DIX_PROT_TCPIP 0x00020000 /* host connection features */ #define FSF_FEATURE_NPIV_MODE 0x00000001 -#define FSF_FEATURE_VM_ASSIGNED_WWPN 0x00000002 /* 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 { u8 cssid; @@ -265,11 +178,6 @@ struct fsf_queue_designator { 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; @@ -287,17 +195,35 @@ struct fsf_bit_error_payload { u32 current_transmit_b2b_credit; } __attribute__ ((packed)); +struct fsf_link_down_info { + u32 error_code; + u32 res1; + u8 res2[2]; + u8 primary_status; + u8 ioerr_code; + u8 action_code; + u8 reason_code; + u8 explanation_code; + u8 vendor_specific_code; +} __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; + u8 res2; + u8 d_id[3]; u32 class; u64 fcp_lun; u8 res3[24]; - u8 payload[FSF_STATUS_READ_PAYLOAD_SIZE]; + union { + u8 data[FSF_STATUS_READ_PAYLOAD_SIZE]; + u32 word[FSF_STATUS_READ_PAYLOAD_SIZE/sizeof(u32)]; + struct fsf_link_down_info link_down_info; + struct fsf_bit_error_payload bit_error; + } payload; } __attribute__ ((packed)); struct fsf_qual_version_error { @@ -310,23 +236,19 @@ struct fsf_qual_sequence_error { u32 res1[3]; } __attribute__ ((packed)); -struct fsf_link_down_info { - u32 error_code; - u32 res1; - u8 res2[2]; - u8 primary_status; - u8 ioerr_code; - u8 action_code; - u8 reason_code; - u8 explanation_code; - u8 vendor_specific_code; +struct fsf_qual_latency_info { + u32 channel_lat; + u32 fabric_lat; + u8 res1[8]; } __attribute__ ((packed)); union fsf_prot_status_qual { + u32 word[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u32)]; u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)]; struct fsf_qual_version_error version_error; struct fsf_qual_sequence_error sequence_error; struct fsf_link_down_info link_down_info; + struct fsf_qual_latency_info latency_info; } __attribute__ ((packed)); struct fsf_qtcb_prefix { @@ -340,6 +262,15 @@ struct fsf_qtcb_prefix { u8 res1[20]; } __attribute__ ((packed)); +struct fsf_statistics_info { + u64 input_req; + u64 output_req; + u64 control_req; + u64 input_mb; + u64 output_mb; + u64 seconds_act; +} __attribute__ ((packed)); + union fsf_status_qual { u8 byte[FSF_STATUS_QUALIFIER_SIZE]; u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)]; @@ -364,21 +295,7 @@ struct fsf_qtcb_header { 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]; -} __attribute__ ((packed)); - -struct fsf_plogi { - u32 code; - struct fsf_nport_serv_param serv_param; -} __attribute__ ((packed)); +#define FSF_PLOGI_MIN_LEN 112 #define FSF_FCP_CMND_SIZE 288 #define FSF_FCP_RSP_SIZE 128 @@ -386,9 +303,14 @@ struct fsf_plogi { struct fsf_qtcb_bottom_io { u32 data_direction; u32 service_class; - u8 res1[8]; + u8 res1; + u8 data_prot_flags; + u16 app_tag_value; + u32 ref_tag_value; u32 fcp_cmnd_length; - u8 res2[12]; + u32 data_block_length; + u32 prot_data_length; + u8 res2[4]; u8 fcp_cmnd[FSF_FCP_CMND_SIZE]; u8 fcp_rsp[FSF_FCP_RSP_SIZE]; u8 res3[64]; @@ -396,8 +318,8 @@ struct fsf_qtcb_bottom_io { struct fsf_qtcb_bottom_support { u32 operation_subtype; - u8 res1[12]; - u32 d_id; + u8 res1[13]; + u8 d_id[3]; u32 option; u64 fcp_lun; u64 res2; @@ -414,6 +336,8 @@ struct fsf_qtcb_bottom_support { u8 els[256]; } __attribute__ ((packed)); +#define ZFCP_FSF_TIMER_INT_MASK 0x3FFF + struct fsf_qtcb_bottom_config { u32 lic_version; u32 feature_selection; @@ -426,17 +350,20 @@ struct fsf_qtcb_bottom_config { 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 reserved_nport_serv_param[16]; + u8 res0; + u8 peer_d_id[3]; + u16 status_read_buf_num; + u16 timer_interval; + u8 res2[9]; + u8 s_id[3]; + u8 nport_serv_param[128]; u8 res3[8]; u32 adapter_ports; u32 hardware_version; u8 serial_number[32]; - struct fsf_nport_serv_param plogi_payload; - u8 res4[160]; + u8 plogi_payload[112]; + struct fsf_statistics_info stat_info; + u8 res4[112]; } __attribute__ ((packed)); struct fsf_qtcb_bottom_port { @@ -469,7 +396,10 @@ struct fsf_qtcb_bottom_port { 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]; + u8 cp_util; + u8 cb_util; + u8 a_util; + u8 res2[253]; } __attribute__ ((packed)); union fsf_qtcb_bottom { @@ -486,4 +416,34 @@ struct fsf_qtcb { u8 log[FSF_QTCB_LOG_SIZE]; } __attribute__ ((packed)); +struct zfcp_blk_drv_data { +#define ZFCP_BLK_DRV_DATA_MAGIC 0x1 + u32 magic; +#define ZFCP_BLK_LAT_VALID 0x1 +#define ZFCP_BLK_REQ_ERROR 0x2 + u16 flags; + u8 inb_usage; + u8 outb_usage; + u64 channel_lat; + u64 fabric_lat; +} __attribute__ ((packed)); + +/** + * struct zfcp_fsf_ct_els - zfcp data for ct or els request + * @req: scatter-gather list for request + * @resp: scatter-gather list for response + * @handler: handler function (called for response to the request) + * @handler_data: data passed to handler function + * @port: Optional pointer to port for zfcp internal ELS (only test link ADISC) + * @status: used to pass error status to calling function + */ +struct zfcp_fsf_ct_els { + struct scatterlist *req; + struct scatterlist *resp; + void (*handler)(void *); + void *handler_data; + struct zfcp_port *port; + int status; +}; + #endif /* FSF_H */ diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 22fdc17e0d0..06025cdaa4a 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -1,693 +1,511 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Setup and helper functions to access QDIO. * - * 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. + * Copyright IBM Corp. 2002, 2010 */ +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/slab.h> +#include <linux/module.h> #include "zfcp_ext.h" +#include "zfcp_qdio.h" -static 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 volatile struct qdio_buffer_element *zfcp_qdio_sbal_chain - (struct zfcp_fsf_req *, unsigned long); -static volatile struct qdio_buffer_element *zfcp_qdio_sbale_next - (struct zfcp_fsf_req *, unsigned long); -static int zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *, int, int); -static inline int zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *); -static void zfcp_qdio_sbale_fill - (struct zfcp_fsf_req *, unsigned long, void *, int); -static int zfcp_qdio_sbals_from_segment - (struct zfcp_fsf_req *, unsigned long, void *, unsigned long); - -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, int, int); - -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_QDIO +#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer)) -/* - * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array - * in the adapter struct sbuf is the pointer array. - * - * locks: must only be called with zfcp_data.config_sema taken - */ -static void -zfcp_qdio_buffers_dequeue(struct qdio_buffer **sbuf) -{ - int pos; +static bool enable_multibuffer = 1; +module_param_named(datarouter, enable_multibuffer, bool, 0400); +MODULE_PARM_DESC(datarouter, "Enable hardware data router support (default on)"); - for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) - free_page((unsigned long) sbuf[pos]); -} - -/* - * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t - * array in the adapter struct. - * Cur_buf is the pointer array - * - * returns: zero on success else -ENOMEM - * locks: must only be called with zfcp_data.config_sema taken - */ -static int -zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbuf) +static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal) { int pos; for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) { - sbuf[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL); - if (!sbuf[pos]) { - zfcp_qdio_buffers_dequeue(sbuf); + sbal[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL); + if (!sbal[pos]) return -ENOMEM; - } } for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos++) if (pos % QBUFF_PER_PAGE) - sbuf[pos] = sbuf[pos - 1] + 1; + sbal[pos] = sbal[pos - 1] + 1; return 0; } -/* locks: must only be called with zfcp_data.config_sema taken */ -int -zfcp_qdio_allocate_queues(struct zfcp_adapter *adapter) +static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *id, + unsigned int qdio_err) { - int ret; + struct zfcp_adapter *adapter = qdio->adapter; - ret = zfcp_qdio_buffers_enqueue(adapter->request_queue.buffer); - if (ret) - return ret; - return zfcp_qdio_buffers_enqueue(adapter->response_queue.buffer); -} - -/* 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); + dev_warn(&adapter->ccw_device->dev, "A QDIO problem occurred\n"); - ZFCP_LOG_TRACE("freeing response_queue buffers\n"); - zfcp_qdio_buffers_dequeue(adapter->response_queue.buffer); + if (qdio_err & QDIO_ERROR_SLSB_STATE) { + zfcp_qdio_siosl(adapter); + zfcp_erp_adapter_shutdown(adapter, 0, id); + return; + } + zfcp_erp_adapter_reopen(adapter, + ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | + ZFCP_STATUS_COMMON_ERP_FAILED, id); } -int -zfcp_qdio_allocate(struct zfcp_adapter *adapter) +static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt) { - 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, zfcp_get_busid_by_adapter(adapter), 8); - ASCEBC(init_data->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); -} + int i, sbal_idx; -/* - * function: zfcp_qdio_handler_error_check - * - * purpose: called by the response handler to determine error condition - * - * returns: error flag - * - */ -static int -zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, unsigned int status, - unsigned int qdio_error, unsigned int siga_error, - int first_element, int elements_processed) -{ - int retval = 0; - - if (unlikely(status & QDIO_STATUS_LOOK_FOR_ERROR)) { - retval = -EIO; - - ZFCP_LOG_INFO("QDIO problem occurred (status=0x%x, " - "qdio_error=0x%x, siga_error=0x%x)\n", - status, qdio_error, siga_error); - - zfcp_hba_dbf_event_qdio(adapter, status, qdio_error, siga_error, - first_element, elements_processed); - /* - * Restarting IO on the failed adapter from scratch. - * 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 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); + for (i = first; i < first + cnt; i++) { + sbal_idx = i % QDIO_MAX_BUFFERS_PER_Q; + memset(sbal[sbal_idx], 0, sizeof(struct qdio_buffer)); } - 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) +/* this needs to be called prior to updating the queue fill level */ +static inline void zfcp_qdio_account(struct zfcp_qdio *qdio) { - 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, first_element, - elements_processed))) - 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; + unsigned long long now, span; + int used; + + now = get_tod_clock_monotonic(); + span = (now - qdio->req_q_time) >> 12; + used = QDIO_MAX_BUFFERS_PER_Q - atomic_read(&qdio->req_q_free); + qdio->req_q_util += used * span; + qdio->req_q_time = now; } -/** - * zfcp_qdio_reqid_check - checks for valid reqids. - */ -static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, - unsigned long req_id) +static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err, + int queue_no, int idx, int count, + unsigned long parm) { - struct zfcp_fsf_req *fsf_req; - unsigned long flags; + struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm; - debug_long_event(adapter->erp_dbf, 4, req_id); - - spin_lock_irqsave(&adapter->req_list_lock, flags); - fsf_req = zfcp_reqlist_find(adapter, req_id); - - if (!fsf_req) - /* - * Unknown request means that we have potentially memory - * corruption and must stop the machine immediatly. - */ - panic("error: unknown request id (%ld) on adapter %s.\n", - req_id, zfcp_get_busid_by_adapter(adapter)); + if (unlikely(qdio_err)) { + zfcp_qdio_handler_error(qdio, "qdireq1", qdio_err); + return; + } - zfcp_reqlist_remove(adapter, fsf_req); - atomic_dec(&adapter->reqs_active); - spin_unlock_irqrestore(&adapter->req_list_lock, flags); + /* cleanup all SBALs being program-owned now */ + zfcp_qdio_zero_sbals(qdio->req_q, idx, count); - /* finish the FSF request */ - zfcp_fsf_req_complete(fsf_req); + spin_lock_irq(&qdio->stat_lock); + zfcp_qdio_account(qdio); + spin_unlock_irq(&qdio->stat_lock); + atomic_add(count, &qdio->req_q_free); + wake_up(&qdio->req_q_wq); } -/* - * 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) +static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, + int queue_no, int idx, int count, + unsigned long 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, first_element, - elements_processed))) - goto out; - - /* - * we stored address of struct zfcp_adapter data structure - * associated with irq in int_parm - */ + struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm; + struct zfcp_adapter *adapter = qdio->adapter; + int sbal_no, sbal_idx; + + if (unlikely(qdio_err)) { + if (zfcp_adapter_multi_buffer_active(adapter)) { + void *pl[ZFCP_QDIO_MAX_SBALS_PER_REQ + 1]; + struct qdio_buffer_element *sbale; + u64 req_id; + u8 scount; + + memset(pl, 0, + ZFCP_QDIO_MAX_SBALS_PER_REQ * sizeof(void *)); + sbale = qdio->res_q[idx]->element; + req_id = (u64) sbale->addr; + scount = min(sbale->scount + 1, + ZFCP_QDIO_MAX_SBALS_PER_REQ + 1); + /* incl. signaling SBAL */ + + for (sbal_no = 0; sbal_no < scount; sbal_no++) { + sbal_idx = (idx + sbal_no) % + QDIO_MAX_BUFFERS_PER_Q; + pl[sbal_no] = qdio->res_q[sbal_idx]; + } + zfcp_dbf_hba_def_err(adapter, req_id, scount, pl); + } + zfcp_qdio_handler_error(qdio, "qdires1", qdio_err); + return; + } - 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]; - + for (sbal_no = 0; sbal_no < count; sbal_no++) { + sbal_idx = (idx + sbal_no) % QDIO_MAX_BUFFERS_PER_Q; /* 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]; - zfcp_qdio_reqid_check(adapter, - (unsigned long) buffere->addr); - - /* - * 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"); - } + zfcp_fsf_reqid_check(qdio, sbal_idx); } /* - * put range of SBALs back to response queue - * (including SBALs which have already been free before) + * put SBALs back to response queue */ - count = atomic_read(&queue->free_count) + elements_processed; - start = queue->free_index; + if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, idx, count)) + zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2"); +} - 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]); +static struct qdio_buffer_element * +zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) +{ + struct qdio_buffer_element *sbale; - retval = do_QDIO(ccw_device, - QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT, - 0, start, count, NULL); + /* set last entry flag in current SBALE of current SBAL */ + sbale = zfcp_qdio_sbale_curr(qdio, q_req); + sbale->eflags |= SBAL_EFLAGS_LAST_ENTRY; - 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; -} + /* don't exceed last allowed SBAL */ + if (q_req->sbal_last == q_req->sbal_limit) + return NULL; -/** - * 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]; + /* set chaining flag in first SBALE of current SBAL */ + sbale = zfcp_qdio_sbale_req(qdio, q_req); + sbale->sflags |= SBAL_SFLAGS0_MORE_SBALS; + + /* calculate index of next SBAL */ + q_req->sbal_last++; + q_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q; + + /* keep this requests number of SBALs up-to-date */ + q_req->sbal_number++; + BUG_ON(q_req->sbal_number > ZFCP_QDIO_MAX_SBALS_PER_REQ); + + /* start at first SBALE of new SBAL */ + q_req->sbale_curr = 0; + + /* set storage-block type for new SBAL */ + sbale = zfcp_qdio_sbale_curr(qdio, q_req); + sbale->sflags |= q_req->sbtype; + + return sbale; } -/** - * zfcp_qdio_sbale_req - return pointer to SBALE of request_queue for - * a struct zfcp_fsf_req - */ -volatile struct qdio_buffer_element * -zfcp_qdio_sbale_req(struct zfcp_fsf_req *fsf_req, int sbal, int sbale) +static struct qdio_buffer_element * +zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) { - return zfcp_qdio_sbale_get(&fsf_req->adapter->request_queue, - sbal, sbale); + if (q_req->sbale_curr == qdio->max_sbale_per_sbal - 1) + return zfcp_qdio_sbal_chain(qdio, q_req); + q_req->sbale_curr++; + return zfcp_qdio_sbale_curr(qdio, q_req); } /** - * zfcp_qdio_sbale_resp - return pointer to SBALE of response_queue for - * a struct zfcp_fsf_req + * zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list + * @qdio: pointer to struct zfcp_qdio + * @q_req: pointer to struct zfcp_qdio_req + * @sg: scatter-gather list + * @max_sbals: upper bound for number of SBALs to be used + * Returns: zero or -EINVAL on error */ -static inline volatile struct qdio_buffer_element * -zfcp_qdio_sbale_resp(struct zfcp_fsf_req *fsf_req, int sbal, int sbale) +int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, + struct scatterlist *sg) { - return zfcp_qdio_sbale_get(&fsf_req->adapter->response_queue, - sbal, sbale); + struct qdio_buffer_element *sbale; + + /* set storage-block type for this request */ + sbale = zfcp_qdio_sbale_req(qdio, q_req); + sbale->sflags |= q_req->sbtype; + + for (; sg; sg = sg_next(sg)) { + sbale = zfcp_qdio_sbale_next(qdio, q_req); + if (!sbale) { + atomic_inc(&qdio->req_q_full); + zfcp_qdio_zero_sbals(qdio->req_q, q_req->sbal_first, + q_req->sbal_number); + return -EINVAL; + } + sbale->addr = sg_virt(sg); + sbale->length = sg->length; + } + return 0; } -/** - * zfcp_qdio_sbale_curr - return current SBALE on request_queue for - * a struct zfcp_fsf_req - */ -volatile struct qdio_buffer_element * -zfcp_qdio_sbale_curr(struct zfcp_fsf_req *fsf_req) +static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio) { - return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, - fsf_req->sbale_curr); + if (atomic_read(&qdio->req_q_free) || + !(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) + return 1; + return 0; } /** - * 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 + * zfcp_qdio_sbal_get - get free sbal in request queue, wait if necessary + * @qdio: pointer to struct zfcp_qdio + * + * The req_q_lock must be held by the caller of this function, and + * this function may only be called from process context; it will + * sleep when waiting for a free sbal. * - * Note: We can assume at least one free SBAL in the request_queue when called. + * Returns: 0 on success, -EIO if there is no free sbal after waiting. */ -static void -zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals) +int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) { - 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; + long ret; + + ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq, + zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ); + + if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) + return -EIO; + + if (ret > 0) + return 0; + + if (!ret) { + atomic_inc(&qdio->req_q_full); + /* assume hanging outbound queue, try queue recovery */ + zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1"); + } + + return -EIO; } /** - * 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. + * zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO + * @qdio: pointer to struct zfcp_qdio + * @q_req: pointer to struct zfcp_qdio_req + * Returns: 0 on success, error otherwise */ -static volatile struct qdio_buffer_element * -zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype) +int zfcp_qdio_send(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) { - volatile struct qdio_buffer_element *sbale; + int retval; + u8 sbal_number = q_req->sbal_number; - /* set last entry flag in current SBALE of current SBAL */ - sbale = zfcp_qdio_sbale_curr(fsf_req); - sbale->flags |= SBAL_FLAGS_LAST_ENTRY; + spin_lock(&qdio->stat_lock); + zfcp_qdio_account(qdio); + spin_unlock(&qdio->stat_lock); - /* don't exceed last allowed SBAL */ - if (fsf_req->sbal_curr == fsf_req->sbal_last) - return NULL; + retval = do_QDIO(qdio->adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, + q_req->sbal_first, sbal_number); - /* 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; + if (unlikely(retval)) { + zfcp_qdio_zero_sbals(qdio->req_q, q_req->sbal_first, + sbal_number); + return retval; + } - /* keep this requests number of SBALs up-to-date */ - fsf_req->sbal_number++; + /* account for transferred buffers */ + atomic_sub(sbal_number, &qdio->req_q_free); + qdio->req_q_idx += sbal_number; + qdio->req_q_idx %= QDIO_MAX_BUFFERS_PER_Q; - /* start at first SBALE of new SBAL */ - fsf_req->sbale_curr = 0; + return 0; +} - /* set storage-block type for new SBAL */ - sbale = zfcp_qdio_sbale_curr(fsf_req); - sbale->flags |= sbtype; - return sbale; +static void zfcp_qdio_setup_init_data(struct qdio_initialize *id, + struct zfcp_qdio *qdio) +{ + memset(id, 0, sizeof(*id)); + id->cdev = qdio->adapter->ccw_device; + id->q_format = QDIO_ZFCP_QFMT; + memcpy(id->adapter_name, dev_name(&id->cdev->dev), 8); + ASCEBC(id->adapter_name, 8); + id->qib_rflags = QIB_RFLAGS_ENABLE_DATA_DIV; + if (enable_multibuffer) + id->qdr_ac |= QDR_AC_MULTI_BUFFER_ENABLE; + id->no_input_qs = 1; + id->no_output_qs = 1; + id->input_handler = zfcp_qdio_int_resp; + id->output_handler = zfcp_qdio_int_req; + id->int_parm = (unsigned long) qdio; + id->input_sbal_addr_array = (void **) (qdio->res_q); + id->output_sbal_addr_array = (void **) (qdio->req_q); + id->scan_threshold = + QDIO_MAX_BUFFERS_PER_Q - ZFCP_QDIO_MAX_SBALS_PER_REQ * 2; } /** - * zfcp_qdio_sbale_next - switch to next SBALE, chain SBALs if needed + * zfcp_qdio_allocate - allocate queue memory and initialize QDIO data + * @adapter: pointer to struct zfcp_adapter + * Returns: -ENOMEM on memory allocation error or return value from + * qdio_allocate */ -static volatile struct qdio_buffer_element * -zfcp_qdio_sbale_next(struct zfcp_fsf_req *fsf_req, unsigned long sbtype) +static int zfcp_qdio_allocate(struct zfcp_qdio *qdio) { - if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL) - return zfcp_qdio_sbal_chain(fsf_req, sbtype); + struct qdio_initialize init_data; - fsf_req->sbale_curr++; + if (zfcp_qdio_buffers_enqueue(qdio->req_q) || + zfcp_qdio_buffers_enqueue(qdio->res_q)) + return -ENOMEM; - return zfcp_qdio_sbale_curr(fsf_req); + zfcp_qdio_setup_init_data(&init_data, qdio); + init_waitqueue_head(&qdio->req_q_wq); + + return qdio_allocate(&init_data); } /** - * zfcp_qdio_sbals_zero - initialize SBALs between first and last in queue - * with zero from + * zfcp_close_qdio - close qdio queues for an adapter + * @qdio: pointer to structure zfcp_qdio */ -static int -zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *queue, int first, int last) +void zfcp_qdio_close(struct zfcp_qdio *qdio) { - 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; -} + struct zfcp_adapter *adapter = qdio->adapter; + int idx, count; + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) + return; -/** - * 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); -} + /* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */ + spin_lock_irq(&qdio->req_q_lock); + atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status); + spin_unlock_irq(&qdio->req_q_lock); + wake_up(&qdio->req_q_wq); -/** - * zfcp_qdio_sbale_fill - set address and length in current SBALE - * on request_queue - */ -static void -zfcp_qdio_sbale_fill(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, - void *addr, int length) -{ - volatile struct qdio_buffer_element *sbale; + qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR); - sbale = zfcp_qdio_sbale_curr(fsf_req); - sbale->addr = addr; - sbale->length = length; + /* cleanup used outbound sbals */ + count = atomic_read(&qdio->req_q_free); + if (count < QDIO_MAX_BUFFERS_PER_Q) { + idx = (qdio->req_q_idx + count) % QDIO_MAX_BUFFERS_PER_Q; + count = QDIO_MAX_BUFFERS_PER_Q - count; + zfcp_qdio_zero_sbals(qdio->req_q, idx, count); + } + qdio->req_q_idx = 0; + atomic_set(&qdio->req_q_free, 0); } /** - * 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. + * zfcp_qdio_open - prepare and initialize response queue + * @qdio: pointer to struct zfcp_qdio + * Returns: 0 on success, otherwise -EIO */ -static int -zfcp_qdio_sbals_from_segment(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, - void *start_addr, unsigned long total_length) +int zfcp_qdio_open(struct zfcp_qdio *qdio) { - 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); + struct qdio_buffer_element *sbale; + struct qdio_initialize init_data; + struct zfcp_adapter *adapter = qdio->adapter; + struct ccw_device *cdev = adapter->ccw_device; + struct qdio_ssqd_desc ssqd; + int cc; + + if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP) + return -EIO; + + atomic_clear_mask(ZFCP_STATUS_ADAPTER_SIOSL_ISSUED, + &qdio->adapter->status); + + zfcp_qdio_setup_init_data(&init_data, qdio); + + if (qdio_establish(&init_data)) + goto failed_establish; + + if (qdio_get_ssqd_desc(init_data.cdev, &ssqd)) + goto failed_qdio; + + if (ssqd.qdioac2 & CHSC_AC2_DATA_DIV_ENABLED) + atomic_set_mask(ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED, + &qdio->adapter->status); + + if (ssqd.qdioac2 & CHSC_AC2_MULTI_BUFFER_ENABLED) { + atomic_set_mask(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status); + qdio->max_sbale_per_sbal = QDIO_MAX_ELEMENTS_PER_BUFFER; + } else { + atomic_clear_mask(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status); + qdio->max_sbale_per_sbal = QDIO_MAX_ELEMENTS_PER_BUFFER - 1; } - return total_length; -} + qdio->max_sbale_per_req = + ZFCP_QDIO_MAX_SBALS_PER_REQ * qdio->max_sbale_per_sbal + - 2; + if (qdio_activate(cdev)) + goto failed_qdio; + + for (cc = 0; cc < QDIO_MAX_BUFFERS_PER_Q; cc++) { + sbale = &(qdio->res_q[cc]->element[0]); + sbale->length = 0; + sbale->eflags = SBAL_EFLAGS_LAST_ENTRY; + sbale->sflags = 0; + sbale->addr = NULL; + } -/** - * 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 - */ -int -zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, - struct scatterlist *sgl, 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_each_sg(sgl, sg_segment, sg_count, sg_index) { - 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; + if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q)) + goto failed_qdio; + + /* set index of first available SBALS / number of available SBALS */ + qdio->req_q_idx = 0; + atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q); + atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status); + + if (adapter->scsi_host) { + adapter->scsi_host->sg_tablesize = qdio->max_sbale_per_req; + adapter->scsi_host->max_sectors = qdio->max_sbale_per_req * 8; } - /* 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; -} + return 0; -/** - * 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 - */ -int -zfcp_qdio_sbals_from_scsicmnd(struct zfcp_fsf_req *fsf_req, - unsigned long sbtype, struct scsi_cmnd *scsi_cmnd) +failed_qdio: + qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); +failed_establish: + dev_err(&cdev->dev, + "Setting up the QDIO connection to the FCP adapter failed\n"); + return -EIO; +} + +void zfcp_qdio_destroy(struct zfcp_qdio *qdio) { - return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, scsi_sglist(scsi_cmnd), - scsi_sg_count(scsi_cmnd), - ZFCP_MAX_SBALS_PER_REQ); + int p; + + if (!qdio) + return; + + if (qdio->adapter->ccw_device) + qdio_free(qdio->adapter->ccw_device); + + for (p = 0; p < QDIO_MAX_BUFFERS_PER_Q; p += QBUFF_PER_PAGE) { + free_page((unsigned long) qdio->req_q[p]); + free_page((unsigned long) qdio->res_q[p]); + } + + kfree(qdio); } -/** - * 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 zfcp_qdio_setup(struct zfcp_adapter *adapter) { - 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; + struct zfcp_qdio *qdio; + + qdio = kzalloc(sizeof(struct zfcp_qdio), GFP_KERNEL); + if (!qdio) + return -ENOMEM; + + qdio->adapter = adapter; + + if (zfcp_qdio_allocate(qdio)) { + zfcp_qdio_destroy(qdio); + return -ENOMEM; } - return new_distance_from_int; + + spin_lock_init(&qdio->req_q_lock); + spin_lock_init(&qdio->stat_lock); + + adapter->qdio = qdio; + return 0; } -/* - * function: zfcp_zero_sbals +/** + * zfcp_qdio_siosl - Trigger logging in FCP channel + * @adapter: The zfcp_adapter where to trigger logging * - * purpose: zeros specified range of SBALs + * Call the cio siosl function to trigger hardware logging. This + * wrapper function sets a flag to ensure hardware logging is only + * triggered once before going through qdio shutdown. * - * returns: + * The triggers are always run from qdio tasklet context, so no + * additional synchronization is necessary. */ -void -zfcp_qdio_zero_sbals(struct qdio_buffer *buf[], int first, int clean_count) +void zfcp_qdio_siosl(struct zfcp_adapter *adapter) { - 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]); - } -} + int rc; -#undef ZFCP_LOG_AREA + if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_SIOSL_ISSUED) + return; + + rc = ccw_device_siosl(adapter->ccw_device); + if (!rc) + atomic_set_mask(ZFCP_STATUS_ADAPTER_SIOSL_ISSUED, + &adapter->status); +} diff --git a/drivers/s390/scsi/zfcp_qdio.h b/drivers/s390/scsi/zfcp_qdio.h new file mode 100644 index 00000000000..497cd379b0d --- /dev/null +++ b/drivers/s390/scsi/zfcp_qdio.h @@ -0,0 +1,271 @@ +/* + * zfcp device driver + * + * Header file for zfcp qdio interface + * + * Copyright IBM Corp. 2010 + */ + +#ifndef ZFCP_QDIO_H +#define ZFCP_QDIO_H + +#include <asm/qdio.h> + +#define ZFCP_QDIO_SBALE_LEN PAGE_SIZE + +/* Max SBALS for chaining */ +#define ZFCP_QDIO_MAX_SBALS_PER_REQ 36 + +/** + * struct zfcp_qdio - basic qdio data structure + * @res_q: response queue + * @req_q: request queue + * @req_q_idx: index of next free buffer + * @req_q_free: number of free buffers in queue + * @stat_lock: lock to protect req_q_util and req_q_time + * @req_q_lock: lock to serialize access to request queue + * @req_q_time: time of last fill level change + * @req_q_util: used for accounting + * @req_q_full: queue full incidents + * @req_q_wq: used to wait for SBAL availability + * @adapter: adapter used in conjunction with this qdio structure + */ +struct zfcp_qdio { + struct qdio_buffer *res_q[QDIO_MAX_BUFFERS_PER_Q]; + struct qdio_buffer *req_q[QDIO_MAX_BUFFERS_PER_Q]; + u8 req_q_idx; + atomic_t req_q_free; + spinlock_t stat_lock; + spinlock_t req_q_lock; + unsigned long long req_q_time; + u64 req_q_util; + atomic_t req_q_full; + wait_queue_head_t req_q_wq; + struct zfcp_adapter *adapter; + u16 max_sbale_per_sbal; + u16 max_sbale_per_req; +}; + +/** + * struct zfcp_qdio_req - qdio queue related values for a request + * @sbtype: sbal type flags for sbale 0 + * @sbal_number: number of free sbals + * @sbal_first: first sbal for this request + * @sbal_last: last sbal for this request + * @sbal_limit: last possible sbal for this request + * @sbale_curr: current sbale at creation of this request + * @sbal_response: sbal used in interrupt + * @qdio_outb_usage: usage of outbound queue + */ +struct zfcp_qdio_req { + u8 sbtype; + u8 sbal_number; + u8 sbal_first; + u8 sbal_last; + u8 sbal_limit; + u8 sbale_curr; + u8 sbal_response; + u16 qdio_outb_usage; +}; + +/** + * zfcp_qdio_sbale_req - return pointer to sbale on req_q for a request + * @qdio: pointer to struct zfcp_qdio + * @q_rec: pointer to struct zfcp_qdio_req + * Returns: pointer to qdio_buffer_element (sbale) structure + */ +static inline struct qdio_buffer_element * +zfcp_qdio_sbale_req(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) +{ + return &qdio->req_q[q_req->sbal_last]->element[0]; +} + +/** + * zfcp_qdio_sbale_curr - return current sbale on req_q for a request + * @qdio: pointer to struct zfcp_qdio + * @fsf_req: pointer to struct zfcp_fsf_req + * Returns: pointer to qdio_buffer_element (sbale) structure + */ +static inline struct qdio_buffer_element * +zfcp_qdio_sbale_curr(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) +{ + return &qdio->req_q[q_req->sbal_last]->element[q_req->sbale_curr]; +} + +/** + * zfcp_qdio_req_init - initialize qdio request + * @qdio: request queue where to start putting the request + * @q_req: the qdio request to start + * @req_id: The request id + * @sbtype: type flags to set for all sbals + * @data: First data block + * @len: Length of first data block + * + * This is the start of putting the request into the queue, the last + * step is passing the request to zfcp_qdio_send. The request queue + * lock must be held during the whole process from init to send. + */ +static inline +void zfcp_qdio_req_init(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, + unsigned long req_id, u8 sbtype, void *data, u32 len) +{ + struct qdio_buffer_element *sbale; + int count = min(atomic_read(&qdio->req_q_free), + ZFCP_QDIO_MAX_SBALS_PER_REQ); + + q_req->sbal_first = q_req->sbal_last = qdio->req_q_idx; + q_req->sbal_number = 1; + q_req->sbtype = sbtype; + q_req->sbale_curr = 1; + q_req->sbal_limit = (q_req->sbal_first + count - 1) + % QDIO_MAX_BUFFERS_PER_Q; + + sbale = zfcp_qdio_sbale_req(qdio, q_req); + sbale->addr = (void *) req_id; + sbale->eflags = 0; + sbale->sflags = SBAL_SFLAGS0_COMMAND | sbtype; + + if (unlikely(!data)) + return; + sbale++; + sbale->addr = data; + sbale->length = len; +} + +/** + * zfcp_qdio_fill_next - Fill next sbale, only for single sbal requests + * @qdio: pointer to struct zfcp_qdio + * @q_req: pointer to struct zfcp_queue_req + * + * This is only required for single sbal requests, calling it when + * wrapping around to the next sbal is a bug. + */ +static inline +void zfcp_qdio_fill_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, + void *data, u32 len) +{ + struct qdio_buffer_element *sbale; + + BUG_ON(q_req->sbale_curr == qdio->max_sbale_per_sbal - 1); + q_req->sbale_curr++; + sbale = zfcp_qdio_sbale_curr(qdio, q_req); + sbale->addr = data; + sbale->length = len; +} + +/** + * zfcp_qdio_set_sbale_last - set last entry flag in current sbale + * @qdio: pointer to struct zfcp_qdio + * @q_req: pointer to struct zfcp_queue_req + */ +static inline +void zfcp_qdio_set_sbale_last(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req) +{ + struct qdio_buffer_element *sbale; + + sbale = zfcp_qdio_sbale_curr(qdio, q_req); + sbale->eflags |= SBAL_EFLAGS_LAST_ENTRY; +} + +/** + * zfcp_qdio_sg_one_sbal - check if one sbale is enough for sg data + * @sg: The scatterlist where to check the data size + * + * Returns: 1 when one sbale is enough for the data in the scatterlist, + * 0 if not. + */ +static inline +int zfcp_qdio_sg_one_sbale(struct scatterlist *sg) +{ + return sg_is_last(sg) && sg->length <= ZFCP_QDIO_SBALE_LEN; +} + +/** + * zfcp_qdio_skip_to_last_sbale - skip to last sbale in sbal + * @q_req: The current zfcp_qdio_req + */ +static inline +void zfcp_qdio_skip_to_last_sbale(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req) +{ + q_req->sbale_curr = qdio->max_sbale_per_sbal - 1; +} + +/** + * zfcp_qdio_sbal_limit - set the sbal limit for a request in q_req + * @qdio: pointer to struct zfcp_qdio + * @q_req: The current zfcp_qdio_req + * @max_sbals: maximum number of SBALs allowed + */ +static inline +void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req, int max_sbals) +{ + int count = min(atomic_read(&qdio->req_q_free), max_sbals); + + q_req->sbal_limit = (q_req->sbal_first + count - 1) % + QDIO_MAX_BUFFERS_PER_Q; +} + +/** + * zfcp_qdio_set_data_div - set data division count + * @qdio: pointer to struct zfcp_qdio + * @q_req: The current zfcp_qdio_req + * @count: The data division count + */ +static inline +void zfcp_qdio_set_data_div(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req, u32 count) +{ + struct qdio_buffer_element *sbale; + + sbale = qdio->req_q[q_req->sbal_first]->element; + sbale->length = count; +} + +/** + * zfcp_qdio_sbale_count - count sbale used + * @sg: pointer to struct scatterlist + */ +static inline +unsigned int zfcp_qdio_sbale_count(struct scatterlist *sg) +{ + unsigned int count = 0; + + for (; sg; sg = sg_next(sg)) + count++; + + return count; +} + +/** + * zfcp_qdio_real_bytes - count bytes used + * @sg: pointer to struct scatterlist + */ +static inline +unsigned int zfcp_qdio_real_bytes(struct scatterlist *sg) +{ + unsigned int real_bytes = 0; + + for (; sg; sg = sg_next(sg)) + real_bytes += sg->length; + + return real_bytes; +} + +/** + * zfcp_qdio_set_scount - set SBAL count value + * @qdio: pointer to struct zfcp_qdio + * @q_req: The current zfcp_qdio_req + */ +static inline +void zfcp_qdio_set_scount(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) +{ + struct qdio_buffer_element *sbale; + + sbale = qdio->req_q[q_req->sbal_first]->element; + sbale->scount = q_req->sbal_number - 1; +} + +#endif /* ZFCP_QDIO_H */ diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h new file mode 100644 index 00000000000..7c2c6194dfc --- /dev/null +++ b/drivers/s390/scsi/zfcp_reqlist.h @@ -0,0 +1,183 @@ +/* + * zfcp device driver + * + * Data structure and helper functions for tracking pending FSF + * requests. + * + * Copyright IBM Corp. 2009 + */ + +#ifndef ZFCP_REQLIST_H +#define ZFCP_REQLIST_H + +/* number of hash buckets */ +#define ZFCP_REQ_LIST_BUCKETS 128 + +/** + * struct zfcp_reqlist - Container for request list (reqlist) + * @lock: Spinlock for protecting the hash list + * @list: Array of hashbuckets, each is a list of requests in this bucket + */ +struct zfcp_reqlist { + spinlock_t lock; + struct list_head buckets[ZFCP_REQ_LIST_BUCKETS]; +}; + +static inline int zfcp_reqlist_hash(unsigned long req_id) +{ + return req_id % ZFCP_REQ_LIST_BUCKETS; +} + +/** + * zfcp_reqlist_alloc - Allocate and initialize reqlist + * + * Returns pointer to allocated reqlist on success, or NULL on + * allocation failure. + */ +static inline struct zfcp_reqlist *zfcp_reqlist_alloc(void) +{ + unsigned int i; + struct zfcp_reqlist *rl; + + rl = kzalloc(sizeof(struct zfcp_reqlist), GFP_KERNEL); + if (!rl) + return NULL; + + spin_lock_init(&rl->lock); + + for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) + INIT_LIST_HEAD(&rl->buckets[i]); + + return rl; +} + +/** + * zfcp_reqlist_isempty - Check whether the request list empty + * @rl: pointer to reqlist + * + * Returns: 1 if list is empty, 0 if not + */ +static inline int zfcp_reqlist_isempty(struct zfcp_reqlist *rl) +{ + unsigned int i; + + for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) + if (!list_empty(&rl->buckets[i])) + return 0; + return 1; +} + +/** + * zfcp_reqlist_free - Free allocated memory for reqlist + * @rl: The reqlist where to free memory + */ +static inline void zfcp_reqlist_free(struct zfcp_reqlist *rl) +{ + /* sanity check */ + BUG_ON(!zfcp_reqlist_isempty(rl)); + + kfree(rl); +} + +static inline struct zfcp_fsf_req * +_zfcp_reqlist_find(struct zfcp_reqlist *rl, unsigned long req_id) +{ + struct zfcp_fsf_req *req; + unsigned int i; + + i = zfcp_reqlist_hash(req_id); + list_for_each_entry(req, &rl->buckets[i], list) + if (req->req_id == req_id) + return req; + return NULL; +} + +/** + * zfcp_reqlist_find - Lookup FSF request by its request id + * @rl: The reqlist where to lookup the FSF request + * @req_id: The request id to look for + * + * Returns a pointer to the FSF request with the specified request id + * or NULL if there is no known FSF request with this id. + */ +static inline struct zfcp_fsf_req * +zfcp_reqlist_find(struct zfcp_reqlist *rl, unsigned long req_id) +{ + unsigned long flags; + struct zfcp_fsf_req *req; + + spin_lock_irqsave(&rl->lock, flags); + req = _zfcp_reqlist_find(rl, req_id); + spin_unlock_irqrestore(&rl->lock, flags); + + return req; +} + +/** + * zfcp_reqlist_find_rm - Lookup request by id and remove it from reqlist + * @rl: reqlist where to search and remove entry + * @req_id: The request id of the request to look for + * + * This functions tries to find the FSF request with the specified + * id and then removes it from the reqlist. The reqlist lock is held + * during both steps of the operation. + * + * Returns: Pointer to the FSF request if the request has been found, + * NULL if it has not been found. + */ +static inline struct zfcp_fsf_req * +zfcp_reqlist_find_rm(struct zfcp_reqlist *rl, unsigned long req_id) +{ + unsigned long flags; + struct zfcp_fsf_req *req; + + spin_lock_irqsave(&rl->lock, flags); + req = _zfcp_reqlist_find(rl, req_id); + if (req) + list_del(&req->list); + spin_unlock_irqrestore(&rl->lock, flags); + + return req; +} + +/** + * zfcp_reqlist_add - Add entry to reqlist + * @rl: reqlist where to add the entry + * @req: The entry to add + * + * The request id always increases. As an optimization new requests + * are added here with list_add_tail at the end of the bucket lists + * while old requests are looked up starting at the beginning of the + * lists. + */ +static inline void zfcp_reqlist_add(struct zfcp_reqlist *rl, + struct zfcp_fsf_req *req) +{ + unsigned int i; + unsigned long flags; + + i = zfcp_reqlist_hash(req->req_id); + + spin_lock_irqsave(&rl->lock, flags); + list_add_tail(&req->list, &rl->buckets[i]); + spin_unlock_irqrestore(&rl->lock, flags); +} + +/** + * zfcp_reqlist_move - Move all entries from reqlist to simple list + * @rl: The zfcp_reqlist where to remove all entries + * @list: The list where to move all entries + */ +static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl, + struct list_head *list) +{ + unsigned int i; + unsigned long flags; + + spin_lock_irqsave(&rl->lock, flags); + for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) + list_splice_init(&rl->buckets[i], list); + spin_unlock_irqrestore(&rl->lock, flags); +} + +#endif /* ZFCP_REQLIST_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index b9daf5c0586..7b353647cb9 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -1,611 +1,374 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver * - * (C) Copyright IBM Corp. 2002, 2006 + * Interface to Linux SCSI midlayer. * - * 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. + * Copyright IBM Corp. 2002, 2013 */ -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <scsi/fc/fc_fcp.h> +#include <scsi/scsi_eh.h> +#include <linux/atomic.h> #include "zfcp_ext.h" -#include <asm/atomic.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_host_reset_handler(struct scsi_cmnd *); -static int zfcp_task_management_function(struct zfcp_unit *, u8, - struct scsi_cmnd *); - -static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, - unsigned int, unsigned int); - -static struct device_attribute *zfcp_sysfs_sdev_attrs[]; - -struct zfcp_data zfcp_data = { - .scsi_host_template = { - .name = ZFCP_NAME, - .module = THIS_MODULE, - .proc_name = "zfcp", - .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_host_reset_handler = zfcp_scsi_eh_host_reset_handler, - .can_queue = 4096, - .this_id = -1, - .sg_tablesize = ZFCP_MAX_SBALES_PER_REQ, - .cmd_per_lun = 1, - .use_clustering = 1, - .sdev_attrs = zfcp_sysfs_sdev_attrs, - .max_sectors = ZFCP_MAX_SECTORS, - }, - .driver_version = ZFCP_VERSION, -}; - -/* 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; -} +#include "zfcp_dbf.h" +#include "zfcp_fc.h" +#include "zfcp_reqlist.h" -/* 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; +static unsigned int default_depth = 32; +module_param_named(queue_depth, default_depth, uint, 0600); +MODULE_PARM_DESC(queue_depth, "Default queue depth for new SCSI devices"); - 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; +static bool enable_dif; +module_param_named(dif, enable_dif, bool, 0400); +MODULE_PARM_DESC(dif, "Enable DIF/DIX data integrity support"); - return fcp_sns_info_ptr; -} +static bool allow_lun_scan = 1; +module_param(allow_lun_scan, bool, 0600); +MODULE_PARM_DESC(allow_lun_scan, "For NPIV, scan and attach all storage LUNs"); -static fcp_dl_t * -zfcp_get_fcp_dl_ptr(struct fcp_cmnd_iu * fcp_cmd) +static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth, + int reason) { - 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(int *result, char status, char pos) -{ - *result |= status << (pos * 8); -} - -void -set_host_byte(int *result, char status) -{ - set_byte(result, status, 2); + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + case SCSI_QDEPTH_QFULL: + scsi_track_queue_full(sdev, depth); + break; + case SCSI_QDEPTH_RAMP_UP: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + default: + return -EOPNOTSUPP; + } + return sdev->queue_depth; } -void -set_driver_byte(int *result, char status) +static void zfcp_scsi_slave_destroy(struct scsi_device *sdev) { - set_byte(result, status, 3); -} + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); -static int -zfcp_scsi_slave_alloc(struct scsi_device *sdp) -{ - struct zfcp_adapter *adapter; - struct zfcp_unit *unit; - unsigned long flags; - int retval = -ENXIO; - - 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 && atomic_test_mask(ZFCP_STATUS_UNIT_REGISTERED, - &unit->status)) { - sdp->hostdata = unit; - unit->device = sdp; - zfcp_unit_get(unit); - retval = 0; - } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); - out: - return retval; -} + /* if previous slave_alloc returned early, there is nothing to do */ + if (!zfcp_sdev->port) + return; -/** - * zfcp_scsi_slave_destroy - called when scsi device is removed - * - * Remove reference to associated scsi device for an zfcp_unit. - * Mark zfcp_unit as failed. The scsi device might be deleted via sysfs - * or a scan for this device might have failed. - */ -static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt) -{ - struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata; - - if (unit) { - zfcp_erp_wait(unit->port->adapter); - atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); - sdpnt->hostdata = NULL; - unit->device = NULL; - zfcp_erp_unit_failed(unit); - zfcp_unit_put(unit); - } else - ZFCP_LOG_NORMAL("bug: no unit associated with SCSI device at " - "address %p\n", sdpnt); + zfcp_erp_lun_shutdown_wait(sdev, "scssd_1"); + put_device(&zfcp_sdev->port->dev); } -/* - * called from scsi midlayer to allow finetuning of a device. - */ -static int -zfcp_scsi_slave_configure(struct scsi_device *sdp) +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); + scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, default_depth); 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) +static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result) { - set_host_byte(&scpnt->result, result); - if ((scpnt->device != NULL) && (scpnt->device->host != NULL)) - zfcp_scsi_dbf_event_result("fail", 4, - (struct zfcp_adapter*) scpnt->device->host->hostdata[0], - scpnt, NULL); - /* return directly */ + set_host_byte(scpnt, result); + zfcp_dbf_scsi_fail_send(scpnt); 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, int use_timer) +static +int zfcp_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scpnt) { - int tmp; - int retval; - - retval = 0; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); + struct fc_rport *rport = starget_to_rport(scsi_target(scpnt->device)); + int status, scsi_result, ret; - BUG_ON((adapter == NULL) || (adapter != unit->port->adapter)); - BUG_ON(scpnt->scsi_done == NULL); + /* reset the status for this request */ + scpnt->result = 0; + scpnt->host_scribble = NULL; - if (unlikely(NULL == unit)) { - zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT); - goto out; + scsi_result = fc_remote_port_chkready(rport); + if (unlikely(scsi_result)) { + scpnt->result = scsi_result; + zfcp_dbf_scsi_fail_send(scpnt); + scpnt->scsi_done(scpnt); + return 0; } - 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)); + status = atomic_read(&zfcp_sdev->status); + if (unlikely(status & ZFCP_STATUS_COMMON_ERP_FAILED) && + !(atomic_read(&zfcp_sdev->port->status) & + ZFCP_STATUS_COMMON_ERP_FAILED)) { + /* only LUN access denied, but port is good + * not covered by FC transport, have to fail here */ zfcp_scsi_command_fail(scpnt, DID_ERROR); - goto out; + return 0; } - tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, use_timer, - ZFCP_REQ_AUTO_CLEANUP); - if (unlikely(tmp == -EBUSY)) { - 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); - zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT); - goto out; + if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) { + /* This could be either + * open LUN pending: this is temporary, will result in + * open LUN or ERP_FAILED, so retry command + * call to rport_delete pending: mimic retry from + * fc_remote_port_chkready until rport is BLOCKED + */ + zfcp_scsi_command_fail(scpnt, DID_IMM_RETRY); + return 0; } - if (unlikely(tmp < 0)) { - ZFCP_LOG_DEBUG("error: initiation of Send FCP Cmnd failed\n"); - retval = SCSI_MLQUEUE_HOST_BUSY; - } + ret = zfcp_fsf_fcp_cmnd(scpnt); + if (unlikely(ret == -EBUSY)) + return SCSI_MLQUEUE_DEVICE_BUSY; + else if (unlikely(ret < 0)) + return SCSI_MLQUEUE_HOST_BUSY; -out: - return retval; + return ret; } -static void -zfcp_scsi_command_sync_handler(struct scsi_cmnd *scpnt) +static int zfcp_scsi_slave_alloc(struct scsi_device *sdev) { - 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 - * @use_timer: indicates whether timer should be setup or not - * Return: 0 - * - * Errors are indicated in scpnt->result - */ -int -zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt, - int use_timer) -{ - int ret; - DECLARE_COMPLETION_ONSTACK(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, - use_timer); - if (ret == 0) - wait_for_completion(&wait); + struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); + struct zfcp_adapter *adapter = + (struct zfcp_adapter *) sdev->host->hostdata[0]; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); + struct zfcp_port *port; + struct zfcp_unit *unit; + int npiv = adapter->connection_features & FSF_FEATURE_NPIV_MODE; - scpnt->SCp.ptr = NULL; + port = zfcp_get_port_by_wwpn(adapter, rport->port_name); + if (!port) + return -ENXIO; - return 0; -} + unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev)); + if (unit) + put_device(&unit->dev); -/* - * function: zfcp_scsi_queuecommand - * - * purpose: enqueues a SCSI command to the specified target device - * - * returns: 0 - success, SCSI command enqueued - * !0 - failure - */ -static 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; + if (!unit && !(allow_lun_scan && npiv)) { + put_device(&port->dev); + return -ENXIO; + } - /* - * 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; + zfcp_sdev->port = port; + zfcp_sdev->latencies.write.channel.min = 0xFFFFFFFF; + zfcp_sdev->latencies.write.fabric.min = 0xFFFFFFFF; + zfcp_sdev->latencies.read.channel.min = 0xFFFFFFFF; + zfcp_sdev->latencies.read.fabric.min = 0xFFFFFFFF; + zfcp_sdev->latencies.cmd.channel.min = 0xFFFFFFFF; + zfcp_sdev->latencies.cmd.fabric.min = 0xFFFFFFFF; + spin_lock_init(&zfcp_sdev->latencies.lock); - return zfcp_scsi_command_async(adapter, unit, scpnt, 0); -} + zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_lun_reopen(sdev, 0, "scsla_1"); + zfcp_erp_wait(port->adapter); -static struct zfcp_unit * -zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, unsigned int id, - unsigned int lun) -{ - struct zfcp_port *port; - struct zfcp_unit *unit, *retval = NULL; - - list_for_each_entry(port, &adapter->port_list_head, list) { - if (!port->rport || (id != port->rport->scsi_target_id)) - continue; - list_for_each_entry(unit, &port->unit_list_head, list) - if (lun == unit->scsi_lun) { - retval = unit; - goto out; - } - } - out: - return retval; + return 0; } -/** - * zfcp_scsi_eh_abort_handler - abort the specified SCSI command - * @scpnt: pointer to scsi_cmnd to be aborted - * Return: SUCCESS - command has been aborted and cleaned up in internal - * bookkeeping, SCSI stack won't be called for aborted command - * FAILED - otherwise - * - * 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.) - */ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) { - struct Scsi_Host *scsi_host; - struct zfcp_adapter *adapter; - struct zfcp_unit *unit; - struct zfcp_fsf_req *fsf_req; + struct Scsi_Host *scsi_host = scpnt->device->host; + struct zfcp_adapter *adapter = + (struct zfcp_adapter *) scsi_host->hostdata[0]; + struct zfcp_fsf_req *old_req, *abrt_req; unsigned long flags; - unsigned long old_req_id; - int retval = SUCCESS; - - scsi_host = scpnt->device->host; - adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; - unit = (struct zfcp_unit *) scpnt->device->hostdata; - - ZFCP_LOG_INFO("aborting scsi_cmnd=%p on adapter %s\n", - scpnt, zfcp_get_busid_by_adapter(adapter)); + unsigned long old_reqid = (unsigned long) scpnt->host_scribble; + int retval = SUCCESS, ret; + int retry = 3; + char *dbf_tag; /* avoid race condition between late normal completion and abort */ write_lock_irqsave(&adapter->abort_lock, flags); - /* Check whether corresponding fsf_req is still pending */ - spin_lock(&adapter->req_list_lock); - fsf_req = zfcp_reqlist_find(adapter, - (unsigned long) scpnt->host_scribble); - spin_unlock(&adapter->req_list_lock); - if (!fsf_req) { + old_req = zfcp_reqlist_find(adapter->req_list, old_reqid); + if (!old_req) { write_unlock_irqrestore(&adapter->abort_lock, flags); - zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL, 0); - retval = SUCCESS; - goto out; + zfcp_dbf_scsi_abort("abrt_or", scpnt, NULL); + return FAILED; /* completion could be in progress */ } - fsf_req->data = 0; - fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING; - old_req_id = fsf_req->req_id; + old_req->data = NULL; /* don't access old fsf_req after releasing the abort_lock */ write_unlock_irqrestore(&adapter->abort_lock, flags); - fsf_req = zfcp_fsf_abort_fcp_command(old_req_id, adapter, unit, 0); - if (!fsf_req) { - ZFCP_LOG_INFO("error: initiation of Abort FCP Cmnd failed\n"); - zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL, - old_req_id); - retval = FAILED; - goto out; - } + while (retry--) { + abrt_req = zfcp_fsf_abort_fcp_cmnd(scpnt); + if (abrt_req) + break; - __wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); - - if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) { - zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, fsf_req, 0); - retval = SUCCESS; - } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) { - zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, fsf_req, 0); - retval = SUCCESS; - } else { - zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, fsf_req, 0); - retval = FAILED; + zfcp_erp_wait(adapter); + ret = fc_block_scsi_eh(scpnt); + if (ret) { + zfcp_dbf_scsi_abort("abrt_bl", scpnt, NULL); + return ret; + } + if (!(atomic_read(&adapter->status) & + ZFCP_STATUS_COMMON_RUNNING)) { + zfcp_dbf_scsi_abort("abrt_ru", scpnt, NULL); + return SUCCESS; + } + } + if (!abrt_req) { + zfcp_dbf_scsi_abort("abrt_ar", scpnt, NULL); + return FAILED; } - zfcp_fsf_req_free(fsf_req); - out: - return retval; -} -static int -zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) -{ - int retval; - struct zfcp_unit *unit = (struct zfcp_unit *) scpnt->device->hostdata; + wait_for_completion(&abrt_req->completion); - if (!unit) { - ZFCP_LOG_NORMAL("bug: Tried reset for nonexistent unit\n"); - retval = SUCCESS; - goto out; - } - ZFCP_LOG_NORMAL("resetting unit 0x%016Lx on port 0x%016Lx, adapter %s\n", - unit->fcp_lun, unit->port->wwpn, - zfcp_get_busid_by_adapter(unit->port->adapter)); - - /* - * 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, - scpnt); - 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, scpnt); - if (retval) { - ZFCP_LOG_DEBUG("target reset failed (unit=%p)\n", unit); + if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) + dbf_tag = "abrt_ok"; + else if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) + dbf_tag = "abrt_nn"; + else { + dbf_tag = "abrt_fa"; retval = FAILED; - } else { - ZFCP_LOG_DEBUG("target reset succeeded (unit=%p)\n", unit); - retval = SUCCESS; } - out: + zfcp_dbf_scsi_abort(dbf_tag, scpnt, abrt_req); + zfcp_fsf_req_free(abrt_req); return retval; } -static int -zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags, - struct scsi_cmnd *scpnt) +static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) { - struct zfcp_adapter *adapter = unit->port->adapter; - struct zfcp_fsf_req *fsf_req; - int retval = 0; - - /* 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)); - zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit, scpnt); - retval = -ENOMEM; - goto out; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); + struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; + struct zfcp_fsf_req *fsf_req = NULL; + int retval = SUCCESS, ret; + int retry = 3; + + while (retry--) { + fsf_req = zfcp_fsf_fcp_task_mgmt(scpnt, tm_flags); + if (fsf_req) + break; + + zfcp_erp_wait(adapter); + ret = fc_block_scsi_eh(scpnt); + if (ret) + return ret; + + if (!(atomic_read(&adapter->status) & + ZFCP_STATUS_COMMON_RUNNING)) { + zfcp_dbf_scsi_devreset("nres", scpnt, tm_flags); + return SUCCESS; + } } + if (!fsf_req) + return FAILED; - __wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + wait_for_completion(&fsf_req->completion); - /* - * check completion status of task management function - */ if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { - zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt); - retval = -EIO; - } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) { - zfcp_scsi_dbf_event_devreset("nsup", tm_flags, unit, scpnt); - retval = -ENOTSUPP; + zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags); + retval = FAILED; } else - zfcp_scsi_dbf_event_devreset("okay", tm_flags, unit, scpnt); + zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags); zfcp_fsf_req_free(fsf_req); - out: return retval; } -/** - * zfcp_scsi_eh_host_reset_handler - handler for host reset - */ -static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) +static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) { - struct zfcp_unit *unit; - struct zfcp_adapter *adapter; + return zfcp_task_mgmt_function(scpnt, FCP_TMF_LUN_RESET); +} - unit = (struct zfcp_unit*) scpnt->device->hostdata; - adapter = unit->port->adapter; +static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt) +{ + return zfcp_task_mgmt_function(scpnt, FCP_TMF_TGT_RESET); +} - ZFCP_LOG_NORMAL("host reset because of problems with " - "unit 0x%016Lx on port 0x%016Lx, adapter %s\n", - unit->fcp_lun, unit->port->wwpn, - zfcp_get_busid_by_adapter(unit->port->adapter)); +static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) +{ + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); + struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; + int ret; - zfcp_erp_adapter_reopen(adapter, 0); + zfcp_erp_adapter_reopen(adapter, 0, "schrh_1"); zfcp_erp_wait(adapter); + ret = fc_block_scsi_eh(scpnt); + if (ret) + return ret; return SUCCESS; } -int -zfcp_adapter_scsi_register(struct zfcp_adapter *adapter) +struct scsi_transport_template *zfcp_scsi_transport_template; + +static struct scsi_host_template zfcp_scsi_host_template = { + .module = THIS_MODULE, + .name = "zfcp", + .queuecommand = zfcp_scsi_queuecommand, + .eh_abort_handler = zfcp_scsi_eh_abort_handler, + .eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler, + .eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler, + .eh_host_reset_handler = zfcp_scsi_eh_host_reset_handler, + .slave_alloc = zfcp_scsi_slave_alloc, + .slave_configure = zfcp_scsi_slave_configure, + .slave_destroy = zfcp_scsi_slave_destroy, + .change_queue_depth = zfcp_scsi_change_queue_depth, + .proc_name = "zfcp", + .can_queue = 4096, + .this_id = -1, + .sg_tablesize = (((QDIO_MAX_ELEMENTS_PER_BUFFER - 1) + * ZFCP_QDIO_MAX_SBALS_PER_REQ) - 2), + /* GCD, adjusted later */ + .max_sectors = (((QDIO_MAX_ELEMENTS_PER_BUFFER - 1) + * ZFCP_QDIO_MAX_SBALS_PER_REQ) - 2) * 8, + /* GCD, adjusted later */ + .dma_boundary = ZFCP_QDIO_SBALE_LEN - 1, + .cmd_per_lun = 1, + .use_clustering = 1, + .shost_attrs = zfcp_sysfs_shost_attrs, + .sdev_attrs = zfcp_sysfs_sdev_attrs, +}; + +/** + * zfcp_scsi_adapter_register - Register SCSI and FC host with SCSI midlayer + * @adapter: The zfcp adapter to register with the SCSI midlayer + */ +int zfcp_scsi_adapter_register(struct zfcp_adapter *adapter) { - int retval = 0; - static unsigned int unique_id = 0; + struct ccw_dev_id dev_id; if (adapter->scsi_host) - goto out; + return 0; + ccw_device_get_id(adapter->ccw_device, &dev_id); /* register adapter as SCSI host with mid layer of SCSI stack */ - adapter->scsi_host = scsi_host_alloc(&zfcp_data.scsi_host_template, + adapter->scsi_host = scsi_host_alloc(&zfcp_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; + dev_err(&adapter->ccw_device->dev, + "Registering the FCP device with the " + "SCSI stack failed\n"); + return -EIO; } - 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_id = 511; + adapter->scsi_host->max_lun = 0xFFFFFFFF; 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_data.scsi_transport_template; - - /* - * save a pointer to our own adapter data structure within - * hostdata field of SCSI host data structure - */ + adapter->scsi_host->unique_id = dev_id.devno; + adapter->scsi_host->max_cmd_len = 16; /* in struct fcp_cmnd */ + adapter->scsi_host->transportt = zfcp_scsi_transport_template; + 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; + return -EIO; } - atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status); - out: - return retval; + + return 0; } -void -zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter) +/** + * zfcp_scsi_adapter_unregister - Unregister SCSI and FC host from SCSI midlayer + * @adapter: The zfcp adapter to unregister. + */ +void zfcp_scsi_adapter_unregister(struct zfcp_adapter *adapter) { struct Scsi_Host *shost; struct zfcp_port *port; @@ -613,23 +376,18 @@ zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter) shost = adapter->scsi_host; if (!shost) return; - read_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(port, &adapter->port_list_head, list) - if (port->rport) - port->rport = NULL; - read_unlock_irq(&zfcp_data.config_lock); + + read_lock_irq(&adapter->port_list_lock); + list_for_each_entry(port, &adapter->port_list, list) + port->rport = NULL; + read_unlock_irq(&adapter->port_list_lock); + fc_remove_host(shost); scsi_remove_host(shost); scsi_host_put(shost); adapter->scsi_host = NULL; - atomic_clear_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status); - - return; } -/* - * Support functions for FC transport class - */ static struct fc_host_statistics* zfcp_init_fc_host_stats(struct zfcp_adapter *adapter) { @@ -639,19 +397,18 @@ zfcp_init_fc_host_stats(struct zfcp_adapter *adapter) fc_stats = kmalloc(sizeof(*fc_stats), GFP_KERNEL); if (!fc_stats) return NULL; - adapter->fc_stats = fc_stats; /* freed in adater_dequeue */ + adapter->fc_stats = fc_stats; /* freed in adapter_release */ } memset(adapter->fc_stats, 0, sizeof(*adapter->fc_stats)); return adapter->fc_stats; } -static void -zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats, - struct fsf_qtcb_bottom_port *data, - struct fsf_qtcb_bottom_port *old) +static void zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats, + struct fsf_qtcb_bottom_port *data, + struct fsf_qtcb_bottom_port *old) { - fc_stats->seconds_since_last_reset = data->seconds_since_last_reset - - old->seconds_since_last_reset; + fc_stats->seconds_since_last_reset = + data->seconds_since_last_reset - old->seconds_since_last_reset; fc_stats->tx_frames = data->tx_frames - old->tx_frames; fc_stats->tx_words = data->tx_words - old->tx_words; fc_stats->rx_frames = data->rx_frames - old->rx_frames; @@ -662,26 +419,25 @@ zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats, fc_stats->dumped_frames = data->dumped_frames - old->dumped_frames; fc_stats->link_failure_count = data->link_failure - old->link_failure; fc_stats->loss_of_sync_count = data->loss_of_sync - old->loss_of_sync; - fc_stats->loss_of_signal_count = data->loss_of_signal - - old->loss_of_signal; - fc_stats->prim_seq_protocol_err_count = data->psp_error_counts - - old->psp_error_counts; - fc_stats->invalid_tx_word_count = data->invalid_tx_words - - old->invalid_tx_words; + fc_stats->loss_of_signal_count = + data->loss_of_signal - old->loss_of_signal; + fc_stats->prim_seq_protocol_err_count = + data->psp_error_counts - old->psp_error_counts; + fc_stats->invalid_tx_word_count = + data->invalid_tx_words - old->invalid_tx_words; fc_stats->invalid_crc_count = data->invalid_crcs - old->invalid_crcs; - fc_stats->fcp_input_requests = data->input_requests - - old->input_requests; - fc_stats->fcp_output_requests = data->output_requests - - old->output_requests; - fc_stats->fcp_control_requests = data->control_requests - - old->control_requests; + fc_stats->fcp_input_requests = + data->input_requests - old->input_requests; + fc_stats->fcp_output_requests = + data->output_requests - old->output_requests; + fc_stats->fcp_control_requests = + data->control_requests - old->control_requests; fc_stats->fcp_input_megabytes = data->input_mb - old->input_mb; fc_stats->fcp_output_megabytes = data->output_mb - old->output_mb; } -static void -zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats, - struct fsf_qtcb_bottom_port *data) +static void zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats, + struct fsf_qtcb_bottom_port *data) { fc_stats->seconds_since_last_reset = data->seconds_since_last_reset; fc_stats->tx_frames = data->tx_frames; @@ -705,22 +461,14 @@ zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats, fc_stats->fcp_output_megabytes = data->output_mb; } -/** - * zfcp_get_fc_host_stats - provide fc_host_statistics for scsi_transport_fc - * - * assumption: scsi_transport_fc synchronizes calls of - * get_fc_host_stats and reset_fc_host_stats - * (XXX to be checked otherwise introduce locking) - */ -static struct fc_host_statistics * -zfcp_get_fc_host_stats(struct Scsi_Host *shost) +static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host) { struct zfcp_adapter *adapter; struct fc_host_statistics *fc_stats; struct fsf_qtcb_bottom_port *data; int ret; - adapter = (struct zfcp_adapter *)shost->hostdata[0]; + adapter = (struct zfcp_adapter *)host->hostdata[0]; fc_stats = zfcp_init_fc_host_stats(adapter); if (!fc_stats) return NULL; @@ -729,29 +477,28 @@ zfcp_get_fc_host_stats(struct Scsi_Host *shost) if (!data) return NULL; - ret = zfcp_fsf_exchange_port_data_sync(adapter, data); + ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data); if (ret) { kfree(data); - return NULL; /* XXX return zeroed fc_stats? */ + return NULL; } if (adapter->stats_reset && ((jiffies/HZ - adapter->stats_reset) < - data->seconds_since_last_reset)) { + data->seconds_since_last_reset)) zfcp_adjust_fc_host_stats(fc_stats, data, adapter->stats_reset_data); - } else + else zfcp_set_fc_host_stats(fc_stats, data); kfree(data); return fc_stats; } -static void -zfcp_reset_fc_host_stats(struct Scsi_Host *shost) +static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost) { struct zfcp_adapter *adapter; - struct fsf_qtcb_bottom_port *data, *old_data; + struct fsf_qtcb_bottom_port *data; int ret; adapter = (struct zfcp_adapter *)shost->hostdata[0]; @@ -759,23 +506,199 @@ zfcp_reset_fc_host_stats(struct Scsi_Host *shost) if (!data) return; - ret = zfcp_fsf_exchange_port_data_sync(adapter, data); - if (ret) { + ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data); + if (ret) kfree(data); - } else { + else { adapter->stats_reset = jiffies/HZ; - old_data = adapter->stats_reset_data; + kfree(adapter->stats_reset_data); adapter->stats_reset_data = data; /* finally freed in - adater_dequeue */ - kfree(old_data); + adapter_release */ } } +static void zfcp_get_host_port_state(struct Scsi_Host *shost) +{ + struct zfcp_adapter *adapter = + (struct zfcp_adapter *)shost->hostdata[0]; + int status = atomic_read(&adapter->status); + + if ((status & ZFCP_STATUS_COMMON_RUNNING) && + !(status & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)) + fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + else if (status & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED) + fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; + else if (status & ZFCP_STATUS_COMMON_ERP_FAILED) + fc_host_port_state(shost) = FC_PORTSTATE_ERROR; + else + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; +} + static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) { rport->dev_loss_tmo = timeout; } +/** + * zfcp_scsi_terminate_rport_io - Terminate all I/O on a rport + * @rport: The FC rport where to teminate I/O + * + * Abort all pending SCSI commands for a port by closing the + * port. Using a reopen avoids a conflict with a shutdown + * overwriting a reopen. The "forced" ensures that a disappeared port + * is not opened again as valid due to the cached plogi data in + * non-NPIV mode. + */ +static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) +{ + struct zfcp_port *port; + struct Scsi_Host *shost = rport_to_shost(rport); + struct zfcp_adapter *adapter = + (struct zfcp_adapter *)shost->hostdata[0]; + + port = zfcp_get_port_by_wwpn(adapter, rport->port_name); + + if (port) { + zfcp_erp_port_forced_reopen(port, 0, "sctrpi1"); + put_device(&port->dev); + } +} + +static void zfcp_scsi_rport_register(struct zfcp_port *port) +{ + struct fc_rport_identifiers ids; + struct fc_rport *rport; + + if (port->rport) + return; + + ids.node_name = port->wwnn; + ids.port_name = port->wwpn; + ids.port_id = port->d_id; + ids.roles = FC_RPORT_ROLE_FCP_TARGET; + + rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids); + if (!rport) { + dev_err(&port->adapter->ccw_device->dev, + "Registering port 0x%016Lx failed\n", + (unsigned long long)port->wwpn); + return; + } + + rport->maxframe_size = port->maxframe_size; + rport->supported_classes = port->supported_classes; + port->rport = rport; + port->starget_id = rport->scsi_target_id; + + zfcp_unit_queue_scsi_scan(port); +} + +static void zfcp_scsi_rport_block(struct zfcp_port *port) +{ + struct fc_rport *rport = port->rport; + + if (rport) { + fc_remote_port_delete(rport); + port->rport = NULL; + } +} + +void zfcp_scsi_schedule_rport_register(struct zfcp_port *port) +{ + get_device(&port->dev); + port->rport_task = RPORT_ADD; + + if (!queue_work(port->adapter->work_queue, &port->rport_work)) + put_device(&port->dev); +} + +void zfcp_scsi_schedule_rport_block(struct zfcp_port *port) +{ + get_device(&port->dev); + port->rport_task = RPORT_DEL; + + if (port->rport && queue_work(port->adapter->work_queue, + &port->rport_work)) + return; + + put_device(&port->dev); +} + +void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter) +{ + unsigned long flags; + struct zfcp_port *port; + + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) + zfcp_scsi_schedule_rport_block(port); + read_unlock_irqrestore(&adapter->port_list_lock, flags); +} + +void zfcp_scsi_rport_work(struct work_struct *work) +{ + struct zfcp_port *port = container_of(work, struct zfcp_port, + rport_work); + + while (port->rport_task) { + if (port->rport_task == RPORT_ADD) { + port->rport_task = RPORT_NONE; + zfcp_scsi_rport_register(port); + } else { + port->rport_task = RPORT_NONE; + zfcp_scsi_rport_block(port); + } + } + + put_device(&port->dev); +} + +/** + * zfcp_scsi_set_prot - Configure DIF/DIX support in scsi_host + * @adapter: The adapter where to configure DIF/DIX for the SCSI host + */ +void zfcp_scsi_set_prot(struct zfcp_adapter *adapter) +{ + unsigned int mask = 0; + unsigned int data_div; + struct Scsi_Host *shost = adapter->scsi_host; + + data_div = atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED; + + if (enable_dif && + adapter->adapter_features & FSF_FEATURE_DIF_PROT_TYPE1) + mask |= SHOST_DIF_TYPE1_PROTECTION; + + if (enable_dif && data_div && + adapter->adapter_features & FSF_FEATURE_DIX_PROT_TCPIP) { + mask |= SHOST_DIX_TYPE1_PROTECTION; + scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP); + shost->sg_prot_tablesize = adapter->qdio->max_sbale_per_req / 2; + shost->sg_tablesize = adapter->qdio->max_sbale_per_req / 2; + shost->max_sectors = shost->sg_tablesize * 8; + } + + scsi_host_set_prot(shost, mask); +} + +/** + * zfcp_scsi_dif_sense_error - Report DIF/DIX error as driver sense error + * @scmd: The SCSI command to report the error for + * @ascq: The ASCQ to put in the sense buffer + * + * See the error handling in sd_done for the sense codes used here. + * Set DID_SOFT_ERROR to retry the request, if possible. + */ +void zfcp_scsi_dif_sense_error(struct scsi_cmnd *scmd, int ascq) +{ + scsi_build_sense_buffer(1, scmd->sense_buffer, + ILLEGAL_REQUEST, 0x10, ascq); + set_driver_byte(scmd, DRIVER_SENSE); + scmd->result |= SAM_STAT_CHECK_CONDITION; + set_host_byte(scmd, DID_SOFT_ERROR); +} + struct fc_function_template zfcp_transport_functions = { .show_starget_port_id = 1, .show_starget_port_name = 1, @@ -787,51 +710,24 @@ struct fc_function_template zfcp_transport_functions = { .show_host_port_name = 1, .show_host_permanent_port_name = 1, .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, .show_host_supported_speeds = 1, .show_host_maxframe_size = 1, .show_host_serial_number = 1, .get_fc_host_stats = zfcp_get_fc_host_stats, .reset_fc_host_stats = zfcp_reset_fc_host_stats, .set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo, + .get_host_port_state = zfcp_get_host_port_state, + .terminate_rport_io = zfcp_scsi_terminate_rport_io, + .show_host_port_state = 1, + .show_host_active_fc4s = 1, + .bsg_request = zfcp_fc_exec_bsg_job, + .bsg_timeout = zfcp_fc_timeout_bsg_job, /* no functions registered for following dynamic attributes but directly set by LLDD */ .show_host_port_type = 1, + .show_host_symbolic_name = 1, .show_host_speed = 1, .show_host_port_id = 1, - .disable_target_scan = 1, + .dd_bsg_size = sizeof(struct zfcp_fsf_ct_els), }; - -/** - * 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, struct device_attribute *attr, \ - 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.c b/drivers/s390/scsi/zfcp_sysfs.c new file mode 100644 index 00000000000..672b57219e1 --- /dev/null +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -0,0 +1,577 @@ +/* + * zfcp device driver + * + * sysfs attributes. + * + * Copyright IBM Corp. 2008, 2010 + */ + +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/slab.h> +#include "zfcp_ext.h" + +#define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \ +struct device_attribute dev_attr_##_feat##_##_name = __ATTR(_name, _mode,\ + _show, _store) +#define ZFCP_DEFINE_ATTR(_feat_def, _feat, _name, _format, _value) \ +static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \ + struct device_attribute *at,\ + char *buf) \ +{ \ + struct _feat_def *_feat = container_of(dev, struct _feat_def, dev); \ + \ + return sprintf(buf, _format, _value); \ +} \ +static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \ + zfcp_sysfs_##_feat##_##_name##_show, NULL); + +#define ZFCP_DEFINE_ATTR_CONST(_feat, _name, _format, _value) \ +static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \ + struct device_attribute *at,\ + char *buf) \ +{ \ + return sprintf(buf, _format, _value); \ +} \ +static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \ + zfcp_sysfs_##_feat##_##_name##_show, NULL); + +#define ZFCP_DEFINE_A_ATTR(_name, _format, _value) \ +static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \ + struct device_attribute *at,\ + char *buf) \ +{ \ + struct ccw_device *cdev = to_ccwdev(dev); \ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); \ + int i; \ + \ + if (!adapter) \ + return -ENODEV; \ + \ + i = sprintf(buf, _format, _value); \ + zfcp_ccw_adapter_put(adapter); \ + return i; \ +} \ +static ZFCP_DEV_ATTR(adapter, _name, S_IRUGO, \ + zfcp_sysfs_adapter_##_name##_show, NULL); + +ZFCP_DEFINE_A_ATTR(status, "0x%08x\n", atomic_read(&adapter->status)); +ZFCP_DEFINE_A_ATTR(peer_wwnn, "0x%016llx\n", + (unsigned long long) adapter->peer_wwnn); +ZFCP_DEFINE_A_ATTR(peer_wwpn, "0x%016llx\n", + (unsigned long long) adapter->peer_wwpn); +ZFCP_DEFINE_A_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id); +ZFCP_DEFINE_A_ATTR(card_version, "0x%04x\n", adapter->hydra_version); +ZFCP_DEFINE_A_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version); +ZFCP_DEFINE_A_ATTR(hardware_version, "0x%08x\n", adapter->hardware_version); +ZFCP_DEFINE_A_ATTR(in_recovery, "%d\n", (atomic_read(&adapter->status) & + ZFCP_STATUS_COMMON_ERP_INUSE) != 0); + +ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n", + atomic_read(&port->status)); +ZFCP_DEFINE_ATTR(zfcp_port, port, in_recovery, "%d\n", + (atomic_read(&port->status) & + ZFCP_STATUS_COMMON_ERP_INUSE) != 0); +ZFCP_DEFINE_ATTR(zfcp_port, port, access_denied, "%d\n", + (atomic_read(&port->status) & + ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); + +ZFCP_DEFINE_ATTR(zfcp_unit, unit, status, "0x%08x\n", + zfcp_unit_sdev_status(unit)); +ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n", + (zfcp_unit_sdev_status(unit) & + ZFCP_STATUS_COMMON_ERP_INUSE) != 0); +ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n", + (zfcp_unit_sdev_status(unit) & + ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); +ZFCP_DEFINE_ATTR_CONST(unit, access_shared, "%d\n", 0); +ZFCP_DEFINE_ATTR_CONST(unit, access_readonly, "%d\n", 0); + +static ssize_t zfcp_sysfs_port_failed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); + + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + return sprintf(buf, "1\n"); + + return sprintf(buf, "0\n"); +} + +static ssize_t zfcp_sysfs_port_failed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val) || val != 0) + return -EINVAL; + + zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, "sypfai2"); + zfcp_erp_wait(port->adapter); + + return count; +} +static ZFCP_DEV_ATTR(port, failed, S_IWUSR | S_IRUGO, + zfcp_sysfs_port_failed_show, + zfcp_sysfs_port_failed_store); + +static ssize_t zfcp_sysfs_unit_failed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev); + struct scsi_device *sdev; + unsigned int status, failed = 1; + + sdev = zfcp_unit_sdev(unit); + if (sdev) { + status = atomic_read(&sdev_to_zfcp(sdev)->status); + failed = status & ZFCP_STATUS_COMMON_ERP_FAILED ? 1 : 0; + scsi_device_put(sdev); + } + + return sprintf(buf, "%d\n", failed); +} + +static ssize_t zfcp_sysfs_unit_failed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev); + unsigned long val; + struct scsi_device *sdev; + + if (kstrtoul(buf, 0, &val) || val != 0) + return -EINVAL; + + sdev = zfcp_unit_sdev(unit); + if (sdev) { + zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, + "syufai2"); + zfcp_erp_wait(unit->port->adapter); + } else + zfcp_unit_scsi_scan(unit); + + return count; +} +static ZFCP_DEV_ATTR(unit, failed, S_IWUSR | S_IRUGO, + zfcp_sysfs_unit_failed_show, + zfcp_sysfs_unit_failed_store); + +static ssize_t zfcp_sysfs_adapter_failed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + int i; + + if (!adapter) + return -ENODEV; + + if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + i = sprintf(buf, "1\n"); + else + i = sprintf(buf, "0\n"); + + zfcp_ccw_adapter_put(adapter); + return i; +} + +static ssize_t zfcp_sysfs_adapter_failed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + unsigned long val; + int retval = 0; + + if (!adapter) + return -ENODEV; + + if (kstrtoul(buf, 0, &val) || val != 0) { + retval = -EINVAL; + goto out; + } + + zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + "syafai2"); + zfcp_erp_wait(adapter); +out: + zfcp_ccw_adapter_put(adapter); + return retval ? retval : (ssize_t) count; +} +static ZFCP_DEV_ATTR(adapter, failed, S_IWUSR | S_IRUGO, + zfcp_sysfs_adapter_failed_show, + zfcp_sysfs_adapter_failed_store); + +static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return -ENODEV; + + /* sync the user-space- with the kernel-invocation of scan_work */ + queue_work(adapter->work_queue, &adapter->scan_work); + flush_work(&adapter->scan_work); + zfcp_ccw_adapter_put(adapter); + + return (ssize_t) count; +} +static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, + zfcp_sysfs_port_rescan_store); + +DEFINE_MUTEX(zfcp_sysfs_port_units_mutex); + +static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + struct zfcp_port *port; + u64 wwpn; + int retval = -EINVAL; + + if (!adapter) + return -ENODEV; + + if (kstrtoull(buf, 0, (unsigned long long *) &wwpn)) + goto out; + + port = zfcp_get_port_by_wwpn(adapter, wwpn); + if (!port) + goto out; + else + retval = 0; + + mutex_lock(&zfcp_sysfs_port_units_mutex); + if (atomic_read(&port->units) > 0) { + retval = -EBUSY; + mutex_unlock(&zfcp_sysfs_port_units_mutex); + goto out; + } + /* port is about to be removed, so no more unit_add */ + atomic_set(&port->units, -1); + mutex_unlock(&zfcp_sysfs_port_units_mutex); + + write_lock_irq(&adapter->port_list_lock); + list_del(&port->list); + write_unlock_irq(&adapter->port_list_lock); + + put_device(&port->dev); + + zfcp_erp_port_shutdown(port, 0, "syprs_1"); + device_unregister(&port->dev); + out: + zfcp_ccw_adapter_put(adapter); + return retval ? retval : (ssize_t) count; +} +static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL, + zfcp_sysfs_port_remove_store); + +static struct attribute *zfcp_adapter_attrs[] = { + &dev_attr_adapter_failed.attr, + &dev_attr_adapter_in_recovery.attr, + &dev_attr_adapter_port_remove.attr, + &dev_attr_adapter_port_rescan.attr, + &dev_attr_adapter_peer_wwnn.attr, + &dev_attr_adapter_peer_wwpn.attr, + &dev_attr_adapter_peer_d_id.attr, + &dev_attr_adapter_card_version.attr, + &dev_attr_adapter_lic_version.attr, + &dev_attr_adapter_status.attr, + &dev_attr_adapter_hardware_version.attr, + NULL +}; + +struct attribute_group zfcp_sysfs_adapter_attrs = { + .attrs = zfcp_adapter_attrs, +}; + +static ssize_t zfcp_sysfs_unit_add_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); + u64 fcp_lun; + int retval; + + if (kstrtoull(buf, 0, (unsigned long long *) &fcp_lun)) + return -EINVAL; + + retval = zfcp_unit_add(port, fcp_lun); + if (retval) + return retval; + + return count; +} +static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store); + +static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); + u64 fcp_lun; + + if (kstrtoull(buf, 0, (unsigned long long *) &fcp_lun)) + return -EINVAL; + + if (zfcp_unit_remove(port, fcp_lun)) + return -EINVAL; + + return count; +} +static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store); + +static struct attribute *zfcp_port_attrs[] = { + &dev_attr_unit_add.attr, + &dev_attr_unit_remove.attr, + &dev_attr_port_failed.attr, + &dev_attr_port_in_recovery.attr, + &dev_attr_port_status.attr, + &dev_attr_port_access_denied.attr, + NULL +}; +static struct attribute_group zfcp_port_attr_group = { + .attrs = zfcp_port_attrs, +}; +const struct attribute_group *zfcp_port_attr_groups[] = { + &zfcp_port_attr_group, + NULL, +}; + +static struct attribute *zfcp_unit_attrs[] = { + &dev_attr_unit_failed.attr, + &dev_attr_unit_in_recovery.attr, + &dev_attr_unit_status.attr, + &dev_attr_unit_access_denied.attr, + &dev_attr_unit_access_shared.attr, + &dev_attr_unit_access_readonly.attr, + NULL +}; +static struct attribute_group zfcp_unit_attr_group = { + .attrs = zfcp_unit_attrs, +}; +const struct attribute_group *zfcp_unit_attr_groups[] = { + &zfcp_unit_attr_group, + NULL, +}; + +#define ZFCP_DEFINE_LATENCY_ATTR(_name) \ +static ssize_t \ +zfcp_sysfs_unit_##_name##_latency_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) { \ + struct scsi_device *sdev = to_scsi_device(dev); \ + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); \ + struct zfcp_latencies *lat = &zfcp_sdev->latencies; \ + struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; \ + unsigned long long fsum, fmin, fmax, csum, cmin, cmax, cc; \ + \ + spin_lock_bh(&lat->lock); \ + fsum = lat->_name.fabric.sum * adapter->timer_ticks; \ + fmin = lat->_name.fabric.min * adapter->timer_ticks; \ + fmax = lat->_name.fabric.max * adapter->timer_ticks; \ + csum = lat->_name.channel.sum * adapter->timer_ticks; \ + cmin = lat->_name.channel.min * adapter->timer_ticks; \ + cmax = lat->_name.channel.max * adapter->timer_ticks; \ + cc = lat->_name.counter; \ + spin_unlock_bh(&lat->lock); \ + \ + do_div(fsum, 1000); \ + do_div(fmin, 1000); \ + do_div(fmax, 1000); \ + do_div(csum, 1000); \ + do_div(cmin, 1000); \ + do_div(cmax, 1000); \ + \ + return sprintf(buf, "%llu %llu %llu %llu %llu %llu %llu\n", \ + fmin, fmax, fsum, cmin, cmax, csum, cc); \ +} \ +static ssize_t \ +zfcp_sysfs_unit_##_name##_latency_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct scsi_device *sdev = to_scsi_device(dev); \ + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); \ + struct zfcp_latencies *lat = &zfcp_sdev->latencies; \ + unsigned long flags; \ + \ + spin_lock_irqsave(&lat->lock, flags); \ + lat->_name.fabric.sum = 0; \ + lat->_name.fabric.min = 0xFFFFFFFF; \ + lat->_name.fabric.max = 0; \ + lat->_name.channel.sum = 0; \ + lat->_name.channel.min = 0xFFFFFFFF; \ + lat->_name.channel.max = 0; \ + lat->_name.counter = 0; \ + spin_unlock_irqrestore(&lat->lock, flags); \ + \ + return (ssize_t) count; \ +} \ +static DEVICE_ATTR(_name##_latency, S_IWUSR | S_IRUGO, \ + zfcp_sysfs_unit_##_name##_latency_show, \ + zfcp_sysfs_unit_##_name##_latency_store); + +ZFCP_DEFINE_LATENCY_ATTR(read); +ZFCP_DEFINE_LATENCY_ATTR(write); +ZFCP_DEFINE_LATENCY_ATTR(cmd); + +#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value) \ +static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, \ + struct device_attribute *attr,\ + char *buf) \ +{ \ + struct scsi_device *sdev = to_scsi_device(dev); \ + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); \ + struct zfcp_port *port = zfcp_sdev->port; \ + \ + 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", + dev_name(&port->adapter->ccw_device->dev)); +ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", + (unsigned long long) port->wwpn); + +static ssize_t zfcp_sysfs_scsi_fcp_lun_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + return sprintf(buf, "0x%016llx\n", zfcp_scsi_dev_lun(sdev)); +} +static DEVICE_ATTR(fcp_lun, S_IRUGO, zfcp_sysfs_scsi_fcp_lun_show, NULL); + +struct device_attribute *zfcp_sysfs_sdev_attrs[] = { + &dev_attr_fcp_lun, + &dev_attr_wwpn, + &dev_attr_hba_id, + &dev_attr_read_latency, + &dev_attr_write_latency, + &dev_attr_cmd_latency, + NULL +}; + +static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *scsi_host = dev_to_shost(dev); + struct fsf_qtcb_bottom_port *qtcb_port; + struct zfcp_adapter *adapter; + int retval; + + adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; + if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)) + return -EOPNOTSUPP; + + qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL); + if (!qtcb_port) + return -ENOMEM; + + retval = zfcp_fsf_exchange_port_data_sync(adapter->qdio, qtcb_port); + if (!retval) + retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util, + qtcb_port->cb_util, qtcb_port->a_util); + kfree(qtcb_port); + return retval; +} +static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL); + +static int zfcp_sysfs_adapter_ex_config(struct device *dev, + struct fsf_statistics_info *stat_inf) +{ + struct Scsi_Host *scsi_host = dev_to_shost(dev); + struct fsf_qtcb_bottom_config *qtcb_config; + struct zfcp_adapter *adapter; + int retval; + + adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; + if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)) + return -EOPNOTSUPP; + + qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config), + GFP_KERNEL); + if (!qtcb_config) + return -ENOMEM; + + retval = zfcp_fsf_exchange_config_data_sync(adapter->qdio, qtcb_config); + if (!retval) + *stat_inf = qtcb_config->stat_info; + + kfree(qtcb_config); + return retval; +} + +#define ZFCP_SHOST_ATTR(_name, _format, _arg...) \ +static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \ + struct device_attribute *attr,\ + char *buf) \ +{ \ + struct fsf_statistics_info stat_info; \ + int retval; \ + \ + retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); \ + if (retval) \ + return retval; \ + \ + return sprintf(buf, _format, ## _arg); \ +} \ +static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL); + +ZFCP_SHOST_ATTR(requests, "%llu %llu %llu\n", + (unsigned long long) stat_info.input_req, + (unsigned long long) stat_info.output_req, + (unsigned long long) stat_info.control_req); + +ZFCP_SHOST_ATTR(megabytes, "%llu %llu\n", + (unsigned long long) stat_info.input_mb, + (unsigned long long) stat_info.output_mb); + +ZFCP_SHOST_ATTR(seconds_active, "%llu\n", + (unsigned long long) stat_info.seconds_act); + +static ssize_t zfcp_sysfs_adapter_q_full_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *scsi_host = class_to_shost(dev); + struct zfcp_qdio *qdio = + ((struct zfcp_adapter *) scsi_host->hostdata[0])->qdio; + u64 util; + + spin_lock_bh(&qdio->stat_lock); + util = qdio->req_q_util; + spin_unlock_bh(&qdio->stat_lock); + + return sprintf(buf, "%d %llu\n", atomic_read(&qdio->req_q_full), + (unsigned long long)util); +} +static DEVICE_ATTR(queue_full, S_IRUGO, zfcp_sysfs_adapter_q_full_show, NULL); + +struct device_attribute *zfcp_sysfs_shost_attrs[] = { + &dev_attr_utilization, + &dev_attr_requests, + &dev_attr_megabytes, + &dev_attr_seconds_active, + &dev_attr_queue_full, + NULL +}; diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c deleted file mode 100644 index 705c6d4428f..00000000000 --- a/drivers/s390/scsi/zfcp_sysfs_adapter.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. - * - * (C) Copyright IBM Corp. 2002, 2006 - * - * 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. - */ - -#include "zfcp_ext.h" - -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG - -/** - * 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, struct device_attribute *attr, \ - 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(peer_wwnn, "0x%016llx\n", adapter->peer_wwnn); -ZFCP_DEFINE_ADAPTER_ATTR(peer_wwpn, "0x%016llx\n", adapter->peer_wwpn); -ZFCP_DEFINE_ADAPTER_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_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(hardware_version, "0x%08x\n", - adapter->hardware_version); -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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, - 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, struct device_attribute *attr, 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_peer_wwnn.attr, - &dev_attr_peer_wwpn.attr, - &dev_attr_peer_d_id.attr, - &dev_attr_card_version.attr, - &dev_attr_lic_version.attr, - &dev_attr_status.attr, - &dev_attr_hardware_version.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 deleted file mode 100644 index 651edd58906..00000000000 --- a/drivers/s390/scsi/zfcp_sysfs_driver.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. - * - * (C) Copyright IBM Corp. 2002, 2006 - * - * 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. - */ - -#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, -}; - -struct attribute_group *zfcp_driver_attr_groups[] = { - &zfcp_driver_attr_group, - NULL, -}; - -#undef ZFCP_LOG_AREA diff --git a/drivers/s390/scsi/zfcp_sysfs_port.c b/drivers/s390/scsi/zfcp_sysfs_port.c deleted file mode 100644 index 1320c059143..00000000000 --- a/drivers/s390/scsi/zfcp_sysfs_port.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. - * - * (C) Copyright IBM Corp. 2002, 2006 - * - * 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. - */ - -#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, struct device_attribute *attr, \ - 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(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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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, struct device_attribute *attr, 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_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, - 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 deleted file mode 100644 index 63f75ee95c3..00000000000 --- a/drivers/s390/scsi/zfcp_sysfs_unit.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. - * - * (C) Copyright IBM Corp. 2002, 2006 - * - * 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. - */ - -#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, struct device_attribute *attr, \ - 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(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, struct device_attribute *attr, 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, struct device_attribute *attr, 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_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 diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c new file mode 100644 index 00000000000..39f5446f721 --- /dev/null +++ b/drivers/s390/scsi/zfcp_unit.c @@ -0,0 +1,255 @@ +/* + * zfcp device driver + * + * Tracking of manually configured LUNs and helper functions to + * register the LUNs with the SCSI midlayer. + * + * Copyright IBM Corp. 2010 + */ + +#include "zfcp_def.h" +#include "zfcp_ext.h" + +/** + * zfcp_unit_scsi_scan - Register LUN with SCSI midlayer + * @unit: The zfcp LUN/unit to register + * + * When the SCSI midlayer is not allowed to automatically scan and + * attach SCSI devices, zfcp has to register the single devices with + * the SCSI midlayer. + */ +void zfcp_unit_scsi_scan(struct zfcp_unit *unit) +{ + struct fc_rport *rport = unit->port->rport; + unsigned int lun; + + lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun); + + if (rport && rport->port_state == FC_PORTSTATE_ONLINE) + scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, lun, 1); +} + +static void zfcp_unit_scsi_scan_work(struct work_struct *work) +{ + struct zfcp_unit *unit = container_of(work, struct zfcp_unit, + scsi_work); + + zfcp_unit_scsi_scan(unit); + put_device(&unit->dev); +} + +/** + * zfcp_unit_queue_scsi_scan - Register configured units on port + * @port: The zfcp_port where to register units + * + * After opening a port, all units configured on this port have to be + * registered with the SCSI midlayer. This function should be called + * after calling fc_remote_port_add, so that the fc_rport is already + * ONLINE and the call to scsi_scan_target runs the same way as the + * call in the FC transport class. + */ +void zfcp_unit_queue_scsi_scan(struct zfcp_port *port) +{ + struct zfcp_unit *unit; + + read_lock_irq(&port->unit_list_lock); + list_for_each_entry(unit, &port->unit_list, list) { + get_device(&unit->dev); + if (scsi_queue_work(port->adapter->scsi_host, + &unit->scsi_work) <= 0) + put_device(&unit->dev); + } + read_unlock_irq(&port->unit_list_lock); +} + +static struct zfcp_unit *_zfcp_unit_find(struct zfcp_port *port, u64 fcp_lun) +{ + struct zfcp_unit *unit; + + list_for_each_entry(unit, &port->unit_list, list) + if (unit->fcp_lun == fcp_lun) { + get_device(&unit->dev); + return unit; + } + + return NULL; +} + +/** + * zfcp_unit_find - Find and return zfcp_unit with specified FCP LUN + * @port: zfcp_port where to look for the unit + * @fcp_lun: 64 Bit FCP LUN used to identify the zfcp_unit + * + * If zfcp_unit is found, a reference is acquired that has to be + * released later. + * + * Returns: Pointer to the zfcp_unit, or NULL if there is no zfcp_unit + * with the specified FCP LUN. + */ +struct zfcp_unit *zfcp_unit_find(struct zfcp_port *port, u64 fcp_lun) +{ + struct zfcp_unit *unit; + + read_lock_irq(&port->unit_list_lock); + unit = _zfcp_unit_find(port, fcp_lun); + read_unlock_irq(&port->unit_list_lock); + return unit; +} + +/** + * zfcp_unit_release - Drop reference to zfcp_port and free memory of zfcp_unit. + * @dev: pointer to device in zfcp_unit + */ +static void zfcp_unit_release(struct device *dev) +{ + struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev); + + atomic_dec(&unit->port->units); + kfree(unit); +} + +/** + * 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 + * Returns: 0 success + * + * Sets up some unit internal structures and creates sysfs entry. + */ +int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) +{ + struct zfcp_unit *unit; + int retval = 0; + + mutex_lock(&zfcp_sysfs_port_units_mutex); + if (atomic_read(&port->units) == -1) { + /* port is already gone */ + retval = -ENODEV; + goto out; + } + + unit = zfcp_unit_find(port, fcp_lun); + if (unit) { + put_device(&unit->dev); + retval = -EEXIST; + goto out; + } + + unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); + if (!unit) { + retval = -ENOMEM; + goto out; + } + + unit->port = port; + unit->fcp_lun = fcp_lun; + unit->dev.parent = &port->dev; + unit->dev.release = zfcp_unit_release; + unit->dev.groups = zfcp_unit_attr_groups; + INIT_WORK(&unit->scsi_work, zfcp_unit_scsi_scan_work); + + if (dev_set_name(&unit->dev, "0x%016llx", + (unsigned long long) fcp_lun)) { + kfree(unit); + retval = -ENOMEM; + goto out; + } + + if (device_register(&unit->dev)) { + put_device(&unit->dev); + retval = -ENOMEM; + goto out; + } + + atomic_inc(&port->units); /* under zfcp_sysfs_port_units_mutex ! */ + + write_lock_irq(&port->unit_list_lock); + list_add_tail(&unit->list, &port->unit_list); + write_unlock_irq(&port->unit_list_lock); + + zfcp_unit_scsi_scan(unit); + +out: + mutex_unlock(&zfcp_sysfs_port_units_mutex); + return retval; +} + +/** + * zfcp_unit_sdev - Return SCSI device for zfcp_unit + * @unit: The zfcp_unit where to get the SCSI device for + * + * Returns: scsi_device pointer on success, NULL if there is no SCSI + * device for this zfcp_unit + * + * On success, the caller also holds a reference to the SCSI device + * that must be released with scsi_device_put. + */ +struct scsi_device *zfcp_unit_sdev(struct zfcp_unit *unit) +{ + struct Scsi_Host *shost; + struct zfcp_port *port; + unsigned int lun; + + lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun); + port = unit->port; + shost = port->adapter->scsi_host; + return scsi_device_lookup(shost, 0, port->starget_id, lun); +} + +/** + * zfcp_unit_sdev_status - Return zfcp LUN status for SCSI device + * @unit: The unit to lookup the SCSI device for + * + * Returns the zfcp LUN status field of the SCSI device if the SCSI device + * for the zfcp_unit exists, 0 otherwise. + */ +unsigned int zfcp_unit_sdev_status(struct zfcp_unit *unit) +{ + unsigned int status = 0; + struct scsi_device *sdev; + struct zfcp_scsi_dev *zfcp_sdev; + + sdev = zfcp_unit_sdev(unit); + if (sdev) { + zfcp_sdev = sdev_to_zfcp(sdev); + status = atomic_read(&zfcp_sdev->status); + scsi_device_put(sdev); + } + + return status; +} + +/** + * zfcp_unit_remove - Remove entry from list of configured units + * @port: The port where to remove the unit from the configuration + * @fcp_lun: The 64 bit LUN of the unit to remove + * + * Returns: -EINVAL if a unit with the specified LUN does not exist, + * 0 on success. + */ +int zfcp_unit_remove(struct zfcp_port *port, u64 fcp_lun) +{ + struct zfcp_unit *unit; + struct scsi_device *sdev; + + write_lock_irq(&port->unit_list_lock); + unit = _zfcp_unit_find(port, fcp_lun); + if (unit) + list_del(&unit->list); + write_unlock_irq(&port->unit_list_lock); + + if (!unit) + return -EINVAL; + + sdev = zfcp_unit_sdev(unit); + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } + + put_device(&unit->dev); + + device_unregister(&unit->dev); + + return 0; +} |
