X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fs390%2Fscsi%2Fzfcp_aux.c;h=870fe79bc7ea6ad642fe3a673667a7fd32ecdb0b;hb=d46f384a89c2378cb7858747faa1935db17e22a8;hp=6a43322ccb0a570a99e9ebe1dfabec475728ca47;hpb=1da177e4c3f41524e886b7f1b8a0c1fc7321cac2;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 6a43322..870fe79 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -1,261 +1,78 @@ /* + * zfcp device driver * - * linux/drivers/s390/scsi/zfcp_aux.c + * Module interface and handling of zfcp data structures. * - * FCP adapter driver for IBM eServer zSeries - * - * (C) Copyright IBM Corp. 2002, 2004 - * - * Author(s): Martin Peschke - * Raimund Schroeder + * Copyright IBM Corporation 2002, 2009 + */ + +/* + * Driver authors: + * Martin Peschke (originator of the driver) + * Raimund Schroeder * Aron Zeh * Wolfgang Taphorn - * Stefan Bader - * Heiko Carstens - * Andreas Herrmann - * - * 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. + * Stefan Bader + * Heiko Carstens (kernel 2.6 port of the driver) + * Andreas Herrmann + * Maxim Shchetynin + * Volker Sameske + * Ralph Wuerthner + * Michael Loehr + * Swen Schillig + * Christof Schmitt + * Martin Petermann + * Sven Schuetz */ -#define ZFCP_AUX_REVISION "$Revision: 1.145 $" +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include +#include #include "zfcp_ext.h" -/* accumulated log level (module parameter) */ -static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS; -static char *device; -/*********************** FUNCTION PROTOTYPES *********************************/ - -/* written against the module interface */ -static int __init zfcp_module_init(void); - -/* FCP related */ -static void zfcp_ns_gid_pn_handler(unsigned long); - -/* miscellaneous */ -static inline int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t); -static inline void zfcp_sg_list_free(struct zfcp_sg_list *); -static inline int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *, - void __user *, size_t); -static inline int zfcp_sg_list_copy_to_user(void __user *, - struct zfcp_sg_list *, size_t); - -static int zfcp_cfdc_dev_ioctl(struct inode *, struct file *, - unsigned int, unsigned long); - -#define ZFCP_CFDC_IOC_MAGIC 0xDD -#define ZFCP_CFDC_IOC \ - _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_sense_data) - -#ifdef CONFIG_COMPAT -static struct ioctl_trans zfcp_ioctl_trans = {ZFCP_CFDC_IOC, (void*) sys_ioctl}; -#endif - -static struct file_operations zfcp_cfdc_fops = { - .ioctl = zfcp_cfdc_dev_ioctl -}; - -static struct miscdevice zfcp_cfdc_misc = { - .minor = ZFCP_CFDC_DEV_MINOR, - .name = ZFCP_CFDC_DEV_NAME, - .fops = &zfcp_cfdc_fops -}; +#define ZFCP_BUS_ID_SIZE 20 -/*********************** KERNEL/MODULE PARAMETERS ***************************/ - -/* declare driver module init/cleanup functions */ -module_init(zfcp_module_init); - -MODULE_AUTHOR("Heiko Carstens , " - "Andreas Herrman , " - "Martin Peschke , " - "Raimund Schroeder , " - "Wolfgang Taphorn , " - "Aron Zeh , " - "IBM Deutschland Entwicklung GmbH"); -MODULE_DESCRIPTION - ("FCP (SCSI over Fibre Channel) HBA driver for IBM eServer zSeries"); +MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com"); +MODULE_DESCRIPTION("FCP HBA driver"); MODULE_LICENSE("GPL"); -module_param(device, charp, 0); +static char *init_device; +module_param_named(device, init_device, charp, 0400); MODULE_PARM_DESC(device, "specify initial device"); -module_param(loglevel, uint, 0); -MODULE_PARM_DESC(loglevel, - "log levels, 8 nibbles: " - "FC ERP QDIO CIO Config FSF SCSI Other, " - "levels: 0=none 1=normal 2=devel 3=trace"); - -#ifdef ZFCP_PRINT_FLAGS -u32 flags_dump = 0; -module_param(flags_dump, uint, 0); -#endif - -/****************************************************************/ -/************** Functions without logging ***********************/ -/****************************************************************/ - -void -_zfcp_hex_dump(char *addr, int count) -{ - int i; - for (i = 0; i < count; i++) { - printk("%02x", addr[i]); - if ((i % 4) == 3) - printk(" "); - if ((i % 32) == 31) - printk("\n"); - } - if (((i-1) % 32) != 31) - printk("\n"); -} - -/****************************************************************/ -/************** Uncategorised Functions *************************/ -/****************************************************************/ - -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER - -static inline int -zfcp_fsf_req_is_scsi_cmnd(struct zfcp_fsf_req *fsf_req) -{ - return ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) && - !(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)); -} - -void -zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req, - void *add_data, int add_length) -{ - struct zfcp_adapter *adapter = fsf_req->adapter; - struct scsi_cmnd *scsi_cmnd; - int level = 3; - int i; - unsigned long flags; - - spin_lock_irqsave(&adapter->dbf_lock, flags); - if (zfcp_fsf_req_is_scsi_cmnd(fsf_req)) { - scsi_cmnd = fsf_req->data.send_fcp_command_task.scsi_cmnd; - debug_text_event(adapter->cmd_dbf, level, "fsferror"); - debug_text_event(adapter->cmd_dbf, level, text); - debug_event(adapter->cmd_dbf, level, &fsf_req, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, - sizeof (u32)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd, - min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len)); - for (i = 0; i < add_length; i += ZFCP_CMD_DBF_LENGTH) - debug_event(adapter->cmd_dbf, - level, - (char *) add_data + i, - min(ZFCP_CMD_DBF_LENGTH, add_length - i)); - } - spin_unlock_irqrestore(&adapter->dbf_lock, flags); -} - -/* XXX additionally log unit if available */ -/* ---> introduce new parameter for unit, see 2.4 code */ -void -zfcp_cmd_dbf_event_scsi(const char *text, struct scsi_cmnd *scsi_cmnd) +static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter) { - struct zfcp_adapter *adapter; - union zfcp_req_data *req_data; - struct zfcp_fsf_req *fsf_req; - int level = ((host_byte(scsi_cmnd->result) != 0) ? 1 : 5); - unsigned long flags; - - adapter = (struct zfcp_adapter *) scsi_cmnd->device->host->hostdata[0]; - req_data = (union zfcp_req_data *) scsi_cmnd->host_scribble; - fsf_req = (req_data ? req_data->send_fcp_command_task.fsf_req : NULL); - spin_lock_irqsave(&adapter->dbf_lock, flags); - debug_text_event(adapter->cmd_dbf, level, "hostbyte"); - debug_text_event(adapter->cmd_dbf, level, text); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof (u32)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd, - min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len)); - if (likely(fsf_req)) { - debug_event(adapter->cmd_dbf, level, &fsf_req, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, - sizeof (u32)); - } else { - debug_text_event(adapter->cmd_dbf, level, ""); - debug_text_event(adapter->cmd_dbf, level, ""); - } - spin_unlock_irqrestore(&adapter->dbf_lock, flags); -} + int idx; -void -zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text, - struct fsf_status_read_buffer *status_buffer, int length) -{ - int level = 1; - int i; + adapter->req_list = kcalloc(REQUEST_LIST_SIZE, sizeof(struct list_head), + GFP_KERNEL); + if (!adapter->req_list) + return -ENOMEM; - debug_text_event(adapter->in_els_dbf, level, text); - debug_event(adapter->in_els_dbf, level, &status_buffer->d_id, 8); - for (i = 0; i < length; i += ZFCP_IN_ELS_DBF_LENGTH) - debug_event(adapter->in_els_dbf, - level, - (char *) status_buffer->payload + i, - min(ZFCP_IN_ELS_DBF_LENGTH, length - i)); + for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) + INIT_LIST_HEAD(&adapter->req_list[idx]); + return 0; } /** - * zfcp_device_setup - setup function - * @str: pointer to parameter string + * zfcp_reqlist_isempty - is the request list empty + * @adapter: pointer to struct zfcp_adapter * - * Parse "device=..." parameter string. + * Returns: true if list is empty, false otherwise */ -static int __init -zfcp_device_setup(char *str) +int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) { - char *tmp; - - if (!str) - return 0; + unsigned int idx; - tmp = strchr(str, ','); - if (!tmp) - goto err_out; - *tmp++ = '\0'; - strncpy(zfcp_data.init_busid, str, BUS_ID_SIZE); - zfcp_data.init_busid[BUS_ID_SIZE-1] = '\0'; - - zfcp_data.init_wwpn = simple_strtoull(tmp, &tmp, 0); - if (*tmp++ != ',') - goto err_out; - if (*tmp == '\0') - goto err_out; - - zfcp_data.init_fcp_lun = simple_strtoull(tmp, &tmp, 0); - if (*tmp != '\0') - goto err_out; + for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) + if (!list_empty(&adapter->req_list[idx])) + return 0; return 1; - - err_out: - ZFCP_LOG_NORMAL("Parse error for device parameter string %s\n", str); - return 0; } -static void __init -zfcp_init_device_configure(void) +static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) { struct zfcp_adapter *adapter; struct zfcp_port *port; @@ -263,613 +80,210 @@ zfcp_init_device_configure(void) down(&zfcp_data.config_sema); read_lock_irq(&zfcp_data.config_lock); - adapter = zfcp_get_adapter_by_busid(zfcp_data.init_busid); + adapter = zfcp_get_adapter_by_busid(busid); if (adapter) zfcp_adapter_get(adapter); read_unlock_irq(&zfcp_data.config_lock); - if (adapter == NULL) + if (!adapter) goto out_adapter; - port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0, 0); - if (!port) + port = zfcp_port_enqueue(adapter, wwpn, 0, 0); + if (IS_ERR(port)) goto out_port; - unit = zfcp_unit_enqueue(port, zfcp_data.init_fcp_lun); - if (!unit) + unit = zfcp_unit_enqueue(port, lun); + if (IS_ERR(unit)) goto out_unit; up(&zfcp_data.config_sema); ccw_device_set_online(adapter->ccw_device); + zfcp_erp_wait(adapter); + flush_work(&unit->scsi_work); + down(&zfcp_data.config_sema); zfcp_unit_put(unit); - out_unit: +out_unit: zfcp_port_put(port); - out_port: +out_port: zfcp_adapter_put(adapter); - out_adapter: +out_adapter: up(&zfcp_data.config_sema); return; } -static int __init -zfcp_module_init(void) +static struct kmem_cache *zfcp_cache_create(int size, char *name) { - - int retval = 0; - - atomic_set(&zfcp_data.loglevel, loglevel); - - /* initialize adapter list */ - INIT_LIST_HEAD(&zfcp_data.adapter_list_head); - - /* initialize adapters to be removed list head */ - INIT_LIST_HEAD(&zfcp_data.adapter_remove_lh); - - zfcp_transport_template = fc_attach_transport(&zfcp_transport_functions); - if (!zfcp_transport_template) - return -ENODEV; - - retval = register_ioctl32_conversion(zfcp_ioctl_trans.cmd, - zfcp_ioctl_trans.handler); - if (retval != 0) { - ZFCP_LOG_INFO("registration of ioctl32 conversion failed\n"); - goto out; - } - - retval = misc_register(&zfcp_cfdc_misc); - if (retval != 0) { - ZFCP_LOG_INFO("registration of misc device " - "zfcp_cfdc failed\n"); - goto out_misc_register; - } else { - ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n", - ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor); - } - - /* Initialise proc semaphores */ - sema_init(&zfcp_data.config_sema, 1); - - /* initialise configuration rw lock */ - rwlock_init(&zfcp_data.config_lock); - - /* save address of data structure managing the driver module */ - zfcp_data.scsi_host_template.module = THIS_MODULE; - - /* setup dynamic I/O */ - retval = zfcp_ccw_register(); - if (retval) { - ZFCP_LOG_NORMAL("registration with common I/O layer failed\n"); - goto out_ccw_register; - } - - if (zfcp_device_setup(device)) - zfcp_init_device_configure(); - - goto out; - - out_ccw_register: - misc_deregister(&zfcp_cfdc_misc); - out_misc_register: - unregister_ioctl32_conversion(zfcp_ioctl_trans.cmd); - out: - return retval; + int align = 1; + while ((size - align) > 0) + align <<= 1; + return kmem_cache_create(name , size, align, 0, NULL); } -/* - * function: zfcp_cfdc_dev_ioctl - * - * purpose: Handle control file upload/download transaction via IOCTL - * interface - * - * returns: 0 - Operation completed successfuly - * -ENOTTY - Unknown IOCTL command - * -EINVAL - Invalid sense data record - * -ENXIO - The FCP adapter is not available - * -EOPNOTSUPP - The FCP adapter does not have CFDC support - * -ENOMEM - Insufficient memory - * -EFAULT - User space memory I/O operation fault - * -EPERM - Cannot create or queue FSF request or create SBALs - * -ERESTARTSYS- Received signal (is mapped to EAGAIN by VFS) - */ -static int -zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, - unsigned int command, unsigned long buffer) +static void __init zfcp_init_device_setup(char *devstr) { - struct zfcp_cfdc_sense_data *sense_data, __user *sense_data_user; - struct zfcp_adapter *adapter = NULL; - struct zfcp_fsf_req *fsf_req = NULL; - struct zfcp_sg_list *sg_list = NULL; - u32 fsf_command, option; - char *bus_id = NULL; - int retval = 0; - - sense_data = kmalloc(sizeof(struct zfcp_cfdc_sense_data), GFP_KERNEL); - if (sense_data == NULL) { - retval = -ENOMEM; - goto out; - } - - sg_list = kmalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL); - if (sg_list == NULL) { - retval = -ENOMEM; - goto out; - } - memset(sg_list, 0, sizeof(*sg_list)); - - if (command != ZFCP_CFDC_IOC) { - ZFCP_LOG_INFO("IOC request code 0x%x invalid\n", command); - retval = -ENOTTY; - goto out; - } - - if ((sense_data_user = (void __user *) buffer) == NULL) { - ZFCP_LOG_INFO("sense data record is required\n"); - retval = -EINVAL; - goto out; - } - - retval = copy_from_user(sense_data, sense_data_user, - sizeof(struct zfcp_cfdc_sense_data)); - if (retval) { - retval = -EFAULT; - goto out; - } - - if (sense_data->signature != ZFCP_CFDC_SIGNATURE) { - ZFCP_LOG_INFO("invalid sense data request signature 0x%08x\n", - ZFCP_CFDC_SIGNATURE); - retval = -EINVAL; - goto out; - } - - switch (sense_data->command) { - - case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL: - fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; - option = FSF_CFDC_OPTION_NORMAL_MODE; - break; - - case ZFCP_CFDC_CMND_DOWNLOAD_FORCE: - fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; - option = FSF_CFDC_OPTION_FORCE; - break; - - case ZFCP_CFDC_CMND_FULL_ACCESS: - fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; - option = FSF_CFDC_OPTION_FULL_ACCESS; - break; - - case ZFCP_CFDC_CMND_RESTRICTED_ACCESS: - fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; - option = FSF_CFDC_OPTION_RESTRICTED_ACCESS; - break; - - case ZFCP_CFDC_CMND_UPLOAD: - fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE; - option = 0; - break; - - default: - ZFCP_LOG_INFO("invalid command code 0x%08x\n", - sense_data->command); - retval = -EINVAL; - goto out; - } - - bus_id = kmalloc(BUS_ID_SIZE, GFP_KERNEL); - if (bus_id == NULL) { - retval = -ENOMEM; - goto out; - } - snprintf(bus_id, BUS_ID_SIZE, "%d.%d.%04x", - (sense_data->devno >> 24), - (sense_data->devno >> 16) & 0xFF, - (sense_data->devno & 0xFFFF)); - - read_lock_irq(&zfcp_data.config_lock); - adapter = zfcp_get_adapter_by_busid(bus_id); - if (adapter) - zfcp_adapter_get(adapter); - read_unlock_irq(&zfcp_data.config_lock); - - kfree(bus_id); - - if (adapter == NULL) { - ZFCP_LOG_INFO("invalid adapter\n"); - retval = -ENXIO; - goto out; - } - - if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) { - retval = zfcp_sg_list_alloc(sg_list, - ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); - if (retval) { - retval = -ENOMEM; - goto out; - } - } - - if ((sense_data->command & ZFCP_CFDC_DOWNLOAD) && - (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE)) { - retval = zfcp_sg_list_copy_from_user( - sg_list, &sense_data_user->control_file, - ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); - if (retval) { - retval = -EFAULT; - goto out; - } - } + char *token; + char *str; + char busid[ZFCP_BUS_ID_SIZE]; + u64 wwpn, lun; - 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; - } + /* duplicate devstr and keep the original for sysfs presentation*/ + str = kmalloc(strlen(devstr) + 1, GFP_KERNEL); + if (!str) + return; - 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; - } - } + strcpy(str, devstr); - out: - if (fsf_req != NULL) - zfcp_fsf_req_cleanup(fsf_req); + token = strsep(&str, ","); + if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE) + goto err_out; + strncpy(busid, token, ZFCP_BUS_ID_SIZE); - if ((adapter != NULL) && (retval != -ENXIO)) - zfcp_adapter_put(adapter); + token = strsep(&str, ","); + if (!token || strict_strtoull(token, 0, (unsigned long long *) &wwpn)) + goto err_out; - if (sg_list != NULL) { - zfcp_sg_list_free(sg_list); - kfree(sg_list); - } + token = strsep(&str, ","); + if (!token || strict_strtoull(token, 0, (unsigned long long *) &lun)) + goto err_out; - if (sense_data != NULL) - kfree(sense_data); + kfree(str); + zfcp_init_device_configure(busid, wwpn, lun); + return; - return retval; + err_out: + kfree(str); + pr_err("%s is not a valid SCSI device\n", devstr); } - -/** - * zfcp_sg_list_alloc - create a scatter-gather list of the specified size - * @sg_list: structure describing a scatter gather list - * @size: size of scatter-gather list - * Return: 0 on success, else -ENOMEM - * - * In sg_list->sg a pointer to the created scatter-gather list is returned, - * or NULL if we run out of memory. sg_list->count specifies the number of - * elements of the scatter-gather list. The maximum size of a single element - * in the scatter-gather list is PAGE_SIZE. - */ -static inline int -zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) +static int __init zfcp_module_init(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 = kmalloc(sg_list->count * sizeof(struct scatterlist), - GFP_KERNEL); - if (sg_list->sg == NULL) { - sg_list->count = 0; - retval = -ENOMEM; - goto out; - } - memset(sg_list->sg, 0, sg_list->count * sizeof(struct scatterlist)); - - for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) { - sg->length = min(size, PAGE_SIZE); - sg->offset = 0; - address = (void *) get_zeroed_page(GFP_KERNEL); - if (address == NULL) { - sg_list->count = i; - zfcp_sg_list_free(sg_list); - retval = -ENOMEM; - goto out; - } - zfcp_address_to_sg(address, sg); - size -= sg->length; - } + int retval = -ENOMEM; - out: - return retval; -} + zfcp_data.fsf_req_qtcb_cache = zfcp_cache_create( + sizeof(struct zfcp_fsf_req_qtcb), "zfcp_fsf"); + if (!zfcp_data.fsf_req_qtcb_cache) + goto out; + zfcp_data.sr_buffer_cache = zfcp_cache_create( + sizeof(struct fsf_status_read_buffer), "zfcp_sr"); + if (!zfcp_data.sr_buffer_cache) + goto out_sr_cache; -/** - * zfcp_sg_list_free - free memory of a scatter-gather list - * @sg_list: structure describing a scatter-gather list - * - * Memory for each element in the scatter-gather list is freed. - * Finally sg_list->sg is freed itself and sg_list->count is reset. - */ -static inline void -zfcp_sg_list_free(struct zfcp_sg_list *sg_list) -{ - struct scatterlist *sg; - unsigned int i; + zfcp_data.gid_pn_cache = zfcp_cache_create( + sizeof(struct zfcp_gid_pn_data), "zfcp_gid"); + if (!zfcp_data.gid_pn_cache) + goto out_gid_cache; - BUG_ON(sg_list == NULL); + zfcp_data.work_queue = create_singlethread_workqueue("zfcp_wq"); - for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) - free_page((unsigned long) zfcp_sg_to_address(sg)); + sema_init(&zfcp_data.config_sema, 1); + rwlock_init(&zfcp_data.config_lock); - sg_list->count = 0; - kfree(sg_list->sg); -} + zfcp_data.scsi_transport_template = + fc_attach_transport(&zfcp_transport_functions); + if (!zfcp_data.scsi_transport_template) + goto out_transport; -/** - * zfcp_sg_size - determine size of a scatter-gather list - * @sg: array of (struct scatterlist) - * @sg_count: elements in array - * Return: size of entire scatter-gather list - */ -size_t -zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count) -{ - unsigned int i; - struct scatterlist *p; - size_t size; - - size = 0; - for (i = 0, p = sg; i < sg_count; i++, p++) { - BUG_ON(p == NULL); - size += p->length; + retval = misc_register(&zfcp_cfdc_misc); + if (retval) { + pr_err("Registering the misc device zfcp_cfdc failed\n"); + goto out_misc; } - return size; -} - - -/** - * zfcp_sg_list_copy_from_user -copy data from user space to scatter-gather list - * @sg_list: structure describing a scatter-gather list - * @user_buffer: pointer to buffer in user space - * @size: number of bytes to be copied - * Return: 0 on success, -EFAULT if copy_from_user fails. - */ -static inline int -zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, - void __user *user_buffer, - size_t size) -{ - struct scatterlist *sg; - unsigned int length; - void *zfcp_buffer; - int retval = 0; - - BUG_ON(sg_list == NULL); - - if (zfcp_sg_size(sg_list->sg, sg_list->count) < size) - return -EFAULT; - - for (sg = sg_list->sg; size > 0; sg++) { - length = min((unsigned int)size, sg->length); - zfcp_buffer = zfcp_sg_to_address(sg); - if (copy_from_user(zfcp_buffer, user_buffer, length)) { - retval = -EFAULT; - goto out; - } - user_buffer += length; - size -= length; + retval = zfcp_ccw_register(); + if (retval) { + pr_err("The zfcp device driver could not register with " + "the common I/O layer\n"); + goto out_ccw_register; } - out: - return retval; -} - - -/** - * zfcp_sg_list_copy_to_user - copy data from scatter-gather list to user space - * @user_buffer: pointer to buffer in user space - * @sg_list: structure describing a scatter-gather list - * @size: number of bytes to be copied - * Return: 0 on success, -EFAULT if copy_to_user fails - */ -static inline int -zfcp_sg_list_copy_to_user(void __user *user_buffer, - struct zfcp_sg_list *sg_list, - size_t size) -{ - struct scatterlist *sg; - unsigned int length; - void *zfcp_buffer; - int retval = 0; - - BUG_ON(sg_list == NULL); - - if (zfcp_sg_size(sg_list->sg, sg_list->count) < size) - return -EFAULT; - - for (sg = sg_list->sg; size > 0; sg++) { - length = min((unsigned int) size, sg->length); - zfcp_buffer = zfcp_sg_to_address(sg); - if (copy_to_user(user_buffer, zfcp_buffer, length)) { - retval = -EFAULT; - goto out; - } - user_buffer += length; - size -= length; - } + if (init_device) + zfcp_init_device_setup(init_device); + return 0; - 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; } - -#undef ZFCP_LOG_AREA - -/****************************************************************/ -/****** Functions for configuration/set-up of structures ********/ -/****************************************************************/ - -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG +module_init(zfcp_module_init); /** * 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. + * + * Returns: pointer to zfcp_unit or NULL */ -struct zfcp_unit * -zfcp_get_unit_by_lun(struct zfcp_port *port, fcp_lun_t fcp_lun) +struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) { struct zfcp_unit *unit; - int found = 0; - list_for_each_entry(unit, &port->unit_list_head, list) { + 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; + !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE)) + return unit; + return NULL; } /** * zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn * @adapter: pointer to adapter to search for port * @wwpn: wwpn to search for - * Traverse list of all ports of an adapter and return pointer to a port - * with the given wwpn. + * + * Returns: pointer to zfcp_port or NULL */ -struct zfcp_port * -zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn) +struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, + u64 wwpn) { struct zfcp_port *port; - int found = 0; - list_for_each_entry(port, &adapter->port_list_head, list) { + 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; + !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE)) + return port; + return NULL; } -/** - * zfcp_get_port_by_did - find port in port list of adapter by d_id - * @adapter: pointer to adapter to search for port - * @d_id: d_id to search for - * Traverse list of all ports of an adapter and return pointer to a port - * with the given d_id. - */ -struct zfcp_port * -zfcp_get_port_by_did(struct zfcp_adapter *adapter, u32 d_id) +static void zfcp_sysfs_unit_release(struct device *dev) { - struct zfcp_port *port; - int found = 0; - - list_for_each_entry(port, &adapter->port_list_head, list) { - if ((port->d_id == d_id) && - !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) - { - found = 1; - break; - } - } - return found ? port : NULL; -} - -/** - * zfcp_get_adapter_by_busid - find adpater in adapter list by bus_id - * @bus_id: bus_id to search for - * Traverse list of all adapters and return pointer to an adapter - * with the given bus_id. - */ -struct zfcp_adapter * -zfcp_get_adapter_by_busid(char *bus_id) -{ - struct zfcp_adapter *adapter; - int found = 0; - - list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) { - if ((strncmp(bus_id, zfcp_get_busid_by_adapter(adapter), - BUS_ID_SIZE) == 0) && - !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, - &adapter->status)){ - found = 1; - break; - } - } - return found ? adapter : NULL; + kfree(container_of(dev, struct zfcp_unit, sysfs_device)); } /** * 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 + * Returns: pointer to enqueued unit on success, ERR_PTR on error * Locks: config_sema must be held to serialize changes to the unit list * * Sets up some unit internal structures and creates sysfs entry. */ -struct zfcp_unit * -zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun) +struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) { - struct zfcp_unit *unit, *tmp_unit; - scsi_lun_t scsi_lun; - int found; - - /* - * check that there is no unit with this FCP_LUN already in list - * and enqueue it. - * Note: Unlike for the adapter and the port, this is an error - */ - read_lock_irq(&zfcp_data.config_lock); - unit = zfcp_get_unit_by_lun(port, fcp_lun); - read_unlock_irq(&zfcp_data.config_lock); - if (unit) - return NULL; + struct zfcp_unit *unit; - unit = kmalloc(sizeof (struct zfcp_unit), GFP_KERNEL); + unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); if (!unit) - return NULL; - memset(unit, 0, sizeof (struct zfcp_unit)); + return ERR_PTR(-ENOMEM); - /* initialise reference count stuff */ atomic_set(&unit->refcount, 0); init_waitqueue_head(&unit->remove_wq); + INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); 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); + dev_set_name(&unit->sysfs_device, "0x%016llx", + (unsigned long long) fcp_lun); unit->sysfs_device.parent = &port->sysfs_device; unit->sysfs_device.release = zfcp_sysfs_unit_release; dev_set_drvdata(&unit->sysfs_device, unit); @@ -877,138 +291,107 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun) /* 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; + spin_lock_init(&unit->latencies.lock); + unit->latencies.write.channel.min = 0xFFFFFFFF; + unit->latencies.write.fabric.min = 0xFFFFFFFF; + unit->latencies.read.channel.min = 0xFFFFFFFF; + unit->latencies.read.fabric.min = 0xFFFFFFFF; + unit->latencies.cmd.channel.min = 0xFFFFFFFF; + unit->latencies.cmd.fabric.min = 0xFFFFFFFF; + + read_lock_irq(&zfcp_data.config_lock); + if (zfcp_get_unit_by_lun(port, fcp_lun)) { + read_unlock_irq(&zfcp_data.config_lock); + goto err_out_free; } + read_unlock_irq(&zfcp_data.config_lock); + + if (device_register(&unit->sysfs_device)) + goto err_out_free; - if (zfcp_sysfs_unit_create_files(&unit->sysfs_device)) { + if (sysfs_create_group(&unit->sysfs_device.kobj, + &zfcp_sysfs_unit_attrs)) { device_unregister(&unit->sysfs_device); - return NULL; + return ERR_PTR(-EIO); } zfcp_unit_get(unit); - scsi_lun = 0; - found = 0; write_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(tmp_unit, &port->unit_list_head, list) { - if (tmp_unit->scsi_lun != scsi_lun) { - found = 1; - break; - } - scsi_lun++; - } - unit->scsi_lun = scsi_lun; - if (found) - list_add_tail(&unit->list, &tmp_unit->list); - else - list_add_tail(&unit->list, &port->unit_list_head); + 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; + +err_out_free: + kfree(unit); + return ERR_PTR(-EINVAL); } -void -zfcp_unit_dequeue(struct zfcp_unit *unit) +/** + * zfcp_unit_dequeue - dequeue unit + * @unit: pointer to zfcp_unit + * + * waits until all work is done on unit and removes it then from the unit->list + * of the associated port. + */ +void zfcp_unit_dequeue(struct zfcp_unit *unit) { - zfcp_unit_wait(unit); + wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0); 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); + sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs); device_unregister(&unit->sysfs_device); } -static void * -zfcp_mempool_alloc(unsigned int __nocast gfp_mask, void *size) -{ - return kmalloc((size_t) size, gfp_mask); -} - -static void -zfcp_mempool_free(void *element, void *size) -{ - kfree(element); -} - -/* - * Allocates a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI - * commands. - * It also genrates fcp-nameserver request/response buffer and unsolicited - * status read fsf_req buffers. - * - * locks: must only be called with zfcp_data.config_sema taken - */ -static int -zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) +static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) { + /* must only be called with zfcp_data.config_sema taken */ adapter->pool.fsf_req_erp = - mempool_create(ZFCP_POOL_FSF_REQ_ERP_NR, - zfcp_mempool_alloc, zfcp_mempool_free, (void *) - sizeof(struct zfcp_fsf_req_pool_element)); - - if (NULL == adapter->pool.fsf_req_erp) + mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache); + if (!adapter->pool.fsf_req_erp) return -ENOMEM; adapter->pool.fsf_req_scsi = - mempool_create(ZFCP_POOL_FSF_REQ_SCSI_NR, - zfcp_mempool_alloc, zfcp_mempool_free, (void *) - sizeof(struct zfcp_fsf_req_pool_element)); - - if (NULL == adapter->pool.fsf_req_scsi) + mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache); + if (!adapter->pool.fsf_req_scsi) return -ENOMEM; adapter->pool.fsf_req_abort = - mempool_create(ZFCP_POOL_FSF_REQ_ABORT_NR, - zfcp_mempool_alloc, zfcp_mempool_free, (void *) - sizeof(struct zfcp_fsf_req_pool_element)); - - if (NULL == adapter->pool.fsf_req_abort) + mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache); + if (!adapter->pool.fsf_req_abort) return -ENOMEM; adapter->pool.fsf_req_status_read = - mempool_create(ZFCP_POOL_STATUS_READ_NR, - zfcp_mempool_alloc, zfcp_mempool_free, - (void *) sizeof(struct zfcp_fsf_req)); - - if (NULL == adapter->pool.fsf_req_status_read) + mempool_create_kmalloc_pool(FSF_STATUS_READS_RECOM, + sizeof(struct zfcp_fsf_req)); + if (!adapter->pool.fsf_req_status_read) return -ENOMEM; adapter->pool.data_status_read = - mempool_create(ZFCP_POOL_STATUS_READ_NR, - zfcp_mempool_alloc, zfcp_mempool_free, - (void *) sizeof(struct fsf_status_read_buffer)); - - if (NULL == adapter->pool.data_status_read) + mempool_create_slab_pool(FSF_STATUS_READS_RECOM, + zfcp_data.sr_buffer_cache); + if (!adapter->pool.data_status_read) return -ENOMEM; adapter->pool.data_gid_pn = - mempool_create(ZFCP_POOL_DATA_GID_PN_NR, - zfcp_mempool_alloc, zfcp_mempool_free, (void *) - sizeof(struct zfcp_gid_pn_data)); - - if (NULL == adapter->pool.data_gid_pn) + mempool_create_slab_pool(1, zfcp_data.gid_pn_cache); + if (!adapter->pool.data_gid_pn) return -ENOMEM; return 0; } -/** - * zfcp_free_low_mem_buffers - free memory pools of an adapter - * @adapter: pointer to zfcp_adapter for which memory pools should be freed - * locking: zfcp_data.config_sema must be held - */ -static void -zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) +static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { + /* zfcp_data.config_sema must be held */ if (adapter->pool.fsf_req_erp) mempool_destroy(adapter->pool.fsf_req_erp); if (adapter->pool.fsf_req_scsi) @@ -1024,268 +407,178 @@ zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) } /** - * 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 + * 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 */ -int -zfcp_adapter_debug_register(struct zfcp_adapter *adapter) -{ - char dbf_name[20]; - - /* debug feature area which records SCSI command failures (hostbyte) */ - spin_lock_init(&adapter->dbf_lock); - - sprintf(dbf_name, ZFCP_CMD_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->cmd_dbf = debug_register(dbf_name, ZFCP_CMD_DBF_INDEX, - ZFCP_CMD_DBF_AREAS, - ZFCP_CMD_DBF_LENGTH); - debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL); - - /* debug feature area which records SCSI command aborts */ - sprintf(dbf_name, ZFCP_ABORT_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->abort_dbf = debug_register(dbf_name, ZFCP_ABORT_DBF_INDEX, - ZFCP_ABORT_DBF_AREAS, - ZFCP_ABORT_DBF_LENGTH); - debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL); - - /* debug feature area which records incoming ELS commands */ - sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->in_els_dbf = debug_register(dbf_name, ZFCP_IN_ELS_DBF_INDEX, - ZFCP_IN_ELS_DBF_AREAS, - ZFCP_IN_ELS_DBF_LENGTH); - debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL); - - /* debug feature area which records erp events */ - sprintf(dbf_name, ZFCP_ERP_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->erp_dbf = debug_register(dbf_name, ZFCP_ERP_DBF_INDEX, - ZFCP_ERP_DBF_AREAS, - ZFCP_ERP_DBF_LENGTH); - debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL); - - if (!(adapter->cmd_dbf && adapter->abort_dbf && - adapter->in_els_dbf && adapter->erp_dbf)) { - zfcp_adapter_debug_unregister(adapter); - return -ENOMEM; - } - +int zfcp_status_read_refill(struct zfcp_adapter *adapter) +{ + while (atomic_read(&adapter->stat_miss) > 0) + if (zfcp_fsf_status_read(adapter)) { + if (atomic_read(&adapter->stat_miss) >= 16) { + zfcp_erp_adapter_reopen(adapter, 0, "axsref1", + NULL); + return 1; + } + break; + } else + atomic_dec(&adapter->stat_miss); return 0; - } -/** - * zfcp_adapter_debug_unregister - unregisters debug feature for an adapter - * @adapter: pointer to adapter for which debug features should be unregistered - */ -void -zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter) +static void _zfcp_status_read_scheduler(struct work_struct *work) { - debug_unregister(adapter->abort_dbf); - debug_unregister(adapter->cmd_dbf); - debug_unregister(adapter->erp_dbf); - debug_unregister(adapter->in_els_dbf); - adapter->abort_dbf = NULL; - adapter->cmd_dbf = NULL; - adapter->erp_dbf = NULL; - adapter->in_els_dbf = NULL; + zfcp_status_read_refill(container_of(work, struct zfcp_adapter, + stat_work)); } -void -zfcp_dummy_release(struct device *dev) +static void zfcp_print_sl(struct seq_file *m, struct service_level *sl) { - return; + 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); } -/* +/** + * zfcp_adapter_enqueue - enqueue a new adapter to the list + * @ccw_device: pointer to the struct cc_device + * + * Returns: 0 if a new adapter was successfully enqueued + * -ENOMEM if alloc failed * Enqueues an adapter at the end of the adapter list in the driver data. * All adapter internal structures are set up. * Proc-fs entries are also created. - * - * returns: 0 if a new adapter was successfully enqueued - * ZFCP_KNOWN if an adapter with this devno was already present - * -ENOMEM if alloc failed * locks: config_sema must be held to serialise changes to the adapter list */ -struct zfcp_adapter * -zfcp_adapter_enqueue(struct ccw_device *ccw_device) +int 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 + * Note: It is safe to release the list_lock, as any list changes * are protected by the config_sema, which must be held to get here */ - /* try to allocate new adapter data structure (zeroed) */ - adapter = kmalloc(sizeof (struct zfcp_adapter), GFP_KERNEL); - if (!adapter) { - ZFCP_LOG_INFO("error: allocation of base adapter " - "structure failed\n"); - goto out; + adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + adapter->gs = kzalloc(sizeof(struct zfcp_wka_ports), GFP_KERNEL); + if (!adapter->gs) { + kfree(adapter); + return -ENOMEM; } - memset(adapter, 0, sizeof (struct zfcp_adapter)); ccw_device->handler = NULL; - - /* save ccw_device pointer */ adapter->ccw_device = ccw_device; + atomic_set(&adapter->refcount, 0); - retval = zfcp_qdio_allocate_queues(adapter); - if (retval) - goto queues_alloc_failed; - - retval = zfcp_qdio_allocate(adapter); - if (retval) + if (zfcp_qdio_allocate(adapter)) goto qdio_allocate_failed; - retval = zfcp_allocate_low_mem_buffers(adapter); - if (retval) { - ZFCP_LOG_INFO("error: pool allocation failed\n"); + if (zfcp_allocate_low_mem_buffers(adapter)) goto failed_low_mem_buffers; - } - /* initialise reference count stuff */ - atomic_set(&adapter->refcount, 0); + if (zfcp_reqlist_alloc(adapter)) + goto failed_low_mem_buffers; + + if (zfcp_adapter_debug_register(adapter)) + goto debug_register_failed; + init_waitqueue_head(&adapter->remove_wq); + init_waitqueue_head(&adapter->erp_thread_wqh); + init_waitqueue_head(&adapter->erp_done_wqh); - /* initialise list of ports */ INIT_LIST_HEAD(&adapter->port_list_head); + INIT_LIST_HEAD(&adapter->erp_ready_head); + INIT_LIST_HEAD(&adapter->erp_running_head); - /* initialise list of ports to be removed */ - INIT_LIST_HEAD(&adapter->port_remove_lh); + spin_lock_init(&adapter->req_list_lock); - /* initialize list of fsf requests */ - rwlock_init(&adapter->fsf_req_list_lock); - INIT_LIST_HEAD(&adapter->fsf_req_list_head); + spin_lock_init(&adapter->req_q_lock); + spin_lock_init(&adapter->qdio_stat_lock); - /* 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); - - /* intitialise SCSI ER timer */ - init_timer(&adapter->scsi_er_timer); + sema_init(&adapter->erp_ready_sem, 0); - /* set FC service class used per default */ - adapter->fc_service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT; + INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); + INIT_WORK(&adapter->scan_work, _zfcp_scan_ports_later); - sprintf(adapter->name, "%s", zfcp_get_busid_by_adapter(adapter)); - ASCEBC(adapter->name, strlen(adapter->name)); + adapter->service_level.seq_print = zfcp_print_sl; /* mark adapter unusable as long as sysfs registration is not complete */ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - adapter->ccw_device = ccw_device; dev_set_drvdata(&ccw_device->dev, adapter); - if (zfcp_sysfs_adapter_create_files(&ccw_device->dev)) + if (sysfs_create_group(&ccw_device->dev.kobj, + &zfcp_sysfs_adapter_attrs)) goto sysfs_failed; - adapter->generic_services.parent = &adapter->ccw_device->dev; - adapter->generic_services.release = zfcp_dummy_release; - snprintf(adapter->generic_services.bus_id, BUS_ID_SIZE, - "generic_services"); - - if (device_register(&adapter->generic_services)) - goto generic_services_failed; - - /* put allocated adapter at list tail */ - write_lock_irq(&zfcp_data.config_lock); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - list_add_tail(&adapter->list, &zfcp_data.adapter_list_head); - write_unlock_irq(&zfcp_data.config_lock); - - zfcp_data.adapters++; + zfcp_fc_wka_ports_init(adapter); - goto out; + if (!zfcp_adapter_scsi_register(adapter)) + return 0; - generic_services_failed: - zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); - sysfs_failed: +sysfs_failed: + zfcp_adapter_debug_unregister(adapter); +debug_register_failed: dev_set_drvdata(&ccw_device->dev, NULL); - failed_low_mem_buffers: + kfree(adapter->req_list); +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: +qdio_allocate_failed: + zfcp_qdio_free(adapter); kfree(adapter); - adapter = NULL; - out: - return adapter; + return -ENOMEM; } -/* - * returns: 0 - struct zfcp_adapter data structure successfully removed - * !0 - struct zfcp_adapter data structure could not be removed - * (e.g. still used) +/** + * zfcp_adapter_dequeue - remove the adapter from the resource list + * @adapter: pointer to struct zfcp_adapter which should be removed * locks: adapter list write lock is assumed to be held by caller - * adapter->fsf_req_list_lock is taken and released within this - * function and must not be held on entry */ -void -zfcp_adapter_dequeue(struct zfcp_adapter *adapter) +void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) { int retval = 0; unsigned long flags; - device_unregister(&adapter->generic_services); - zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); + cancel_work_sync(&adapter->scan_work); + cancel_work_sync(&adapter->stat_work); + zfcp_adapter_scsi_unregister(adapter); + sysfs_remove_group(&adapter->ccw_device->dev.kobj, + &zfcp_sysfs_adapter_attrs); dev_set_drvdata(&adapter->ccw_device->dev, NULL); /* sanity check: no pending FSF requests */ - read_lock_irqsave(&adapter->fsf_req_list_lock, flags); - retval = !list_empty(&adapter->fsf_req_list_head); - read_unlock_irqrestore(&adapter->fsf_req_list_lock, flags); - if (retval) { - ZFCP_LOG_NORMAL("bug: adapter %s (%p) still in use, " - "%i requests outstanding\n", - zfcp_get_busid_by_adapter(adapter), adapter, - atomic_read(&adapter->fsf_reqs_active)); - retval = -EBUSY; - goto out; - } - - /* remove specified adapter data structure from list */ - write_lock_irq(&zfcp_data.config_lock); - list_del(&adapter->list); - write_unlock_irq(&zfcp_data.config_lock); - - /* decrease number of adapters in list */ - zfcp_data.adapters--; - - ZFCP_LOG_TRACE("adapter %s (%p) removed from list, " - "%i adapters still in list\n", - zfcp_get_busid_by_adapter(adapter), - adapter, zfcp_data.adapters); - - retval = qdio_free(adapter->ccw_device); - if (retval) - ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n", - zfcp_get_busid_by_adapter(adapter)); - + spin_lock_irqsave(&adapter->req_list_lock, flags); + retval = zfcp_reqlist_isempty(adapter); + spin_unlock_irqrestore(&adapter->req_list_lock, flags); + if (!retval) + return; + + zfcp_adapter_debug_unregister(adapter); + zfcp_qdio_free(adapter); zfcp_free_low_mem_buffers(adapter); - /* free memory of adapter data structure and queues */ - zfcp_qdio_free_queues(adapter); - ZFCP_LOG_TRACE("freeing adapter structure\n"); + kfree(adapter->req_list); + kfree(adapter->fc_stats); + kfree(adapter->stats_reset_data); + kfree(adapter->gs); kfree(adapter); - out: - return; +} + +static void zfcp_sysfs_port_release(struct device *dev) +{ + kfree(container_of(dev, struct zfcp_port, sysfs_device)); } /** @@ -1294,684 +587,139 @@ 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 + * Returns: pointer to enqueued port on success, ERR_PTR on error * Locks: config_sema must be held to serialize changes to the port list * * All port internal structures are set up and the sysfs entry is generated. * d_id is used to enqueue ports with a well known address like the Directory * Service for nameserver lookup. */ -struct zfcp_port * -zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status, - u32 d_id) +struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, + u32 status, u32 d_id) { - struct zfcp_port *port, *tmp_port; - int check_wwpn; - scsi_id_t scsi_id; - int found; - - check_wwpn = !(status & ZFCP_STATUS_PORT_NO_WWPN); - - /* - * check that there is no port with this WWPN already in list - */ - if (check_wwpn) { - read_lock_irq(&zfcp_data.config_lock); - port = zfcp_get_port_by_wwpn(adapter, wwpn); - read_unlock_irq(&zfcp_data.config_lock); - if (port) - return NULL; - } + struct zfcp_port *port; + int retval; - port = kmalloc(sizeof (struct zfcp_port), GFP_KERNEL); + port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); if (!port) - return NULL; - memset(port, 0, sizeof (struct zfcp_port)); + return ERR_PTR(-ENOMEM); - /* initialise reference count stuff */ - atomic_set(&port->refcount, 0); init_waitqueue_head(&port->remove_wq); - INIT_LIST_HEAD(&port->unit_list_head); - INIT_LIST_HEAD(&port->unit_remove_lh); + INIT_WORK(&port->gid_pn_work, zfcp_erp_port_strategy_open_lookup); + INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); + INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); port->adapter = adapter; + port->d_id = d_id; + port->wwpn = wwpn; + port->rport_task = RPORT_NONE; - if (check_wwpn) - port->wwpn = wwpn; - - atomic_set_mask(status, &port->status); + /* mark port unusable as long as sysfs registration is not complete */ + atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status); + atomic_set(&port->refcount, 0); - /* 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); + dev_set_name(&port->sysfs_device, "0x%016llx", + (unsigned long long)wwpn); port->sysfs_device.parent = &adapter->ccw_device->dev; - } + port->sysfs_device.release = zfcp_sysfs_port_release; dev_set_drvdata(&port->sysfs_device, port); - /* mark port unusable as long as sysfs registration is not complete */ - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); - - if (device_register(&port->sysfs_device)) { - kfree(port); - return NULL; + read_lock_irq(&zfcp_data.config_lock); + if (zfcp_get_port_by_wwpn(adapter, wwpn)) { + read_unlock_irq(&zfcp_data.config_lock); + goto err_out_free; } + read_unlock_irq(&zfcp_data.config_lock); + + if (device_register(&port->sysfs_device)) + goto err_out_free; - if (zfcp_sysfs_port_create_files(&port->sysfs_device, status)) { + retval = sysfs_create_group(&port->sysfs_device.kobj, + &zfcp_sysfs_port_attrs); + + if (retval) { device_unregister(&port->sysfs_device); - return NULL; + goto err_out; } zfcp_port_get(port); - scsi_id = 1; - found = 0; write_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(tmp_port, &adapter->port_list_head, list) { - if (atomic_test_mask(ZFCP_STATUS_PORT_NO_SCSI_ID, - &tmp_port->status)) - continue; - if (tmp_port->scsi_id != scsi_id) { - found = 1; - break; - } - scsi_id++; - } - port->scsi_id = scsi_id; - if (found) - list_add_tail(&port->list, &tmp_port->list); - else - list_add_tail(&port->list, &adapter->port_list_head); + list_add_tail(&port->list, &adapter->port_list_head); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status); - if (d_id == ZFCP_DID_DIRECTORY_SERVICE) - if (!adapter->nameserver_port) - adapter->nameserver_port = port; - adapter->ports++; + write_unlock_irq(&zfcp_data.config_lock); zfcp_adapter_get(adapter); - return port; + +err_out_free: + kfree(port); +err_out: + return ERR_PTR(-EINVAL); } -void -zfcp_port_dequeue(struct zfcp_port *port) +/** + * zfcp_port_dequeue - dequeues a port from the port list of the adapter + * @port: pointer to struct zfcp_port which should be removed + */ +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) { + port->rport->dd_data = NULL; + port->rport = NULL; + } + wait_event(port->remove_wq, atomic_read(&port->refcount) == 0); + cancel_work_sync(&port->rport_work); /* usually not necessary */ zfcp_adapter_put(port->adapter); - zfcp_sysfs_port_remove_files(&port->sysfs_device, - atomic_read(&port->status)); + sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs); device_unregister(&port->sysfs_device); } -/* Enqueues a nameserver port */ -int -zfcp_nameserver_enqueue(struct zfcp_adapter *adapter) -{ - struct zfcp_port *port; - - port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_WKA, - ZFCP_DID_DIRECTORY_SERVICE); - if (!port) { - ZFCP_LOG_INFO("error: enqueue of nameserver port for " - "adapter %s failed\n", - zfcp_get_busid_by_adapter(adapter)); - return -ENXIO; - } - zfcp_port_put(port); - - return 0; -} - -#undef ZFCP_LOG_AREA - -/****************************************************************/ -/******* Fibre Channel Standard related Functions **************/ -/****************************************************************/ - -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FC - -void -zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, - struct fsf_status_read_buffer *status_buffer) -{ - struct fcp_rscn_head *fcp_rscn_head; - struct fcp_rscn_element *fcp_rscn_element; - struct zfcp_port *port; - u16 i; - u16 no_entries; - u32 range_mask; - unsigned long flags; - - fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload; - fcp_rscn_element = (struct fcp_rscn_element *) status_buffer->payload; - - /* see FC-FS */ - no_entries = (fcp_rscn_head->payload_len / 4); - - zfcp_in_els_dbf_event(adapter, "##rscn", status_buffer, - fcp_rscn_head->payload_len); - - debug_text_event(adapter->erp_dbf, 1, "unsol_els_rscn:"); - for (i = 1; i < no_entries; i++) { - /* skip head and start with 1st element */ - fcp_rscn_element++; - switch (fcp_rscn_element->addr_format) { - case ZFCP_PORT_ADDRESS: - ZFCP_LOG_FLAGS(1, "ZFCP_PORT_ADDRESS\n"); - range_mask = ZFCP_PORTS_RANGE_PORT; - break; - case ZFCP_AREA_ADDRESS: - ZFCP_LOG_FLAGS(1, "ZFCP_AREA_ADDRESS\n"); - range_mask = ZFCP_PORTS_RANGE_AREA; - break; - case ZFCP_DOMAIN_ADDRESS: - ZFCP_LOG_FLAGS(1, "ZFCP_DOMAIN_ADDRESS\n"); - range_mask = ZFCP_PORTS_RANGE_DOMAIN; - break; - case ZFCP_FABRIC_ADDRESS: - ZFCP_LOG_FLAGS(1, "ZFCP_FABRIC_ADDRESS\n"); - range_mask = ZFCP_PORTS_RANGE_FABRIC; - break; - default: - ZFCP_LOG_INFO("incoming RSCN with unknown " - "address format\n"); - continue; - } - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &adapter->port_list_head, list) { - if (atomic_test_mask - (ZFCP_STATUS_PORT_WKA, &port->status)) - continue; - /* Do we know this port? If not skip it. */ - if (!atomic_test_mask - (ZFCP_STATUS_PORT_DID_DID, &port->status)) { - ZFCP_LOG_INFO("incoming RSCN, trying to open " - "port 0x%016Lx\n", port->wwpn); - debug_text_event(adapter->erp_dbf, 1, - "unsol_els_rscnu:"); - zfcp_erp_port_reopen(port, - ZFCP_STATUS_COMMON_ERP_FAILED); - continue; - } - - /* - * FIXME: race: d_id might being invalidated - * (...DID_DID reset) - */ - if ((port->d_id & range_mask) - == (fcp_rscn_element->nport_did & range_mask)) { - ZFCP_LOG_TRACE("reopen did 0x%08x\n", - fcp_rscn_element->nport_did); - /* - * Unfortunately, an RSCN does not specify the - * type of change a target underwent. We assume - * that it makes sense to reopen the link. - * FIXME: Shall we try to find out more about - * the target and link state before closing it? - * How to accomplish this? (nameserver?) - * Where would such code be put in? - * (inside or outside erp) - */ - ZFCP_LOG_INFO("incoming RSCN, trying to open " - "port 0x%016Lx\n", port->wwpn); - debug_text_event(adapter->erp_dbf, 1, - "unsol_els_rscnk:"); - zfcp_test_link(port); - } - } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); - } -} - -static void -zfcp_fsf_incoming_els_plogi(struct zfcp_adapter *adapter, - struct fsf_status_read_buffer *status_buffer) -{ - logi *els_logi = (logi *) status_buffer->payload; - struct zfcp_port *port; - unsigned long flags; - - zfcp_in_els_dbf_event(adapter, "##plogi", status_buffer, 28); - - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &adapter->port_list_head, list) { - if (port->wwpn == (*(wwn_t *) & els_logi->nport_wwn)) - break; - } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); - - if (!port || (port->wwpn != (*(wwn_t *) & els_logi->nport_wwn))) { - ZFCP_LOG_DEBUG("ignored incoming PLOGI for nonexisting port " - "with d_id 0x%08x on adapter %s\n", - status_buffer->d_id, - zfcp_get_busid_by_adapter(adapter)); - } else { - debug_text_event(adapter->erp_dbf, 1, "unsol_els_plogi:"); - debug_event(adapter->erp_dbf, 1, &els_logi->nport_wwn, 8); - zfcp_erp_port_forced_reopen(port, 0); - } -} - -static void -zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter, - struct fsf_status_read_buffer *status_buffer) -{ - struct fcp_logo *els_logo = (struct fcp_logo *) status_buffer->payload; - struct zfcp_port *port; - unsigned long flags; - - zfcp_in_els_dbf_event(adapter, "##logo", status_buffer, 16); - - read_lock_irqsave(&zfcp_data.config_lock, flags); - list_for_each_entry(port, &adapter->port_list_head, list) { - if (port->wwpn == els_logo->nport_wwpn) - break; - } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); - - if (!port || (port->wwpn != els_logo->nport_wwpn)) { - ZFCP_LOG_DEBUG("ignored incoming LOGO for nonexisting port " - "with d_id 0x%08x on adapter %s\n", - status_buffer->d_id, - zfcp_get_busid_by_adapter(adapter)); - } else { - debug_text_event(adapter->erp_dbf, 1, "unsol_els_logo:"); - debug_event(adapter->erp_dbf, 1, &els_logo->nport_wwpn, 8); - zfcp_erp_port_forced_reopen(port, 0); - } -} - -static void -zfcp_fsf_incoming_els_unknown(struct zfcp_adapter *adapter, - struct fsf_status_read_buffer *status_buffer) -{ - zfcp_in_els_dbf_event(adapter, "##undef", status_buffer, 24); - ZFCP_LOG_NORMAL("warning: unknown incoming ELS 0x%08x " - "for adapter %s\n", *(u32 *) (status_buffer->payload), - zfcp_get_busid_by_adapter(adapter)); - -} - -void -zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req) -{ - struct fsf_status_read_buffer *status_buffer; - u32 els_type; - struct zfcp_adapter *adapter; - - status_buffer = fsf_req->data.status_read.buffer; - els_type = *(u32 *) (status_buffer->payload); - adapter = fsf_req->adapter; - - if (els_type == LS_PLOGI) - zfcp_fsf_incoming_els_plogi(adapter, status_buffer); - else if (els_type == LS_LOGO) - zfcp_fsf_incoming_els_logo(adapter, status_buffer); - else if ((els_type & 0xffff0000) == LS_RSCN) - /* we are only concerned with the command, not the length */ - zfcp_fsf_incoming_els_rscn(adapter, status_buffer); - else - zfcp_fsf_incoming_els_unknown(adapter, status_buffer); -} - - -/** - * zfcp_gid_pn_buffers_alloc - allocate buffers for GID_PN nameserver request - * @gid_pn: pointer to return pointer to struct zfcp_gid_pn_data - * @pool: pointer to mempool_t if non-null memory pool is used for allocation - */ -static int -zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool) -{ - struct zfcp_gid_pn_data *data; - - if (pool != NULL) { - data = mempool_alloc(pool, GFP_ATOMIC); - if (likely(data != NULL)) { - data->ct.pool = pool; - } - } else { - data = kmalloc(sizeof(struct zfcp_gid_pn_data), GFP_ATOMIC); - } - - if (NULL == data) - return -ENOMEM; - - memset(data, 0, sizeof(*data)); - data->ct.req = &data->req; - data->ct.resp = &data->resp; - data->ct.req_count = data->ct.resp_count = 1; - zfcp_address_to_sg(&data->ct_iu_req, &data->req); - zfcp_address_to_sg(&data->ct_iu_resp, &data->resp); - data->req.length = sizeof(struct ct_iu_gid_pn_req); - data->resp.length = sizeof(struct ct_iu_gid_pn_resp); - - *gid_pn = data; - return 0; -} - -/** - * zfcp_gid_pn_buffers_free - free buffers for GID_PN nameserver request - * @gid_pn: pointer to struct zfcp_gid_pn_data which has to be freed - */ -static void -zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn) -{ - if ((gid_pn->ct.pool != 0)) - mempool_free(gid_pn, gid_pn->ct.pool); - else - kfree(gid_pn); - - return; -} - -/** - * zfcp_ns_gid_pn_request - initiate GID_PN nameserver request - * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed - */ -int -zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action) -{ - int ret; - struct ct_iu_gid_pn_req *ct_iu_req; - struct zfcp_gid_pn_data *gid_pn; - struct zfcp_adapter *adapter = erp_action->adapter; - - ret = zfcp_gid_pn_buffers_alloc(&gid_pn, adapter->pool.data_gid_pn); - if (ret < 0) { - ZFCP_LOG_INFO("error: buffer allocation for gid_pn nameserver " - "request failed for adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - goto out; - } - - /* setup nameserver request */ - ct_iu_req = zfcp_sg_to_address(gid_pn->ct.req); - ct_iu_req->header.revision = ZFCP_CT_REVISION; - ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE; - ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER; - ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS; - ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GID_PN; - ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE; - ct_iu_req->wwpn = erp_action->port->wwpn; - - /* setup parameters for send generic command */ - gid_pn->ct.port = adapter->nameserver_port; - gid_pn->ct.handler = zfcp_ns_gid_pn_handler; - gid_pn->ct.handler_data = (unsigned long) gid_pn; - gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT; - gid_pn->ct.timer = &erp_action->timer; - gid_pn->port = erp_action->port; - - ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp, - erp_action); - if (ret) { - ZFCP_LOG_INFO("error: initiation of gid_pn nameserver request " - "failed for adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - - zfcp_gid_pn_buffers_free(gid_pn); - } - - out: - return ret; -} - -/** - * zfcp_ns_gid_pn_handler - handler for GID_PN nameserver request - * @data: unsigned long, contains pointer to struct zfcp_gid_pn_data - */ -static void zfcp_ns_gid_pn_handler(unsigned long data) -{ - struct zfcp_port *port; - struct zfcp_send_ct *ct; - struct ct_iu_gid_pn_req *ct_iu_req; - struct ct_iu_gid_pn_resp *ct_iu_resp; - struct zfcp_gid_pn_data *gid_pn; - - - gid_pn = (struct zfcp_gid_pn_data *) data; - port = gid_pn->port; - ct = &gid_pn->ct; - ct_iu_req = zfcp_sg_to_address(ct->req); - ct_iu_resp = zfcp_sg_to_address(ct->resp); - - if ((ct->status != 0) || zfcp_check_ct_response(&ct_iu_resp->header)) { - /* FIXME: do we need some specific erp entry points */ - atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status); - goto failed; - } - /* paranoia */ - if (ct_iu_req->wwpn != port->wwpn) { - ZFCP_LOG_NORMAL("bug: wwpn 0x%016Lx returned by nameserver " - "lookup does not match expected wwpn 0x%016Lx " - "for adapter %s\n", ct_iu_req->wwpn, port->wwpn, - zfcp_get_busid_by_port(port)); - goto mismatch; - } - - /* looks like a valid d_id */ - port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK; - atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status); - ZFCP_LOG_DEBUG("adapter %s: wwpn=0x%016Lx ---> d_id=0x%08x\n", - zfcp_get_busid_by_port(port), port->wwpn, port->d_id); - goto out; - - mismatch: - ZFCP_LOG_DEBUG("CT IUs do not match:\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_req, - sizeof(struct ct_iu_gid_pn_req)); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_resp, - sizeof(struct ct_iu_gid_pn_resp)); - - failed: - ZFCP_LOG_NORMAL("warning: failed gid_pn nameserver request for wwpn " - "0x%016Lx for adapter %s\n", - port->wwpn, zfcp_get_busid_by_port(port)); - out: - zfcp_gid_pn_buffers_free(gid_pn); - return; -} - -/* reject CT_IU reason codes acc. to FC-GS-4 */ -static const struct zfcp_rc_entry zfcp_ct_rc[] = { - {0x01, "invalid command code"}, - {0x02, "invalid version level"}, - {0x03, "logical error"}, - {0x04, "invalid CT_IU size"}, - {0x05, "logical busy"}, - {0x07, "protocol error"}, - {0x09, "unable to perform command request"}, - {0x0b, "command not supported"}, - {0x0d, "server not available"}, - {0x0e, "session could not be established"}, - {0xff, "vendor specific error"}, - {0, NULL}, -}; - -/* LS_RJT reason codes acc. to FC-FS */ -static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = { - {0x01, "invalid LS_Command code"}, - {0x03, "logical error"}, - {0x05, "logical busy"}, - {0x07, "protocol error"}, - {0x09, "unable to perform command request"}, - {0x0b, "command not supported"}, - {0x0e, "command already in progress"}, - {0xff, "vendor specific error"}, - {0, NULL}, -}; - -/* reject reason codes according to FC-PH/FC-FS */ -static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = { - {0x01, "invalid D_ID"}, - {0x02, "invalid S_ID"}, - {0x03, "Nx_Port not available, temporary"}, - {0x04, "Nx_Port not available, permament"}, - {0x05, "class not supported"}, - {0x06, "delimiter usage error"}, - {0x07, "TYPE not supported"}, - {0x08, "invalid Link_Control"}, - {0x09, "invalid R_CTL field"}, - {0x0a, "invalid F_CTL field"}, - {0x0b, "invalid OX_ID"}, - {0x0c, "invalid RX_ID"}, - {0x0d, "invalid SEQ_ID"}, - {0x0e, "invalid DF_CTL"}, - {0x0f, "invalid SEQ_CNT"}, - {0x10, "invalid parameter field"}, - {0x11, "exchange error"}, - {0x12, "protocol error"}, - {0x13, "incorrect length"}, - {0x14, "unsupported ACK"}, - {0x15, "class of service not supported by entity at FFFFFE"}, - {0x16, "login required"}, - {0x17, "excessive sequences attempted"}, - {0x18, "unable to establish exchange"}, - {0x1a, "fabric path not available"}, - {0x1b, "invalid VC_ID (class 4)"}, - {0x1c, "invalid CS_CTL field"}, - {0x1d, "insufficient resources for VC (class 4)"}, - {0x1f, "invalid class of service"}, - {0x20, "preemption request rejected"}, - {0x21, "preemption not enabled"}, - {0x22, "multicast error"}, - {0x23, "multicast error terminate"}, - {0x24, "process login required"}, - {0xff, "vendor specific reject"}, - {0, NULL}, -}; - /** - * zfcp_rc_description - return description for given reaon code - * @code: reason code - * @rc_table: table of reason codes and descriptions + * 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 */ -static inline const char * -zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table) +void zfcp_sg_free_table(struct scatterlist *sg, int count) { - 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 inline void -zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par, - const struct zfcp_rc_entry *rc_table) -{ - ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n", - zfcp_rc_description(rjt_par->reason_code, rc_table), - (u32) rjt_par->action, (u32) rjt_par->reason_code, - (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique); -} - -/** - * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject - * @sq: status qualifier word - * @rjt_par: reject parameter as described in FC-PH and FC-FS - * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else - */ -int -zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par) -{ - int ret = -EIO; - - if (sq == FSF_IOSTAT_NPORT_RJT) { - ZFCP_LOG_INFO("ELS rejected (P_RJT)\n"); - zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc); - /* invalid d_id */ - if (rjt_par->reason_code == 0x01) - ret = -EREMCHG; - } else if (sq == FSF_IOSTAT_FABRIC_RJT) { - ZFCP_LOG_INFO("ELS rejected (F_RJT)\n"); - zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc); - /* invalid d_id */ - if (rjt_par->reason_code == 0x01) - ret = -EREMCHG; - } else if (sq == FSF_IOSTAT_LS_RJT) { - ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n"); - zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc); - ret = -EREMOTEIO; - } else - ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq); - - return ret; + return 0; } - -#undef ZFCP_LOG_AREA