📄 zfcp_fsf.c
字号:
/* * * linux/drivers/s390/scsi/zfcp_fsf.c * * FCP adapter driver for IBM eServer zSeries * * (C) Copyright IBM Corp. 2002, 2004 * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> * Aron Zeh * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * Andreas Herrmann <aherrman@de.ibm.com> * Volker Sameske <sameske@de.ibm.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#define ZFCP_FSF_C_REVISION "$Revision: 1.92 $"#include "zfcp_ext.h"static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *);static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *);static int zfcp_fsf_open_port_handler(struct zfcp_fsf_req *);static int zfcp_fsf_close_port_handler(struct zfcp_fsf_req *);static int zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *);static int zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *);static int zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *);static int zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *);static int zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *);static int zfcp_fsf_send_fcp_command_task_management_handler( struct zfcp_fsf_req *);static int zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *);static int zfcp_fsf_status_read_handler(struct zfcp_fsf_req *);static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *);static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *);static int zfcp_fsf_control_file_handler(struct zfcp_fsf_req *);static inline int zfcp_fsf_req_sbal_check( unsigned long *, struct zfcp_qdio_queue *, int);static inline int zfcp_use_one_sbal( struct scatterlist *, int, struct scatterlist *, int);static struct zfcp_fsf_req *zfcp_fsf_req_alloc(mempool_t *, int);static int zfcp_fsf_req_send(struct zfcp_fsf_req *, struct timer_list *);static int zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *);static int zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *);static int zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *);static void zfcp_fsf_link_down_info_eval(struct zfcp_adapter *, struct fsf_link_down_info *);static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *);static void zfcp_fsf_req_dismiss(struct zfcp_fsf_req *);/* association between FSF command and FSF QTCB type */static u32 fsf_qtcb_type[] = { [FSF_QTCB_FCP_CMND] = FSF_IO_COMMAND, [FSF_QTCB_ABORT_FCP_CMND] = FSF_SUPPORT_COMMAND, [FSF_QTCB_OPEN_PORT_WITH_DID] = FSF_SUPPORT_COMMAND, [FSF_QTCB_OPEN_LUN] = FSF_SUPPORT_COMMAND, [FSF_QTCB_CLOSE_LUN] = FSF_SUPPORT_COMMAND, [FSF_QTCB_CLOSE_PORT] = FSF_SUPPORT_COMMAND, [FSF_QTCB_CLOSE_PHYSICAL_PORT] = FSF_SUPPORT_COMMAND, [FSF_QTCB_SEND_ELS] = FSF_SUPPORT_COMMAND, [FSF_QTCB_SEND_GENERIC] = FSF_SUPPORT_COMMAND, [FSF_QTCB_EXCHANGE_CONFIG_DATA] = FSF_CONFIG_COMMAND, [FSF_QTCB_EXCHANGE_PORT_DATA] = FSF_PORT_COMMAND, [FSF_QTCB_DOWNLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND, [FSF_QTCB_UPLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND};static const char zfcp_act_subtable_type[5][8] = { "unknown", "OS", "WWPN", "DID", "LUN"};/****************************************************************//*************** FSF related Functions *************************//****************************************************************/#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF/* * function: zfcp_fsf_req_alloc * * purpose: Obtains an fsf_req and potentially a qtcb (for all but * unsolicited requests) via helper functions * Does some initial fsf request set-up. * * returns: pointer to allocated fsf_req if successfull * NULL otherwise * * locks: none * */static struct zfcp_fsf_req *zfcp_fsf_req_alloc(mempool_t *pool, int req_flags){ size_t size; void *ptr; struct zfcp_fsf_req *fsf_req = NULL; if (req_flags & ZFCP_REQ_NO_QTCB) size = sizeof(struct zfcp_fsf_req); else size = sizeof(struct zfcp_fsf_req_pool_element); if (likely(pool != NULL)) ptr = mempool_alloc(pool, GFP_ATOMIC); else ptr = kmalloc(size, GFP_ATOMIC); if (unlikely(NULL == ptr)) goto out; memset(ptr, 0, size); if (req_flags & ZFCP_REQ_NO_QTCB) { fsf_req = (struct zfcp_fsf_req *) ptr; } else { fsf_req = &((struct zfcp_fsf_req_pool_element *) ptr)->fsf_req; fsf_req->qtcb = &((struct zfcp_fsf_req_pool_element *) ptr)->qtcb; } fsf_req->pool = pool; out: return fsf_req;}/* * function: zfcp_fsf_req_free * * purpose: Frees the memory of an fsf_req (and potentially a qtcb) or * returns it into the pool via helper functions. * * returns: sod all * * locks: none */voidzfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req){ if (likely(fsf_req->pool != NULL)) mempool_free(fsf_req, fsf_req->pool); else kfree(fsf_req);}/* * function: * * purpose: * * returns: * * note: qdio queues shall be down (no ongoing inbound processing) */intzfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter){ struct zfcp_fsf_req *fsf_req, *tmp; unsigned long flags; LIST_HEAD(remove_queue); spin_lock_irqsave(&adapter->fsf_req_list_lock, flags); list_splice_init(&adapter->fsf_req_list_head, &remove_queue); atomic_set(&adapter->fsf_reqs_active, 0); spin_unlock_irqrestore(&adapter->fsf_req_list_lock, flags); list_for_each_entry_safe(fsf_req, tmp, &remove_queue, list) { list_del(&fsf_req->list); zfcp_fsf_req_dismiss(fsf_req); } return 0;}/* * function: * * purpose: * * returns: */static voidzfcp_fsf_req_dismiss(struct zfcp_fsf_req *fsf_req){ fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; zfcp_fsf_req_complete(fsf_req);}/* * function: zfcp_fsf_req_complete * * purpose: Updates active counts and timers for openfcp-reqs * May cleanup request after req_eval returns * * returns: 0 - success * !0 - failure * * context: */intzfcp_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 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 intzfcp_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 (unlikely(qtcb->header.log_length)) { /* do not trust them ;-) */ if (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 ((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); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -