|  | /* | 
|  | * This file is part of the zfcp device driver for | 
|  | * FCP adapters for IBM System z9 and zSeries. | 
|  | * | 
|  | * (C) Copyright IBM Corp. 2002, 2006 | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; either version 2, or (at your option) | 
|  | * any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License | 
|  | * along with this program; if not, write to the Free Software | 
|  | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | */ | 
|  |  | 
|  | #include "zfcp_ext.h" | 
|  |  | 
|  | static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *); | 
|  | static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_open_port_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_close_port_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_send_fcp_command_task_management_handler( | 
|  | struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_status_read_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_control_file_handler(struct zfcp_fsf_req *); | 
|  | static inline int zfcp_fsf_req_sbal_check( | 
|  | unsigned long *, struct zfcp_qdio_queue *, int); | 
|  | static inline int zfcp_use_one_sbal( | 
|  | struct scatterlist *, int, struct scatterlist *, int); | 
|  | static struct zfcp_fsf_req *zfcp_fsf_req_alloc(mempool_t *, int); | 
|  | static int zfcp_fsf_req_send(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *); | 
|  | static int zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *); | 
|  | static void zfcp_fsf_link_down_info_eval(struct zfcp_adapter *, | 
|  | struct fsf_link_down_info *); | 
|  | static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *); | 
|  |  | 
|  | /* association between FSF command and FSF QTCB type */ | 
|  | static u32 fsf_qtcb_type[] = { | 
|  | [FSF_QTCB_FCP_CMND] =             FSF_IO_COMMAND, | 
|  | [FSF_QTCB_ABORT_FCP_CMND] =       FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_OPEN_PORT_WITH_DID] =   FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_OPEN_LUN] =             FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_CLOSE_LUN] =            FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_CLOSE_PORT] =           FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_CLOSE_PHYSICAL_PORT] =  FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_SEND_ELS] =             FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_SEND_GENERIC] =         FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_EXCHANGE_CONFIG_DATA] = FSF_CONFIG_COMMAND, | 
|  | [FSF_QTCB_EXCHANGE_PORT_DATA] =   FSF_PORT_COMMAND, | 
|  | [FSF_QTCB_DOWNLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND, | 
|  | [FSF_QTCB_UPLOAD_CONTROL_FILE] =  FSF_SUPPORT_COMMAND | 
|  | }; | 
|  |  | 
|  | static const char zfcp_act_subtable_type[5][8] = { | 
|  | "unknown", "OS", "WWPN", "DID", "LUN" | 
|  | }; | 
|  |  | 
|  | /****************************************************************/ | 
|  | /*************** FSF related Functions  *************************/ | 
|  | /****************************************************************/ | 
|  |  | 
|  | #define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF | 
|  |  | 
|  | /* | 
|  | * function:	zfcp_fsf_req_alloc | 
|  | * | 
|  | * purpose:     Obtains an fsf_req and potentially a qtcb (for all but | 
|  | *              unsolicited requests) via helper functions | 
|  | *              Does some initial fsf request set-up. | 
|  | * | 
|  | * returns:	pointer to allocated fsf_req if successfull | 
|  | *              NULL otherwise | 
|  | * | 
|  | * locks:       none | 
|  | * | 
|  | */ | 
|  | static struct zfcp_fsf_req * | 
|  | zfcp_fsf_req_alloc(mempool_t *pool, int req_flags) | 
|  | { | 
|  | size_t size; | 
|  | void *ptr; | 
|  | struct zfcp_fsf_req *fsf_req = NULL; | 
|  |  | 
|  | if (req_flags & ZFCP_REQ_NO_QTCB) | 
|  | size = sizeof(struct zfcp_fsf_req); | 
|  | else | 
|  | size = sizeof(struct zfcp_fsf_req_qtcb); | 
|  |  | 
|  | if (likely(pool)) | 
|  | ptr = mempool_alloc(pool, GFP_ATOMIC); | 
|  | else { | 
|  | if (req_flags & ZFCP_REQ_NO_QTCB) | 
|  | ptr = kmalloc(size, GFP_ATOMIC); | 
|  | else | 
|  | ptr = kmem_cache_alloc(zfcp_data.fsf_req_qtcb_cache, | 
|  | GFP_ATOMIC); | 
|  | } | 
|  |  | 
|  | if (unlikely(!ptr)) | 
|  | goto out; | 
|  |  | 
|  | memset(ptr, 0, size); | 
|  |  | 
|  | if (req_flags & ZFCP_REQ_NO_QTCB) { | 
|  | fsf_req = (struct zfcp_fsf_req *) ptr; | 
|  | } else { | 
|  | fsf_req = &((struct zfcp_fsf_req_qtcb *) ptr)->fsf_req; | 
|  | fsf_req->qtcb =	&((struct zfcp_fsf_req_qtcb *) ptr)->qtcb; | 
|  | } | 
|  |  | 
|  | fsf_req->pool = pool; | 
|  |  | 
|  | out: | 
|  | return fsf_req; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:	zfcp_fsf_req_free | 
|  | * | 
|  | * purpose:     Frees the memory of an fsf_req (and potentially a qtcb) or | 
|  | *              returns it into the pool via helper functions. | 
|  | * | 
|  | * returns:     sod all | 
|  | * | 
|  | * locks:       none | 
|  | */ | 
|  | void | 
|  | zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | if (likely(fsf_req->pool)) { | 
|  | mempool_free(fsf_req, fsf_req->pool); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (fsf_req->qtcb) { | 
|  | kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, fsf_req); | 
|  | return; | 
|  | } | 
|  |  | 
|  | kfree(fsf_req); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Never ever call this without shutting down the adapter first. | 
|  | * Otherwise the adapter would continue using and corrupting s390 storage. | 
|  | * Included BUG_ON() call to ensure this is done. | 
|  | * ERP is supposed to be the only user of this function. | 
|  | */ | 
|  | void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter) | 
|  | { | 
|  | struct zfcp_fsf_req *fsf_req, *tmp; | 
|  | unsigned long flags; | 
|  | LIST_HEAD(remove_queue); | 
|  | unsigned int i; | 
|  |  | 
|  | BUG_ON(atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)); | 
|  | spin_lock_irqsave(&adapter->req_list_lock, flags); | 
|  | atomic_set(&adapter->reqs_active, 0); | 
|  | for (i = 0; i < REQUEST_LIST_SIZE; i++) | 
|  | list_splice_init(&adapter->req_list[i], &remove_queue); | 
|  | spin_unlock_irqrestore(&adapter->req_list_lock, flags); | 
|  |  | 
|  | list_for_each_entry_safe(fsf_req, tmp, &remove_queue, list) { | 
|  | list_del(&fsf_req->list); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; | 
|  | zfcp_fsf_req_complete(fsf_req); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_req_complete | 
|  | * | 
|  | * purpose:	Updates active counts and timers for openfcp-reqs | 
|  | *              May cleanup request after req_eval returns | 
|  | * | 
|  | * returns:	0 - success | 
|  | *		!0 - failure | 
|  | * | 
|  | * context: | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_req_complete(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = 0; | 
|  | int cleanup; | 
|  |  | 
|  | if (unlikely(fsf_req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS)) { | 
|  | ZFCP_LOG_DEBUG("Status read response received\n"); | 
|  | /* | 
|  | * Note: all cleanup handling is done in the callchain of | 
|  | * the function call-chain below. | 
|  | */ | 
|  | zfcp_fsf_status_read_handler(fsf_req); | 
|  | goto out; | 
|  | } else { | 
|  | del_timer(&fsf_req->timer); | 
|  | zfcp_fsf_protstatus_eval(fsf_req); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * fsf_req may be deleted due to waking up functions, so | 
|  | * cleanup is saved here and used later | 
|  | */ | 
|  | if (likely(fsf_req->status & ZFCP_STATUS_FSFREQ_CLEANUP)) | 
|  | cleanup = 1; | 
|  | else | 
|  | cleanup = 0; | 
|  |  | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_COMPLETED; | 
|  |  | 
|  | /* cleanup request if requested by initiator */ | 
|  | if (likely(cleanup)) { | 
|  | ZFCP_LOG_TRACE("removing FSF request %p\n", fsf_req); | 
|  | /* | 
|  | * lock must not be held here since it will be | 
|  | * grabed by the called routine, too | 
|  | */ | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | } else { | 
|  | /* notify initiator waiting for the requests completion */ | 
|  | ZFCP_LOG_TRACE("waking initiator of FSF request %p\n",fsf_req); | 
|  | /* | 
|  | * FIXME: Race! We must not access fsf_req here as it might have been | 
|  | * cleaned up already due to the set ZFCP_STATUS_FSFREQ_COMPLETED | 
|  | * flag. It's an improbable case. But, we have the same paranoia for | 
|  | * the cleanup flag already. | 
|  | * Might better be handled using complete()? | 
|  | * (setting the flag and doing wakeup ought to be atomic | 
|  | *  with regard to checking the flag as long as waitqueue is | 
|  | *  part of the to be released structure) | 
|  | */ | 
|  | wake_up(&fsf_req->completion_wq); | 
|  | } | 
|  |  | 
|  | out: | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_protstatus_eval | 
|  | * | 
|  | * purpose:	evaluates the QTCB of the finished FSF request | 
|  | *		and initiates appropriate actions | 
|  | *		(usually calling FSF command specific handlers) | 
|  | * | 
|  | * returns: | 
|  | * | 
|  | * context: | 
|  | * | 
|  | * locks: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = 0; | 
|  | struct zfcp_adapter *adapter = fsf_req->adapter; | 
|  | struct fsf_qtcb *qtcb = fsf_req->qtcb; | 
|  | union fsf_prot_status_qual *prot_status_qual = | 
|  | &qtcb->prefix.prot_status_qual; | 
|  |  | 
|  | zfcp_hba_dbf_event_fsf_response(fsf_req); | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { | 
|  | ZFCP_LOG_DEBUG("fsf_req 0x%lx has been dismissed\n", | 
|  | (unsigned long) fsf_req); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | | 
|  | ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */ | 
|  | goto skip_protstatus; | 
|  | } | 
|  |  | 
|  | /* log additional information provided by FSF (if any) */ | 
|  | if (likely(qtcb->header.log_length)) { | 
|  | /* do not trust them ;-) */ | 
|  | if (unlikely(qtcb->header.log_start > | 
|  | sizeof(struct fsf_qtcb))) { | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: ULP (FSF logging) log data starts " | 
|  | "beyond end of packet header. Ignored. " | 
|  | "(start=%i, size=%li)\n", | 
|  | qtcb->header.log_start, | 
|  | sizeof(struct fsf_qtcb)); | 
|  | goto forget_log; | 
|  | } | 
|  | if (unlikely((size_t) (qtcb->header.log_start + | 
|  | qtcb->header.log_length) > | 
|  | sizeof(struct fsf_qtcb))) { | 
|  | ZFCP_LOG_NORMAL("bug: ULP (FSF logging) log data ends " | 
|  | "beyond end of packet header. Ignored. " | 
|  | "(start=%i, length=%i, size=%li)\n", | 
|  | qtcb->header.log_start, | 
|  | qtcb->header.log_length, | 
|  | sizeof(struct fsf_qtcb)); | 
|  | goto forget_log; | 
|  | } | 
|  | ZFCP_LOG_TRACE("ULP log data: \n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, | 
|  | (char *) qtcb + qtcb->header.log_start, | 
|  | qtcb->header.log_length); | 
|  | } | 
|  | forget_log: | 
|  |  | 
|  | /* evaluate FSF Protocol Status */ | 
|  | switch (qtcb->prefix.prot_status) { | 
|  |  | 
|  | case FSF_PROT_GOOD: | 
|  | case FSF_PROT_FSF_STATUS_PRESENTED: | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_QTCB_VERSION_ERROR: | 
|  | ZFCP_LOG_NORMAL("error: The adapter %s contains " | 
|  | "microcode of version 0x%x, the device driver " | 
|  | "only supports 0x%x. Aborting.\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | prot_status_qual->version_error.fsf_version, | 
|  | ZFCP_QTCB_VERSION); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_SEQ_NUMB_ERROR: | 
|  | ZFCP_LOG_NORMAL("bug: Sequence number mismatch between " | 
|  | "driver (0x%x) and adapter %s (0x%x). " | 
|  | "Restarting all operations on this adapter.\n", | 
|  | qtcb->prefix.req_seq_no, | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | prot_status_qual->sequence_error.exp_req_seq_no); | 
|  | zfcp_erp_adapter_reopen(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY; | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_UNSUPP_QTCB_TYPE: | 
|  | ZFCP_LOG_NORMAL("error: Packet header type used by the " | 
|  | "device driver is incompatible with " | 
|  | "that used on adapter %s. " | 
|  | "Stopping all operations on this adapter.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_HOST_CONNECTION_INITIALIZING: | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, | 
|  | &(adapter->status)); | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_DUPLICATE_REQUEST_ID: | 
|  | ZFCP_LOG_NORMAL("bug: The request identifier 0x%Lx " | 
|  | "to the adapter %s is ambiguous. " | 
|  | "Stopping all operations on this adapter.\n", | 
|  | *(unsigned long long*) | 
|  | (&qtcb->bottom.support.req_handle), | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_LINK_DOWN: | 
|  | zfcp_fsf_link_down_info_eval(adapter, | 
|  | &prot_status_qual->link_down_info); | 
|  | zfcp_erp_adapter_reopen(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_REEST_QUEUE: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter with " | 
|  | "%s was re-plugged. " | 
|  | "Re-starting operations on this adapter.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | /* All ports should be marked as ready to run again */ | 
|  | zfcp_erp_modify_adapter_status(adapter, | 
|  | ZFCP_STATUS_COMMON_RUNNING, | 
|  | ZFCP_SET); | 
|  | zfcp_erp_adapter_reopen(adapter, | 
|  | ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | 
|  | | ZFCP_STATUS_COMMON_ERP_FAILED); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PROT_ERROR_STATE: | 
|  | ZFCP_LOG_NORMAL("error: The adapter %s " | 
|  | "has entered the error state. " | 
|  | "Restarting all operations on this " | 
|  | "adapter.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_erp_adapter_reopen(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY; | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: Transfer protocol status information " | 
|  | "provided by the adapter %s " | 
|  | "is not compatible with the device driver. " | 
|  | "Stopping all operations on this adapter. " | 
|  | "(debug info 0x%x).\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | qtcb->prefix.prot_status); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | } | 
|  |  | 
|  | skip_protstatus: | 
|  | /* | 
|  | * always call specific handlers to give them a chance to do | 
|  | * something meaningful even in error cases | 
|  | */ | 
|  | zfcp_fsf_fsfstatus_eval(fsf_req); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:	zfcp_fsf_fsfstatus_eval | 
|  | * | 
|  | * purpose:	evaluates FSF status of completed FSF request | 
|  | *		and acts accordingly | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = 0; | 
|  |  | 
|  | if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) { | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* evaluate FSF Status */ | 
|  | switch (fsf_req->qtcb->header.fsf_status) { | 
|  | case FSF_UNKNOWN_COMMAND: | 
|  | ZFCP_LOG_NORMAL("bug: Command issued by the device driver is " | 
|  | "not known by the adapter %s " | 
|  | "Stopping all operations on this adapter. " | 
|  | "(debug info 0x%x).\n", | 
|  | zfcp_get_busid_by_adapter(fsf_req->adapter), | 
|  | fsf_req->qtcb->header.fsf_command); | 
|  | zfcp_erp_adapter_shutdown(fsf_req->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_FCP_RSP_AVAILABLE: | 
|  | ZFCP_LOG_DEBUG("FCP Sense data will be presented to the " | 
|  | "SCSI stack.\n"); | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | zfcp_fsf_fsfstatus_qual_eval(fsf_req); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | /* | 
|  | * always call specific handlers to give them a chance to do | 
|  | * something meaningful even in error cases | 
|  | */ | 
|  | zfcp_fsf_req_dispatch(fsf_req); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:	zfcp_fsf_fsfstatus_qual_eval | 
|  | * | 
|  | * purpose:	evaluates FSF status-qualifier of completed FSF request | 
|  | *		and acts accordingly | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = 0; | 
|  |  | 
|  | switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { | 
|  | case FSF_SQ_FCP_RSP_AVAILABLE: | 
|  | break; | 
|  | case FSF_SQ_RETRY_IF_POSSIBLE: | 
|  | /* The SCSI-stack may now issue retries or escalate */ | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_COMMAND_ABORTED: | 
|  | /* Carry the aborted state on to upper layer */ | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED; | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_NO_RECOM: | 
|  | ZFCP_LOG_NORMAL("bug: No recommendation could be given for a " | 
|  | "problem on the adapter %s " | 
|  | "Stopping all operations on this adapter. ", | 
|  | zfcp_get_busid_by_adapter(fsf_req->adapter)); | 
|  | zfcp_erp_adapter_shutdown(fsf_req->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_PROGRAMMING_ERROR: | 
|  | ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer " | 
|  | "(adapter %s)\n", | 
|  | zfcp_get_busid_by_adapter(fsf_req->adapter)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | case FSF_SQ_NO_RETRY_POSSIBLE: | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* dealt with in the respective functions */ | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: Additional status info could " | 
|  | "not be interpreted properly.\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, | 
|  | (char *) &fsf_req->qtcb->header.fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_link_down_info_eval - evaluate link down information block | 
|  | */ | 
|  | static void | 
|  | zfcp_fsf_link_down_info_eval(struct zfcp_adapter *adapter, | 
|  | struct fsf_link_down_info *link_down) | 
|  | { | 
|  | if (atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, | 
|  | &adapter->status)) | 
|  | return; | 
|  |  | 
|  | atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); | 
|  |  | 
|  | if (link_down == NULL) | 
|  | goto out; | 
|  |  | 
|  | switch (link_down->error_code) { | 
|  | case FSF_PSQ_LINK_NO_LIGHT: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(no light detected)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_WRAP_PLUG: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(wrap plug detected)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_NO_FCP: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(adjacent node on link does not support FCP)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_FIRMWARE_UPDATE: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(firmware update in progress)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_INVALID_WWPN: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(duplicate or invalid WWPN detected)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_NO_NPIV_SUPPORT: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(no support for NPIV by Fabric)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_NO_FCP_RESOURCES: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(out of resource in FCP daughtercard)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_NO_FABRIC_RESOURCES: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(out of resource in Fabric)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(unable to Fabric login)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED: | 
|  | ZFCP_LOG_NORMAL("WWPN assignment file corrupted on adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED: | 
|  | ZFCP_LOG_NORMAL("Mode table corrupted on adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT: | 
|  | ZFCP_LOG_NORMAL("No WWPN for assignment table on adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("The local link to adapter %s is down " | 
|  | "(warning: unknown reason code %d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | link_down->error_code); | 
|  | } | 
|  |  | 
|  | if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) | 
|  | ZFCP_LOG_DEBUG("Debug information to link down: " | 
|  | "primary_status=0x%02x " | 
|  | "ioerr_code=0x%02x " | 
|  | "action_code=0x%02x " | 
|  | "reason_code=0x%02x " | 
|  | "explanation_code=0x%02x " | 
|  | "vendor_specific_code=0x%02x\n", | 
|  | link_down->primary_status, | 
|  | link_down->ioerr_code, | 
|  | link_down->action_code, | 
|  | link_down->reason_code, | 
|  | link_down->explanation_code, | 
|  | link_down->vendor_specific_code); | 
|  |  | 
|  | out: | 
|  | zfcp_erp_adapter_failed(adapter); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:	zfcp_fsf_req_dispatch | 
|  | * | 
|  | * purpose:	calls the appropriate command specific handler | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct zfcp_erp_action *erp_action = fsf_req->erp_action; | 
|  | struct zfcp_adapter *adapter = fsf_req->adapter; | 
|  | int retval = 0; | 
|  |  | 
|  |  | 
|  | switch (fsf_req->fsf_command) { | 
|  |  | 
|  | case FSF_QTCB_FCP_CMND: | 
|  | zfcp_fsf_send_fcp_command_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_ABORT_FCP_CMND: | 
|  | zfcp_fsf_abort_fcp_command_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_SEND_GENERIC: | 
|  | zfcp_fsf_send_ct_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_OPEN_PORT_WITH_DID: | 
|  | zfcp_fsf_open_port_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_OPEN_LUN: | 
|  | zfcp_fsf_open_unit_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_CLOSE_LUN: | 
|  | zfcp_fsf_close_unit_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_CLOSE_PORT: | 
|  | zfcp_fsf_close_port_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_CLOSE_PHYSICAL_PORT: | 
|  | zfcp_fsf_close_physical_port_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_EXCHANGE_CONFIG_DATA: | 
|  | zfcp_fsf_exchange_config_data_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_EXCHANGE_PORT_DATA: | 
|  | zfcp_fsf_exchange_port_data_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_SEND_ELS: | 
|  | zfcp_fsf_send_els_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_DOWNLOAD_CONTROL_FILE: | 
|  | zfcp_fsf_control_file_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_UPLOAD_CONTROL_FILE: | 
|  | zfcp_fsf_control_file_handler(fsf_req); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | ZFCP_LOG_NORMAL("bug: Command issued by the device driver is " | 
|  | "not supported by the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | if (fsf_req->fsf_command != fsf_req->qtcb->header.fsf_command) | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: Command issued by the device driver differs " | 
|  | "from the command returned by the adapter %s " | 
|  | "(debug info 0x%x, 0x%x).\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | fsf_req->fsf_command, | 
|  | fsf_req->qtcb->header.fsf_command); | 
|  | } | 
|  |  | 
|  | if (!erp_action) | 
|  | return retval; | 
|  |  | 
|  | zfcp_erp_async_handler(erp_action, 0); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_status_read | 
|  | * | 
|  | * purpose:	initiates a Status Read command at the specified adapter | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags) | 
|  | { | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | struct fsf_status_read_buffer *status_buffer; | 
|  | unsigned long lock_flags; | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | int retval = 0; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS, | 
|  | req_flags | ZFCP_REQ_NO_QTCB, | 
|  | adapter->pool.fsf_req_status_read, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create unsolicited status " | 
|  | "buffer for adapter %s.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | goto failed_req_create; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS; | 
|  | sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  | fsf_req->sbale_curr = 2; | 
|  |  | 
|  | status_buffer = | 
|  | mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC); | 
|  | if (!status_buffer) { | 
|  | ZFCP_LOG_NORMAL("bug: could not get some buffer\n"); | 
|  | goto failed_buf; | 
|  | } | 
|  | memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer)); | 
|  | fsf_req->data = (unsigned long) status_buffer; | 
|  |  | 
|  | /* insert pointer to respective buffer */ | 
|  | sbale = zfcp_qdio_sbale_curr(fsf_req); | 
|  | sbale->addr = (void *) status_buffer; | 
|  | sbale->length = sizeof(struct fsf_status_read_buffer); | 
|  |  | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_DEBUG("error: Could not set-up unsolicited status " | 
|  | "environment.\n"); | 
|  | goto failed_req_send; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_TRACE("Status Read request initiated (adapter%s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | goto out; | 
|  |  | 
|  | failed_req_send: | 
|  | mempool_free(status_buffer, adapter->pool.data_status_read); | 
|  |  | 
|  | failed_buf: | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | failed_req_create: | 
|  | zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL); | 
|  | out: | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static int | 
|  | zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct fsf_status_read_buffer *status_buffer; | 
|  | struct zfcp_adapter *adapter; | 
|  | struct zfcp_port *port; | 
|  | unsigned long flags; | 
|  |  | 
|  | status_buffer = (struct fsf_status_read_buffer *) fsf_req->data; | 
|  | adapter = fsf_req->adapter; | 
|  |  | 
|  | read_lock_irqsave(&zfcp_data.config_lock, flags); | 
|  | list_for_each_entry(port, &adapter->port_list_head, list) | 
|  | if (port->d_id == (status_buffer->d_id & ZFCP_DID_MASK)) | 
|  | break; | 
|  | read_unlock_irqrestore(&zfcp_data.config_lock, flags); | 
|  |  | 
|  | if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) { | 
|  | ZFCP_LOG_NORMAL("bug: Reopen port indication received for " | 
|  | "nonexisting port with d_id 0x%06x on " | 
|  | "adapter %s. Ignored.\n", | 
|  | status_buffer->d_id & ZFCP_DID_MASK, | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | switch (status_buffer->status_subtype) { | 
|  |  | 
|  | case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT: | 
|  | debug_text_event(adapter->erp_dbf, 3, "unsol_pc_phys:"); | 
|  | zfcp_erp_port_reopen(port, 0); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_SUB_ERROR_PORT: | 
|  | debug_text_event(adapter->erp_dbf, 1, "unsol_pc_err:"); | 
|  | zfcp_erp_port_shutdown(port, 0); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | debug_text_event(adapter->erp_dbf, 0, "unsol_unk_sub:"); | 
|  | debug_exception(adapter->erp_dbf, 0, | 
|  | &status_buffer->status_subtype, sizeof (u32)); | 
|  | ZFCP_LOG_NORMAL("bug: Undefined status subtype received " | 
|  | "for a reopen indication on port with " | 
|  | "d_id 0x%06x on the adapter %s. " | 
|  | "Ignored. (debug info 0x%x)\n", | 
|  | status_buffer->d_id, | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | status_buffer->status_subtype); | 
|  | } | 
|  | out: | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_status_read_handler | 
|  | * | 
|  | * purpose:	is called for finished Open Port command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = 0; | 
|  | struct zfcp_adapter *adapter = fsf_req->adapter; | 
|  | struct fsf_status_read_buffer *status_buffer = | 
|  | (struct fsf_status_read_buffer *) fsf_req->data; | 
|  | struct fsf_bit_error_payload *fsf_bit_error; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { | 
|  | zfcp_hba_dbf_event_fsf_unsol("dism", adapter, status_buffer); | 
|  | mempool_free(status_buffer, adapter->pool.data_status_read); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | zfcp_hba_dbf_event_fsf_unsol("read", adapter, status_buffer); | 
|  |  | 
|  | switch (status_buffer->status_type) { | 
|  |  | 
|  | case FSF_STATUS_READ_PORT_CLOSED: | 
|  | zfcp_fsf_status_read_port_closed(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_INCOMING_ELS: | 
|  | zfcp_fsf_incoming_els(fsf_req); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_SENSE_DATA_AVAIL: | 
|  | ZFCP_LOG_INFO("unsolicited sense data received (adapter %s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_BIT_ERROR_THRESHOLD: | 
|  | fsf_bit_error = (struct fsf_bit_error_payload *) | 
|  | status_buffer->payload; | 
|  | ZFCP_LOG_NORMAL("Warning: bit error threshold data " | 
|  | "received (adapter %s, " | 
|  | "link failures = %i, loss of sync errors = %i, " | 
|  | "loss of signal errors = %i, " | 
|  | "primitive sequence errors = %i, " | 
|  | "invalid transmission word errors = %i, " | 
|  | "CRC errors = %i)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | fsf_bit_error->link_failure_error_count, | 
|  | fsf_bit_error->loss_of_sync_error_count, | 
|  | fsf_bit_error->loss_of_signal_error_count, | 
|  | fsf_bit_error->primitive_sequence_error_count, | 
|  | fsf_bit_error->invalid_transmission_word_error_count, | 
|  | fsf_bit_error->crc_error_count); | 
|  | ZFCP_LOG_INFO("Additional bit error threshold data " | 
|  | "(adapter %s, " | 
|  | "primitive sequence event time-outs = %i, " | 
|  | "elastic buffer overrun errors = %i, " | 
|  | "advertised receive buffer-to-buffer credit = %i, " | 
|  | "current receice buffer-to-buffer credit = %i, " | 
|  | "advertised transmit buffer-to-buffer credit = %i, " | 
|  | "current transmit buffer-to-buffer credit = %i)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | fsf_bit_error->primitive_sequence_event_timeout_count, | 
|  | fsf_bit_error->elastic_buffer_overrun_error_count, | 
|  | fsf_bit_error->advertised_receive_b2b_credit, | 
|  | fsf_bit_error->current_receive_b2b_credit, | 
|  | fsf_bit_error->advertised_transmit_b2b_credit, | 
|  | fsf_bit_error->current_transmit_b2b_credit); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_LINK_DOWN: | 
|  | switch (status_buffer->status_subtype) { | 
|  | case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK: | 
|  | ZFCP_LOG_INFO("Physical link to adapter %s is down\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_fsf_link_down_info_eval(adapter, | 
|  | (struct fsf_link_down_info *) | 
|  | &status_buffer->payload); | 
|  | break; | 
|  | case FSF_STATUS_READ_SUB_FDISC_FAILED: | 
|  | ZFCP_LOG_INFO("Local link to adapter %s is down " | 
|  | "due to failed FDISC login\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_fsf_link_down_info_eval(adapter, | 
|  | (struct fsf_link_down_info *) | 
|  | &status_buffer->payload); | 
|  | break; | 
|  | case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE: | 
|  | ZFCP_LOG_INFO("Local link to adapter %s is down " | 
|  | "due to firmware update on adapter\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_fsf_link_down_info_eval(adapter, NULL); | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_INFO("Local link to adapter %s is down " | 
|  | "due to unknown reason\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_fsf_link_down_info_eval(adapter, NULL); | 
|  | }; | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_LINK_UP: | 
|  | ZFCP_LOG_NORMAL("Local link to adapter %s was replugged. " | 
|  | "Restarting operations on this adapter\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | /* All ports should be marked as ready to run again */ | 
|  | zfcp_erp_modify_adapter_status(adapter, | 
|  | ZFCP_STATUS_COMMON_RUNNING, | 
|  | ZFCP_SET); | 
|  | zfcp_erp_adapter_reopen(adapter, | 
|  | ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | 
|  | | ZFCP_STATUS_COMMON_ERP_FAILED); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_NOTIFICATION_LOST: | 
|  | ZFCP_LOG_NORMAL("Unsolicited status notification(s) lost: " | 
|  | "adapter %s%s%s%s%s%s%s%s%s\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_INCOMING_ELS) ? | 
|  | ", incoming ELS" : "", | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_SENSE_DATA) ? | 
|  | ", sense data" : "", | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_LINK_STATUS) ? | 
|  | ", link status change" : "", | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_PORT_CLOSED) ? | 
|  | ", port close" : "", | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_BIT_ERROR_THRESHOLD) ? | 
|  | ", bit error exception" : "", | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_ACT_UPDATED) ? | 
|  | ", ACT update" : "", | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_ACT_HARDENED) ? | 
|  | ", ACT hardening" : "", | 
|  | (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_FEATURE_UPDATE_ALERT) ? | 
|  | ", adapter feature change" : ""); | 
|  |  | 
|  | if (status_buffer->status_subtype & | 
|  | FSF_STATUS_READ_SUB_ACT_UPDATED) | 
|  | zfcp_erp_adapter_access_changed(adapter); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_CFDC_UPDATED: | 
|  | ZFCP_LOG_NORMAL("CFDC has been updated on the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_erp_adapter_access_changed(adapter); | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_CFDC_HARDENED: | 
|  | switch (status_buffer->status_subtype) { | 
|  | case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE: | 
|  | ZFCP_LOG_NORMAL("CFDC of adapter %s saved on SE\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2: | 
|  | ZFCP_LOG_NORMAL("CFDC of adapter %s has been copied " | 
|  | "to the secondary SE\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("CFDC of adapter %s has been hardened\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_STATUS_READ_FEATURE_UPDATE_ALERT: | 
|  | debug_text_event(adapter->erp_dbf, 2, "unsol_features:"); | 
|  | ZFCP_LOG_INFO("List of supported features on adapter %s has " | 
|  | "been changed from 0x%08X to 0x%08X\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | *(u32*) (status_buffer->payload + 4), | 
|  | *(u32*) (status_buffer->payload)); | 
|  | adapter->adapter_features = *(u32*) status_buffer->payload; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("warning: An unsolicited status packet of unknown " | 
|  | "type was received (debug info 0x%x)\n", | 
|  | status_buffer->status_type); | 
|  | ZFCP_LOG_DEBUG("Dump of status_read_buffer %p:\n", | 
|  | status_buffer); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) status_buffer, | 
|  | sizeof (struct fsf_status_read_buffer)); | 
|  | break; | 
|  | } | 
|  | mempool_free(status_buffer, adapter->pool.data_status_read); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | /* | 
|  | * recycle buffer and start new request repeat until outbound | 
|  | * queue is empty or adapter shutdown is requested | 
|  | */ | 
|  | /* | 
|  | * FIXME(qdio): | 
|  | * we may wait in the req_create for 5s during shutdown, so | 
|  | * qdio_cleanup will have to wait at least that long before returning | 
|  | * with failure to allow us a proper cleanup under all circumstances | 
|  | */ | 
|  | /* | 
|  | * FIXME: | 
|  | * allocation failure possible? (Is this code needed?) | 
|  | */ | 
|  | retval = zfcp_fsf_status_read(adapter, 0); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("Failed to create unsolicited status read " | 
|  | "request for the adapter %s.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | /* temporary fix to avoid status read buffer shortage */ | 
|  | adapter->status_read_failed++; | 
|  | if ((ZFCP_STATUS_READS_RECOM - adapter->status_read_failed) | 
|  | < ZFCP_STATUS_READ_FAILED_THRESHOLD) { | 
|  | ZFCP_LOG_INFO("restart adapter %s due to status read " | 
|  | "buffer shortage\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_erp_adapter_reopen(adapter, 0); | 
|  | } | 
|  | } | 
|  | out: | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_abort_fcp_command | 
|  | * | 
|  | * purpose:	tells FSF to abort a running SCSI command | 
|  | * | 
|  | * returns:	address of initiated FSF request | 
|  | *		NULL - request could not be initiated | 
|  | * | 
|  | * FIXME(design): should be watched by a timeout !!! | 
|  | * FIXME(design) shouldn't this be modified to return an int | 
|  | *               also...don't know how though | 
|  | */ | 
|  | struct zfcp_fsf_req * | 
|  | zfcp_fsf_abort_fcp_command(unsigned long old_req_id, | 
|  | struct zfcp_adapter *adapter, | 
|  | struct zfcp_unit *unit, int req_flags) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req = NULL; | 
|  | unsigned long lock_flags; | 
|  | int retval = 0; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND, | 
|  | req_flags, adapter->pool.fsf_req_abort, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Failed to create an abort command " | 
|  | "request for lun 0x%016Lx on port 0x%016Lx " | 
|  | "on adapter %s.\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, | 
|  | &unit->status))) | 
|  | goto unit_blocked; | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | fsf_req->data = (unsigned long) unit; | 
|  |  | 
|  | /* set handles of unit and its parent port in QTCB */ | 
|  | fsf_req->qtcb->header.lun_handle = unit->handle; | 
|  | fsf_req->qtcb->header.port_handle = unit->port->handle; | 
|  |  | 
|  | /* set handle of request which should be aborted */ | 
|  | fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id; | 
|  |  | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (!retval) | 
|  | goto out; | 
|  |  | 
|  | unit_blocked: | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | fsf_req = NULL; | 
|  |  | 
|  | out: | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  | return fsf_req; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_abort_fcp_command_handler | 
|  | * | 
|  | * purpose:	is called for finished Abort FCP Command request | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req) | 
|  | { | 
|  | int retval = -EINVAL; | 
|  | struct zfcp_unit *unit; | 
|  | union fsf_status_qual *fsf_stat_qual = | 
|  | &new_fsf_req->qtcb->header.fsf_status_qual; | 
|  |  | 
|  | if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | /* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */ | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | unit = (struct zfcp_unit *) new_fsf_req->data; | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (new_fsf_req->qtcb->header.fsf_status) { | 
|  |  | 
|  | case FSF_PORT_HANDLE_NOT_VALID: | 
|  | if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) { | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 3, | 
|  | "fsf_s_phand_nv0"); | 
|  | /* | 
|  | * In this case a command that was sent prior to a port | 
|  | * reopen was aborted (handles are different). This is | 
|  | * fine. | 
|  | */ | 
|  | } else { | 
|  | ZFCP_LOG_INFO("Temporary port identifier 0x%x for " | 
|  | "port 0x%016Lx on adapter %s invalid. " | 
|  | "This may happen occasionally.\n", | 
|  | unit->port->handle, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | ZFCP_LOG_INFO("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, | 
|  | (char *) &new_fsf_req->qtcb->header. | 
|  | fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | /* Let's hope this sorts out the mess */ | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_phand_nv1"); | 
|  | zfcp_erp_adapter_reopen(unit->port->adapter, 0); | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_LUN_HANDLE_NOT_VALID: | 
|  | if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) { | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 3, | 
|  | "fsf_s_lhand_nv0"); | 
|  | /* | 
|  | * In this case a command that was sent prior to a unit | 
|  | * reopen was aborted (handles are different). | 
|  | * This is fine. | 
|  | */ | 
|  | } else { | 
|  | ZFCP_LOG_INFO | 
|  | ("Warning: Temporary LUN identifier 0x%x of LUN " | 
|  | "0x%016Lx on port 0x%016Lx on adapter %s is " | 
|  | "invalid. This may happen in rare cases. " | 
|  | "Trying to re-establish link.\n", | 
|  | unit->handle, | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | ZFCP_LOG_DEBUG("Status qualifier data:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &new_fsf_req->qtcb->header. | 
|  | fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | /* Let's hope this sorts out the mess */ | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_lhand_nv1"); | 
|  | zfcp_erp_port_reopen(unit->port, 0); | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_FCP_COMMAND_DOES_NOT_EXIST: | 
|  | retval = 0; | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 3, | 
|  | "fsf_s_no_exist"); | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED; | 
|  | break; | 
|  |  | 
|  | case FSF_PORT_BOXED: | 
|  | ZFCP_LOG_INFO("Remote port 0x%016Lx on adapter %s needs to " | 
|  | "be reopened\n", unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 2, | 
|  | "fsf_s_pboxed"); | 
|  | zfcp_erp_port_boxed(unit->port); | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | 
|  | | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | case FSF_LUN_BOXED: | 
|  | ZFCP_LOG_INFO( | 
|  | "unit 0x%016Lx on port 0x%016Lx on adapter %s needs " | 
|  | "to be reopened\n", | 
|  | unit->fcp_lun, unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 1, "fsf_s_lboxed"); | 
|  | zfcp_erp_unit_boxed(unit); | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | 
|  | | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (new_fsf_req->qtcb->header.fsf_status_qual.word[0]) { | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ltest"); | 
|  | zfcp_test_link(unit->port); | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* SCSI stack will escalate */ | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ulp"); | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: Wrong status qualifier 0x%x arrived.\n", | 
|  | new_fsf_req->qtcb->header.fsf_status_qual.word[0]); | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_sq_inval:"); | 
|  | debug_exception(new_fsf_req->adapter->erp_dbf, 0, | 
|  | &new_fsf_req->qtcb->header. | 
|  | fsf_status_qual.word[0], sizeof (u32)); | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_GOOD: | 
|  | retval = 0; | 
|  | new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " | 
|  | "(debug info 0x%x)\n", | 
|  | new_fsf_req->qtcb->header.fsf_status); | 
|  | debug_text_event(new_fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_s_inval:"); | 
|  | debug_exception(new_fsf_req->adapter->erp_dbf, 0, | 
|  | &new_fsf_req->qtcb->header.fsf_status, | 
|  | sizeof (u32)); | 
|  | break; | 
|  | } | 
|  | skip_fsfstatus: | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_use_one_sbal - checks whether req buffer and resp bother each fit into | 
|  | *	one SBALE | 
|  | * Two scatter-gather lists are passed, one for the reqeust and one for the | 
|  | * response. | 
|  | */ | 
|  | static inline int | 
|  | zfcp_use_one_sbal(struct scatterlist *req, int req_count, | 
|  | struct scatterlist *resp, int resp_count) | 
|  | { | 
|  | return ((req_count == 1) && | 
|  | (resp_count == 1) && | 
|  | (((unsigned long) zfcp_sg_to_address(&req[0]) & | 
|  | PAGE_MASK) == | 
|  | ((unsigned long) (zfcp_sg_to_address(&req[0]) + | 
|  | req[0].length - 1) & PAGE_MASK)) && | 
|  | (((unsigned long) zfcp_sg_to_address(&resp[0]) & | 
|  | PAGE_MASK) == | 
|  | ((unsigned long) (zfcp_sg_to_address(&resp[0]) + | 
|  | resp[0].length - 1) & PAGE_MASK))); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS) | 
|  | * @ct: pointer to struct zfcp_send_ct which conatins all needed data for | 
|  | *	the request | 
|  | * @pool: pointer to memory pool, if non-null this pool is used to allocate | 
|  | *	a struct zfcp_fsf_req | 
|  | * @erp_action: pointer to erp_action, if non-null the Generic Service request | 
|  | *	is sent within error recovery | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool, | 
|  | struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_port *port; | 
|  | struct zfcp_adapter *adapter; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int bytes; | 
|  | int ret = 0; | 
|  |  | 
|  | port = ct->port; | 
|  | adapter = port->adapter; | 
|  |  | 
|  | ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC, | 
|  | ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, | 
|  | pool, &lock_flags, &fsf_req); | 
|  | if (ret < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create CT request (FC-GS) for " | 
|  | "adapter: %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | goto failed_req; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | if (zfcp_use_one_sbal(ct->req, ct->req_count, | 
|  | ct->resp, ct->resp_count)){ | 
|  | /* both request buffer and response buffer | 
|  | fit into one sbale each */ | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ; | 
|  | sbale[2].addr = zfcp_sg_to_address(&ct->req[0]); | 
|  | sbale[2].length = ct->req[0].length; | 
|  | sbale[3].addr = zfcp_sg_to_address(&ct->resp[0]); | 
|  | sbale[3].length = ct->resp[0].length; | 
|  | sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  | } else if (adapter->adapter_features & | 
|  | FSF_FEATURE_ELS_CT_CHAINED_SBALS) { | 
|  | /* try to use chained SBALs */ | 
|  | bytes = zfcp_qdio_sbals_from_sg(fsf_req, | 
|  | SBAL_FLAGS0_TYPE_WRITE_READ, | 
|  | ct->req, ct->req_count, | 
|  | ZFCP_MAX_SBALS_PER_CT_REQ); | 
|  | if (bytes <= 0) { | 
|  | ZFCP_LOG_INFO("error: creation of CT request failed " | 
|  | "on adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | if (bytes == 0) | 
|  | ret = -ENOMEM; | 
|  | else | 
|  | ret = bytes; | 
|  |  | 
|  | goto failed_send; | 
|  | } | 
|  | fsf_req->qtcb->bottom.support.req_buf_length = bytes; | 
|  | fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL; | 
|  | bytes = zfcp_qdio_sbals_from_sg(fsf_req, | 
|  | SBAL_FLAGS0_TYPE_WRITE_READ, | 
|  | ct->resp, ct->resp_count, | 
|  | ZFCP_MAX_SBALS_PER_CT_REQ); | 
|  | if (bytes <= 0) { | 
|  | ZFCP_LOG_INFO("error: creation of CT request failed " | 
|  | "on adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | if (bytes == 0) | 
|  | ret = -ENOMEM; | 
|  | else | 
|  | ret = bytes; | 
|  |  | 
|  | goto failed_send; | 
|  | } | 
|  | fsf_req->qtcb->bottom.support.resp_buf_length = bytes; | 
|  | } else { | 
|  | /* reject send generic request */ | 
|  | ZFCP_LOG_INFO( | 
|  | "error: microcode does not support chained SBALs," | 
|  | "CT request too big (adapter %s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | ret = -EOPNOTSUPP; | 
|  | goto failed_send; | 
|  | } | 
|  |  | 
|  | /* settings in QTCB */ | 
|  | fsf_req->qtcb->header.port_handle = port->handle; | 
|  | fsf_req->qtcb->bottom.support.service_class = | 
|  | ZFCP_FC_SERVICE_CLASS_DEFAULT; | 
|  | fsf_req->qtcb->bottom.support.timeout = ct->timeout; | 
|  | fsf_req->data = (unsigned long) ct; | 
|  |  | 
|  | zfcp_san_dbf_event_ct_request(fsf_req); | 
|  |  | 
|  | if (erp_action) { | 
|  | erp_action->fsf_req = fsf_req; | 
|  | fsf_req->erp_action = erp_action; | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  | } else | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); | 
|  |  | 
|  | ret = zfcp_fsf_req_send(fsf_req); | 
|  | if (ret) { | 
|  | ZFCP_LOG_DEBUG("error: initiation of CT request failed " | 
|  | "(adapter %s, port 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), port->wwpn); | 
|  | goto failed_send; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_DEBUG("CT request initiated (adapter %s, port 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), port->wwpn); | 
|  | goto out; | 
|  |  | 
|  | failed_send: | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | if (erp_action != NULL) { | 
|  | erp_action->fsf_req = NULL; | 
|  | } | 
|  | failed_req: | 
|  | out: | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_send_ct_handler - handler for Generic Service requests | 
|  | * @fsf_req: pointer to struct zfcp_fsf_req | 
|  | * | 
|  | * Data specific for the Generic Service request is passed using | 
|  | * fsf_req->data. There we find the pointer to struct zfcp_send_ct. | 
|  | * Usually a specific handler for the CT request is called which is | 
|  | * found in this structure. | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct zfcp_port *port; | 
|  | struct zfcp_adapter *adapter; | 
|  | struct zfcp_send_ct *send_ct; | 
|  | struct fsf_qtcb_header *header; | 
|  | struct fsf_qtcb_bottom_support *bottom; | 
|  | int retval = -EINVAL; | 
|  | u16 subtable, rule, counter; | 
|  |  | 
|  | adapter = fsf_req->adapter; | 
|  | send_ct = (struct zfcp_send_ct *) fsf_req->data; | 
|  | port = send_ct->port; | 
|  | header = &fsf_req->qtcb->header; | 
|  | bottom = &fsf_req->qtcb->bottom.support; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) | 
|  | goto skip_fsfstatus; | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (header->fsf_status) { | 
|  |  | 
|  | case FSF_GOOD: | 
|  | zfcp_san_dbf_event_ct_response(fsf_req); | 
|  | retval = 0; | 
|  | break; | 
|  |  | 
|  | case FSF_SERVICE_CLASS_NOT_SUPPORTED: | 
|  | ZFCP_LOG_INFO("error: adapter %s does not support fc " | 
|  | "class %d.\n", | 
|  | zfcp_get_busid_by_port(port), | 
|  | ZFCP_FC_SERVICE_CLASS_DEFAULT); | 
|  | /* stop operation for this adapter */ | 
|  | debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup"); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (header->fsf_status_qual.word[0]){ | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | /* reopening link to port */ | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ltest"); | 
|  | zfcp_test_link(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* ERP strategy will escalate */ | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x " | 
|  | "arrived.\n", | 
|  | header->fsf_status_qual.word[0]); | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_ACCESS_DENIED: | 
|  | ZFCP_LOG_NORMAL("access denied, cannot send generic service " | 
|  | "command (adapter %s, port d_id=0x%06x)\n", | 
|  | zfcp_get_busid_by_port(port), port->d_id); | 
|  | for (counter = 0; counter < 2; counter++) { | 
|  | subtable = header->fsf_status_qual.halfword[counter * 2]; | 
|  | rule = header->fsf_status_qual.halfword[counter * 2 + 1]; | 
|  | switch (subtable) { | 
|  | case FSF_SQ_CFDC_SUBTABLE_OS: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_DID: | 
|  | case FSF_SQ_CFDC_SUBTABLE_LUN: | 
|  | ZFCP_LOG_INFO("Access denied (%s rule %d)\n", | 
|  | zfcp_act_subtable_type[subtable], rule); | 
|  | break; | 
|  | } | 
|  | } | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_s_access"); | 
|  | zfcp_erp_port_access_denied(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_GENERIC_COMMAND_REJECTED: | 
|  | ZFCP_LOG_INFO("generic service command rejected " | 
|  | "(adapter %s, port d_id=0x%06x)\n", | 
|  | zfcp_get_busid_by_port(port), port->d_id); | 
|  | ZFCP_LOG_INFO("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_s_gcom_rej"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PORT_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_DEBUG("Temporary port identifier 0x%x for port " | 
|  | "0x%016Lx on adapter %s invalid. This may " | 
|  | "happen occasionally.\n", port->handle, | 
|  | port->wwpn, zfcp_get_busid_by_port(port)); | 
|  | ZFCP_LOG_INFO("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_s_phandle_nv"); | 
|  | zfcp_erp_adapter_reopen(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PORT_BOXED: | 
|  | ZFCP_LOG_INFO("port needs to be reopened " | 
|  | "(adapter %s, port d_id=0x%06x)\n", | 
|  | zfcp_get_busid_by_port(port), port->d_id); | 
|  | debug_text_event(adapter->erp_dbf, 2, "fsf_s_pboxed"); | 
|  | zfcp_erp_port_boxed(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | 
|  | | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | /* following states should never occure, all cases avoided | 
|  | in zfcp_fsf_send_ct - but who knows ... */ | 
|  | case FSF_PAYLOAD_SIZE_MISMATCH: | 
|  | ZFCP_LOG_INFO("payload size mismatch (adapter: %s, " | 
|  | "req_buf_length=%d, resp_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->req_buf_length, bottom->resp_buf_length); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_REQUEST_SIZE_TOO_LARGE: | 
|  | ZFCP_LOG_INFO("request size too large (adapter: %s, " | 
|  | "req_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->req_buf_length); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_RESPONSE_SIZE_TOO_LARGE: | 
|  | ZFCP_LOG_INFO("response size too large (adapter: %s, " | 
|  | "resp_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->resp_buf_length); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SBAL_MISMATCH: | 
|  | ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, " | 
|  | "resp_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->req_buf_length, bottom->resp_buf_length); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " | 
|  | "(debug info 0x%x)\n", header->fsf_status); | 
|  | debug_text_event(adapter->erp_dbf, 0, "fsf_sq_inval:"); | 
|  | debug_exception(adapter->erp_dbf, 0, | 
|  | &header->fsf_status_qual.word[0], sizeof (u32)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | send_ct->status = retval; | 
|  |  | 
|  | if (send_ct->handler != NULL) | 
|  | send_ct->handler(send_ct->handler_data); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_send_els - initiate an ELS command (FC-FS) | 
|  | * @els: pointer to struct zfcp_send_els which contains all needed data for | 
|  | *	the command. | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_send_els(struct zfcp_send_els *els) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | u32 d_id; | 
|  | struct zfcp_adapter *adapter; | 
|  | unsigned long lock_flags; | 
|  | int bytes; | 
|  | int ret = 0; | 
|  |  | 
|  | d_id = els->d_id; | 
|  | adapter = els->adapter; | 
|  |  | 
|  | ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS, | 
|  | ZFCP_REQ_AUTO_CLEANUP, | 
|  | NULL, &lock_flags, &fsf_req); | 
|  | if (ret < 0) { | 
|  | ZFCP_LOG_INFO("error: creation of ELS request failed " | 
|  | "(adapter %s, port d_id: 0x%06x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), d_id); | 
|  | goto failed_req; | 
|  | } | 
|  |  | 
|  | if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, | 
|  | &els->port->status))) { | 
|  | ret = -EBUSY; | 
|  | goto port_blocked; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | if (zfcp_use_one_sbal(els->req, els->req_count, | 
|  | els->resp, els->resp_count)){ | 
|  | /* both request buffer and response buffer | 
|  | fit into one sbale each */ | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ; | 
|  | sbale[2].addr = zfcp_sg_to_address(&els->req[0]); | 
|  | sbale[2].length = els->req[0].length; | 
|  | sbale[3].addr = zfcp_sg_to_address(&els->resp[0]); | 
|  | sbale[3].length = els->resp[0].length; | 
|  | sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  | } else if (adapter->adapter_features & | 
|  | FSF_FEATURE_ELS_CT_CHAINED_SBALS) { | 
|  | /* try to use chained SBALs */ | 
|  | bytes = zfcp_qdio_sbals_from_sg(fsf_req, | 
|  | SBAL_FLAGS0_TYPE_WRITE_READ, | 
|  | els->req, els->req_count, | 
|  | ZFCP_MAX_SBALS_PER_ELS_REQ); | 
|  | if (bytes <= 0) { | 
|  | ZFCP_LOG_INFO("error: creation of ELS request failed " | 
|  | "(adapter %s, port d_id: 0x%06x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), d_id); | 
|  | if (bytes == 0) { | 
|  | ret = -ENOMEM; | 
|  | } else { | 
|  | ret = bytes; | 
|  | } | 
|  | goto failed_send; | 
|  | } | 
|  | fsf_req->qtcb->bottom.support.req_buf_length = bytes; | 
|  | fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL; | 
|  | bytes = zfcp_qdio_sbals_from_sg(fsf_req, | 
|  | SBAL_FLAGS0_TYPE_WRITE_READ, | 
|  | els->resp, els->resp_count, | 
|  | ZFCP_MAX_SBALS_PER_ELS_REQ); | 
|  | if (bytes <= 0) { | 
|  | ZFCP_LOG_INFO("error: creation of ELS request failed " | 
|  | "(adapter %s, port d_id: 0x%06x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), d_id); | 
|  | if (bytes == 0) { | 
|  | ret = -ENOMEM; | 
|  | } else { | 
|  | ret = bytes; | 
|  | } | 
|  | goto failed_send; | 
|  | } | 
|  | fsf_req->qtcb->bottom.support.resp_buf_length = bytes; | 
|  | } else { | 
|  | /* reject request */ | 
|  | ZFCP_LOG_INFO("error: microcode does not support chained SBALs" | 
|  | ", ELS request too big (adapter %s, " | 
|  | "port d_id: 0x%06x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), d_id); | 
|  | ret = -EOPNOTSUPP; | 
|  | goto failed_send; | 
|  | } | 
|  |  | 
|  | /* settings in QTCB */ | 
|  | fsf_req->qtcb->bottom.support.d_id = d_id; | 
|  | fsf_req->qtcb->bottom.support.service_class = | 
|  | ZFCP_FC_SERVICE_CLASS_DEFAULT; | 
|  | fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT; | 
|  | fsf_req->data = (unsigned long) els; | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  |  | 
|  | zfcp_san_dbf_event_els_request(fsf_req); | 
|  |  | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); | 
|  | ret = zfcp_fsf_req_send(fsf_req); | 
|  | if (ret) { | 
|  | ZFCP_LOG_DEBUG("error: initiation of ELS request failed " | 
|  | "(adapter %s, port d_id: 0x%06x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), d_id); | 
|  | goto failed_send; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_DEBUG("ELS request initiated (adapter %s, port d_id: " | 
|  | "0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); | 
|  | goto out; | 
|  |  | 
|  | port_blocked: | 
|  | failed_send: | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  |  | 
|  | failed_req: | 
|  | out: | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_send_els_handler - handler for ELS commands | 
|  | * @fsf_req: pointer to struct zfcp_fsf_req | 
|  | * | 
|  | * Data specific for the ELS command is passed using | 
|  | * fsf_req->data. There we find the pointer to struct zfcp_send_els. | 
|  | * Usually a specific handler for the ELS command is called which is | 
|  | * found in this structure. | 
|  | */ | 
|  | static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct zfcp_adapter *adapter; | 
|  | struct zfcp_port *port; | 
|  | u32 d_id; | 
|  | struct fsf_qtcb_header *header; | 
|  | struct fsf_qtcb_bottom_support *bottom; | 
|  | struct zfcp_send_els *send_els; | 
|  | int retval = -EINVAL; | 
|  | u16 subtable, rule, counter; | 
|  |  | 
|  | send_els = (struct zfcp_send_els *) fsf_req->data; | 
|  | adapter = send_els->adapter; | 
|  | port = send_els->port; | 
|  | d_id = send_els->d_id; | 
|  | header = &fsf_req->qtcb->header; | 
|  | bottom = &fsf_req->qtcb->bottom.support; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) | 
|  | goto skip_fsfstatus; | 
|  |  | 
|  | switch (header->fsf_status) { | 
|  |  | 
|  | case FSF_GOOD: | 
|  | zfcp_san_dbf_event_els_response(fsf_req); | 
|  | retval = 0; | 
|  | break; | 
|  |  | 
|  | case FSF_SERVICE_CLASS_NOT_SUPPORTED: | 
|  | ZFCP_LOG_INFO("error: adapter %s does not support fc " | 
|  | "class %d.\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | ZFCP_FC_SERVICE_CLASS_DEFAULT); | 
|  | /* stop operation for this adapter */ | 
|  | debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup"); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (header->fsf_status_qual.word[0]){ | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ltest"); | 
|  | if (port && (send_els->ls_code != ZFCP_LS_ADISC)) | 
|  | zfcp_test_link(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = | 
|  | zfcp_handle_els_rjt(header->fsf_status_qual.word[1], | 
|  | (struct zfcp_ls_rjt_par *) | 
|  | &header->fsf_status_qual.word[2]); | 
|  | break; | 
|  | case FSF_SQ_RETRY_IF_POSSIBLE: | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_sq_retry"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x\n", | 
|  | header->fsf_status_qual.word[0]); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, | 
|  | (char*)header->fsf_status_qual.word, 16); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_ELS_COMMAND_REJECTED: | 
|  | ZFCP_LOG_INFO("ELS has been rejected because command filter " | 
|  | "prohibited sending " | 
|  | "(adapter: %s, port d_id: 0x%06x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), d_id); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case FSF_PAYLOAD_SIZE_MISMATCH: | 
|  | ZFCP_LOG_INFO( | 
|  | "ELS request size and ELS response size must be either " | 
|  | "both 0, or both greater than 0 " | 
|  | "(adapter: %s, req_buf_length=%d resp_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->req_buf_length, | 
|  | bottom->resp_buf_length); | 
|  | break; | 
|  |  | 
|  | case FSF_REQUEST_SIZE_TOO_LARGE: | 
|  | ZFCP_LOG_INFO( | 
|  | "Length of the ELS request buffer, " | 
|  | "specified in QTCB bottom, " | 
|  | "exceeds the size of the buffers " | 
|  | "that have been allocated for ELS request data " | 
|  | "(adapter: %s, req_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->req_buf_length); | 
|  | break; | 
|  |  | 
|  | case FSF_RESPONSE_SIZE_TOO_LARGE: | 
|  | ZFCP_LOG_INFO( | 
|  | "Length of the ELS response buffer, " | 
|  | "specified in QTCB bottom, " | 
|  | "exceeds the size of the buffers " | 
|  | "that have been allocated for ELS response data " | 
|  | "(adapter: %s, resp_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->resp_buf_length); | 
|  | break; | 
|  |  | 
|  | case FSF_SBAL_MISMATCH: | 
|  | /* should never occure, avoided in zfcp_fsf_send_els */ | 
|  | ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, " | 
|  | "resp_buf_length=%d)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->req_buf_length, bottom->resp_buf_length); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ACCESS_DENIED: | 
|  | ZFCP_LOG_NORMAL("access denied, cannot send ELS command " | 
|  | "(adapter %s, port d_id=0x%06x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), d_id); | 
|  | for (counter = 0; counter < 2; counter++) { | 
|  | subtable = header->fsf_status_qual.halfword[counter * 2]; | 
|  | rule = header->fsf_status_qual.halfword[counter * 2 + 1]; | 
|  | switch (subtable) { | 
|  | case FSF_SQ_CFDC_SUBTABLE_OS: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_DID: | 
|  | case FSF_SQ_CFDC_SUBTABLE_LUN: | 
|  | ZFCP_LOG_INFO("Access denied (%s rule %d)\n", | 
|  | zfcp_act_subtable_type[subtable], rule); | 
|  | break; | 
|  | } | 
|  | } | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_s_access"); | 
|  | if (port != NULL) | 
|  | zfcp_erp_port_access_denied(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "bug: An unknown FSF Status was presented " | 
|  | "(adapter: %s, fsf_status=0x%08x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | header->fsf_status); | 
|  | debug_text_event(adapter->erp_dbf, 0, "fsf_sq_inval"); | 
|  | debug_exception(adapter->erp_dbf, 0, | 
|  | &header->fsf_status_qual.word[0], sizeof(u32)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | send_els->status = retval; | 
|  |  | 
|  | if (send_els->handler) | 
|  | send_els->handler(send_els->handler_data); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | int | 
|  | zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | struct zfcp_adapter *adapter = erp_action->adapter; | 
|  | unsigned long lock_flags; | 
|  | int retval; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, | 
|  | FSF_QTCB_EXCHANGE_CONFIG_DATA, | 
|  | ZFCP_REQ_AUTO_CLEANUP, | 
|  | adapter->pool.fsf_req_erp, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not create exchange configuration " | 
|  | "data request for adapter %s.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | fsf_req->qtcb->bottom.config.feature_selection = | 
|  | FSF_FEATURE_CFDC | | 
|  | FSF_FEATURE_LUN_SHARING | | 
|  | FSF_FEATURE_NOTIFICATION_LOST | | 
|  | FSF_FEATURE_UPDATE_ALERT; | 
|  | fsf_req->erp_action = erp_action; | 
|  | erp_action->fsf_req = fsf_req; | 
|  |  | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not send exchange configuration " | 
|  | "data command on the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | erp_action->fsf_req = NULL; | 
|  | } | 
|  | else | 
|  | ZFCP_LOG_DEBUG("exchange configuration data request initiated " | 
|  | "(adapter %s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | int | 
|  | zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter, | 
|  | struct fsf_qtcb_bottom_config *data) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int retval; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA, | 
|  | 0, NULL, &lock_flags, &fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not create exchange configuration " | 
|  | "data request for adapter %s.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | fsf_req->qtcb->bottom.config.feature_selection = | 
|  | FSF_FEATURE_CFDC | | 
|  | FSF_FEATURE_LUN_SHARING | | 
|  | FSF_FEATURE_NOTIFICATION_LOST | | 
|  | FSF_FEATURE_UPDATE_ALERT; | 
|  |  | 
|  | if (data) | 
|  | fsf_req->data = (unsigned long) data; | 
|  |  | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | if (retval) | 
|  | ZFCP_LOG_INFO("error: Could not send exchange configuration " | 
|  | "data command on the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | else | 
|  | wait_event(fsf_req->completion_wq, | 
|  | fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); | 
|  |  | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_exchange_config_evaluate | 
|  | * @fsf_req: fsf_req which belongs to xchg config data request | 
|  | * @xchg_ok: specifies if xchg config data was incomplete or complete (0/1) | 
|  | * | 
|  | * returns: -EIO on error, 0 otherwise | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) | 
|  | { | 
|  | struct fsf_qtcb_bottom_config *bottom; | 
|  | struct zfcp_adapter *adapter = fsf_req->adapter; | 
|  | struct Scsi_Host *shost = adapter->scsi_host; | 
|  |  | 
|  | bottom = &fsf_req->qtcb->bottom.config; | 
|  | ZFCP_LOG_DEBUG("low/high QTCB version 0x%x/0x%x of FSF\n", | 
|  | bottom->low_qtcb_version, bottom->high_qtcb_version); | 
|  | adapter->fsf_lic_version = bottom->lic_version; | 
|  | adapter->adapter_features = bottom->adapter_features; | 
|  | adapter->connection_features = bottom->connection_features; | 
|  | adapter->peer_wwpn = 0; | 
|  | adapter->peer_wwnn = 0; | 
|  | adapter->peer_d_id = 0; | 
|  |  | 
|  | if (xchg_ok) { | 
|  |  | 
|  | if (fsf_req->data) | 
|  | memcpy((struct fsf_qtcb_bottom_config *) fsf_req->data, | 
|  | bottom, sizeof (struct fsf_qtcb_bottom_config)); | 
|  |  | 
|  | fc_host_node_name(shost) = bottom->nport_serv_param.wwnn; | 
|  | fc_host_port_name(shost) = bottom->nport_serv_param.wwpn; | 
|  | fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK; | 
|  | fc_host_speed(shost) = bottom->fc_link_speed; | 
|  | fc_host_supported_classes(shost) = | 
|  | FC_COS_CLASS2 | FC_COS_CLASS3; | 
|  | adapter->hydra_version = bottom->adapter_type; | 
|  | if (fc_host_permanent_port_name(shost) == -1) | 
|  | fc_host_permanent_port_name(shost) = | 
|  | fc_host_port_name(shost); | 
|  | if (bottom->fc_topology == FSF_TOPO_P2P) { | 
|  | adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK; | 
|  | adapter->peer_wwpn = bottom->plogi_payload.wwpn; | 
|  | adapter->peer_wwnn = bottom->plogi_payload.wwnn; | 
|  | fc_host_port_type(shost) = FC_PORTTYPE_PTP; | 
|  | } else if (bottom->fc_topology == FSF_TOPO_FABRIC) | 
|  | fc_host_port_type(shost) = FC_PORTTYPE_NPORT; | 
|  | else if (bottom->fc_topology == FSF_TOPO_AL) | 
|  | fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; | 
|  | else | 
|  | fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; | 
|  | } else { | 
|  | fc_host_node_name(shost) = 0; | 
|  | fc_host_port_name(shost) = 0; | 
|  | fc_host_port_id(shost) = 0; | 
|  | fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; | 
|  | fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; | 
|  | adapter->hydra_version = 0; | 
|  | } | 
|  |  | 
|  | if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) { | 
|  | adapter->hardware_version = bottom->hardware_version; | 
|  | memcpy(fc_host_serial_number(shost), bottom->serial_number, | 
|  | min(FC_SERIAL_NUMBER_SIZE, 17)); | 
|  | EBCASC(fc_host_serial_number(shost), | 
|  | min(FC_SERIAL_NUMBER_SIZE, 17)); | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_NORMAL("The adapter %s reported the following " | 
|  | "characteristics:\n" | 
|  | "WWNN 0x%016Lx, " | 
|  | "WWPN 0x%016Lx, " | 
|  | "S_ID 0x%06x,\n" | 
|  | "adapter version 0x%x, " | 
|  | "LIC version 0x%x, " | 
|  | "FC link speed %d Gb/s\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | (wwn_t) fc_host_node_name(shost), | 
|  | (wwn_t) fc_host_port_name(shost), | 
|  | fc_host_port_id(shost), | 
|  | adapter->hydra_version, | 
|  | adapter->fsf_lic_version, | 
|  | fc_host_speed(shost)); | 
|  | if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) { | 
|  | ZFCP_LOG_NORMAL("error: the adapter %s " | 
|  | "only supports newer control block " | 
|  | "versions in comparison to this device " | 
|  | "driver (try updated device driver)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | debug_text_event(adapter->erp_dbf, 0, "low_qtcb_ver"); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | return -EIO; | 
|  | } | 
|  | if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) { | 
|  | ZFCP_LOG_NORMAL("error: the adapter %s " | 
|  | "only supports older control block " | 
|  | "versions than this device driver uses" | 
|  | "(consider a microcode upgrade)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | debug_text_event(adapter->erp_dbf, 0, "high_qtcb_ver"); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | return -EIO; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * function:    zfcp_fsf_exchange_config_data_handler | 
|  | * | 
|  | * purpose:     is called for finished Exchange Configuration Data command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct fsf_qtcb_bottom_config *bottom; | 
|  | struct zfcp_adapter *adapter = fsf_req->adapter; | 
|  | struct fsf_qtcb *qtcb = fsf_req->qtcb; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) | 
|  | return -EIO; | 
|  |  | 
|  | switch (qtcb->header.fsf_status) { | 
|  |  | 
|  | case FSF_GOOD: | 
|  | if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1)) | 
|  | return -EIO; | 
|  |  | 
|  | switch (fc_host_port_type(adapter->scsi_host)) { | 
|  | case FC_PORTTYPE_PTP: | 
|  | ZFCP_LOG_NORMAL("Point-to-Point fibrechannel " | 
|  | "configuration detected at adapter %s\n" | 
|  | "Peer WWNN 0x%016llx, " | 
|  | "peer WWPN 0x%016llx, " | 
|  | "peer d_id 0x%06x\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | adapter->peer_wwnn, | 
|  | adapter->peer_wwpn, | 
|  | adapter->peer_d_id); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "top-p-to-p"); | 
|  | break; | 
|  | case FC_PORTTYPE_NLPORT: | 
|  | ZFCP_LOG_NORMAL("error: Arbitrated loop fibrechannel " | 
|  | "topology detected at adapter %s " | 
|  | "unsupported, shutting down adapter\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "top-al"); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | return -EIO; | 
|  | case FC_PORTTYPE_NPORT: | 
|  | ZFCP_LOG_NORMAL("Switched fabric fibrechannel " | 
|  | "network detected at adapter %s.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: The fibrechannel topology " | 
|  | "reported by the exchange " | 
|  | "configuration command for " | 
|  | "the adapter %s is not " | 
|  | "of a type known to the zfcp " | 
|  | "driver, shutting down adapter\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | debug_text_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | "unknown-topo"); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | return -EIO; | 
|  | } | 
|  | bottom = &qtcb->bottom.config; | 
|  | if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) { | 
|  | ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) " | 
|  | "allowed by the adapter %s " | 
|  | "is lower than the minimum " | 
|  | "required by the driver (%ld bytes).\n", | 
|  | bottom->max_qtcb_size, | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | sizeof(struct fsf_qtcb)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "qtcb-size"); | 
|  | debug_event(fsf_req->adapter->erp_dbf, 0, | 
|  | &bottom->max_qtcb_size, sizeof (u32)); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | return -EIO; | 
|  | } | 
|  | atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, | 
|  | &adapter->status); | 
|  | break; | 
|  | case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: | 
|  | debug_text_event(adapter->erp_dbf, 0, "xchg-inco"); | 
|  |  | 
|  | if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0)) | 
|  | return -EIO; | 
|  |  | 
|  | atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, | 
|  | &adapter->status); | 
|  |  | 
|  | zfcp_fsf_link_down_info_eval(adapter, | 
|  | &qtcb->header.fsf_status_qual.link_down_info); | 
|  | break; | 
|  | default: | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf-stat-ng"); | 
|  | debug_event(fsf_req->adapter->erp_dbf, 0, | 
|  | &fsf_req->qtcb->header.fsf_status, sizeof(u32)); | 
|  | zfcp_erp_adapter_shutdown(adapter, 0); | 
|  | return -EIO; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_exchange_port_data - request information about local port | 
|  | * @erp_action: ERP action for the adapter for which port data is requested | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | struct zfcp_adapter *adapter = erp_action->adapter; | 
|  | unsigned long lock_flags; | 
|  | int retval; | 
|  |  | 
|  | if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) { | 
|  | ZFCP_LOG_INFO("error: exchange port data " | 
|  | "command not supported by adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, | 
|  | ZFCP_REQ_AUTO_CLEANUP, | 
|  | adapter->pool.fsf_req_erp, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Out of resources. Could not create an " | 
|  | "exchange port data request for " | 
|  | "the adapter %s.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | erp_action->fsf_req = fsf_req; | 
|  | fsf_req->erp_action = erp_action; | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  |  | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  |  | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not send an exchange port data " | 
|  | "command on the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | erp_action->fsf_req = NULL; | 
|  | } | 
|  | else | 
|  | ZFCP_LOG_DEBUG("exchange port data request initiated " | 
|  | "(adapter %s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_exchange_port_data_sync - request information about local port | 
|  | * and wait until information is ready | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter, | 
|  | struct fsf_qtcb_bottom_port *data) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int retval; | 
|  |  | 
|  | if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) { | 
|  | ZFCP_LOG_INFO("error: exchange port data " | 
|  | "command not supported by adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, | 
|  | 0, NULL, &lock_flags, &fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Out of resources. Could not create an " | 
|  | "exchange port data request for " | 
|  | "the adapter %s.\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | if (data) | 
|  | fsf_req->data = (unsigned long) data; | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  |  | 
|  | if (retval) | 
|  | ZFCP_LOG_INFO("error: Could not send an exchange port data " | 
|  | "command on the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | else | 
|  | wait_event(fsf_req->completion_wq, | 
|  | fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); | 
|  |  | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_exchange_port_evaluate | 
|  | * @fsf_req: fsf_req which belongs to xchg port data request | 
|  | * @xchg_ok: specifies if xchg port data was incomplete or complete (0/1) | 
|  | */ | 
|  | static void | 
|  | zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) | 
|  | { | 
|  | struct zfcp_adapter *adapter; | 
|  | struct fsf_qtcb_bottom_port *bottom; | 
|  | struct Scsi_Host *shost; | 
|  |  | 
|  | adapter = fsf_req->adapter; | 
|  | bottom = &fsf_req->qtcb->bottom.port; | 
|  | shost = adapter->scsi_host; | 
|  |  | 
|  | if (fsf_req->data) | 
|  | memcpy((struct fsf_qtcb_bottom_port*) fsf_req->data, bottom, | 
|  | sizeof(struct fsf_qtcb_bottom_port)); | 
|  |  | 
|  | if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) | 
|  | fc_host_permanent_port_name(shost) = bottom->wwpn; | 
|  | else | 
|  | fc_host_permanent_port_name(shost) = fc_host_port_name(shost); | 
|  | fc_host_maxframe_size(shost) = bottom->maximum_frame_size; | 
|  | fc_host_supported_speeds(shost) = bottom->supported_speed; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_exchange_port_data_handler - handler for exchange_port_data request | 
|  | * @fsf_req: pointer to struct zfcp_fsf_req | 
|  | */ | 
|  | static void | 
|  | zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct zfcp_adapter *adapter; | 
|  | struct fsf_qtcb *qtcb; | 
|  |  | 
|  | adapter = fsf_req->adapter; | 
|  | qtcb = fsf_req->qtcb; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) | 
|  | return; | 
|  |  | 
|  | switch (qtcb->header.fsf_status) { | 
|  | case FSF_GOOD: | 
|  | zfcp_fsf_exchange_port_evaluate(fsf_req, 1); | 
|  | atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); | 
|  | break; | 
|  | case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: | 
|  | zfcp_fsf_exchange_port_evaluate(fsf_req, 0); | 
|  | atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); | 
|  | zfcp_fsf_link_down_info_eval(adapter, | 
|  | &qtcb->header.fsf_status_qual.link_down_info); | 
|  | break; | 
|  | default: | 
|  | debug_text_event(adapter->erp_dbf, 0, "xchg-port-ng"); | 
|  | debug_event(adapter->erp_dbf, 0, | 
|  | &fsf_req->qtcb->header.fsf_status, sizeof(u32)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_open_port | 
|  | * | 
|  | * purpose: | 
|  | * | 
|  | * returns:	address of initiated FSF request | 
|  | *		NULL - request could not be initiated | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int retval = 0; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(erp_action->adapter, | 
|  | FSF_QTCB_OPEN_PORT_WITH_DID, | 
|  | ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, | 
|  | erp_action->adapter->pool.fsf_req_erp, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create open port request " | 
|  | "for port 0x%016Lx on adapter %s.\n", | 
|  | erp_action->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter)); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id; | 
|  | atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status); | 
|  | fsf_req->data = (unsigned long) erp_action->port; | 
|  | fsf_req->erp_action = erp_action; | 
|  | erp_action->fsf_req = fsf_req; | 
|  |  | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not send open port request for " | 
|  | "port 0x%016Lx on adapter %s.\n", | 
|  | erp_action->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter)); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | erp_action->fsf_req = NULL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_DEBUG("open port request initiated " | 
|  | "(adapter %s,  port 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn); | 
|  | out: | 
|  | write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_open_port_handler | 
|  | * | 
|  | * purpose:	is called for finished Open Port command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = -EINVAL; | 
|  | struct zfcp_port *port; | 
|  | struct fsf_plogi *plogi; | 
|  | struct fsf_qtcb_header *header; | 
|  | u16 subtable, rule, counter; | 
|  |  | 
|  | port = (struct zfcp_port *) fsf_req->data; | 
|  | header = &fsf_req->qtcb->header; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | /* don't change port status in our bookkeeping */ | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (header->fsf_status) { | 
|  |  | 
|  | case FSF_PORT_ALREADY_OPEN: | 
|  | ZFCP_LOG_NORMAL("bug: remote port 0x%016Lx on adapter %s " | 
|  | "is already open.\n", | 
|  | port->wwpn, zfcp_get_busid_by_port(port)); | 
|  | debug_text_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_s_popen"); | 
|  | /* | 
|  | * This is a bug, however operation should continue normally | 
|  | * if it is simply ignored | 
|  | */ | 
|  | break; | 
|  |  | 
|  | case FSF_ACCESS_DENIED: | 
|  | ZFCP_LOG_NORMAL("Access denied, cannot open port 0x%016Lx " | 
|  | "on adapter %s\n", | 
|  | port->wwpn, zfcp_get_busid_by_port(port)); | 
|  | for (counter = 0; counter < 2; counter++) { | 
|  | subtable = header->fsf_status_qual.halfword[counter * 2]; | 
|  | rule = header->fsf_status_qual.halfword[counter * 2 + 1]; | 
|  | switch (subtable) { | 
|  | case FSF_SQ_CFDC_SUBTABLE_OS: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_DID: | 
|  | case FSF_SQ_CFDC_SUBTABLE_LUN: | 
|  | ZFCP_LOG_INFO("Access denied (%s rule %d)\n", | 
|  | zfcp_act_subtable_type[subtable], rule); | 
|  | break; | 
|  | } | 
|  | } | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); | 
|  | zfcp_erp_port_access_denied(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED: | 
|  | ZFCP_LOG_INFO("error: The FSF adapter is out of resources. " | 
|  | "The remote port 0x%016Lx on adapter %s " | 
|  | "could not be opened. Disabling it.\n", | 
|  | port->wwpn, zfcp_get_busid_by_port(port)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_max_ports"); | 
|  | zfcp_erp_port_failed(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (header->fsf_status_qual.word[0]) { | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ltest"); | 
|  | /* ERP strategy will escalate */ | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* ERP strategy will escalate */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ulp"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_NO_RETRY_POSSIBLE: | 
|  | ZFCP_LOG_NORMAL("The remote port 0x%016Lx on " | 
|  | "adapter %s could not be opened. " | 
|  | "Disabling it.\n", | 
|  | port->wwpn, | 
|  | zfcp_get_busid_by_port(port)); | 
|  | debug_text_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_sq_no_retry"); | 
|  | zfcp_erp_port_failed(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: Wrong status qualifier 0x%x arrived.\n", | 
|  | header->fsf_status_qual.word[0]); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_sq_inval:"); | 
|  | debug_exception( | 
|  | fsf_req->adapter->erp_dbf, 0, | 
|  | &header->fsf_status_qual.word[0], | 
|  | sizeof (u32)); | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_GOOD: | 
|  | /* save port handle assigned by FSF */ | 
|  | port->handle = header->port_handle; | 
|  | ZFCP_LOG_INFO("The remote port 0x%016Lx via adapter %s " | 
|  | "was opened, it's port handle is 0x%x\n", | 
|  | port->wwpn, zfcp_get_busid_by_port(port), | 
|  | port->handle); | 
|  | /* mark port as open */ | 
|  | atomic_set_mask(ZFCP_STATUS_COMMON_OPEN | | 
|  | ZFCP_STATUS_PORT_PHYS_OPEN, &port->status); | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | | 
|  | ZFCP_STATUS_COMMON_ACCESS_BOXED, | 
|  | &port->status); | 
|  | retval = 0; | 
|  | /* check whether D_ID has changed during open */ | 
|  | /* | 
|  | * FIXME: This check is not airtight, as the FCP channel does | 
|  | * not monitor closures of target port connections caused on | 
|  | * the remote side. Thus, they might miss out on invalidating | 
|  | * locally cached WWPNs (and other N_Port parameters) of gone | 
|  | * target ports. So, our heroic attempt to make things safe | 
|  | * could be undermined by 'open port' response data tagged with | 
|  | * obsolete WWPNs. Another reason to monitor potential | 
|  | * connection closures ourself at least (by interpreting | 
|  | * incoming ELS' and unsolicited status). It just crosses my | 
|  | * mind that one should be able to cross-check by means of | 
|  | * another GID_PN straight after a port has been opened. | 
|  | * Alternately, an ADISC/PDISC ELS should suffice, as well. | 
|  | */ | 
|  | plogi = (struct fsf_plogi *) fsf_req->qtcb->bottom.support.els; | 
|  | if (!atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN, &port->status)) | 
|  | { | 
|  | if (fsf_req->qtcb->bottom.support.els1_length < | 
|  | sizeof (struct fsf_plogi)) { | 
|  | ZFCP_LOG_INFO( | 
|  | "warning: insufficient length of " | 
|  | "PLOGI payload (%i)\n", | 
|  | fsf_req->qtcb->bottom.support.els1_length); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_s_short_plogi:"); | 
|  | /* skip sanity check and assume wwpn is ok */ | 
|  | } else { | 
|  | if (plogi->serv_param.wwpn != port->wwpn) { | 
|  | ZFCP_LOG_INFO("warning: d_id of port " | 
|  | "0x%016Lx changed during " | 
|  | "open\n", port->wwpn); | 
|  | debug_text_event( | 
|  | fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_s_did_change:"); | 
|  | atomic_clear_mask( | 
|  | ZFCP_STATUS_PORT_DID_DID, | 
|  | &port->status); | 
|  | } else { | 
|  | port->wwnn = plogi->serv_param.wwnn; | 
|  | zfcp_plogi_evaluate(port, plogi); | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_UNKNOWN_OP_SUBTYPE: | 
|  | /* should never occure, subtype not set in zfcp_fsf_open_port */ | 
|  | ZFCP_LOG_INFO("unknown operation subtype (adapter: %s, " | 
|  | "op_subtype=0x%x)\n", | 
|  | zfcp_get_busid_by_port(port), | 
|  | fsf_req->qtcb->bottom.support.operation_subtype); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " | 
|  | "(debug info 0x%x)\n", | 
|  | header->fsf_status); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); | 
|  | debug_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | &header->fsf_status, sizeof (u32)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &port->status); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_close_port | 
|  | * | 
|  | * purpose:     submit FSF command "close port" | 
|  | * | 
|  | * returns:     address of initiated FSF request | 
|  | *              NULL - request could not be initiated | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int retval = 0; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(erp_action->adapter, | 
|  | FSF_QTCB_CLOSE_PORT, | 
|  | ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, | 
|  | erp_action->adapter->pool.fsf_req_erp, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create a close port request " | 
|  | "for port 0x%016Lx on adapter %s.\n", | 
|  | erp_action->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter)); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status); | 
|  | fsf_req->data = (unsigned long) erp_action->port; | 
|  | fsf_req->erp_action = erp_action; | 
|  | fsf_req->qtcb->header.port_handle = erp_action->port->handle; | 
|  | fsf_req->erp_action = erp_action; | 
|  | erp_action->fsf_req = fsf_req; | 
|  |  | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not send a close port request for " | 
|  | "port 0x%016Lx on adapter %s.\n", | 
|  | erp_action->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter)); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | erp_action->fsf_req = NULL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_TRACE("close port request initiated " | 
|  | "(adapter %s, port 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn); | 
|  | out: | 
|  | write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_close_port_handler | 
|  | * | 
|  | * purpose:     is called for finished Close Port FSF command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_close_port_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = -EINVAL; | 
|  | struct zfcp_port *port; | 
|  |  | 
|  | port = (struct zfcp_port *) fsf_req->data; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | /* don't change port status in our bookkeeping */ | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (fsf_req->qtcb->header.fsf_status) { | 
|  |  | 
|  | case FSF_PORT_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_INFO("Temporary port identifier 0x%x for port " | 
|  | "0x%016Lx on adapter %s invalid. This may happen " | 
|  | "occasionally.\n", port->handle, | 
|  | port->wwpn, zfcp_get_busid_by_port(port)); | 
|  | ZFCP_LOG_DEBUG("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &fsf_req->qtcb->header.fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_phand_nv"); | 
|  | zfcp_erp_adapter_reopen(port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | /* Note: FSF has actually closed the port in this case. | 
|  | * The status code is just daft. Fingers crossed for a change | 
|  | */ | 
|  | retval = 0; | 
|  | break; | 
|  |  | 
|  | case FSF_GOOD: | 
|  | ZFCP_LOG_TRACE("remote port 0x016%Lx on adapter %s closed, " | 
|  | "port handle 0x%x\n", port->wwpn, | 
|  | zfcp_get_busid_by_port(port), port->handle); | 
|  | zfcp_erp_modify_port_status(port, | 
|  | ZFCP_STATUS_COMMON_OPEN, | 
|  | ZFCP_CLEAR); | 
|  | retval = 0; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " | 
|  | "(debug info 0x%x)\n", | 
|  | fsf_req->qtcb->header.fsf_status); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); | 
|  | debug_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | &fsf_req->qtcb->header.fsf_status, | 
|  | sizeof (u32)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &port->status); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_close_physical_port | 
|  | * | 
|  | * purpose:     submit FSF command "close physical port" | 
|  | * | 
|  | * returns:     address of initiated FSF request | 
|  | *              NULL - request could not be initiated | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int retval = 0; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(erp_action->adapter, | 
|  | FSF_QTCB_CLOSE_PHYSICAL_PORT, | 
|  | ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, | 
|  | erp_action->adapter->pool.fsf_req_erp, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create close physical port " | 
|  | "request (adapter %s, port 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn); | 
|  |  | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | /* mark port as being closed */ | 
|  | atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, | 
|  | &erp_action->port->status); | 
|  | /* save a pointer to this port */ | 
|  | fsf_req->data = (unsigned long) erp_action->port; | 
|  | fsf_req->qtcb->header.port_handle = erp_action->port->handle; | 
|  | fsf_req->erp_action = erp_action; | 
|  | erp_action->fsf_req = fsf_req; | 
|  |  | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not send close physical port " | 
|  | "request (adapter %s, port 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | erp_action->fsf_req = NULL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_TRACE("close physical port request initiated " | 
|  | "(adapter %s, port 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn); | 
|  | out: | 
|  | write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_close_physical_port_handler | 
|  | * | 
|  | * purpose:     is called for finished Close Physical Port FSF command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = -EINVAL; | 
|  | struct zfcp_port *port; | 
|  | struct zfcp_unit *unit; | 
|  | struct fsf_qtcb_header *header; | 
|  | u16 subtable, rule, counter; | 
|  |  | 
|  | port = (struct zfcp_port *) fsf_req->data; | 
|  | header = &fsf_req->qtcb->header; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | /* don't change port status in our bookkeeping */ | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (header->fsf_status) { | 
|  |  | 
|  | case FSF_PORT_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_INFO("Temporary port identifier 0x%x invalid" | 
|  | "(adapter %s, port 0x%016Lx). " | 
|  | "This may happen occasionally.\n", | 
|  | port->handle, | 
|  | zfcp_get_busid_by_port(port), | 
|  | port->wwpn); | 
|  | ZFCP_LOG_DEBUG("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_phand_nv"); | 
|  | zfcp_erp_adapter_reopen(port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ACCESS_DENIED: | 
|  | ZFCP_LOG_NORMAL("Access denied, cannot close " | 
|  | "physical port 0x%016Lx on adapter %s\n", | 
|  | port->wwpn, zfcp_get_busid_by_port(port)); | 
|  | for (counter = 0; counter < 2; counter++) { | 
|  | subtable = header->fsf_status_qual.halfword[counter * 2]; | 
|  | rule = header->fsf_status_qual.halfword[counter * 2 + 1]; | 
|  | switch (subtable) { | 
|  | case FSF_SQ_CFDC_SUBTABLE_OS: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_DID: | 
|  | case FSF_SQ_CFDC_SUBTABLE_LUN: | 
|  | ZFCP_LOG_INFO("Access denied (%s rule %d)\n", | 
|  | zfcp_act_subtable_type[subtable], rule); | 
|  | break; | 
|  | } | 
|  | } | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); | 
|  | zfcp_erp_port_access_denied(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PORT_BOXED: | 
|  | ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter " | 
|  | "%s needs to be reopened but it was attempted " | 
|  | "to close it physically.\n", | 
|  | port->wwpn, | 
|  | zfcp_get_busid_by_port(port)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_pboxed"); | 
|  | zfcp_erp_port_boxed(port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | | 
|  | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (header->fsf_status_qual.word[0]) { | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ltest"); | 
|  | /* This will now be escalated by ERP */ | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* ERP strategy will escalate */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ulp"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: Wrong status qualifier 0x%x arrived.\n", | 
|  | header->fsf_status_qual.word[0]); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_sq_inval:"); | 
|  | debug_exception( | 
|  | fsf_req->adapter->erp_dbf, 0, | 
|  | &header->fsf_status_qual.word[0], sizeof (u32)); | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_GOOD: | 
|  | ZFCP_LOG_DEBUG("Remote port 0x%016Lx via adapter %s " | 
|  | "physically closed, port handle 0x%x\n", | 
|  | port->wwpn, | 
|  | zfcp_get_busid_by_port(port), port->handle); | 
|  | /* can't use generic zfcp_erp_modify_port_status because | 
|  | * ZFCP_STATUS_COMMON_OPEN must not be reset for the port | 
|  | */ | 
|  | atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status); | 
|  | list_for_each_entry(unit, &port->unit_list_head, list) | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status); | 
|  | retval = 0; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " | 
|  | "(debug info 0x%x)\n", | 
|  | header->fsf_status); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); | 
|  | debug_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | &header->fsf_status, sizeof (u32)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &port->status); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_open_unit | 
|  | * | 
|  | * purpose: | 
|  | * | 
|  | * returns: | 
|  | * | 
|  | * assumptions:	This routine does not check whether the associated | 
|  | *		remote port has already been opened. This should be | 
|  | *		done by calling routines. Otherwise some status | 
|  | *		may be presented by FSF | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int retval = 0; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(erp_action->adapter, | 
|  | FSF_QTCB_OPEN_LUN, | 
|  | ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, | 
|  | erp_action->adapter->pool.fsf_req_erp, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create open unit request for " | 
|  | "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n", | 
|  | erp_action->unit->fcp_lun, | 
|  | erp_action->unit->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter)); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | fsf_req->qtcb->header.port_handle = erp_action->port->handle; | 
|  | fsf_req->qtcb->bottom.support.fcp_lun =	erp_action->unit->fcp_lun; | 
|  | if (!(erp_action->adapter->connection_features & FSF_FEATURE_NPIV_MODE)) | 
|  | fsf_req->qtcb->bottom.support.option = | 
|  | FSF_OPEN_LUN_SUPPRESS_BOXING; | 
|  | atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status); | 
|  | fsf_req->data = (unsigned long) erp_action->unit; | 
|  | fsf_req->erp_action = erp_action; | 
|  | erp_action->fsf_req = fsf_req; | 
|  |  | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  | retval = zfcp_fsf_req_send(erp_action->fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not send an open unit request " | 
|  | "on the adapter %s, port 0x%016Lx for " | 
|  | "unit 0x%016Lx\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn, | 
|  | erp_action->unit->fcp_lun); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | erp_action->fsf_req = NULL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_TRACE("Open LUN request initiated (adapter %s, " | 
|  | "port 0x%016Lx, unit 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn, erp_action->unit->fcp_lun); | 
|  | out: | 
|  | write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_open_unit_handler | 
|  | * | 
|  | * purpose:	is called for finished Open LUN command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = -EINVAL; | 
|  | struct zfcp_adapter *adapter; | 
|  | struct zfcp_unit *unit; | 
|  | struct fsf_qtcb_header *header; | 
|  | struct fsf_qtcb_bottom_support *bottom; | 
|  | struct fsf_queue_designator *queue_designator; | 
|  | u16 subtable, rule, counter; | 
|  | int exclusive, readwrite; | 
|  |  | 
|  | unit = (struct zfcp_unit *) fsf_req->data; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | /* don't change unit status in our bookkeeping */ | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | adapter = fsf_req->adapter; | 
|  | header = &fsf_req->qtcb->header; | 
|  | bottom = &fsf_req->qtcb->bottom.support; | 
|  | queue_designator = &header->fsf_status_qual.fsf_queue_designator; | 
|  |  | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | | 
|  | ZFCP_STATUS_COMMON_ACCESS_BOXED | | 
|  | ZFCP_STATUS_UNIT_SHARED | | 
|  | ZFCP_STATUS_UNIT_READONLY, | 
|  | &unit->status); | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (header->fsf_status) { | 
|  |  | 
|  | case FSF_PORT_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_INFO("Temporary port identifier 0x%x " | 
|  | "for port 0x%016Lx on adapter %s invalid " | 
|  | "This may happen occasionally\n", | 
|  | unit->port->handle, | 
|  | unit->port->wwpn, zfcp_get_busid_by_unit(unit)); | 
|  | ZFCP_LOG_DEBUG("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_s_ph_nv"); | 
|  | zfcp_erp_adapter_reopen(unit->port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_LUN_ALREADY_OPEN: | 
|  | ZFCP_LOG_NORMAL("bug: Attempted to open unit 0x%016Lx on " | 
|  | "remote port 0x%016Lx on adapter %s twice.\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, zfcp_get_busid_by_unit(unit)); | 
|  | debug_text_exception(adapter->erp_dbf, 0, | 
|  | "fsf_s_uopen"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ACCESS_DENIED: | 
|  | ZFCP_LOG_NORMAL("Access denied, cannot open unit 0x%016Lx on " | 
|  | "remote port 0x%016Lx on adapter %s\n", | 
|  | unit->fcp_lun, unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | for (counter = 0; counter < 2; counter++) { | 
|  | subtable = header->fsf_status_qual.halfword[counter * 2]; | 
|  | rule = header->fsf_status_qual.halfword[counter * 2 + 1]; | 
|  | switch (subtable) { | 
|  | case FSF_SQ_CFDC_SUBTABLE_OS: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_DID: | 
|  | case FSF_SQ_CFDC_SUBTABLE_LUN: | 
|  | ZFCP_LOG_INFO("Access denied (%s rule %d)\n", | 
|  | zfcp_act_subtable_type[subtable], rule); | 
|  | break; | 
|  | } | 
|  | } | 
|  | debug_text_event(adapter->erp_dbf, 1, "fsf_s_access"); | 
|  | zfcp_erp_unit_access_denied(unit); | 
|  | atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status); | 
|  | atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PORT_BOXED: | 
|  | ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s " | 
|  | "needs to be reopened\n", | 
|  | unit->port->wwpn, zfcp_get_busid_by_unit(unit)); | 
|  | debug_text_event(adapter->erp_dbf, 2, "fsf_s_pboxed"); | 
|  | zfcp_erp_port_boxed(unit->port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | | 
|  | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | case FSF_LUN_SHARING_VIOLATION: | 
|  | if (header->fsf_status_qual.word[0] != 0) { | 
|  | ZFCP_LOG_NORMAL("FCP-LUN 0x%Lx at the remote port " | 
|  | "with WWPN 0x%Lx " | 
|  | "connected to the adapter %s " | 
|  | "is already in use in LPAR%d, CSS%d\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | queue_designator->hla, | 
|  | queue_designator->cssid); | 
|  | } else { | 
|  | subtable = header->fsf_status_qual.halfword[4]; | 
|  | rule = header->fsf_status_qual.halfword[5]; | 
|  | switch (subtable) { | 
|  | case FSF_SQ_CFDC_SUBTABLE_OS: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_DID: | 
|  | case FSF_SQ_CFDC_SUBTABLE_LUN: | 
|  | ZFCP_LOG_NORMAL("Access to FCP-LUN 0x%Lx at the " | 
|  | "remote port with WWPN 0x%Lx " | 
|  | "connected to the adapter %s " | 
|  | "is denied (%s rule %d)\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | zfcp_act_subtable_type[subtable], | 
|  | rule); | 
|  | break; | 
|  | } | 
|  | } | 
|  | ZFCP_LOG_DEBUG("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(adapter->erp_dbf, 2, | 
|  | "fsf_s_l_sh_vio"); | 
|  | zfcp_erp_unit_access_denied(unit); | 
|  | atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status); | 
|  | atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED: | 
|  | ZFCP_LOG_INFO("error: The adapter ran out of resources. " | 
|  | "There is no handle (temporary port identifier) " | 
|  | "available for unit 0x%016Lx on port 0x%016Lx " | 
|  | "on adapter %s\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | debug_text_event(adapter->erp_dbf, 1, | 
|  | "fsf_s_max_units"); | 
|  | zfcp_erp_unit_failed(unit); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (header->fsf_status_qual.word[0]) { | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | /* Re-establish link to port */ | 
|  | debug_text_event(adapter->erp_dbf, 1, | 
|  | "fsf_sq_ltest"); | 
|  | zfcp_test_link(unit->port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* ERP strategy will escalate */ | 
|  | debug_text_event(adapter->erp_dbf, 1, | 
|  | "fsf_sq_ulp"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: Wrong status qualifier 0x%x arrived.\n", | 
|  | header->fsf_status_qual.word[0]); | 
|  | debug_text_event(adapter->erp_dbf, 0, | 
|  | "fsf_sq_inval:"); | 
|  | debug_exception(adapter->erp_dbf, 0, | 
|  | &header->fsf_status_qual.word[0], | 
|  | sizeof (u32)); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_INVALID_COMMAND_OPTION: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "Invalid option 0x%x has been specified " | 
|  | "in QTCB bottom sent to the adapter %s\n", | 
|  | bottom->option, | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EINVAL; | 
|  | break; | 
|  |  | 
|  | case FSF_GOOD: | 
|  | /* save LUN handle assigned by FSF */ | 
|  | unit->handle = header->lun_handle; | 
|  | ZFCP_LOG_TRACE("unit 0x%016Lx on remote port 0x%016Lx on " | 
|  | "adapter %s opened, port handle 0x%x\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | unit->handle); | 
|  | /* mark unit as open */ | 
|  | atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status); | 
|  |  | 
|  | if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE) && | 
|  | (adapter->adapter_features & FSF_FEATURE_LUN_SHARING) && | 
|  | (adapter->ccw_device->id.dev_model != ZFCP_DEVICE_MODEL_PRIV)) { | 
|  | exclusive = (bottom->lun_access_info & | 
|  | FSF_UNIT_ACCESS_EXCLUSIVE); | 
|  | readwrite = (bottom->lun_access_info & | 
|  | FSF_UNIT_ACCESS_OUTBOUND_TRANSFER); | 
|  |  | 
|  | if (!exclusive) | 
|  | atomic_set_mask(ZFCP_STATUS_UNIT_SHARED, | 
|  | &unit->status); | 
|  |  | 
|  | if (!readwrite) { | 
|  | atomic_set_mask(ZFCP_STATUS_UNIT_READONLY, | 
|  | &unit->status); | 
|  | ZFCP_LOG_NORMAL("read-only access for unit " | 
|  | "(adapter %s, wwpn=0x%016Lx, " | 
|  | "fcp_lun=0x%016Lx)\n", | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | unit->port->wwpn, | 
|  | unit->fcp_lun); | 
|  | } | 
|  |  | 
|  | if (exclusive && !readwrite) { | 
|  | ZFCP_LOG_NORMAL("exclusive access of read-only " | 
|  | "unit not supported\n"); | 
|  | zfcp_erp_unit_failed(unit); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | zfcp_erp_unit_shutdown(unit, 0); | 
|  | } else if (!exclusive && readwrite) { | 
|  | ZFCP_LOG_NORMAL("shared access of read-write " | 
|  | "unit not supported\n"); | 
|  | zfcp_erp_unit_failed(unit); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | zfcp_erp_unit_shutdown(unit, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | retval = 0; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " | 
|  | "(debug info 0x%x)\n", | 
|  | header->fsf_status); | 
|  | debug_text_event(adapter->erp_dbf, 0, "fsf_s_inval:"); | 
|  | debug_exception(adapter->erp_dbf, 0, | 
|  | &header->fsf_status, sizeof (u32)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &unit->status); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_close_unit | 
|  | * | 
|  | * purpose: | 
|  | * | 
|  | * returns:	address of fsf_req - request successfully initiated | 
|  | *		NULL - | 
|  | * | 
|  | * assumptions: This routine does not check whether the associated | 
|  | *              remote port/lun has already been opened. This should be | 
|  | *              done by calling routines. Otherwise some status | 
|  | *              may be presented by FSF | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | unsigned long lock_flags; | 
|  | int retval = 0; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(erp_action->adapter, | 
|  | FSF_QTCB_CLOSE_LUN, | 
|  | ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, | 
|  | erp_action->adapter->pool.fsf_req_erp, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create close unit request for " | 
|  | "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n", | 
|  | erp_action->unit->fcp_lun, | 
|  | erp_action->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter)); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | fsf_req->qtcb->header.port_handle = erp_action->port->handle; | 
|  | fsf_req->qtcb->header.lun_handle = erp_action->unit->handle; | 
|  | atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status); | 
|  | fsf_req->data = (unsigned long) erp_action->unit; | 
|  | fsf_req->erp_action = erp_action; | 
|  | erp_action->fsf_req = fsf_req; | 
|  |  | 
|  | zfcp_erp_start_timer(fsf_req); | 
|  | retval = zfcp_fsf_req_send(erp_action->fsf_req); | 
|  | if (retval) { | 
|  | ZFCP_LOG_INFO("error: Could not send a close unit request for " | 
|  | "unit 0x%016Lx on port 0x%016Lx onadapter %s.\n", | 
|  | erp_action->unit->fcp_lun, | 
|  | erp_action->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter)); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | erp_action->fsf_req = NULL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_TRACE("Close LUN request initiated (adapter %s, " | 
|  | "port 0x%016Lx, unit 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(erp_action->adapter), | 
|  | erp_action->port->wwpn, erp_action->unit->fcp_lun); | 
|  | out: | 
|  | write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, | 
|  | lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_close_unit_handler | 
|  | * | 
|  | * purpose:     is called for finished Close LUN FSF command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = -EINVAL; | 
|  | struct zfcp_unit *unit; | 
|  |  | 
|  | unit = (struct zfcp_unit *) fsf_req->data; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | /* don't change unit status in our bookkeeping */ | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (fsf_req->qtcb->header.fsf_status) { | 
|  |  | 
|  | case FSF_PORT_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_INFO("Temporary port identifier 0x%x for port " | 
|  | "0x%016Lx on adapter %s invalid. This may " | 
|  | "happen in rare circumstances\n", | 
|  | unit->port->handle, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | ZFCP_LOG_DEBUG("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &fsf_req->qtcb->header.fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_phand_nv"); | 
|  | zfcp_erp_adapter_reopen(unit->port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_LUN_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_INFO("Temporary LUN identifier 0x%x of unit " | 
|  | "0x%016Lx on port 0x%016Lx on adapter %s is " | 
|  | "invalid. This may happen occasionally.\n", | 
|  | unit->handle, | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | ZFCP_LOG_DEBUG("Status qualifier data:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &fsf_req->qtcb->header.fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_lhand_nv"); | 
|  | zfcp_erp_port_reopen(unit->port, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PORT_BOXED: | 
|  | ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s " | 
|  | "needs to be reopened\n", | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed"); | 
|  | zfcp_erp_port_boxed(unit->port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | | 
|  | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | /* re-establish link to port */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ltest"); | 
|  | zfcp_test_link(unit->port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* ERP strategy will escalate */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ulp"); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: Wrong status qualifier 0x%x arrived.\n", | 
|  | fsf_req->qtcb->header.fsf_status_qual.word[0]); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_sq_inval:"); | 
|  | debug_exception( | 
|  | fsf_req->adapter->erp_dbf, 0, | 
|  | &fsf_req->qtcb->header.fsf_status_qual.word[0], | 
|  | sizeof (u32)); | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FSF_GOOD: | 
|  | ZFCP_LOG_TRACE("unit 0x%016Lx on port 0x%016Lx on adapter %s " | 
|  | "closed, port handle 0x%x\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | unit->handle); | 
|  | /* mark unit as closed */ | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status); | 
|  | retval = 0; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " | 
|  | "(debug info 0x%x)\n", | 
|  | fsf_req->qtcb->header.fsf_status); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); | 
|  | debug_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | &fsf_req->qtcb->header.fsf_status, | 
|  | sizeof (u32)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &unit->status); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command) | 
|  | * @adapter: adapter where scsi command is issued | 
|  | * @unit: unit where command is sent to | 
|  | * @scsi_cmnd: scsi command to be sent | 
|  | * @timer: timer to be started when request is initiated | 
|  | * @req_flags: flags for fsf_request | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, | 
|  | struct zfcp_unit *unit, | 
|  | struct scsi_cmnd * scsi_cmnd, | 
|  | int use_timer, int req_flags) | 
|  | { | 
|  | struct zfcp_fsf_req *fsf_req = NULL; | 
|  | struct fcp_cmnd_iu *fcp_cmnd_iu; | 
|  | unsigned int sbtype; | 
|  | unsigned long lock_flags; | 
|  | int real_bytes = 0; | 
|  | int retval = 0; | 
|  | int mask; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags, | 
|  | adapter->pool.fsf_req_scsi, | 
|  | &lock_flags, &fsf_req); | 
|  | if (unlikely(retval < 0)) { | 
|  | ZFCP_LOG_DEBUG("error: Could not create FCP command request " | 
|  | "for unit 0x%016Lx on port 0x%016Lx on " | 
|  | "adapter %s\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | goto failed_req_create; | 
|  | } | 
|  |  | 
|  | if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, | 
|  | &unit->status))) { | 
|  | retval = -EBUSY; | 
|  | goto unit_blocked; | 
|  | } | 
|  |  | 
|  | zfcp_unit_get(unit); | 
|  | fsf_req->unit = unit; | 
|  |  | 
|  | /* associate FSF request with SCSI request (for look up on abort) */ | 
|  | scsi_cmnd->host_scribble = (unsigned char *) fsf_req->req_id; | 
|  |  | 
|  | /* associate SCSI command with FSF request */ | 
|  | fsf_req->data = (unsigned long) scsi_cmnd; | 
|  |  | 
|  | /* set handles of unit and its parent port in QTCB */ | 
|  | fsf_req->qtcb->header.lun_handle = unit->handle; | 
|  | fsf_req->qtcb->header.port_handle = unit->port->handle; | 
|  |  | 
|  | /* FSF does not define the structure of the FCP_CMND IU */ | 
|  | fcp_cmnd_iu = (struct fcp_cmnd_iu *) | 
|  | &(fsf_req->qtcb->bottom.io.fcp_cmnd); | 
|  |  | 
|  | /* | 
|  | * set depending on data direction: | 
|  | *      data direction bits in SBALE (SB Type) | 
|  | *      data direction bits in QTCB | 
|  | *      data direction bits in FCP_CMND IU | 
|  | */ | 
|  | switch (scsi_cmnd->sc_data_direction) { | 
|  | case DMA_NONE: | 
|  | fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND; | 
|  | /* | 
|  | * FIXME(qdio): | 
|  | * what is the correct type for commands | 
|  | * without 'real' data buffers? | 
|  | */ | 
|  | sbtype = SBAL_FLAGS0_TYPE_READ; | 
|  | break; | 
|  | case DMA_FROM_DEVICE: | 
|  | fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ; | 
|  | sbtype = SBAL_FLAGS0_TYPE_READ; | 
|  | fcp_cmnd_iu->rddata = 1; | 
|  | break; | 
|  | case DMA_TO_DEVICE: | 
|  | fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE; | 
|  | sbtype = SBAL_FLAGS0_TYPE_WRITE; | 
|  | fcp_cmnd_iu->wddata = 1; | 
|  | break; | 
|  | case DMA_BIDIRECTIONAL: | 
|  | default: | 
|  | /* | 
|  | * dummy, catch this condition earlier | 
|  | * in zfcp_scsi_queuecommand | 
|  | */ | 
|  | goto failed_scsi_cmnd; | 
|  | } | 
|  |  | 
|  | /* set FC service class in QTCB (3 per default) */ | 
|  | fsf_req->qtcb->bottom.io.service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT; | 
|  |  | 
|  | /* set FCP_LUN in FCP_CMND IU in QTCB */ | 
|  | fcp_cmnd_iu->fcp_lun = unit->fcp_lun; | 
|  |  | 
|  | mask = ZFCP_STATUS_UNIT_READONLY | ZFCP_STATUS_UNIT_SHARED; | 
|  |  | 
|  | /* set task attributes in FCP_CMND IU in QTCB */ | 
|  | if (likely((scsi_cmnd->device->simple_tags) || | 
|  | (atomic_test_mask(mask, &unit->status)))) | 
|  | fcp_cmnd_iu->task_attribute = SIMPLE_Q; | 
|  | else | 
|  | fcp_cmnd_iu->task_attribute = UNTAGGED; | 
|  |  | 
|  | /* set additional length of FCP_CDB in FCP_CMND IU in QTCB, if needed */ | 
|  | if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH)) { | 
|  | fcp_cmnd_iu->add_fcp_cdb_length | 
|  | = (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2; | 
|  | ZFCP_LOG_TRACE("SCSI CDB length is 0x%x, " | 
|  | "additional FCP_CDB length is 0x%x " | 
|  | "(shifted right 2 bits)\n", | 
|  | scsi_cmnd->cmd_len, | 
|  | fcp_cmnd_iu->add_fcp_cdb_length); | 
|  | } | 
|  | /* | 
|  | * copy SCSI CDB (including additional length, if any) to | 
|  | * FCP_CDB in FCP_CMND IU in QTCB | 
|  | */ | 
|  | memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len); | 
|  |  | 
|  | /* FCP CMND IU length in QTCB */ | 
|  | fsf_req->qtcb->bottom.io.fcp_cmnd_length = | 
|  | sizeof (struct fcp_cmnd_iu) + | 
|  | fcp_cmnd_iu->add_fcp_cdb_length + sizeof (fcp_dl_t); | 
|  |  | 
|  | /* generate SBALEs from data buffer */ | 
|  | real_bytes = zfcp_qdio_sbals_from_scsicmnd(fsf_req, sbtype, scsi_cmnd); | 
|  | if (unlikely(real_bytes < 0)) { | 
|  | if (fsf_req->sbal_number < ZFCP_MAX_SBALS_PER_REQ) { | 
|  | ZFCP_LOG_DEBUG( | 
|  | "Data did not fit into available buffer(s), " | 
|  | "waiting for more...\n"); | 
|  | retval = -EIO; | 
|  | } else { | 
|  | ZFCP_LOG_NORMAL("error: No truncation implemented but " | 
|  | "required. Shutting down unit " | 
|  | "(adapter %s, port 0x%016Lx, " | 
|  | "unit 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | unit->port->wwpn, | 
|  | unit->fcp_lun); | 
|  | zfcp_erp_unit_shutdown(unit, 0); | 
|  | retval = -EINVAL; | 
|  | } | 
|  | goto no_fit; | 
|  | } | 
|  |  | 
|  | /* set length of FCP data length in FCP_CMND IU in QTCB */ | 
|  | zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes); | 
|  |  | 
|  | ZFCP_LOG_DEBUG("Sending SCSI command:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) scsi_cmnd->cmnd, scsi_cmnd->cmd_len); | 
|  |  | 
|  | if (use_timer) | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); | 
|  |  | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (unlikely(retval < 0)) { | 
|  | ZFCP_LOG_INFO("error: Could not send FCP command request " | 
|  | "on adapter %s, port 0x%016Lx, unit 0x%016Lx\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | unit->port->wwpn, | 
|  | unit->fcp_lun); | 
|  | goto send_failed; | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_TRACE("Send FCP Command initiated (adapter %s, " | 
|  | "port 0x%016Lx, unit 0x%016Lx)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | unit->port->wwpn, | 
|  | unit->fcp_lun); | 
|  | goto success; | 
|  |  | 
|  | send_failed: | 
|  | no_fit: | 
|  | failed_scsi_cmnd: | 
|  | unit_blocked: | 
|  | zfcp_unit_put(unit); | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | fsf_req = NULL; | 
|  | scsi_cmnd->host_scribble = NULL; | 
|  | success: | 
|  | failed_req_create: | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | struct zfcp_fsf_req * | 
|  | zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter, | 
|  | struct zfcp_unit *unit, | 
|  | u8 tm_flags, int req_flags) | 
|  | { | 
|  | struct zfcp_fsf_req *fsf_req = NULL; | 
|  | int retval = 0; | 
|  | struct fcp_cmnd_iu *fcp_cmnd_iu; | 
|  | unsigned long lock_flags; | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  |  | 
|  | /* setup new FSF request */ | 
|  | retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags, | 
|  | adapter->pool.fsf_req_scsi, | 
|  | &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create FCP command (task " | 
|  | "management) request for adapter %s, port " | 
|  | " 0x%016Lx, unit 0x%016Lx.\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | unit->port->wwpn, unit->fcp_lun); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, | 
|  | &unit->status))) | 
|  | goto unit_blocked; | 
|  |  | 
|  | /* | 
|  | * Used to decide on proper handler in the return path, | 
|  | * could be either zfcp_fsf_send_fcp_command_task_handler or | 
|  | * zfcp_fsf_send_fcp_command_task_management_handler */ | 
|  |  | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT; | 
|  |  | 
|  | /* | 
|  | * hold a pointer to the unit being target of this | 
|  | * task management request | 
|  | */ | 
|  | fsf_req->data = (unsigned long) unit; | 
|  |  | 
|  | /* set FSF related fields in QTCB */ | 
|  | fsf_req->qtcb->header.lun_handle = unit->handle; | 
|  | fsf_req->qtcb->header.port_handle = unit->port->handle; | 
|  | fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND; | 
|  | fsf_req->qtcb->bottom.io.service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT; | 
|  | fsf_req->qtcb->bottom.io.fcp_cmnd_length = | 
|  | sizeof (struct fcp_cmnd_iu) + sizeof (fcp_dl_t); | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE; | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | /* set FCP related fields in FCP_CMND IU in QTCB */ | 
|  | fcp_cmnd_iu = (struct fcp_cmnd_iu *) | 
|  | &(fsf_req->qtcb->bottom.io.fcp_cmnd); | 
|  | fcp_cmnd_iu->fcp_lun = unit->fcp_lun; | 
|  | fcp_cmnd_iu->task_management_flags = tm_flags; | 
|  |  | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (!retval) | 
|  | goto out; | 
|  |  | 
|  | unit_blocked: | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | fsf_req = NULL; | 
|  |  | 
|  | out: | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  | return fsf_req; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_send_fcp_command_handler | 
|  | * | 
|  | * purpose:	is called for finished Send FCP Command | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = -EINVAL; | 
|  | struct zfcp_unit *unit; | 
|  | struct fsf_qtcb_header *header; | 
|  | u16 subtable, rule, counter; | 
|  |  | 
|  | header = &fsf_req->qtcb->header; | 
|  |  | 
|  | if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)) | 
|  | unit = (struct zfcp_unit *) fsf_req->data; | 
|  | else | 
|  | unit = fsf_req->unit; | 
|  |  | 
|  | if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) { | 
|  | /* go directly to calls of special handlers */ | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* evaluate FSF status in QTCB */ | 
|  | switch (header->fsf_status) { | 
|  |  | 
|  | case FSF_PORT_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_INFO("Temporary port identifier 0x%x for port " | 
|  | "0x%016Lx on adapter %s invalid\n", | 
|  | unit->port->handle, | 
|  | unit->port->wwpn, zfcp_get_busid_by_unit(unit)); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_phand_nv"); | 
|  | zfcp_erp_adapter_reopen(unit->port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_LUN_HANDLE_NOT_VALID: | 
|  | ZFCP_LOG_INFO("Temporary LUN identifier 0x%x for unit " | 
|  | "0x%016Lx on port 0x%016Lx on adapter %s is " | 
|  | "invalid. This may happen occasionally.\n", | 
|  | unit->handle, | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | ZFCP_LOG_NORMAL("Status qualifier data:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_uhand_nv"); | 
|  | zfcp_erp_port_reopen(unit->port, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_HANDLE_MISMATCH: | 
|  | ZFCP_LOG_NORMAL("bug: The port handle 0x%x has changed " | 
|  | "unexpectedly. (adapter %s, port 0x%016Lx, " | 
|  | "unit 0x%016Lx)\n", | 
|  | unit->port->handle, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | unit->port->wwpn, | 
|  | unit->fcp_lun); | 
|  | ZFCP_LOG_NORMAL("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_hand_mis"); | 
|  | zfcp_erp_adapter_reopen(unit->port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_SERVICE_CLASS_NOT_SUPPORTED: | 
|  | ZFCP_LOG_INFO("error: adapter %s does not support fc " | 
|  | "class %d.\n", | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | ZFCP_FC_SERVICE_CLASS_DEFAULT); | 
|  | /* stop operation for this adapter */ | 
|  | debug_text_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_s_class_nsup"); | 
|  | zfcp_erp_adapter_shutdown(unit->port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_FCPLUN_NOT_VALID: | 
|  | ZFCP_LOG_NORMAL("bug: unit 0x%016Lx on port 0x%016Lx on " | 
|  | "adapter %s does not have correct unit " | 
|  | "handle 0x%x\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | unit->handle); | 
|  | ZFCP_LOG_DEBUG("status qualifier:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &header->fsf_status_qual, | 
|  | sizeof (union fsf_status_qual)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_s_fcp_lun_nv"); | 
|  | zfcp_erp_port_reopen(unit->port, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_ACCESS_DENIED: | 
|  | ZFCP_LOG_NORMAL("Access denied, cannot send FCP command to " | 
|  | "unit 0x%016Lx on port 0x%016Lx on " | 
|  | "adapter %s\n",	unit->fcp_lun, unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | for (counter = 0; counter < 2; counter++) { | 
|  | subtable = header->fsf_status_qual.halfword[counter * 2]; | 
|  | rule = header->fsf_status_qual.halfword[counter * 2 + 1]; | 
|  | switch (subtable) { | 
|  | case FSF_SQ_CFDC_SUBTABLE_OS: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: | 
|  | case FSF_SQ_CFDC_SUBTABLE_PORT_DID: | 
|  | case FSF_SQ_CFDC_SUBTABLE_LUN: | 
|  | ZFCP_LOG_INFO("Access denied (%s rule %d)\n", | 
|  | zfcp_act_subtable_type[subtable], rule); | 
|  | break; | 
|  | } | 
|  | } | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); | 
|  | zfcp_erp_unit_access_denied(unit); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_DIRECTION_INDICATOR_NOT_VALID: | 
|  | ZFCP_LOG_INFO("bug: Invalid data direction given for unit " | 
|  | "0x%016Lx on port 0x%016Lx on adapter %s " | 
|  | "(debug info %d)\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | fsf_req->qtcb->bottom.io.data_direction); | 
|  | /* stop operation for this adapter */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_s_dir_ind_nv"); | 
|  | zfcp_erp_adapter_shutdown(unit->port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_CMND_LENGTH_NOT_VALID: | 
|  | ZFCP_LOG_NORMAL | 
|  | ("bug: An invalid control-data-block length field " | 
|  | "was found in a command for unit 0x%016Lx on port " | 
|  | "0x%016Lx on adapter %s " "(debug info %d)\n", | 
|  | unit->fcp_lun, unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | fsf_req->qtcb->bottom.io.fcp_cmnd_length); | 
|  | /* stop operation for this adapter */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_s_cmd_len_nv"); | 
|  | zfcp_erp_adapter_shutdown(unit->port->adapter, 0); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_PORT_BOXED: | 
|  | ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s " | 
|  | "needs to be reopened\n", | 
|  | unit->port->wwpn, zfcp_get_busid_by_unit(unit)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed"); | 
|  | zfcp_erp_port_boxed(unit->port); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | | 
|  | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | case FSF_LUN_BOXED: | 
|  | ZFCP_LOG_NORMAL("unit needs to be reopened (adapter %s, " | 
|  | "wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n", | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | unit->port->wwpn, unit->fcp_lun); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_lboxed"); | 
|  | zfcp_erp_unit_boxed(unit); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | 
|  | | ZFCP_STATUS_FSFREQ_RETRY; | 
|  | break; | 
|  |  | 
|  | case FSF_ADAPTER_STATUS_AVAILABLE: | 
|  | switch (header->fsf_status_qual.word[0]) { | 
|  | case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: | 
|  | /* re-establish link to port */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ltest"); | 
|  | zfcp_test_link(unit->port); | 
|  | break; | 
|  | case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: | 
|  | /* FIXME(hw) need proper specs for proper action */ | 
|  | /* let scsi stack deal with retries and escalation */ | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 1, | 
|  | "fsf_sq_ulp"); | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL | 
|  | ("Unknown status qualifier 0x%x arrived.\n", | 
|  | header->fsf_status_qual.word[0]); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, | 
|  | "fsf_sq_inval:"); | 
|  | debug_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | &header->fsf_status_qual.word[0], | 
|  | sizeof(u32)); | 
|  | break; | 
|  | } | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | break; | 
|  |  | 
|  | case FSF_GOOD: | 
|  | break; | 
|  |  | 
|  | case FSF_FCP_RSP_AVAILABLE: | 
|  | break; | 
|  |  | 
|  | default: | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); | 
|  | debug_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | &header->fsf_status, sizeof(u32)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) { | 
|  | retval = | 
|  | zfcp_fsf_send_fcp_command_task_management_handler(fsf_req); | 
|  | } else { | 
|  | retval = zfcp_fsf_send_fcp_command_task_handler(fsf_req); | 
|  | fsf_req->unit = NULL; | 
|  | zfcp_unit_put(unit); | 
|  | } | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_send_fcp_command_task_handler | 
|  | * | 
|  | * purpose:	evaluates FCP_RSP IU | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = 0; | 
|  | struct scsi_cmnd *scpnt; | 
|  | struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *) | 
|  | &(fsf_req->qtcb->bottom.io.fcp_rsp); | 
|  | struct fcp_cmnd_iu *fcp_cmnd_iu = (struct fcp_cmnd_iu *) | 
|  | &(fsf_req->qtcb->bottom.io.fcp_cmnd); | 
|  | u32 sns_len; | 
|  | char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu); | 
|  | unsigned long flags; | 
|  | struct zfcp_unit *unit = fsf_req->unit; | 
|  |  | 
|  | read_lock_irqsave(&fsf_req->adapter->abort_lock, flags); | 
|  | scpnt = (struct scsi_cmnd *) fsf_req->data; | 
|  | if (unlikely(!scpnt)) { | 
|  | ZFCP_LOG_DEBUG | 
|  | ("Command with fsf_req %p is not associated to " | 
|  | "a scsi command anymore. Aborted?\n", fsf_req); | 
|  | goto out; | 
|  | } | 
|  | if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTED)) { | 
|  | /* FIXME: (design) mid-layer should handle DID_ABORT like | 
|  | *        DID_SOFT_ERROR by retrying the request for devices | 
|  | *        that allow retries. | 
|  | */ | 
|  | ZFCP_LOG_DEBUG("Setting DID_SOFT_ERROR and SUGGEST_RETRY\n"); | 
|  | set_host_byte(&scpnt->result, DID_SOFT_ERROR); | 
|  | set_driver_byte(&scpnt->result, SUGGEST_RETRY); | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) { | 
|  | ZFCP_LOG_DEBUG("Setting DID_ERROR\n"); | 
|  | set_host_byte(&scpnt->result, DID_ERROR); | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* set message byte of result in SCSI command */ | 
|  | scpnt->result |= COMMAND_COMPLETE << 8; | 
|  |  | 
|  | /* | 
|  | * copy SCSI status code of FCP_STATUS of FCP_RSP IU to status byte | 
|  | * of result in SCSI command | 
|  | */ | 
|  | scpnt->result |= fcp_rsp_iu->scsi_status; | 
|  | if (unlikely(fcp_rsp_iu->scsi_status)) { | 
|  | /* DEBUG */ | 
|  | ZFCP_LOG_DEBUG("status for SCSI Command:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | scpnt->cmnd, scpnt->cmd_len); | 
|  | ZFCP_LOG_DEBUG("SCSI status code 0x%x\n", | 
|  | fcp_rsp_iu->scsi_status); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (void *) fcp_rsp_iu, sizeof (struct fcp_rsp_iu)); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), | 
|  | fcp_rsp_iu->fcp_sns_len); | 
|  | } | 
|  |  | 
|  | /* check FCP_RSP_INFO */ | 
|  | if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) { | 
|  | ZFCP_LOG_DEBUG("rsp_len is valid\n"); | 
|  | switch (fcp_rsp_info[3]) { | 
|  | case RSP_CODE_GOOD: | 
|  | /* ok, continue */ | 
|  | ZFCP_LOG_TRACE("no failure or Task Management " | 
|  | "Function complete\n"); | 
|  | set_host_byte(&scpnt->result, DID_OK); | 
|  | break; | 
|  | case RSP_CODE_LENGTH_MISMATCH: | 
|  | /* hardware bug */ | 
|  | ZFCP_LOG_NORMAL("bug: FCP response code indictates " | 
|  | "that the fibrechannel protocol data " | 
|  | "length differs from the burst length. " | 
|  | "The problem occured on unit 0x%016Lx " | 
|  | "on port 0x%016Lx on adapter %s", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | /* dump SCSI CDB as prepared by zfcp */ | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &fsf_req->qtcb-> | 
|  | bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); | 
|  | set_host_byte(&scpnt->result, DID_ERROR); | 
|  | goto skip_fsfstatus; | 
|  | case RSP_CODE_FIELD_INVALID: | 
|  | /* driver or hardware bug */ | 
|  | ZFCP_LOG_NORMAL("bug: FCP response code indictates " | 
|  | "that the fibrechannel protocol data " | 
|  | "fields were incorrectly set up. " | 
|  | "The problem occured on the unit " | 
|  | "0x%016Lx on port 0x%016Lx on " | 
|  | "adapter %s", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | /* dump SCSI CDB as prepared by zfcp */ | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &fsf_req->qtcb-> | 
|  | bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); | 
|  | set_host_byte(&scpnt->result, DID_ERROR); | 
|  | goto skip_fsfstatus; | 
|  | case RSP_CODE_RO_MISMATCH: | 
|  | /* hardware bug */ | 
|  | ZFCP_LOG_NORMAL("bug: The FCP response code indicates " | 
|  | "that conflicting  values for the " | 
|  | "fibrechannel payload offset from the " | 
|  | "header were found. " | 
|  | "The problem occured on unit 0x%016Lx " | 
|  | "on port 0x%016Lx on adapter %s.\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | /* dump SCSI CDB as prepared by zfcp */ | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &fsf_req->qtcb-> | 
|  | bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); | 
|  | set_host_byte(&scpnt->result, DID_ERROR); | 
|  | goto skip_fsfstatus; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An invalid FCP response " | 
|  | "code was detected for a command. " | 
|  | "The problem occured on the unit " | 
|  | "0x%016Lx on port 0x%016Lx on " | 
|  | "adapter %s (debug info 0x%x)\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | fcp_rsp_info[3]); | 
|  | /* dump SCSI CDB as prepared by zfcp */ | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, | 
|  | (char *) &fsf_req->qtcb-> | 
|  | bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); | 
|  | set_host_byte(&scpnt->result, DID_ERROR); | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* check for sense data */ | 
|  | if (unlikely(fcp_rsp_iu->validity.bits.fcp_sns_len_valid)) { | 
|  | sns_len = FSF_FCP_RSP_SIZE - | 
|  | sizeof (struct fcp_rsp_iu) + fcp_rsp_iu->fcp_rsp_len; | 
|  | ZFCP_LOG_TRACE("room for %i bytes sense data in QTCB\n", | 
|  | sns_len); | 
|  | sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE); | 
|  | ZFCP_LOG_TRACE("room for %i bytes sense data in SCSI command\n", | 
|  | SCSI_SENSE_BUFFERSIZE); | 
|  | sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len); | 
|  | ZFCP_LOG_TRACE("scpnt->result =0x%x, command was:\n", | 
|  | scpnt->result); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, | 
|  | (void *) &scpnt->cmnd, scpnt->cmd_len); | 
|  |  | 
|  | ZFCP_LOG_TRACE("%i bytes sense data provided by FCP\n", | 
|  | fcp_rsp_iu->fcp_sns_len); | 
|  | memcpy(&scpnt->sense_buffer, | 
|  | zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, | 
|  | (void *) &scpnt->sense_buffer, sns_len); | 
|  | } | 
|  |  | 
|  | /* check for overrun */ | 
|  | if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_over)) { | 
|  | ZFCP_LOG_INFO("A data overrun was detected for a command. " | 
|  | "unit 0x%016Lx, port 0x%016Lx, adapter %s. " | 
|  | "The response data length is " | 
|  | "%d, the original length was %d.\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | fcp_rsp_iu->fcp_resid, | 
|  | (int) zfcp_get_fcp_dl(fcp_cmnd_iu)); | 
|  | } | 
|  |  | 
|  | /* check for underrun */ | 
|  | if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) { | 
|  | ZFCP_LOG_INFO("A data underrun was detected for a command. " | 
|  | "unit 0x%016Lx, port 0x%016Lx, adapter %s. " | 
|  | "The response data length is " | 
|  | "%d, the original length was %d.\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | fcp_rsp_iu->fcp_resid, | 
|  | (int) zfcp_get_fcp_dl(fcp_cmnd_iu)); | 
|  |  | 
|  | scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid); | 
|  | if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) < | 
|  | scpnt->underflow) | 
|  | set_host_byte(&scpnt->result, DID_ERROR); | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | ZFCP_LOG_DEBUG("scpnt->result =0x%x\n", scpnt->result); | 
|  |  | 
|  | if (scpnt->result != 0) | 
|  | zfcp_scsi_dbf_event_result("erro", 3, fsf_req->adapter, scpnt, fsf_req); | 
|  | else if (scpnt->retries > 0) | 
|  | zfcp_scsi_dbf_event_result("retr", 4, fsf_req->adapter, scpnt, fsf_req); | 
|  | else | 
|  | zfcp_scsi_dbf_event_result("norm", 6, fsf_req->adapter, scpnt, fsf_req); | 
|  |  | 
|  | /* cleanup pointer (need this especially for abort) */ | 
|  | scpnt->host_scribble = NULL; | 
|  |  | 
|  | /* always call back */ | 
|  | (scpnt->scsi_done) (scpnt); | 
|  |  | 
|  | /* | 
|  | * We must hold this lock until scsi_done has been called. | 
|  | * Otherwise we may call scsi_done after abort regarding this | 
|  | * command has completed. | 
|  | * Note: scsi_done must not block! | 
|  | */ | 
|  | out: | 
|  | read_unlock_irqrestore(&fsf_req->adapter->abort_lock, flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_send_fcp_command_task_management_handler | 
|  | * | 
|  | * purpose:	evaluates FCP_RSP IU | 
|  | * | 
|  | * returns: | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_send_fcp_command_task_management_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | int retval = 0; | 
|  | struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *) | 
|  | &(fsf_req->qtcb->bottom.io.fcp_rsp); | 
|  | char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu); | 
|  | struct zfcp_unit *unit = (struct zfcp_unit *) fsf_req->data; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | /* check FCP_RSP_INFO */ | 
|  | switch (fcp_rsp_info[3]) { | 
|  | case RSP_CODE_GOOD: | 
|  | /* ok, continue */ | 
|  | ZFCP_LOG_DEBUG("no failure or Task Management " | 
|  | "Function complete\n"); | 
|  | break; | 
|  | case RSP_CODE_TASKMAN_UNSUPP: | 
|  | ZFCP_LOG_NORMAL("bug: A reuested task management function " | 
|  | "is not supported on the target device " | 
|  | "unit 0x%016Lx, port 0x%016Lx, adapter %s\n ", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP; | 
|  | break; | 
|  | case RSP_CODE_TASKMAN_FAILED: | 
|  | ZFCP_LOG_NORMAL("bug: A reuested task management function " | 
|  | "failed to complete successfully. " | 
|  | "unit 0x%016Lx, port 0x%016Lx, adapter %s.\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; | 
|  | break; | 
|  | default: | 
|  | ZFCP_LOG_NORMAL("bug: An invalid FCP response " | 
|  | "code was detected for a command. " | 
|  | "unit 0x%016Lx, port 0x%016Lx, adapter %s " | 
|  | "(debug info 0x%x)\n", | 
|  | unit->fcp_lun, | 
|  | unit->port->wwpn, | 
|  | zfcp_get_busid_by_unit(unit), | 
|  | fcp_rsp_info[3]); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | return retval; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_control_file | 
|  | * | 
|  | * purpose:     Initiator of the control file upload/download FSF requests | 
|  | * | 
|  | * returns:     0           - FSF request is successfuly created and queued | 
|  | *              -EOPNOTSUPP - The FCP adapter does not have Control File support | 
|  | *              -EINVAL     - Invalid direction specified | 
|  | *              -ENOMEM     - Insufficient memory | 
|  | *              -EPERM      - Cannot create FSF request or place it in QDIO queue | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_control_file(struct zfcp_adapter *adapter, | 
|  | struct zfcp_fsf_req **fsf_req_ptr, | 
|  | u32 fsf_command, | 
|  | u32 option, | 
|  | struct zfcp_sg_list *sg_list) | 
|  | { | 
|  | struct zfcp_fsf_req *fsf_req; | 
|  | struct fsf_qtcb_bottom_support *bottom; | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | unsigned long lock_flags; | 
|  | int req_flags = 0; | 
|  | int direction; | 
|  | int retval = 0; | 
|  |  | 
|  | if (!(adapter->adapter_features & FSF_FEATURE_CFDC)) { | 
|  | ZFCP_LOG_INFO("cfdc not supported (adapter %s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | retval = -EOPNOTSUPP; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | switch (fsf_command) { | 
|  |  | 
|  | case FSF_QTCB_DOWNLOAD_CONTROL_FILE: | 
|  | direction = SBAL_FLAGS0_TYPE_WRITE; | 
|  | if ((option != FSF_CFDC_OPTION_FULL_ACCESS) && | 
|  | (option != FSF_CFDC_OPTION_RESTRICTED_ACCESS)) | 
|  | req_flags = ZFCP_WAIT_FOR_SBAL; | 
|  | break; | 
|  |  | 
|  | case FSF_QTCB_UPLOAD_CONTROL_FILE: | 
|  | direction = SBAL_FLAGS0_TYPE_READ; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command); | 
|  | retval = -EINVAL; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | retval = zfcp_fsf_req_create(adapter, fsf_command, req_flags, | 
|  | NULL, &lock_flags, &fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("error: Could not create FSF request for the " | 
|  | "adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | retval = -EPERM; | 
|  | goto unlock_queue_lock; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  | sbale[0].flags |= direction; | 
|  |  | 
|  | bottom = &fsf_req->qtcb->bottom.support; | 
|  | bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE; | 
|  | bottom->option = option; | 
|  |  | 
|  | if (sg_list->count > 0) { | 
|  | int bytes; | 
|  |  | 
|  | bytes = zfcp_qdio_sbals_from_sg(fsf_req, direction, | 
|  | sg_list->sg, sg_list->count, | 
|  | ZFCP_MAX_SBALS_PER_REQ); | 
|  | if (bytes != ZFCP_CFDC_MAX_CONTROL_FILE_SIZE) { | 
|  | ZFCP_LOG_INFO( | 
|  | "error: Could not create sufficient number of " | 
|  | "SBALS for an FSF request to the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | retval = -ENOMEM; | 
|  | goto free_fsf_req; | 
|  | } | 
|  | } else | 
|  | sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; | 
|  |  | 
|  | zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT); | 
|  | retval = zfcp_fsf_req_send(fsf_req); | 
|  | if (retval < 0) { | 
|  | ZFCP_LOG_INFO("initiation of cfdc up/download failed" | 
|  | "(adapter %s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | retval = -EPERM; | 
|  | goto free_fsf_req; | 
|  | } | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  |  | 
|  | ZFCP_LOG_NORMAL("Control file %s FSF request has been sent to the " | 
|  | "adapter %s\n", | 
|  | fsf_command == FSF_QTCB_DOWNLOAD_CONTROL_FILE ? | 
|  | "download" : "upload", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  |  | 
|  | wait_event(fsf_req->completion_wq, | 
|  | fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); | 
|  |  | 
|  | *fsf_req_ptr = fsf_req; | 
|  | goto out; | 
|  |  | 
|  | free_fsf_req: | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | unlock_queue_lock: | 
|  | write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); | 
|  | out: | 
|  | return retval; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_control_file_handler | 
|  | * | 
|  | * purpose:     Handler of the control file upload/download FSF requests | 
|  | * | 
|  | * returns:     0       - FSF request successfuly processed | 
|  | *              -EAGAIN - Operation has to be repeated because of a temporary problem | 
|  | *              -EACCES - There is no permission to execute an operation | 
|  | *              -EPERM  - The control file is not in a right format | 
|  | *              -EIO    - There is a problem with the FCP adapter | 
|  | *              -EINVAL - Invalid operation | 
|  | *              -EFAULT - User space memory I/O operation fault | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_control_file_handler(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct zfcp_adapter *adapter = fsf_req->adapter; | 
|  | struct fsf_qtcb_header *header = &fsf_req->qtcb->header; | 
|  | struct fsf_qtcb_bottom_support *bottom = &fsf_req->qtcb->bottom.support; | 
|  | int retval = 0; | 
|  |  | 
|  | if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { | 
|  | retval = -EINVAL; | 
|  | goto skip_fsfstatus; | 
|  | } | 
|  |  | 
|  | switch (header->fsf_status) { | 
|  |  | 
|  | case FSF_GOOD: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "The FSF request has been successfully completed " | 
|  | "on the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  |  | 
|  | case FSF_OPERATION_PARTIALLY_SUCCESSFUL: | 
|  | if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) { | 
|  | switch (header->fsf_status_qual.word[0]) { | 
|  |  | 
|  | case FSF_SQ_CFDC_HARDENED_ON_SE: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "CFDC on the adapter %s has being " | 
|  | "hardened on primary and secondary SE\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  |  | 
|  | case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "CFDC of the adapter %s could not " | 
|  | "be saved on the SE\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  |  | 
|  | case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "CFDC of the adapter %s could not " | 
|  | "be copied to the secondary SE\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "CFDC could not be hardened " | 
|  | "on the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | } | 
|  | } | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EAGAIN; | 
|  | break; | 
|  |  | 
|  | case FSF_AUTHORIZATION_FAILURE: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "Adapter %s does not accept privileged commands\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EACCES; | 
|  | break; | 
|  |  | 
|  | case FSF_CFDC_ERROR_DETECTED: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "Error at position %d in the CFDC, " | 
|  | "CFDC is discarded by the adapter %s\n", | 
|  | header->fsf_status_qual.word[0], | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EPERM; | 
|  | break; | 
|  |  | 
|  | case FSF_CONTROL_FILE_UPDATE_ERROR: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "Adapter %s cannot harden the control file, " | 
|  | "file is discarded\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EIO; | 
|  | break; | 
|  |  | 
|  | case FSF_CONTROL_FILE_TOO_LARGE: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "Control file is too large, file is discarded " | 
|  | "by the adapter %s\n", | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EIO; | 
|  | break; | 
|  |  | 
|  | case FSF_ACCESS_CONFLICT_DETECTED: | 
|  | if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) | 
|  | ZFCP_LOG_NORMAL( | 
|  | "CFDC has been discarded by the adapter %s, " | 
|  | "because activation would impact " | 
|  | "%d active connection(s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | header->fsf_status_qual.word[0]); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EIO; | 
|  | break; | 
|  |  | 
|  | case FSF_CONFLICTS_OVERRULED: | 
|  | if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) | 
|  | ZFCP_LOG_NORMAL( | 
|  | "CFDC has been activated on the adapter %s, " | 
|  | "but activation has impacted " | 
|  | "%d active connection(s)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | header->fsf_status_qual.word[0]); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EIO; | 
|  | break; | 
|  |  | 
|  | case FSF_UNKNOWN_OP_SUBTYPE: | 
|  | ZFCP_LOG_NORMAL("unknown operation subtype (adapter: %s, " | 
|  | "op_subtype=0x%x)\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | bottom->operation_subtype); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EINVAL; | 
|  | break; | 
|  |  | 
|  | case FSF_INVALID_COMMAND_OPTION: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "Invalid option 0x%x has been specified " | 
|  | "in QTCB bottom sent to the adapter %s\n", | 
|  | bottom->option, | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EINVAL; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ZFCP_LOG_NORMAL( | 
|  | "bug: An unknown/unexpected FSF status 0x%08x " | 
|  | "was presented on the adapter %s\n", | 
|  | header->fsf_status, | 
|  | zfcp_get_busid_by_adapter(adapter)); | 
|  | debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval"); | 
|  | debug_exception(fsf_req->adapter->erp_dbf, 0, | 
|  | &header->fsf_status_qual.word[0], sizeof(u32)); | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; | 
|  | retval = -EINVAL; | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip_fsfstatus: | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static inline int | 
|  | zfcp_fsf_req_sbal_check(unsigned long *flags, | 
|  | struct zfcp_qdio_queue *queue, int needed) | 
|  | { | 
|  | write_lock_irqsave(&queue->queue_lock, *flags); | 
|  | if (likely(atomic_read(&queue->free_count) >= needed)) | 
|  | return 1; | 
|  | write_unlock_irqrestore(&queue->queue_lock, *flags); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * set qtcb pointer in fsf_req and initialize QTCB | 
|  | */ | 
|  | static void | 
|  | zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | if (likely(fsf_req->qtcb != NULL)) { | 
|  | fsf_req->qtcb->prefix.req_seq_no = | 
|  | fsf_req->adapter->fsf_req_seq_no; | 
|  | fsf_req->qtcb->prefix.req_id = fsf_req->req_id; | 
|  | fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION; | 
|  | fsf_req->qtcb->prefix.qtcb_type = | 
|  | fsf_qtcb_type[fsf_req->fsf_command]; | 
|  | fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION; | 
|  | fsf_req->qtcb->header.req_handle = fsf_req->req_id; | 
|  | fsf_req->qtcb->header.fsf_command = fsf_req->fsf_command; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * zfcp_fsf_req_sbal_get - try to get one SBAL in the request queue | 
|  | * @adapter: adapter for which request queue is examined | 
|  | * @req_flags: flags indicating whether to wait for needed SBAL or not | 
|  | * @lock_flags: lock_flags if queue_lock is taken | 
|  | * Return: 0 on success, otherwise -EIO, or -ERESTARTSYS | 
|  | * Locks: lock adapter->request_queue->queue_lock on success | 
|  | */ | 
|  | static int | 
|  | zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter, int req_flags, | 
|  | unsigned long *lock_flags) | 
|  | { | 
|  | long ret; | 
|  | struct zfcp_qdio_queue *req_queue = &adapter->request_queue; | 
|  |  | 
|  | if (unlikely(req_flags & ZFCP_WAIT_FOR_SBAL)) { | 
|  | ret = wait_event_interruptible_timeout(adapter->request_wq, | 
|  | zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1), | 
|  | ZFCP_SBAL_TIMEOUT); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | if (!ret) | 
|  | return -EIO; | 
|  | } else if (!zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1)) | 
|  | return -EIO; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_req_create | 
|  | * | 
|  | * purpose:	create an FSF request at the specified adapter and | 
|  | *		setup common fields | 
|  | * | 
|  | * returns:	-ENOMEM if there was insufficient memory for a request | 
|  | *              -EIO if no qdio buffers could be allocate to the request | 
|  | *              -EINVAL/-EPERM on bug conditions in req_dequeue | 
|  | *              0 in success | 
|  | * | 
|  | * note:        The created request is returned by reference. | 
|  | * | 
|  | * locks:	lock of concerned request queue must not be held, | 
|  | *		but is held on completion (write, irqsave) | 
|  | */ | 
|  | int | 
|  | zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags, | 
|  | mempool_t *pool, unsigned long *lock_flags, | 
|  | struct zfcp_fsf_req **fsf_req_p) | 
|  | { | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | struct zfcp_fsf_req *fsf_req = NULL; | 
|  | int ret = 0; | 
|  | struct zfcp_qdio_queue *req_queue = &adapter->request_queue; | 
|  |  | 
|  | /* allocate new FSF request */ | 
|  | fsf_req = zfcp_fsf_req_alloc(pool, req_flags); | 
|  | if (unlikely(NULL == fsf_req)) { | 
|  | ZFCP_LOG_DEBUG("error: Could not put an FSF request into " | 
|  | "the outbound (send) queue.\n"); | 
|  | ret = -ENOMEM; | 
|  | goto failed_fsf_req; | 
|  | } | 
|  |  | 
|  | fsf_req->adapter = adapter; | 
|  | fsf_req->fsf_command = fsf_cmd; | 
|  | INIT_LIST_HEAD(&fsf_req->list); | 
|  | init_timer(&fsf_req->timer); | 
|  |  | 
|  | /* initialize waitqueue which may be used to wait on | 
|  | this request completion */ | 
|  | init_waitqueue_head(&fsf_req->completion_wq); | 
|  |  | 
|  | ret = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags); | 
|  | if (ret < 0) | 
|  | goto failed_sbals; | 
|  |  | 
|  | /* this is serialized (we are holding req_queue-lock of adapter) */ | 
|  | if (adapter->req_no == 0) | 
|  | adapter->req_no++; | 
|  | fsf_req->req_id = adapter->req_no++; | 
|  |  | 
|  | zfcp_fsf_req_qtcb_init(fsf_req); | 
|  |  | 
|  | /* | 
|  | * We hold queue_lock here. Check if QDIOUP is set and let request fail | 
|  | * if it is not set (see also *_open_qdio and *_close_qdio). | 
|  | */ | 
|  |  | 
|  | if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) { | 
|  | write_unlock_irqrestore(&req_queue->queue_lock, *lock_flags); | 
|  | ret = -EIO; | 
|  | goto failed_sbals; | 
|  | } | 
|  |  | 
|  | if (fsf_req->qtcb) { | 
|  | fsf_req->seq_no = adapter->fsf_req_seq_no; | 
|  | fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no; | 
|  | } | 
|  | fsf_req->sbal_number = 1; | 
|  | fsf_req->sbal_first = req_queue->free_index; | 
|  | fsf_req->sbal_curr = req_queue->free_index; | 
|  | fsf_req->sbale_curr = 1; | 
|  |  | 
|  | if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) { | 
|  | fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; | 
|  | } | 
|  |  | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); | 
|  |  | 
|  | /* setup common SBALE fields */ | 
|  | sbale[0].addr = (void *) fsf_req->req_id; | 
|  | sbale[0].flags |= SBAL_FLAGS0_COMMAND; | 
|  | if (likely(fsf_req->qtcb != NULL)) { | 
|  | sbale[1].addr = (void *) fsf_req->qtcb; | 
|  | sbale[1].length = sizeof(struct fsf_qtcb); | 
|  | } | 
|  |  | 
|  | ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n", | 
|  | fsf_req->sbal_number, fsf_req->sbal_first); | 
|  |  | 
|  | goto success; | 
|  |  | 
|  | failed_sbals: | 
|  | /* dequeue new FSF request previously enqueued */ | 
|  | zfcp_fsf_req_free(fsf_req); | 
|  | fsf_req = NULL; | 
|  |  | 
|  | failed_fsf_req: | 
|  | write_lock_irqsave(&req_queue->queue_lock, *lock_flags); | 
|  | success: | 
|  | *fsf_req_p = fsf_req; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * function:    zfcp_fsf_req_send | 
|  | * | 
|  | * purpose:	start transfer of FSF request via QDIO | 
|  | * | 
|  | * returns:	0 - request transfer succesfully started | 
|  | *		!0 - start of request transfer failed | 
|  | */ | 
|  | static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req) | 
|  | { | 
|  | struct zfcp_adapter *adapter; | 
|  | struct zfcp_qdio_queue *req_queue; | 
|  | volatile struct qdio_buffer_element *sbale; | 
|  | int inc_seq_no; | 
|  | int new_distance_from_int; | 
|  | u64 dbg_tmp[2]; | 
|  | int retval = 0; | 
|  |  | 
|  | adapter = fsf_req->adapter; | 
|  | req_queue = &adapter->request_queue, | 
|  |  | 
|  |  | 
|  | /* FIXME(debug): remove it later */ | 
|  | sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_first, 0); | 
|  | ZFCP_LOG_DEBUG("SBALE0 flags=0x%x\n", sbale[0].flags); | 
|  | ZFCP_LOG_TRACE("HEX DUMP OF SBALE1 PAYLOAD:\n"); | 
|  | ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) sbale[1].addr, | 
|  | sbale[1].length); | 
|  |  | 
|  | /* put allocated FSF request into hash table */ | 
|  | spin_lock(&adapter->req_list_lock); | 
|  | zfcp_reqlist_add(adapter, fsf_req); | 
|  | spin_unlock(&adapter->req_list_lock); | 
|  |  | 
|  | inc_seq_no = (fsf_req->qtcb != NULL); | 
|  |  | 
|  | ZFCP_LOG_TRACE("request queue of adapter %s: " | 
|  | "next free SBAL is %i, %i free SBALs\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | req_queue->free_index, | 
|  | atomic_read(&req_queue->free_count)); | 
|  |  | 
|  | ZFCP_LOG_DEBUG("calling do_QDIO adapter %s, flags=0x%x, queue_no=%i, " | 
|  | "index_in_queue=%i, count=%i, buffers=%p\n", | 
|  | zfcp_get_busid_by_adapter(adapter), | 
|  | QDIO_FLAG_SYNC_OUTPUT, | 
|  | 0, fsf_req->sbal_first, fsf_req->sbal_number, | 
|  | &req_queue->buffer[fsf_req->sbal_first]); | 
|  |  | 
|  | /* | 
|  | * adjust the number of free SBALs in request queue as well as | 
|  | * position of first one | 
|  | */ | 
|  | atomic_sub(fsf_req->sbal_number, &req_queue->free_count); | 
|  | ZFCP_LOG_TRACE("free_count=%d\n", atomic_read(&req_queue->free_count)); | 
|  | req_queue->free_index += fsf_req->sbal_number;	  /* increase */ | 
|  | req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;  /* wrap if needed */ | 
|  | new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req); | 
|  |  | 
|  | fsf_req->issued = get_clock(); | 
|  |  | 
|  | retval = do_QDIO(adapter->ccw_device, | 
|  | QDIO_FLAG_SYNC_OUTPUT, | 
|  | 0, fsf_req->sbal_first, fsf_req->sbal_number, NULL); | 
|  |  | 
|  | dbg_tmp[0] = (unsigned long) sbale[0].addr; | 
|  | dbg_tmp[1] = (u64) retval; | 
|  | debug_event(adapter->erp_dbf, 4, (void *) dbg_tmp, 16); | 
|  |  | 
|  | if (unlikely(retval)) { | 
|  | /* Queues are down..... */ | 
|  | retval = -EIO; | 
|  | del_timer(&fsf_req->timer); | 
|  | spin_lock(&adapter->req_list_lock); | 
|  | zfcp_reqlist_remove(adapter, fsf_req); | 
|  | spin_unlock(&adapter->req_list_lock); | 
|  | /* undo changes in request queue made for this request */ | 
|  | zfcp_qdio_zero_sbals(req_queue->buffer, | 
|  | fsf_req->sbal_first, fsf_req->sbal_number); | 
|  | atomic_add(fsf_req->sbal_number, &req_queue->free_count); | 
|  | req_queue->free_index -= fsf_req->sbal_number; | 
|  | req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q; | 
|  | req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */ | 
|  | zfcp_erp_adapter_reopen(adapter, 0); | 
|  | } else { | 
|  | req_queue->distance_from_int = new_distance_from_int; | 
|  | /* | 
|  | * increase FSF sequence counter - | 
|  | * this must only be done for request successfully enqueued to | 
|  | * QDIO this rejected requests may be cleaned up by calling | 
|  | * routines  resulting in missing sequence counter values | 
|  | * otherwise, | 
|  | */ | 
|  |  | 
|  | /* Don't increase for unsolicited status */ | 
|  | if (inc_seq_no) | 
|  | adapter->fsf_req_seq_no++; | 
|  |  | 
|  | /* count FSF requests pending */ | 
|  | atomic_inc(&adapter->reqs_active); | 
|  | } | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | #undef ZFCP_LOG_AREA |