diff options
| author | James Bottomley <JBottomley@Odin.com> | 2015-11-12 07:06:18 -0500 |
|---|---|---|
| committer | James Bottomley <JBottomley@Odin.com> | 2015-11-12 07:06:18 -0500 |
| commit | febdfbd2137a5727f70dfbf920105c07e6c2a21e (patch) | |
| tree | 9483a5493ad3e08626e1f53ded594f88a6f4e710 /drivers/scsi/mpt2sas/mpt2sas_base.c | |
| parent | 0da39687a15403251bdfd1c6fb18025c0607326b (diff) | |
| parent | 2c5d16d6a9e7218e57b716e4fd9d77c776d21471 (diff) | |
| download | linux-febdfbd2137a5727f70dfbf920105c07e6c2a21e.tar.gz linux-febdfbd2137a5727f70dfbf920105c07e6c2a21e.tar.bz2 linux-febdfbd2137a5727f70dfbf920105c07e6c2a21e.zip | |
Merge tag '4.4-scsi-mkp' into misc
SCSI queue for 4.4.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/mpt2sas/mpt2sas_base.c')
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_base.c | 4899 |
1 files changed, 0 insertions, 4899 deletions
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c deleted file mode 100644 index c167911221e9..000000000000 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ /dev/null @@ -1,4899 +0,0 @@ -/* - * This is the Fusion MPT base driver providing common API layer interface - * for access to MPT (Message Passing Technology) firmware. - * - * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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 - * of the License, 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. - * - * NO WARRANTY - * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT - * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is - * solely responsible for determining the appropriateness of using and - * distributing the Program and assumes all risks associated with its - * exercise of rights under this Agreement, including but not limited to - * the risks and costs of program errors, damage to or loss of data, - * programs or equipment, and unavailability or interruption of operations. - - * DISCLAIMER OF LIABILITY - * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED - * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES - - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/pci.h> -#include <linux/kdev_t.h> -#include <linux/blkdev.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/dma-mapping.h> -#include <linux/sort.h> -#include <linux/io.h> -#include <linux/time.h> -#include <linux/kthread.h> -#include <linux/aer.h> - -#include "mpt2sas_base.h" - -static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; - -#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ - -#define MAX_HBA_QUEUE_DEPTH 30000 -#define MAX_CHAIN_DEPTH 100000 -static int max_queue_depth = -1; -module_param(max_queue_depth, int, 0); -MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); - -static int max_sgl_entries = -1; -module_param(max_sgl_entries, int, 0); -MODULE_PARM_DESC(max_sgl_entries, " max sg entries "); - -static int msix_disable = -1; -module_param(msix_disable, int, 0); -MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); - -static int max_msix_vectors = -1; -module_param(max_msix_vectors, int, 0); -MODULE_PARM_DESC(max_msix_vectors, " max msix vectors "); - -static int mpt2sas_fwfault_debug; -MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault " - "and halt firmware - (default=0)"); - -static int disable_discovery = -1; -module_param(disable_discovery, int, 0); -MODULE_PARM_DESC(disable_discovery, " disable discovery "); - -static int -_base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag); - -static int -_base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag); - -/** - * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. - * - */ -static int -_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) -{ - int ret = param_set_int(val, kp); - struct MPT2SAS_ADAPTER *ioc; - - if (ret) - return ret; - - /* global ioc spinlock to protect controller list on list operations */ - printk(KERN_INFO "setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug); - spin_lock(&gioc_lock); - list_for_each_entry(ioc, &mpt2sas_ioc_list, list) - ioc->fwfault_debug = mpt2sas_fwfault_debug; - spin_unlock(&gioc_lock); - return 0; -} - -module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug, - param_get_int, &mpt2sas_fwfault_debug, 0644); - -/** - * mpt2sas_remove_dead_ioc_func - kthread context to remove dead ioc - * @arg: input argument, used to derive ioc - * - * Return 0 if controller is removed from pci subsystem. - * Return -1 for other case. - */ -static int mpt2sas_remove_dead_ioc_func(void *arg) -{ - struct MPT2SAS_ADAPTER *ioc = (struct MPT2SAS_ADAPTER *)arg; - struct pci_dev *pdev; - - if ((ioc == NULL)) - return -1; - - pdev = ioc->pdev; - if ((pdev == NULL)) - return -1; - pci_stop_and_remove_bus_device_locked(pdev); - return 0; -} - - -/** - * _base_fault_reset_work - workq handling ioc fault conditions - * @work: input argument, used to derive ioc - * Context: sleep. - * - * Return nothing. - */ -static void -_base_fault_reset_work(struct work_struct *work) -{ - struct MPT2SAS_ADAPTER *ioc = - container_of(work, struct MPT2SAS_ADAPTER, fault_reset_work.work); - unsigned long flags; - u32 doorbell; - int rc; - struct task_struct *p; - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->shost_recovery || ioc->pci_error_recovery) - goto rearm_timer; - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - - doorbell = mpt2sas_base_get_iocstate(ioc, 0); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) { - printk(MPT2SAS_INFO_FMT "%s : SAS host is non-operational !!!!\n", - ioc->name, __func__); - - /* It may be possible that EEH recovery can resolve some of - * pci bus failure issues rather removing the dead ioc function - * by considering controller is in a non-operational state. So - * here priority is given to the EEH recovery. If it doesn't - * not resolve this issue, mpt2sas driver will consider this - * controller to non-operational state and remove the dead ioc - * function. - */ - if (ioc->non_operational_loop++ < 5) { - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, - flags); - goto rearm_timer; - } - - /* - * Call _scsih_flush_pending_cmds callback so that we flush all - * pending commands back to OS. This call is required to aovid - * deadlock at block layer. Dead IOC will fail to do diag reset, - * and this call is safe since dead ioc will never return any - * command back from HW. - */ - ioc->schedule_dead_ioc_flush_running_cmds(ioc); - /* - * Set remove_host flag early since kernel thread will - * take some time to execute. - */ - ioc->remove_host = 1; - /*Remove the Dead Host */ - p = kthread_run(mpt2sas_remove_dead_ioc_func, ioc, - "mpt2sas_dead_ioc_%d", ioc->id); - if (IS_ERR(p)) { - printk(MPT2SAS_ERR_FMT - "%s: Running mpt2sas_dead_ioc thread failed !!!!\n", - ioc->name, __func__); - } else { - printk(MPT2SAS_ERR_FMT - "%s: Running mpt2sas_dead_ioc thread success !!!!\n", - ioc->name, __func__); - } - - return; /* don't rearm timer */ - } - - ioc->non_operational_loop = 0; - - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - printk(MPT2SAS_WARN_FMT "%s: hard reset: %s\n", ioc->name, - __func__, (rc == 0) ? "success" : "failed"); - doorbell = mpt2sas_base_get_iocstate(ioc, 0); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) - mpt2sas_base_fault_info(ioc, doorbell & - MPI2_DOORBELL_DATA_MASK); - } - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - rearm_timer: - if (ioc->fault_reset_work_q) - queue_delayed_work(ioc->fault_reset_work_q, - &ioc->fault_reset_work, - msecs_to_jiffies(FAULT_POLLING_INTERVAL)); - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); -} - -/** - * mpt2sas_base_start_watchdog - start the fault_reset_work_q - * @ioc: per adapter object - * Context: sleep. - * - * Return nothing. - */ -void -mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc) -{ - unsigned long flags; - - if (ioc->fault_reset_work_q) - return; - - /* initialize fault polling */ - INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); - snprintf(ioc->fault_reset_work_q_name, - sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id); - ioc->fault_reset_work_q = - create_singlethread_workqueue(ioc->fault_reset_work_q_name); - if (!ioc->fault_reset_work_q) { - printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n", - ioc->name, __func__, __LINE__); - return; - } - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->fault_reset_work_q) - queue_delayed_work(ioc->fault_reset_work_q, - &ioc->fault_reset_work, - msecs_to_jiffies(FAULT_POLLING_INTERVAL)); - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); -} - -/** - * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q - * @ioc: per adapter object - * Context: sleep. - * - * Return nothing. - */ -void -mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc) -{ - unsigned long flags; - struct workqueue_struct *wq; - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - wq = ioc->fault_reset_work_q; - ioc->fault_reset_work_q = NULL; - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - if (wq) { - if (!cancel_delayed_work_sync(&ioc->fault_reset_work)) - flush_workqueue(wq); - destroy_workqueue(wq); - } -} - -/** - * mpt2sas_base_fault_info - verbose translation of firmware FAULT code - * @ioc: per adapter object - * @fault_code: fault code - * - * Return nothing. - */ -void -mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code) -{ - printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n", - ioc->name, fault_code); -} - -/** - * mpt2sas_halt_firmware - halt's mpt controller firmware - * @ioc: per adapter object - * - * For debugging timeout related issues. Writing 0xCOFFEE00 - * to the doorbell register will halt controller firmware. With - * the purpose to stop both driver and firmware, the enduser can - * obtain a ring buffer from controller UART. - */ -void -mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc) -{ - u32 doorbell; - - if (!ioc->fwfault_debug) - return; - - dump_stack(); - - doorbell = readl(&ioc->chip->Doorbell); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) - mpt2sas_base_fault_info(ioc , doorbell); - else { - writel(0xC0FFEE00, &ioc->chip->Doorbell); - printk(MPT2SAS_ERR_FMT "Firmware is halted due to command " - "timeout\n", ioc->name); - } - - panic("panic in %s\n", __func__); -} - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _base_sas_ioc_info - verbose translation of the ioc status - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @request_hdr: request mf - * - * Return nothing. - */ -static void -_base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, - MPI2RequestHeader_t *request_hdr) -{ - u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & - MPI2_IOCSTATUS_MASK; - char *desc = NULL; - u16 frame_sz; - char *func_str = NULL; - - /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */ - if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || - request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || - request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) - return; - - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) - return; - - switch (ioc_status) { - -/**************************************************************************** -* Common IOCStatus values for all replies -****************************************************************************/ - - case MPI2_IOCSTATUS_INVALID_FUNCTION: - desc = "invalid function"; - break; - case MPI2_IOCSTATUS_BUSY: - desc = "busy"; - break; - case MPI2_IOCSTATUS_INVALID_SGL: - desc = "invalid sgl"; - break; - case MPI2_IOCSTATUS_INTERNAL_ERROR: - desc = "internal error"; - break; - case MPI2_IOCSTATUS_INVALID_VPID: - desc = "invalid vpid"; - break; - case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: - desc = "insufficient resources"; - break; - case MPI2_IOCSTATUS_INVALID_FIELD: - desc = "invalid field"; - break; - case MPI2_IOCSTATUS_INVALID_STATE: - desc = "invalid state"; - break; - case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: - desc = "op state not supported"; - break; - -/**************************************************************************** -* Config IOCStatus values -****************************************************************************/ - - case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION: - desc = "config invalid action"; - break; - case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE: - desc = "config invalid type"; - break; - case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE: - desc = "config invalid page"; - break; - case MPI2_IOCSTATUS_CONFIG_INVALID_DATA: - desc = "config invalid data"; - break; - case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS: - desc = "config no defaults"; - break; - case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: - desc = "config cant commit"; - break; - -/**************************************************************************** -* SCSI IO Reply -****************************************************************************/ - - case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: - case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: - case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: - case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: - case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: - case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: - case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: - case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: - case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: - case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: - case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: - case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: - break; - -/**************************************************************************** -* For use by SCSI Initiator and SCSI Target end-to-end data protection -****************************************************************************/ - - case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: - desc = "eedp guard error"; - break; - case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: - desc = "eedp ref tag error"; - break; - case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: - desc = "eedp app tag error"; - break; - -/**************************************************************************** -* SCSI Target values -****************************************************************************/ - - case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX: - desc = "target invalid io index"; - break; - case MPI2_IOCSTATUS_TARGET_ABORTED: - desc = "target aborted"; - break; - case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE: - desc = "target no conn retryable"; - break; - case MPI2_IOCSTATUS_TARGET_NO_CONNECTION: - desc = "target no connection"; - break; - case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH: - desc = "target xfer count mismatch"; - break; - case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR: - desc = "target data offset error"; - break; - case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA: - desc = "target too much write data"; - break; - case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT: - desc = "target iu too short"; - break; - case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT: - desc = "target ack nak timeout"; - break; - case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED: - desc = "target nak received"; - break; - -/**************************************************************************** -* Serial Attached SCSI values -****************************************************************************/ - - case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED: - desc = "smp request failed"; - break; - case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN: - desc = "smp data overrun"; - break; - -/**************************************************************************** -* Diagnostic Buffer Post / Diagnostic Release values -****************************************************************************/ - - case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED: - desc = "diagnostic released"; - break; - default: - break; - } - - if (!desc) - return; - - switch (request_hdr->Function) { - case MPI2_FUNCTION_CONFIG: - frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size; - func_str = "config_page"; - break; - case MPI2_FUNCTION_SCSI_TASK_MGMT: - frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t); - func_str = "task_mgmt"; - break; - case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: - frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t); - func_str = "sas_iounit_ctl"; - break; - case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR: - frame_sz = sizeof(Mpi2SepRequest_t); - func_str = "enclosure"; - break; - case MPI2_FUNCTION_IOC_INIT: - frame_sz = sizeof(Mpi2IOCInitRequest_t); - func_str = "ioc_init"; - break; - case MPI2_FUNCTION_PORT_ENABLE: - frame_sz = sizeof(Mpi2PortEnableRequest_t); - func_str = "port_enable"; - break; - case MPI2_FUNCTION_SMP_PASSTHROUGH: - frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size; - func_str = "smp_passthru"; - break; - default: - frame_sz = 32; - func_str = "unknown"; - break; - } - - printk(MPT2SAS_WARN_FMT "ioc_status: %s(0x%04x), request(0x%p)," - " (%s)\n", ioc->name, desc, ioc_status, request_hdr, func_str); - - _debug_dump_mf(request_hdr, frame_sz/4); -} - -/** - * _base_display_event_data - verbose translation of firmware asyn events - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * - * Return nothing. - */ -static void -_base_display_event_data(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventNotificationReply_t *mpi_reply) -{ - char *desc = NULL; - u16 event; - - if (!(ioc->logging_level & MPT_DEBUG_EVENTS)) - return; - - event = le16_to_cpu(mpi_reply->Event); - - switch (event) { - case MPI2_EVENT_LOG_DATA: - desc = "Log Data"; - break; - case MPI2_EVENT_STATE_CHANGE: - desc = "Status Change"; - break; - case MPI2_EVENT_HARD_RESET_RECEIVED: - desc = "Hard Reset Received"; - break; - case MPI2_EVENT_EVENT_CHANGE: - desc = "Event Change"; - break; - case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: - desc = "Device Status Change"; - break; - case MPI2_EVENT_IR_OPERATION_STATUS: - if (!ioc->hide_ir_msg) - desc = "IR Operation Status"; - break; - case MPI2_EVENT_SAS_DISCOVERY: - { - Mpi2EventDataSasDiscovery_t *event_data = - (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData; - printk(MPT2SAS_INFO_FMT "Discovery: (%s)", ioc->name, - (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? - "start" : "stop"); - if (event_data->DiscoveryStatus) - printk("discovery_status(0x%08x)", - le32_to_cpu(event_data->DiscoveryStatus)); - printk("\n"); - return; - } - case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: - desc = "SAS Broadcast Primitive"; - break; - case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE: - desc = "SAS Init Device Status Change"; - break; - case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW: - desc = "SAS Init Table Overflow"; - break; - case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: - desc = "SAS Topology Change List"; - break; - case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: - desc = "SAS Enclosure Device Status Change"; - break; - case MPI2_EVENT_IR_VOLUME: - if (!ioc->hide_ir_msg) - desc = "IR Volume"; - break; - case MPI2_EVENT_IR_PHYSICAL_DISK: - if (!ioc->hide_ir_msg) - desc = "IR Physical Disk"; - break; - case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - if (!ioc->hide_ir_msg) - desc = "IR Configuration Change List"; - break; - case MPI2_EVENT_LOG_ENTRY_ADDED: - if (!ioc->hide_ir_msg) - desc = "Log Entry Added"; - break; - case MPI2_EVENT_TEMP_THRESHOLD: - desc = "Temperature Threshold"; - break; - } - - if (!desc) - return; - - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, desc); -} -#endif - -/** - * _base_sas_log_info - verbose translation of firmware log info - * @ioc: per adapter object - * @log_info: log info - * - * Return nothing. - */ -static void -_base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info) -{ - union loginfo_type { - u32 loginfo; - struct { - u32 subcode:16; - u32 code:8; - u32 originator:4; - u32 bus_type:4; - } dw; - }; - union loginfo_type sas_loginfo; - char *originator_str = NULL; - - sas_loginfo.loginfo = log_info; - if (sas_loginfo.dw.bus_type != 3 /*SAS*/) - return; - - /* each nexus loss loginfo */ - if (log_info == 0x31170000) - return; - - /* eat the loginfos associated with task aborts */ - if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info == - 0x31140000 || log_info == 0x31130000)) - return; - - switch (sas_loginfo.dw.originator) { - case 0: - originator_str = "IOP"; - break; - case 1: - originator_str = "PL"; - break; - case 2: - if (!ioc->hide_ir_msg) - originator_str = "IR"; - else - originator_str = "WarpDrive"; - break; - } - - printk(MPT2SAS_WARN_FMT "log_info(0x%08x): originator(%s), " - "code(0x%02x), sub_code(0x%04x)\n", ioc->name, log_info, - originator_str, sas_loginfo.dw.code, - sas_loginfo.dw.subcode); -} - -/** - * _base_display_reply_info - - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Return nothing. - */ -static void -_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - u16 ioc_status; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (unlikely(!mpi_reply)) { - printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - ioc_status = le16_to_cpu(mpi_reply->IOCStatus); -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if ((ioc_status & MPI2_IOCSTATUS_MASK) && - (ioc->logging_level & MPT_DEBUG_REPLY)) { - _base_sas_ioc_info(ioc , mpi_reply, - mpt2sas_base_get_msg_frame(ioc, smid)); - } -#endif - if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) - _base_sas_log_info(ioc, le32_to_cpu(mpi_reply->IOCLogInfo)); -} - -/** - * mpt2sas_base_done - base internal command completion routine - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -u8 -mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) - return 1; - - if (ioc->base_cmds.status == MPT2_CMD_NOT_USED) - return 1; - - ioc->base_cmds.status |= MPT2_CMD_COMPLETE; - if (mpi_reply) { - ioc->base_cmds.status |= MPT2_CMD_REPLY_VALID; - memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); - } - ioc->base_cmds.status &= ~MPT2_CMD_PENDING; - - complete(&ioc->base_cmds.done); - return 1; -} - -/** - * _base_async_event - main callback handler for firmware asyn events - * @ioc: per adapter object - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Returns void. - */ -static void -_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) -{ - Mpi2EventNotificationReply_t *mpi_reply; - Mpi2EventAckRequest_t *ack_request; - u16 smid; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (!mpi_reply) - return; - if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) - return; -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - _base_display_event_data(ioc, mpi_reply); -#endif - if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED)) - goto out; - smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - goto out; - } - - ack_request = mpt2sas_base_get_msg_frame(ioc, smid); - memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t)); - ack_request->Function = MPI2_FUNCTION_EVENT_ACK; - ack_request->Event = mpi_reply->Event; - ack_request->EventContext = mpi_reply->EventContext; - ack_request->VF_ID = 0; /* TODO */ - ack_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid); - - out: - - /* scsih callback handler */ - mpt2sas_scsih_event_callback(ioc, msix_index, reply); - - /* ctl callback handler */ - mpt2sas_ctl_event_callback(ioc, msix_index, reply); - - return; -} - -/** - * _base_get_cb_idx - obtain the callback index - * @ioc: per adapter object - * @smid: system request message index - * - * Return callback index. - */ -static u8 -_base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - int i; - u8 cb_idx; - - if (smid < ioc->hi_priority_smid) { - i = smid - 1; - cb_idx = ioc->scsi_lookup[i].cb_idx; - } else if (smid < ioc->internal_smid) { - i = smid - ioc->hi_priority_smid; - cb_idx = ioc->hpr_lookup[i].cb_idx; - } else if (smid <= ioc->hba_queue_depth) { - i = smid - ioc->internal_smid; - cb_idx = ioc->internal_lookup[i].cb_idx; - } else - cb_idx = 0xFF; - return cb_idx; -} - -/** - * _base_mask_interrupts - disable interrupts - * @ioc: per adapter object - * - * Disabling ResetIRQ, Reply and Doorbell Interrupts - * - * Return nothing. - */ -static void -_base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc) -{ - u32 him_register; - - ioc->mask_interrupts = 1; - him_register = readl(&ioc->chip->HostInterruptMask); - him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK; - writel(him_register, &ioc->chip->HostInterruptMask); - readl(&ioc->chip->HostInterruptMask); -} - -/** - * _base_unmask_interrupts - enable interrupts - * @ioc: per adapter object - * - * Enabling only Reply Interrupts - * - * Return nothing. - */ -static void -_base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc) -{ - u32 him_register; - - him_register = readl(&ioc->chip->HostInterruptMask); - him_register &= ~MPI2_HIM_RIM; - writel(him_register, &ioc->chip->HostInterruptMask); - ioc->mask_interrupts = 0; -} - -union reply_descriptor { - u64 word; - struct { - u32 low; - u32 high; - } u; -}; - -/** - * _base_interrupt - MPT adapter (IOC) specific interrupt handler. - * @irq: irq number (not used) - * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure - * @r: pt_regs pointer (not used) - * - * Return IRQ_HANDLE if processed, else IRQ_NONE. - */ -static irqreturn_t -_base_interrupt(int irq, void *bus_id) -{ - struct adapter_reply_queue *reply_q = bus_id; - union reply_descriptor rd; - u32 completed_cmds; - u8 request_desript_type; - u16 smid; - u8 cb_idx; - u32 reply; - u8 msix_index = reply_q->msix_index; - struct MPT2SAS_ADAPTER *ioc = reply_q->ioc; - Mpi2ReplyDescriptorsUnion_t *rpf; - u8 rc; - - if (ioc->mask_interrupts) - return IRQ_NONE; - - if (!atomic_add_unless(&reply_q->busy, 1, 1)) - return IRQ_NONE; - - rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index]; - request_desript_type = rpf->Default.ReplyFlags - & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; - if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { - atomic_dec(&reply_q->busy); - return IRQ_NONE; - } - - completed_cmds = 0; - cb_idx = 0xFF; - do { - rd.word = le64_to_cpu(rpf->Words); - if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX) - goto out; - reply = 0; - smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); - if (request_desript_type == - MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { - reply = le32_to_cpu - (rpf->AddressReply.ReplyFrameAddress); - if (reply > ioc->reply_dma_max_address || - reply < ioc->reply_dma_min_address) - reply = 0; - } else if (request_desript_type == - MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER) - goto next; - else if (request_desript_type == - MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS) - goto next; - if (smid) { - cb_idx = _base_get_cb_idx(ioc, smid); - if ((likely(cb_idx < MPT_MAX_CALLBACKS)) - && (likely(mpt_callbacks[cb_idx] != NULL))) { - rc = mpt_callbacks[cb_idx](ioc, smid, - msix_index, reply); - if (reply) - _base_display_reply_info(ioc, smid, - msix_index, reply); - if (rc) - mpt2sas_base_free_smid(ioc, smid); - } - } - if (!smid) - _base_async_event(ioc, msix_index, reply); - - /* reply free queue handling */ - if (reply) { - ioc->reply_free_host_index = - (ioc->reply_free_host_index == - (ioc->reply_free_queue_depth - 1)) ? - 0 : ioc->reply_free_host_index + 1; - ioc->reply_free[ioc->reply_free_host_index] = - cpu_to_le32(reply); - wmb(); - writel(ioc->reply_free_host_index, - &ioc->chip->ReplyFreeHostIndex); - } - - next: - - rpf->Words = cpu_to_le64(ULLONG_MAX); - reply_q->reply_post_host_index = - (reply_q->reply_post_host_index == - (ioc->reply_post_queue_depth - 1)) ? 0 : - reply_q->reply_post_host_index + 1; - request_desript_type = - reply_q->reply_post_free[reply_q->reply_post_host_index]. - Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; - completed_cmds++; - if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) - goto out; - if (!reply_q->reply_post_host_index) - rpf = reply_q->reply_post_free; - else - rpf++; - } while (1); - - out: - - if (!completed_cmds) { - atomic_dec(&reply_q->busy); - return IRQ_NONE; - } - wmb(); - if (ioc->is_warpdrive) { - writel(reply_q->reply_post_host_index, - ioc->reply_post_host_index[msix_index]); - atomic_dec(&reply_q->busy); - return IRQ_HANDLED; - } - writel(reply_q->reply_post_host_index | (msix_index << - MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex); - atomic_dec(&reply_q->busy); - return IRQ_HANDLED; -} - -/** - * _base_is_controller_msix_enabled - is controller support muli-reply queues - * @ioc: per adapter object - * - */ -static inline int -_base_is_controller_msix_enabled(struct MPT2SAS_ADAPTER *ioc) -{ - return (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable; -} - -/** - * mpt2sas_base_flush_reply_queues - flushing the MSIX reply queues - * @ioc: per adapter object - * Context: ISR conext - * - * Called when a Task Management request has completed. We want - * to flush the other reply queues so all the outstanding IO has been - * completed back to OS before we process the TM completetion. - * - * Return nothing. - */ -void -mpt2sas_base_flush_reply_queues(struct MPT2SAS_ADAPTER *ioc) -{ - struct adapter_reply_queue *reply_q; - - /* If MSIX capability is turned off - * then multi-queues are not enabled - */ - if (!_base_is_controller_msix_enabled(ioc)) - return; - - list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - if (ioc->shost_recovery) - return; - /* TMs are on msix_index == 0 */ - if (reply_q->msix_index == 0) - continue; - _base_interrupt(reply_q->vector, (void *)reply_q); - } -} - -/** - * mpt2sas_base_release_callback_handler - clear interrupt ca |
