X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fscsi%2Fzfcp_aux.c;h=85093b71f9fa52ebc9f0f95716c16f840111a926;hb=refs%2Fheads%2Fvserver;hp=4f4fd5a8eea7aebe2b0bb9724bf896428d060a60;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 4f4fd5a8e..85093b71f 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -1,17 +1,8 @@ /* + * This file is part of the zfcp device driver for + * FCP adapters for IBM System z9 and zSeries. * - * linux/drivers/s390/scsi/zfcp_aux.c - * - * FCP adapter driver for IBM eServer zSeries - * - * (C) Copyright IBM Corp. 2002, 2004 - * - * Author(s): Martin Peschke - * Raimund Schroeder - * Aron Zeh - * Wolfgang Taphorn - * Stefan Bader - * Heiko Carstens + * (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 @@ -28,8 +19,19 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_AUX_REVISION "$Revision: 1.108 $" +/* + * Driver authors: + * Martin Peschke (originator of the driver) + * Raimund Schroeder + * Aron Zeh + * Wolfgang Taphorn + * Stefan Bader + * Heiko Carstens (kernel 2.6 port of the driver) + * Andreas Herrmann + * Maxim Shchetynin + * Volker Sameske + * Ralph Wuerthner + */ #include "zfcp_ext.h" @@ -41,33 +43,29 @@ static char *device; /* written against the module interface */ static int __init zfcp_module_init(void); -int zfcp_reboot_handler(struct notifier_block *, unsigned long, 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 int zfcp_sg_list_free(struct zfcp_sg_list *); -static inline int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *, void *, - size_t); -static inline int zfcp_sg_list_copy_to_user(void *, 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); +static long zfcp_cfdc_dev_ioctl(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_S390_SUPPORT -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 + .unlocked_ioctl = zfcp_cfdc_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = zfcp_cfdc_dev_ioctl +#endif }; static struct miscdevice zfcp_cfdc_misc = { @@ -81,30 +79,20 @@ static struct miscdevice zfcp_cfdc_misc = { /* declare driver module init/cleanup functions */ module_init(zfcp_module_init); -MODULE_AUTHOR("Heiko Carstens , " - "Martin Peschke , " - "Raimund Schroeder , " - "Wolfgang Taphorn , " - "Aron Zeh , " - "IBM Deutschland Entwicklung GmbH"); +MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com"); MODULE_DESCRIPTION - ("FCP (SCSI over Fibre Channel) HBA driver for IBM eServer zSeries"); + ("FCP (SCSI over Fibre Channel) HBA driver for IBM System z9 and zSeries"); MODULE_LICENSE("GPL"); -module_param(device, charp, 0); +module_param(device, charp, 0400); MODULE_PARM_DESC(device, "specify initial device"); -module_param(loglevel, uint, 0); +module_param(loglevel, uint, 0400); MODULE_PARM_DESC(loglevel, "log levels, 8 nibbles: " - "(unassigned) ERP QDIO DIO Config FSF SCSI Other, " + "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 ***********************/ /****************************************************************/ @@ -124,101 +112,115 @@ _zfcp_hex_dump(char *addr, int count) printk("\n"); } + /****************************************************************/ -/************** Uncategorised Functions *************************/ +/****** Functions to handle the request ID hash table ********/ /****************************************************************/ -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER +#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF -static inline int -zfcp_fsf_req_is_scsi_cmnd(struct zfcp_fsf_req *fsf_req) +static int zfcp_reqlist_init(struct zfcp_adapter *adapter) { - return ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) && - !(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)); + int i; + + adapter->req_list = kcalloc(REQUEST_LIST_SIZE, sizeof(struct list_head), + GFP_KERNEL); + + if (!adapter->req_list) + return -ENOMEM; + + for (i=0; ireq_list[i]); + + return 0; } -void -zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req, - void *add_data, int add_length) +static void zfcp_reqlist_free(struct zfcp_adapter *adapter) { - struct zfcp_adapter *adapter = fsf_req->adapter; - struct scsi_cmnd *scsi_cmnd; - int level = 3; - int i; - unsigned long flags; + struct zfcp_fsf_req *request, *tmp; + unsigned int i; - write_lock_irqsave(&adapter->cmd_dbf_lock, flags); - if (zfcp_fsf_req_is_scsi_cmnd(fsf_req)) { - scsi_cmnd = fsf_req->data.send_fcp_command_task.scsi_cmnd; - debug_text_event(adapter->cmd_dbf, level, "fsferror"); - debug_text_event(adapter->cmd_dbf, level, text); - debug_event(adapter->cmd_dbf, level, &fsf_req, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, - sizeof (u32)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd, - sizeof (unsigned long)); - 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)); + for (i=0; ireq_list[i])) + continue; + + list_for_each_entry_safe(request, tmp, + &adapter->req_list[i], list) + list_del(&request->list); } - write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags); + + kfree(adapter->req_list); } -/* 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) +void zfcp_reqlist_add(struct zfcp_adapter *adapter, + struct zfcp_fsf_req *fsf_req) { - 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; + unsigned int i; - adapter = (struct zfcp_adapter *) scsi_cmnd->device->host->hostdata[0]; - req_data = (union zfcp_req_data *) scsi_cmnd->host_scribble; - fsf_req = (req_data ? req_data->send_fcp_command_task.fsf_req : NULL); - write_lock_irqsave(&adapter->cmd_dbf_lock, flags); - debug_text_event(adapter->cmd_dbf, level, "hostbyte"); - debug_text_event(adapter->cmd_dbf, level, text); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof (u32)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd, - sizeof (unsigned long)); - 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, ""); + i = fsf_req->req_id % REQUEST_LIST_SIZE; + list_add_tail(&fsf_req->list, &adapter->req_list[i]); +} + +void zfcp_reqlist_remove(struct zfcp_adapter *adapter, unsigned long req_id) +{ + struct zfcp_fsf_req *request, *tmp; + unsigned int i, counter; + u64 dbg_tmp[2]; + + i = req_id % REQUEST_LIST_SIZE; + BUG_ON(list_empty(&adapter->req_list[i])); + + counter = 0; + list_for_each_entry_safe(request, tmp, &adapter->req_list[i], list) { + if (request->req_id == req_id) { + dbg_tmp[0] = (u64) atomic_read(&adapter->reqs_active); + dbg_tmp[1] = (u64) counter; + debug_event(adapter->erp_dbf, 4, (void *) dbg_tmp, 16); + list_del(&request->list); + break; + } + counter++; } - write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags); } -void -zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text, - struct fsf_status_read_buffer *status_buffer, int length) +struct zfcp_fsf_req *zfcp_reqlist_ismember(struct zfcp_adapter *adapter, + unsigned long req_id) { - int level = 1; - int i; + struct zfcp_fsf_req *request, *tmp; + unsigned int i; + + /* 0 is reserved as an invalid req_id */ + if (req_id == 0) + return NULL; + + i = req_id % REQUEST_LIST_SIZE; - 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)); + list_for_each_entry_safe(request, tmp, &adapter->req_list[i], list) + if (request->req_id == req_id) + return request; + + return NULL; +} + +int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) +{ + unsigned int i; + + for (i=0; ireq_list[i])) + return 0; + + return 1; } +#undef ZFCP_LOG_AREA + +/****************************************************************/ +/************** Uncategorised Functions *************************/ +/****************************************************************/ + +#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER + /** * zfcp_device_setup - setup function * @str: pointer to parameter string @@ -226,13 +228,20 @@ zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text, * Parse "device=..." parameter string. */ static int __init -zfcp_device_setup(char *str) +zfcp_device_setup(char *devstr) { - char *tmp; + char *tmp, *str; + size_t len; - if (!str) + if (!devstr) return 0; + len = strlen(devstr) + 1; + str = kmalloc(len, GFP_KERNEL); + if (!str) + goto err_out; + memcpy(str, devstr, len); + tmp = strchr(str, ','); if (!tmp) goto err_out; @@ -249,34 +258,32 @@ zfcp_device_setup(char *str) zfcp_data.init_fcp_lun = simple_strtoull(tmp, &tmp, 0); if (*tmp != '\0') 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) { - int found = 0; struct zfcp_adapter *adapter; struct zfcp_port *port; struct zfcp_unit *unit; down(&zfcp_data.config_sema); read_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) - if (strcmp(zfcp_data.init_busid, - zfcp_get_busid_by_adapter(adapter)) == 0) { - zfcp_adapter_get(adapter); - found = 1; - break; - } + adapter = zfcp_get_adapter_by_busid(zfcp_data.init_busid); + if (adapter) + zfcp_adapter_get(adapter); read_unlock_irq(&zfcp_data.config_lock); - if (!found) + + if (adapter == NULL) goto out_adapter; - port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0); + 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); @@ -284,7 +291,7 @@ zfcp_init_device_configure(void) goto out_unit; up(&zfcp_data.config_sema); ccw_device_set_online(adapter->ccw_device); - wait_event(unit->scsi_add_wq, atomic_read(&unit->scsi_add_work) == 0); + zfcp_erp_wait(adapter); down(&zfcp_data.config_sema); zfcp_unit_put(unit); out_unit: @@ -296,11 +303,45 @@ zfcp_init_device_configure(void) return; } +static int calc_alignment(int size) +{ + int align = 1; + + if (!size) + return 0; + + while ((size - align) > 0) + align <<= 1; + + return align; +} + 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, NULL); + if (!zfcp_data.fsf_req_qtcb_cache) + goto out; - int retval = 0; + 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, 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, NULL); + if (!zfcp_data.gid_pn_cache) + goto out_gid_cache; atomic_set(&zfcp_data.loglevel, loglevel); @@ -310,36 +351,27 @@ zfcp_module_init(void) /* initialize adapters to be removed list head */ INIT_LIST_HEAD(&zfcp_data.adapter_remove_lh); -#ifdef CONFIG_S390_SUPPORT - 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_ioctl32; - } -#endif + 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_register; - } else { - ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n", - ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor); + goto out_misc; } + 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); - zfcp_data.reboot_notifier.notifier_call = zfcp_reboot_handler; - register_reboot_notifier(&zfcp_data.reboot_notifier); - - /* save address of data structure managing the driver module */ - zfcp_data.scsi_host_template.module = THIS_MODULE; - /* setup dynamic I/O */ retval = zfcp_ccw_register(); if (retval) { @@ -353,35 +385,19 @@ zfcp_module_init(void) goto out; out_ccw_register: - unregister_reboot_notifier(&zfcp_data.reboot_notifier); misc_deregister(&zfcp_cfdc_misc); - out_misc_register: -#ifdef CONFIG_S390_SUPPORT - unregister_ioctl32_conversion(zfcp_ioctl_trans.cmd); - out_ioctl32: -#endif - + 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; } -/* - * This function is called automatically by the kernel whenever a reboot or a - * shut-down is initiated and zfcp is still loaded - * - * locks: zfcp_data.config_sema is taken prior to shutting down the module - * and removing all structures - * returns: NOTIFY_DONE in all cases - */ -int -zfcp_reboot_handler(struct notifier_block *notifier, unsigned long code, - void *ptr) -{ - zfcp_ccw_unregister(); - return NOTIFY_DONE; -} - - /* * function: zfcp_cfdc_dev_ioctl * @@ -396,12 +412,13 @@ zfcp_reboot_handler(struct notifier_block *notifier, unsigned long code, * -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 long +zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command, + unsigned long buffer) { - struct zfcp_cfdc_sense_data sense_data, *sense_data_user; + 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; @@ -409,12 +426,17 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, char *bus_id = NULL; int retval = 0; - sg_list = kmalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL); + 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; } - sg_list->count = 0; if (command != ZFCP_CFDC_IOC) { ZFCP_LOG_INFO("IOC request code 0x%x invalid\n", command); @@ -422,27 +444,27 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, goto out; } - if ((sense_data_user = (struct zfcp_cfdc_sense_data*)buffer) == NULL) { + 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, + 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) { + 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) { + switch (sense_data->command) { case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL: fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; @@ -471,7 +493,7 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, default: ZFCP_LOG_INFO("invalid command code 0x%08x\n", - sense_data.command); + sense_data->command); retval = -EINVAL; goto out; } @@ -482,30 +504,25 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, goto out; } snprintf(bus_id, BUS_ID_SIZE, "%d.%d.%04x", - (sense_data.devno >> 24), - (sense_data.devno >> 16) & 0xFF, - (sense_data.devno & 0xFFFF)); + (sense_data->devno >> 24), + (sense_data->devno >> 16) & 0xFF, + (sense_data->devno & 0xFFFF)); - retval = -ENXIO; read_lock_irq(&zfcp_data.config_lock); - 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) { - zfcp_adapter_get(adapter); - retval = 0; - break; - } - } + 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 (retval != 0) { + if (adapter == NULL) { ZFCP_LOG_INFO("invalid adapter\n"); + retval = -ENXIO; goto out; } - if (sense_data.command & ZFCP_CFDC_WITH_CONTROL_FILE) { + if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) { retval = zfcp_sg_list_alloc(sg_list, ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); if (retval) { @@ -514,8 +531,8 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, } } - if ((sense_data.command & ZFCP_CFDC_DOWNLOAD) && - (sense_data.command & ZFCP_CFDC_WITH_CONTROL_FILE)) { + 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); @@ -525,34 +542,31 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, } } - retval = zfcp_fsf_control_file( - adapter, &fsf_req, fsf_command, option, sg_list); - if (retval == -EOPNOTSUPP) { - ZFCP_LOG_INFO("adapter does not support cfdc\n"); + retval = zfcp_fsf_control_file(adapter, &fsf_req, fsf_command, + option, sg_list); + if (retval) goto out; - } else if (retval != 0) { - ZFCP_LOG_INFO("initiation of cfdc up/download failed\n"); - retval = -EPERM; + + 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; } - wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); - - sense_data.fsf_status = fsf_req->qtcb->header.fsf_status; - memcpy(&sense_data.fsf_status_qual, + 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); + memcpy(&sense_data->payloads, &fsf_req->qtcb->bottom.support.els, 256); - retval = copy_to_user(sense_data_user, &sense_data, + 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) { + 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); @@ -564,7 +578,7 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, out: if (fsf_req != NULL) - zfcp_fsf_req_cleanup(fsf_req); + zfcp_fsf_req_free(fsf_req); if ((adapter != NULL) && (retval != -ENXIO)) zfcp_adapter_put(adapter); @@ -574,17 +588,22 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, kfree(sg_list); } + kfree(sense_data); + return retval; } -/* - * function: zfcp_sg_list_alloc - * - * purpose: Create a scatter-gather list of the specified size +/** + * 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 * - * returns: 0 - Scatter gather list is created - * -ENOMEM - Insufficient memory (*list_ptr is then set to NULL) + * 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) @@ -592,13 +611,17 @@ zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) struct scatterlist *sg; unsigned int i; int retval = 0; + void *address; + + BUG_ON(sg_list == NULL); sg_list->count = size >> PAGE_SHIFT; if (size & ~PAGE_MASK) sg_list->count++; - sg_list->sg = kmalloc(sg_list->count * sizeof(struct scatterlist), + 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; } @@ -606,13 +629,14 @@ zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) { sg->length = min(size, PAGE_SIZE); sg->offset = 0; - sg->page = alloc_pages(GFP_KERNEL, 0); - if (sg->page == NULL) { + 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; } @@ -621,39 +645,61 @@ zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) } -/* - * function: zfcp_sg_list_free - * - * purpose: Destroy a scatter-gather list and release memory +/** + * zfcp_sg_list_free - free memory of a scatter-gather list + * @sg_list: structure describing a scatter-gather list * - * returns: Always 0 + * 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 int +static inline void zfcp_sg_list_free(struct zfcp_sg_list *sg_list) { struct scatterlist *sg; unsigned int i; - int retval = 0; - BUG_ON((sg_list->sg == NULL) || (sg_list == NULL)); + BUG_ON(sg_list == NULL); for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) - __free_pages(sg->page, 0); + free_page((unsigned long) zfcp_sg_to_address(sg)); - return retval; + sg_list->count = 0; + kfree(sg_list->sg); } +/** + * zfcp_sg_size - determine size of a scatter-gather list + * @sg: array of (struct scatterlist) + * @sg_count: elements in array + * Return: size of entire scatter-gather list + */ +size_t +zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count) +{ + unsigned int i; + struct scatterlist *p; + size_t size; + + size = 0; + for (i = 0, p = sg; i < sg_count; i++, p++) { + BUG_ON(p == NULL); + size += p->length; + } -/* - * function: zfcp_sg_list_copy_from_user - * - * purpose: Copy data from user space memory to the scatter-gather list - * - * returns: 0 - The data has been copied from user - * -EFAULT - Memory I/O operation fault + 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_buffer, +zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, + void __user *user_buffer, size_t size) { struct scatterlist *sg; @@ -661,10 +707,14 @@ zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, void *user_buffer, 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 = (void*) - ((page_to_pfn(sg->page) << PAGE_SHIFT) + sg->offset); + zfcp_buffer = zfcp_sg_to_address(sg); if (copy_from_user(zfcp_buffer, user_buffer, length)) { retval = -EFAULT; goto out; @@ -678,16 +728,16 @@ zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, void *user_buffer, } -/* - * function: zfcp_sg_list_copy_to_user - * - * purpose: Copy data from the scatter-gather list to user space memory - * - * returns: 0 - The data has been copied to user - * -EFAULT - Memory I/O operation fault +/** + * 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_buffer, struct zfcp_sg_list *sg_list, +zfcp_sg_list_copy_to_user(void __user *user_buffer, + struct zfcp_sg_list *sg_list, size_t size) { struct scatterlist *sg; @@ -695,10 +745,14 @@ zfcp_sg_list_copy_to_user(void *user_buffer, struct zfcp_sg_list *sg_list, 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 = (void*) - ((page_to_pfn(sg->page) << PAGE_SHIFT) + sg->offset); + 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; @@ -721,13 +775,12 @@ zfcp_sg_list_copy_to_user(void *user_buffer, struct zfcp_sg_list *sg_list, #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG /** - * zfcp_get_unit_by_lun - find unit in unit list of port by fcp lun + * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN * @port: pointer to port to search for unit - * @fcp_lun: lun to search for - * Traverses list of all units of a port and returns pointer to a unit - * if lun of a unit matches. + * @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) { @@ -746,13 +799,12 @@ zfcp_get_unit_by_lun(struct zfcp_port *port, fcp_lun_t fcp_lun) } /** - * zfcp_get_port_by_wwpn - find unit in unit list of port by fcp lun + * 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 - * Traverses list of all ports of an adapter and returns a pointer to a port - * if wwpn of a port matches. + * 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) { @@ -761,6 +813,30 @@ zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn) list_for_each_entry(port, &adapter->port_list_head, list) { if ((port->wwpn == wwpn) && + !(atomic_read(&port->status) & + (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE))) { + found = 1; + break; + } + } + return found ? port : NULL; +} + +/** + * zfcp_get_port_by_did - find port in port list of adapter by d_id + * @adapter: pointer to adapter to search for port + * @d_id: d_id to search for + * Traverse list of all ports of an adapter and return pointer to a port + * with the given d_id. + */ +struct zfcp_port * +zfcp_get_port_by_did(struct zfcp_adapter *adapter, u32 d_id) +{ + struct zfcp_port *port; + int found = 0; + + list_for_each_entry(port, &adapter->port_list_head, list) { + if ((port->d_id == d_id) && !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) { found = 1; @@ -770,20 +846,44 @@ zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn) return found ? port : NULL; } -/* - * Enqueues a logical unit at the end of the unit list associated with the - * specified port. Also sets up some unit internal structures. +/** + * zfcp_get_adapter_by_busid - find adpater in adapter list by bus_id + * @bus_id: bus_id to search for + * Traverse list of all adapters and return pointer to an adapter + * with the given bus_id. + */ +struct zfcp_adapter * +zfcp_get_adapter_by_busid(char *bus_id) +{ + struct zfcp_adapter *adapter; + int found = 0; + + list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) { + if ((strncmp(bus_id, zfcp_get_busid_by_adapter(adapter), + BUS_ID_SIZE) == 0) && + !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, + &adapter->status)){ + found = 1; + break; + } + } + return found ? adapter : NULL; +} + +/** + * zfcp_unit_enqueue - enqueue unit to unit list of a port. + * @port: pointer to port where unit is added + * @fcp_lun: FCP LUN of unit to be enqueued + * Return: pointer to enqueued unit on success, NULL on error + * Locks: config_sema must be held to serialize changes to the unit list * - * returns: pointer to unit with a usecount of 1 if a new unit was - * successfully enqueued - * NULL otherwise - * locks: config_sema must be held to serialise changes to the unit list + * Sets up some unit internal structures and creates sysfs entry. */ struct zfcp_unit * zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun) { struct zfcp_unit *unit, *tmp_unit; - scsi_lun_t scsi_lun; + unsigned int scsi_lun; int found; /* @@ -797,12 +897,10 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun) if (unit) return NULL; - 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)); - init_waitqueue_head(&unit->scsi_add_wq); /* initialise reference count stuff */ atomic_set(&unit->refcount, 0); init_waitqueue_head(&unit->remove_wq); @@ -869,18 +967,6 @@ zfcp_unit_dequeue(struct zfcp_unit *unit) device_unregister(&unit->sysfs_device); } -static void * -zfcp_mempool_alloc(int gfp_mask, void *size) -{ - return kmalloc((size_t) size, gfp_mask); -} - -static void -zfcp_mempool_free(void *element, void *size) -{ - kfree(element); -} - /* * Allocates a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI * commands. @@ -893,51 +979,39 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) { adapter->pool.fsf_req_erp = - mempool_create(ZFCP_POOL_FSF_REQ_ERP_NR, - zfcp_mempool_alloc, zfcp_mempool_free, (void *) - sizeof(struct zfcp_fsf_req_pool_element)); - - if (NULL == adapter->pool.fsf_req_erp) + mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ERP_NR, + 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(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(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(ZFCP_POOL_FSF_REQ_ABORT_NR, + 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(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(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(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(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(ZFCP_POOL_DATA_GID_PN_NR, + zfcp_data.gid_pn_cache); + if (!adapter->pool.data_gid_pn) return -ENOMEM; return 0; @@ -965,77 +1039,10 @@ zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) mempool_destroy(adapter->pool.data_gid_pn); } -/** - * zfcp_adapter_debug_register - registers debug feature for an adapter - * @adapter: pointer to adapter for which debug features should be registered - * return: -ENOMEM on error, 0 otherwise - */ -int -zfcp_adapter_debug_register(struct zfcp_adapter *adapter) -{ - char dbf_name[20]; - - /* debug feature area which records SCSI command failures (hostbyte) */ - rwlock_init(&adapter->cmd_dbf_lock); - sprintf(dbf_name, ZFCP_CMD_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->cmd_dbf = debug_register(dbf_name, - ZFCP_CMD_DBF_INDEX, - ZFCP_CMD_DBF_AREAS, - ZFCP_CMD_DBF_LENGTH); - 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 SCSI command aborts */ - sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->in_els_dbf = debug_register(dbf_name, - ZFCP_IN_ELS_DBF_INDEX, - ZFCP_IN_ELS_DBF_AREAS, - ZFCP_IN_ELS_DBF_LENGTH); - 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) - return 0; - - zfcp_adapter_debug_unregister(adapter); - return -ENOMEM; -} - -/** - * 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) +zfcp_dummy_release(struct device *dev) { - debug_unregister(adapter->erp_dbf); - debug_unregister(adapter->cmd_dbf); - debug_unregister(adapter->abort_dbf); - debug_unregister(adapter->in_els_dbf); + return; } /* @@ -1060,13 +1067,12 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) */ /* try to allocate new adapter data structure (zeroed) */ - adapter = kmalloc(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; } - memset(adapter, 0, sizeof (struct zfcp_adapter)); ccw_device->handler = NULL; @@ -1098,8 +1104,26 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) INIT_LIST_HEAD(&adapter->port_remove_lh); /* initialize list of fsf requests */ - rwlock_init(&adapter->fsf_req_list_lock); - INIT_LIST_HEAD(&adapter->fsf_req_list_head); + spin_lock_init(&adapter->req_list_lock); + retval = zfcp_reqlist_init(adapter); + if (retval) { + ZFCP_LOG_INFO("request list initialization failed\n"); + goto failed_low_mem_buffers; + } + + /* initialize debug locks */ + + 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); + + /* initialize error recovery stuff */ + + 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->abort_lock); @@ -1111,15 +1135,6 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) /* initialize lock of associated request queue */ rwlock_init(&adapter->request_queue.queue_lock); - /* intitialise SCSI ER timer */ - init_timer(&adapter->scsi_er_timer); - - /* set FC service class used per default */ - adapter->fc_service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT; - - sprintf(adapter->name, "%s", zfcp_get_busid_by_adapter(adapter)); - ASCEBC(adapter->name, strlen(adapter->name)); - /* mark adapter unusable as long as sysfs registration is not complete */ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); @@ -1129,6 +1144,14 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) if (zfcp_sysfs_adapter_create_files(&ccw_device->dev)) goto sysfs_failed; + adapter->generic_services.parent = &adapter->ccw_device->dev; + adapter->generic_services.release = zfcp_dummy_release; + snprintf(adapter->generic_services.bus_id, BUS_ID_SIZE, + "generic_services"); + + if (device_register(&adapter->generic_services)) + goto generic_services_failed; + /* put allocated adapter at list tail */ write_lock_irq(&zfcp_data.config_lock); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); @@ -1139,6 +1162,8 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) goto out; + generic_services_failed: + zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); sysfs_failed: dev_set_drvdata(&ccw_device->dev, NULL); failed_low_mem_buffers: @@ -1160,8 +1185,6 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) * !0 - struct zfcp_adapter data structure could not be removed * (e.g. still used) * locks: adapter list write lock is assumed to be held by caller - * adapter->fsf_req_list_lock is taken and released within this - * function and must not be held on entry */ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) @@ -1169,17 +1192,18 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter) int retval = 0; unsigned long flags; + device_unregister(&adapter->generic_services); zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); dev_set_drvdata(&adapter->ccw_device->dev, NULL); /* sanity check: no pending FSF requests */ - read_lock_irqsave(&adapter->fsf_req_list_lock, flags); - retval = !list_empty(&adapter->fsf_req_list_head); - read_unlock_irqrestore(&adapter->fsf_req_list_lock, flags); - if (retval) { + 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->fsf_reqs_active)); + atomic_read(&adapter->reqs_active)); retval = -EBUSY; goto out; } @@ -1205,29 +1229,36 @@ zfcp_adapter_dequeue(struct zfcp_adapter *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->fc_stats); + kfree(adapter->stats_reset_data); ZFCP_LOG_TRACE("freeing adapter structure\n"); kfree(adapter); out: return; } -/* - * Enqueues a remote port to the port list. All port internal structures - * are set up and the sysfs entry is also generated. +/** + * zfcp_port_enqueue - enqueue port to port list of adapter + * @adapter: adapter where remote port is added + * @wwpn: WWPN of the remote port to be enqueued + * @status: initial status for the port + * @d_id: destination id of the remote port to be enqueued + * Return: pointer to enqueued port on success, NULL on error + * Locks: config_sema must be held to serialize changes to the port list * - * returns: pointer to port or NULL - * locks: config_sema must be held to serialise 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) +zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status, + u32 d_id) { - struct zfcp_port *port, *tmp_port; + struct zfcp_port *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 */ @@ -1239,10 +1270,9 @@ zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status) return NULL; } - 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)); /* initialise reference count stuff */ atomic_set(&port->refcount, 0); @@ -1259,12 +1289,39 @@ zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status) atomic_set_mask(status, &port->status); /* setup for sysfs registration */ - if (status & ZFCP_STATUS_PORT_NAMESERVER) - snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, "nameserver"); - else + 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.parent = &adapter->ccw_device->dev; + } port->sysfs_device.release = zfcp_sysfs_port_release; dev_set_drvdata(&port->sysfs_device, port); @@ -1283,29 +1340,16 @@ zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status) 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); - adapter->ports++; zfcp_adapter_get(adapter); return port; @@ -1317,8 +1361,11 @@ zfcp_port_dequeue(struct zfcp_port *port) zfcp_port_wait(port); write_lock_irq(&zfcp_data.config_lock); list_del(&port->list); - write_unlock_irq(&zfcp_data.config_lock); 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)); @@ -1331,17 +1378,14 @@ zfcp_nameserver_enqueue(struct zfcp_adapter *adapter) { struct zfcp_port *port; - /* generate port structure */ - port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_NAMESERVER); + 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; } - /* set special D_ID */ - port->d_id = ZFCP_DID_NAMESERVER; - adapter->nameserver_port = port; zfcp_port_put(port); return 0; @@ -1373,28 +1417,20 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, /* 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: @@ -1405,15 +1441,13 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, 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_NAMESERVER, &port->status)) + (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; @@ -1439,8 +1473,6 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, */ 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); } } @@ -1452,27 +1484,24 @@ 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 fsf_plogi *els_plogi; struct zfcp_port *port; unsigned long flags; - zfcp_in_els_dbf_event(adapter, "##plogi", status_buffer, 28); - + 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_logi->nport_wwn)) + 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_logi->nport_wwn))) { + if (!port || (port->wwpn != (*(wwn_t *) &els_plogi->serv_param.wwpn))) { 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); } } @@ -1485,8 +1514,6 @@ zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter, 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) @@ -1500,8 +1527,6 @@ zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter, 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); } } @@ -1510,7 +1535,6 @@ 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)); @@ -1524,10 +1548,11 @@ zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req) u32 els_type; struct zfcp_adapter *adapter; - status_buffer = fsf_req->data.status_read.buffer; + 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) @@ -1537,7 +1562,6 @@ zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req) zfcp_fsf_incoming_els_rscn(adapter, status_buffer); else zfcp_fsf_incoming_els_unknown(adapter, status_buffer); - } @@ -1626,7 +1650,6 @@ zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action) 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, @@ -1662,15 +1685,10 @@ static void zfcp_ns_gid_pn_handler(unsigned long data) ct_iu_req = zfcp_sg_to_address(ct->req); ct_iu_resp = zfcp_sg_to_address(ct->resp); - if (ct_iu_resp->header.revision != ZFCP_CT_REVISION) + if (ct->status != 0) goto failed; - if (ct_iu_resp->header.gs_type != ZFCP_CT_DIRECTORY_SERVICE) - goto failed; - if (ct_iu_resp->header.gs_subtype != ZFCP_CT_NAME_SERVER) - goto failed; - if (ct_iu_resp->header.options != ZFCP_CT_SYNCHRONOUS) - goto failed; - if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) { + + 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; @@ -1681,7 +1699,7 @@ static void zfcp_ns_gid_pn_handler(unsigned long data) "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 failed; + goto mismatch; } /* looks like a valid d_id */ @@ -1691,19 +1709,206 @@ static void zfcp_ns_gid_pn_handler(unsigned long data) zfcp_get_busid_by_port(port), port->wwpn, port->d_id); goto out; -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)); + mismatch: ZFCP_LOG_DEBUG("CT IUs do not match:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_req, sizeof(struct ct_iu_gid_pn_req)); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_resp, sizeof(struct ct_iu_gid_pn_resp)); + failed: + ZFCP_LOG_NORMAL("warning: failed gid_pn nameserver request for wwpn " + "0x%016Lx for adapter %s\n", + port->wwpn, zfcp_get_busid_by_port(port)); out: zfcp_gid_pn_buffers_free(gid_pn); return; } +/* reject CT_IU reason codes acc. to FC-GS-4 */ +static const struct zfcp_rc_entry zfcp_ct_rc[] = { + {0x01, "invalid command code"}, + {0x02, "invalid version level"}, + {0x03, "logical error"}, + {0x04, "invalid CT_IU size"}, + {0x05, "logical busy"}, + {0x07, "protocol error"}, + {0x09, "unable to perform command request"}, + {0x0b, "command not supported"}, + {0x0d, "server not available"}, + {0x0e, "session could not be established"}, + {0xff, "vendor specific error"}, + {0, NULL}, +}; + +/* LS_RJT reason codes acc. to FC-FS */ +static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = { + {0x01, "invalid LS_Command code"}, + {0x03, "logical error"}, + {0x05, "logical busy"}, + {0x07, "protocol error"}, + {0x09, "unable to perform command request"}, + {0x0b, "command not supported"}, + {0x0e, "command already in progress"}, + {0xff, "vendor specific error"}, + {0, NULL}, +}; + +/* reject reason codes according to FC-PH/FC-FS */ +static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = { + {0x01, "invalid D_ID"}, + {0x02, "invalid S_ID"}, + {0x03, "Nx_Port not available, temporary"}, + {0x04, "Nx_Port not available, permament"}, + {0x05, "class not supported"}, + {0x06, "delimiter usage error"}, + {0x07, "TYPE not supported"}, + {0x08, "invalid Link_Control"}, + {0x09, "invalid R_CTL field"}, + {0x0a, "invalid F_CTL field"}, + {0x0b, "invalid OX_ID"}, + {0x0c, "invalid RX_ID"}, + {0x0d, "invalid SEQ_ID"}, + {0x0e, "invalid DF_CTL"}, + {0x0f, "invalid SEQ_CNT"}, + {0x10, "invalid parameter field"}, + {0x11, "exchange error"}, + {0x12, "protocol error"}, + {0x13, "incorrect length"}, + {0x14, "unsupported ACK"}, + {0x15, "class of service not supported by entity at FFFFFE"}, + {0x16, "login required"}, + {0x17, "excessive sequences attempted"}, + {0x18, "unable to establish exchange"}, + {0x1a, "fabric path not available"}, + {0x1b, "invalid VC_ID (class 4)"}, + {0x1c, "invalid CS_CTL field"}, + {0x1d, "insufficient resources for VC (class 4)"}, + {0x1f, "invalid class of service"}, + {0x20, "preemption request rejected"}, + {0x21, "preemption not enabled"}, + {0x22, "multicast error"}, + {0x23, "multicast error terminate"}, + {0x24, "process login required"}, + {0xff, "vendor specific reject"}, + {0, NULL}, +}; + +/** + * zfcp_rc_description - return description for given reaon code + * @code: reason code + * @rc_table: table of reason codes and descriptions + */ +static inline const char * +zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table) +{ + const char *descr = "unknown reason code"; + + do { + if (code == rc_table->code) { + descr = rc_table->description; + break; + } + rc_table++; + } while (rc_table->code && rc_table->description); + + return descr; +} + +/** + * zfcp_check_ct_response - evaluate reason code for CT_IU + * @rjt: response payload to an CT_IU request + * Return: 0 for accept CT_IU, 1 for reject CT_IU or invlid response code + */ +int +zfcp_check_ct_response(struct ct_hdr *rjt) +{ + if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT) + return 0; + + if (rjt->cmd_rsp_code != ZFCP_CT_REJECT) { + ZFCP_LOG_NORMAL("error: invalid Generic Service command/" + "response code (0x%04hx)\n", + rjt->cmd_rsp_code); + return 1; + } + + ZFCP_LOG_INFO("Generic Service command rejected\n"); + ZFCP_LOG_INFO("%s (0x%02x, 0x%02x, 0x%02x)\n", + zfcp_rc_description(rjt->reason_code, zfcp_ct_rc), + (u32) rjt->reason_code, (u32) rjt->reason_code_expl, + (u32) rjt->vendor_unique); + + return 1; +} + +/** + * zfcp_print_els_rjt - print reject parameter and description for ELS reject + * @rjt_par: reject parameter acc. to FC-PH/FC-FS + * @rc_table: table of reason codes and descriptions + */ +static inline void +zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par, + const struct zfcp_rc_entry *rc_table) +{ + ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n", + zfcp_rc_description(rjt_par->reason_code, rc_table), + (u32) rjt_par->action, (u32) rjt_par->reason_code, + (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique); +} + +/** + * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject + * @sq: status qualifier word + * @rjt_par: reject parameter as described in FC-PH and FC-FS + * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else + */ +int +zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par) +{ + int ret = -EIO; + + if (sq == FSF_IOSTAT_NPORT_RJT) { + ZFCP_LOG_INFO("ELS rejected (P_RJT)\n"); + zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc); + /* invalid d_id */ + if (rjt_par->reason_code == 0x01) + ret = -EREMCHG; + } else if (sq == FSF_IOSTAT_FABRIC_RJT) { + ZFCP_LOG_INFO("ELS rejected (F_RJT)\n"); + zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc); + /* invalid d_id */ + if (rjt_par->reason_code == 0x01) + ret = -EREMCHG; + } else if (sq == FSF_IOSTAT_LS_RJT) { + ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n"); + zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc); + ret = -EREMOTEIO; + } else + ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq); + + return ret; +} + +/** + * 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; +} + #undef ZFCP_LOG_AREA